Release 0.1.47.0 #4
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: Publish | ||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| force_publish: | ||
| description: "Force publish even if version exists on PyPI" | ||
| required: true | ||
| default: "false" | ||
| type: choice | ||
| options: | ||
| - "false" | ||
| - "true" | ||
| push: | ||
| tags: | ||
| - "v*.*.*.*" | ||
| permissions: | ||
| contents: read | ||
| jobs: | ||
| build: | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| publish: ${{ steps.decide.outputs.publish }} | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v4 | ||
| - name: Gate publish | ||
| id: gate | ||
| run: | | ||
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | ||
| echo "publish=true" >> "$GITHUB_OUTPUT" | ||
| exit 0 | ||
| fi | ||
| if echo "${{ github.ref }}" | grep -Eq '^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$'; then | ||
| echo "publish=true" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "publish=false" >> "$GITHUB_OUTPUT" | ||
| fi | ||
| - name: Set up Python | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: "3.12" | ||
| cache: "pip" | ||
| - name: Install build tools | ||
| run: | | ||
| python -m pip install -U pip setuptools wheel | ||
| python -m pip install build twine | ||
| - name: Verify tag matches version | ||
| if: github.event_name != 'workflow_dispatch' && steps.gate.outputs.publish == 'true' | ||
| run: | | ||
| tag_version="${GITHUB_REF#refs/tags/v}" | ||
| file_version=$(python - <<'PY' | ||
| import re | ||
| from pathlib import Path | ||
| text = Path("pyproject.toml").read_text(encoding="utf-8") | ||
| match = re.search(r'^\\s*version\\s*=\\s*\"([^\"]+)\"\\s*$', text, re.MULTILINE) | ||
| if not match: | ||
| raise SystemExit(1) | ||
| print(match.group(1)) | ||
| PY | ||
| ) | ||
| if [ "$tag_version" != "$file_version" ]; then | ||
| echo "Tag version $tag_version does not match pyproject.toml $file_version" >&2 | ||
| exit 1 | ||
| fi | ||
| - name: Check PyPI for existing version | ||
| id: exists | ||
| if: steps.gate.outputs.publish == 'true' && github.event.inputs.force_publish != 'true' | ||
| run: | | ||
| version=$(python - <<'PY' | ||
| import re | ||
| from pathlib import Path | ||
| text = Path("pyproject.toml").read_text(encoding="utf-8") | ||
| match = re.search(r'^\s*version\s*=\s*\"([^\"]+)\"\s*$', text, re.MULTILINE) | ||
| if not match: | ||
| raise SystemExit(1) | ||
| print(match.group(1)) | ||
| PY | ||
| ) | ||
| exists=$(python - <<PY | ||
| import json | ||
| import urllib.request | ||
| version = "${version}" | ||
| with urllib.request.urlopen("https://pypi.org/pypi/pypnm-docsis-cmts/json") as response: | ||
| data = json.load(response) | ||
| exists = bool(data.get("releases", {}).get(version)) | ||
| print("true" if exists else "false") | ||
| PY | ||
| ) | ||
| echo "exists=$exists" >> "$GITHUB_OUTPUT" | ||
| if [ "$exists" = "true" ]; then | ||
| echo "Version ${version} already exists on PyPI; skipping publish." | ||
| fi | ||
| - name: Decide publish | ||
| id: decide | ||
| run: | | ||
| publish="${{ steps.gate.outputs.publish }}" | ||
| if [ "$publish" != "true" ]; then | ||
| echo "publish=false" >> "$GITHUB_OUTPUT" | ||
| exit 0 | ||
| fi | ||
| if [ "${{ github.event.inputs.force_publish }}" = "true" ]; then | ||
| echo "publish=true" >> "$GITHUB_OUTPUT" | ||
| exit 0 | ||
| fi | ||
| if [ "${{ steps.exists.outputs.exists }}" = "true" ]; then | ||
| echo "publish=false" >> "$GITHUB_OUTPUT" | ||
| exit 0 | ||
| fi | ||
| echo "publish=true" >> "$GITHUB_OUTPUT" | ||
| - name: Release summary | ||
| run: | | ||
| ref="${{ github.ref }}" | ||
| exists="${{ steps.exists.outputs.exists }}" | ||
| publish="${{ steps.decide.outputs.publish }}" | ||
| version=$(python - <<'PY' | ||
| import re | ||
| from pathlib import Path | ||
| text = Path("pyproject.toml").read_text(encoding="utf-8") | ||
| match = re.search(r'^\s*version\s*=\s*\"([^\"]+)\"\s*$', text, re.MULTILINE) | ||
| if not match: | ||
| raise SystemExit(1) | ||
| print(match.group(1)) | ||
| PY | ||
| ) | ||
| echo "ref=${ref}" | ||
| echo "version=${version}" | ||
| echo "exists=${exists}" | ||
| echo "publish=${publish}" | ||
| - name: Build package | ||
| run: | | ||
| python -m build | ||
| - name: Twine check | ||
| run: | | ||
| python -m twine check dist/* | ||
| - name: Upload build artifacts | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: dist | ||
| path: dist/* | ||
| publish: | ||
| runs-on: ubuntu-latest | ||
| needs: build | ||
| if: needs.build.outputs.publish == 'true' | ||
| permissions: | ||
| id-token: write | ||
| contents: read | ||
| environment: pypi | ||
| steps: | ||
| - name: Download build artifacts | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| name: dist | ||
| path: dist | ||
| - name: Dist checksums | ||
| run: | | ||
| ls -la dist | ||
| sha256sum dist/* | ||
| - name: Publish with trusted publishing | ||
| uses: pypa/gh-action-pypi-publish@release/v1 | ||
| with: | ||
| repository-url: https://upload.pypi.org/legacy/ | ||