Skip to content

Publish to TestPyPI

Publish to TestPyPI #4

Workflow file for this run

# =============================================================================
# Publish to TestPyPI
# Trigger: Manual workflow dispatch (workflow_dispatch)
# Features:
# 1. Build distribution package
# 2. Publish to TestPyPI (using API Token)
# 3. Verify installation
#
# Usage Instructions:
# 1. Prerequisites:
# - Generate API Token on TestPyPI account: https://test.pypi.org/manage/account/tokens/
# - In GitHub repository Settings > Secrets and variables > Actions, add:
# - Name: TEST_PYPI_API_TOKEN
# - Secret: <your-test-pypi-api-token>
# - Ensure TEST_PYPI_API_TOKEN secret is configured in the environment
#
# 2. Trigger Publishing:
# - Go to GitHub Actions page and select this workflow
# - Click "Run workflow"
# - Enter version tag (e.g., v1.0.0-test, v0.1.0rc1, v2.0.0.dev1)
# - Click "Run workflow"
#
# 3. The workflow will automatically:
# - Build sdist and wheel distributions
# - Publish to TestPyPI
# - Verify installation
# =============================================================================
name: Publish to TestPyPI
on:
workflow_dispatch:
inputs:
version_tag:
description: 'Version tag to test publish (e.g., v1.0.0-test, v0.1.0rc1, v2.0.0.dev1)'
required: true
type: string
jobs:
publish-test:
name: Publish to TestPyPI
runs-on: ubuntu-latest
environment: testpypi
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0 # setuptools-scm requires complete git history
- name: Check git tag exists (fail if not)
shell: bash
run: |
set -euo pipefail
TAG="${{ github.event.inputs.version_tag }}"
git fetch --tags --force
if git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null; then
echo "Tag already exists: ${TAG}"
else
echo "::error::Tag does not exist: ${TAG}"
exit 1
fi
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.8"
- name: Install build tools
run: |
python -m pip install --upgrade pip
pip install build twine
- name: Build package
run: python -m build
- name: Check package
run: twine check dist/*
- name: Publish to TestPyPI (using API Token)
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.TEST_PYPI_API_TOKEN }}
TWINE_REPOSITORY_URL: https://test.pypi.org/legacy/
TWINE_NON_INTERACTIVE: 1
run: twine upload dist/* --skip-existing
- name: Wait for package to be available
run: sleep 30
- name: Extract and validate version from tag
id: version
run: |
TAG="${{ github.event.inputs.version_tag }}"
# Remove 'v' prefix if present to get version string
VERSION="${TAG#v}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Extracted version from tag: $VERSION"
- name: Test installation from TestPyPI
env:
EXPECTED_VERSION: ${{ steps.version.outputs.version }}
run: |
# Create new virtual environment for testing
python -m venv test-env
source test-env/bin/activate
# Install from TestPyPI (use PyPI as extra index for dependencies)
pip install --index-url https://test.pypi.org/simple/ \
--extra-index-url https://pypi.org/simple/ \
deep-solutions
# Create validation script
cat > validate_package.py << 'EOF'
import sys
import deep_solutions
# Expected version from input
expected_version = "$EXPECTED_VERSION"
actual_version = deep_solutions.__version__
print(f"Expected version: {expected_version}")
print(f"Actual version: {actual_version}")
# Validate version matches
if actual_version != expected_version:
print(f"❌ Version mismatch! Expected '{expected_version}', got '{actual_version}'")
sys.exit(1)
else:
print(f"✅ Version validated successfully!")
# Test hello_world function
from deep_solutions import hello_world
result = hello_world()
expected_output = "Hello from deep-solutions!"
print(f"Function output: {result}")
# Validate hello_world output
if result != expected_output:
print(f"❌ Output mismatch! Expected '{expected_output}', got '{result}'")
sys.exit(1)
else:
print(f"✅ hello_world() validated successfully!")
print("\n✅ All validations passed! Package is ready for production PyPI.")
EOF
# Run validation
python validate_package.py
deactivate
- name: Upload build artifacts
uses: actions/upload-artifact@v6
with:
name: dist-testpypi
path: dist/