Release #1
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: Release | |
| on: | |
| workflow_run: | |
| workflows: ["CI"] | |
| types: [completed] | |
| branches: [main] | |
| jobs: | |
| release: | |
| if: >- | |
| github.event.workflow_run.conclusion == 'success' && | |
| github.event.workflow_run.event == 'push' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ github.event.workflow_run.head_sha }} | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.12" | |
| - name: Extract version | |
| id: version | |
| run: | | |
| python -m pip install --no-deps . | |
| python - <<'PY' >> "$GITHUB_OUTPUT" | |
| from importlib.metadata import version | |
| print(f"version={version('cyphal-cynic')}") | |
| PY | |
| - name: Check PyPI | |
| id: pypi | |
| env: | |
| VERSION: ${{ steps.version.outputs.version }} | |
| run: | | |
| python - <<'PY' | |
| import json | |
| import os | |
| import sys | |
| import urllib.error | |
| import urllib.request | |
| version = os.environ["VERSION"] | |
| url = "https://pypi.org/simple/cyphal-cynic/" | |
| request = urllib.request.Request(url, headers={"Accept": "application/vnd.pypi.simple.v1+json"}) | |
| try: | |
| with urllib.request.urlopen(request, timeout=30) as response: | |
| payload = json.load(response) | |
| except urllib.error.HTTPError as ex: | |
| if ex.code == 404: | |
| exists = False | |
| else: | |
| print(f"::error::PyPI returned HTTP {ex.code}", file=sys.stderr) | |
| raise SystemExit(1) from ex | |
| except urllib.error.URLError as ex: | |
| print(f"::error::PyPI request failed: {ex.reason}", file=sys.stderr) | |
| raise SystemExit(1) from ex | |
| except json.JSONDecodeError as ex: | |
| print(f"::error::PyPI returned invalid JSON: {ex}", file=sys.stderr) | |
| raise SystemExit(1) from ex | |
| else: | |
| if not isinstance(payload, dict): | |
| print("::error::PyPI returned an unexpected Simple API response.", file=sys.stderr) | |
| raise SystemExit(1) | |
| versions = payload.get("versions") | |
| if not isinstance(versions, list) or not all(isinstance(v, str) for v in versions): | |
| print("::error::PyPI returned an unexpected Simple API response.", file=sys.stderr) | |
| raise SystemExit(1) | |
| exists = version in versions | |
| if exists: | |
| print(f"PyPI already has cyphal-cynic {version}.") | |
| else: | |
| print(f"PyPI does not have cyphal-cynic {version}; publishing.") | |
| with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as out: | |
| print(f"exists={str(exists).lower()}", file=out) | |
| PY | |
| - name: Build package | |
| if: steps.pypi.outputs.exists == 'false' | |
| run: | | |
| python -m pip install build | |
| python -m build | |
| - name: Ensure tag | |
| if: steps.pypi.outputs.exists == 'false' | |
| env: | |
| EXPECTED_SHA: ${{ github.event.workflow_run.head_sha }} | |
| VERSION: ${{ steps.version.outputs.version }} | |
| run: | | |
| set -e -o pipefail | |
| TAG_SHA=$(git ls-remote --tags origin "refs/tags/$VERSION^{}" | cut -f1) | |
| if [ -z "$TAG_SHA" ]; then | |
| TAG_SHA=$(git ls-remote --tags origin "refs/tags/$VERSION" | cut -f1) | |
| fi | |
| if [ -z "$TAG_SHA" ]; then | |
| echo "Creating tag $VERSION at $EXPECTED_SHA." | |
| git push origin "HEAD:refs/tags/$VERSION" | |
| elif [ "$TAG_SHA" = "$EXPECTED_SHA" ]; then | |
| echo "Tag $VERSION already points to $EXPECTED_SHA." | |
| else | |
| echo "::error::Tag $VERSION points to $TAG_SHA, expected $EXPECTED_SHA." | |
| exit 1 | |
| fi | |
| - name: Publish to PyPI | |
| if: steps.pypi.outputs.exists == 'false' | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| password: ${{ secrets.PYPI_API_TOKEN }} | |
| attestations: false |