Skip to content

2-publish-pypi-stage #29

2-publish-pypi-stage

2-publish-pypi-stage #29

Workflow file for this run

name: CI, TestPyPI Deploy, and Manual PyPI Release
on:
push:
branches: [ main ]
pull_request:
# Run on PRs targeting main
types: [opened, synchronize, reopened] # Be explicit about PR event types
branches: [ main ]
# Allows manual triggering for publishing steps
workflow_dispatch:
jobs:
# --- Test Job (Runs on push/pr/workflow_dispatch) ---
test:
runs-on: ubuntu-latest
# No 'if' condition needed here, controlled by top-level 'on:'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker image with Cache
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
target: dev
tags: my-test-image:latest
load: true
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Run tests with dFBA extra
run: |
docker run --rm \
-v "$(pwd):/app" \
-w /app \
my-test-image:latest \
uv run --with dfba --with pytest --with pytest-cov coverage run --parallel-mode -m pytest ./tests/extras --junitxml=test-report-dfba.xml
- name: Run tests without dFBA extra
run: |
docker run --rm \
-v "$(pwd):/app" \
-w /app \
my-test-image:latest \
uv run --with pytest --with pytest-cov coverage run --parallel-mode -m pytest ./tests/core --junitxml=test-report-core.xml
- name: Combine and Report Coverage
if: success()
run: |
docker run --rm \
-v "$(pwd):/app" \
-w /app \
my-test-image:latest \
sh -c '\
echo "Combining coverage data..." && \
uv run --with pytest --with pytest-cov coverage combine && \
echo "Generating coverage summary..." && \
uv run --with pytest --with pytest-cov coverage report -m > /app/coverage_summary.txt && \
echo "Generating HTML coverage report..." && \
uv run --with pytest --with pytest-cov coverage html -d htmlcov \
'
echo "--- Coverage Summary ---"
cat coverage_summary.txt
echo "------------------------"
- name: Format Coverage Comment Body
if: success() && github.event_name == 'pull_request'
run: |
echo '### :test_tube: Coverage Report' > coverage_comment.md
echo '' >> coverage_comment.md
echo '```text' >> coverage_comment.md
cat coverage_summary.txt >> coverage_comment.md
echo '' >> coverage_comment.md
echo '```' >> coverage_comment.md
- name: Post or Update Coverage Comment
if: success() && github.event_name == 'pull_request'
uses: marocchino/sticky-pull-request-comment@v2
with:
path: coverage_comment.md
# --- Job: Publish to TestPyPI (Runs automatically on PRs after tests pass) ---
publish-testpypi:
name: Publish dev build to TestPyPI
# Run after tests succeed, for PRs to main OR manual triggers
needs: test
# No specific 'if' here, runs if 'test' succeeded for a triggering event
# Implicitly runs on pull_request targeting main (from top-level 'on:')
# Implicitly runs on workflow_dispatch (from top-level 'on:')
runs-on: ubuntu-latest
environment:
name: testpypi
url: https://test.pypi.org/pypi
permissions:
id-token: write # For trusted publishing
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # Needed for accurate versioning if using git tags
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install build dependencies
run: python -m pip install build twine
- name: Generate development version suffix
id: version_suffix
# Use PR number/SHA for PRs, timestamp for manual triggers
run: |
if [ "${{ github.event_name }}" == "pull_request" ]; then
# Suffix for PR builds: .dev<PR_NUMBER>+<SHA>
echo "suffix=dev${{ github.event.number }}+${GITHUB_SHA::7}" >> $GITHUB_OUTPUT
else
# Suffix for other builds (like manual): .dev<TIMESTAMP>
echo "suffix=dev$(date +'%Y%m%d%H%M%S')" >> $GITHUB_OUTPUT
fi
- name: Update version in pyproject.toml for TestPyPI
run: |
# Add the generated .dev suffix to the version line
# This assumes version = "X.Y.Z" format
VERSION_SUFFIX="${{ steps.version_suffix.outputs.suffix }}"
# Use a temporary file for sed compatibility on different OS (though runner is ubuntu)
sed -i.bak "s/^\(version\s*=\s*\"\)\([^\"]*\)\"/\1\2.${VERSION_SUFFIX}\"/" pyproject.toml
rm pyproject.toml.bak # Remove backup file
echo "Updated pyproject.toml with version suffix:"
grep "^version" pyproject.toml
- name: Build package
run: python -m build
- name: Publish package distributions to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
# skip-existing: true # Recommended for dev builds to avoid conflicts
# --- Job: Publish to PyPI (Manual Trigger ONLY on main branch) ---
publish-pypi:
name: Publish to PyPI
# Run ONLY when manually triggered AND on the main branch
if: github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main'
needs: publish-testpypi # Depends on the testpypi build (which runs first on manual trigger)
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/pypi
permissions:
id-token: write # For trusted publishing
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
# Ensure we check out the 'main' branch explicitly for the release build
ref: 'main'
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install build dependencies
run: python -m pip install build twine
- name: Build package
run: |
echo "Building package with version:"
grep "^version" pyproject.toml
python -m build
# This builds using the clean version string from the checked-out main branch code
- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
# Defaults to PyPI, uses trusted publishing