Release (manual, from pyproject) #5
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
| # .github/workflows/release-dispatch.yml | |
| name: Release (manual, from pyproject) | |
| on: | |
| workflow_dispatch: {} | |
| jobs: | |
| release: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write # needed to push tags & create releases | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 # required to create/push tags | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| - name: Read version from pyproject.toml | |
| id: version | |
| shell: bash | |
| run: | | |
| python - <<'PY' | |
| import os, sys, pathlib, re | |
| try: | |
| import tomllib # Python 3.11+ | |
| except Exception: | |
| print("::error::tomllib not available; require Python 3.11+") | |
| sys.exit(1) | |
| p = pathlib.Path("pyproject.toml") | |
| if not p.exists(): | |
| print("::error::pyproject.toml not found") | |
| sys.exit(1) | |
| data = tomllib.loads(p.read_text(encoding="utf-8")) | |
| version = None | |
| # PEP 621 | |
| project = data.get("project") or {} | |
| version = project.get("version") | |
| # Poetry fallback | |
| if not version: | |
| tool = data.get("tool") or {} | |
| poetry = tool.get("poetry") or {} | |
| version = poetry.get("version") | |
| if not version: | |
| print("::error::Version not found in pyproject.toml (project.version or tool.poetry.version).") | |
| sys.exit(1) | |
| # Basic semver guard (e.g., 1.2.3 or 1.2.3rc1) | |
| if not re.fullmatch(r"\d+\.\d+\.\d+(?:[a-zA-Z0-9.-]+)?", version): | |
| print(f"::error::Parsed version '{version}' doesn't look like semver (X.Y.Z).") | |
| sys.exit(1) | |
| out = os.environ.get("GITHUB_OUTPUT") | |
| if not out: | |
| print("::error::GITHUB_OUTPUT not set") | |
| sys.exit(1) | |
| with open(out, "a", encoding="utf-8") as fh: | |
| fh.write(f"version={version}\n") | |
| PY | |
| echo "Detected version: ${{ steps.version.outputs.version }}" | |
| - name: Create and push tag | |
| env: | |
| VERSION: ${{ steps.version.outputs.version }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if [ -z "${VERSION:-}" ]; then | |
| echo "::error::Empty version output"; exit 1 | |
| fi | |
| TAG="v${VERSION}" | |
| # Fail if tag already exists remote or locally | |
| if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then | |
| echo "::error::Tag $TAG already exists locally."; exit 1 | |
| fi | |
| if git ls-remote --exit-code --tags origin "$TAG" >/dev/null 2>&1; then | |
| echo "::error::Tag $TAG already exists on origin."; exit 1 | |
| fi | |
| # Configure bot identity | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| # Tag current HEAD of the checked-out branch | |
| git tag -a "$TAG" -m "$TAG" | |
| git push origin "$TAG" | |
| # Optional: build and attach artifacts | |
| # - name: Build package | |
| # run: | | |
| # python -m pip install --upgrade pip | |
| # python -m pip install build | |
| # python -m build | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: v${{ steps.version.outputs.version }} | |
| name: v${{ steps.version.outputs.version }} | |
| generate_release_notes: true | |
| # files: | | |
| # dist/*.whl | |
| # dist/*.tar.gz | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |