fix(ci): publish Python wheel to GitHub Releases #8
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Python Client CI | |
| on: | |
| push: | |
| branches: [main] | |
| paths: | |
| - 'clients/python/**' | |
| - 'spec/**' | |
| - '.github/workflows/python-ci.yml' | |
| pull_request: | |
| branches: [main] | |
| paths: | |
| - 'clients/python/**' | |
| - 'spec/**' | |
| - '.github/workflows/python-ci.yml' | |
| jobs: | |
| build-and-test: | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: clients/python | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| - name: Install pipx | |
| run: pip install pipx | |
| - name: Generate client from spec | |
| working-directory: . | |
| run: ./scripts/generate.sh | |
| - name: Set up generated symlink | |
| run: ln -sf ../../generated src/ros2_medkit_client/_generated | |
| - name: Install package with dev deps | |
| run: pip install -e '.[dev]' | |
| - name: Test | |
| run: python -m pytest -v | |
| - name: Lint | |
| run: ruff check src/ tests/ | |
| - name: Format check | |
| run: ruff format --check src/ tests/ | |
| version-check: | |
| if: github.event_name == 'pull_request' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| packages: read | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Check version bump | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| # Read local version from pyproject.toml | |
| LOCAL_VERSION=$(python3 -c " | |
| import re, pathlib | |
| text = pathlib.Path('clients/python/pyproject.toml').read_text() | |
| m = re.search(r'^version\s*=\s*\"([^\"]+)\"', text, re.MULTILINE) | |
| print(m.group(1)) | |
| ") | |
| echo "Local version: $LOCAL_VERSION" | |
| # Validate strict semver (MAJOR.MINOR.PATCH, no pre-release) | |
| if ! echo "$LOCAL_VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then | |
| echo "::error::Version '$LOCAL_VERSION' is not valid semver (expected MAJOR.MINOR.PATCH)" | |
| exit 1 | |
| fi | |
| # Query GitHub Packages for published versions | |
| PUBLISHED=$(gh api \ | |
| -H "Accept: application/vnd.github+json" \ | |
| "/orgs/selfpatch/packages/pypi/ros2-medkit-client/versions" \ | |
| --jq '.[].name' 2>/dev/null || echo "") | |
| if [ -z "$PUBLISHED" ]; then | |
| echo "No published versions found - first publish, any version is OK" | |
| exit 0 | |
| fi | |
| # Find the highest published version | |
| LATEST=$(echo "$PUBLISHED" | sort -t. -k1,1n -k2,2n -k3,3n | tail -1) | |
| echo "Latest published version: $LATEST" | |
| # Compare semver: local must be strictly greater than latest | |
| python3 -c " | |
| import sys | |
| def parse(v): | |
| return tuple(int(x) for x in v.split('.')) | |
| local = parse('$LOCAL_VERSION') | |
| latest = parse('$LATEST') | |
| if local <= latest: | |
| print(f'::error::Version $LOCAL_VERSION must be greater than published $LATEST. Bump version in clients/python/pyproject.toml') | |
| sys.exit(1) | |
| print(f'Version check passed: $LOCAL_VERSION > $LATEST') | |
| " | |
| publish: | |
| needs: build-and-test | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| packages: write | |
| contents: read | |
| defaults: | |
| run: | |
| working-directory: clients/python | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| - name: Install build tools | |
| run: pip install pipx build twine | |
| - name: Generate client from spec | |
| working-directory: . | |
| run: ./scripts/generate.sh | |
| - name: Set up generated symlink | |
| run: ln -sf ../../generated src/ros2_medkit_client/_generated | |
| - name: Build wheel | |
| run: python -m build | |
| - name: Publish to GitHub Packages | |
| run: twine upload --repository-url https://pypi.pkg.github.com/selfpatch/ dist/* | |
| env: | |
| TWINE_USERNAME: __token__ | |
| TWINE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} |