Bump version to 0.3.3 #3
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: Auto tag and release | |
| # Turns a version change on main into a tag and a GitHub Release. Publishing | |
| # that release is what triggers publish.yml (PyPI) and release-binaries.yml | |
| # (native binaries), so this workflow is the only manual step a release needs. | |
| # | |
| # Two flows: | |
| # | |
| # 1. dev merge (e.g. 0.3.3.dev4 lands on main): strip the .devN suffix, commit | |
| # the clean version as pamica-bot, tag it vX.Y.Z, create a stable release. | |
| # 2. Clean version already on main (e.g. a hand-prepared 0.4.0, or an | |
| # intentional 0.4.0rc1): tag as-is, marked prerelease when PEP 440 says so. | |
| # | |
| # The strip commit deliberately carries no [skip ci] marker: GitHub applies that | |
| # per commit rather than per event, so it would also suppress the tag-push event | |
| # that publish.yml needs. | |
| on: | |
| push: | |
| branches: [main] | |
| paths: | |
| - 'pyproject.toml' | |
| permissions: | |
| contents: write | |
| jobs: | |
| tag: | |
| # The strip step below commits as pamica-bot and pushes to main, which | |
| # re-triggers this workflow; without this guard that recurses forever. | |
| if: github.event.head_commit.author.email != 'pamica-bot@users.noreply.github.com' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Require the release PAT | |
| # Pushes and releases made with the default GITHUB_TOKEN do not trigger | |
| # other workflows, so without a PAT this job would appear to succeed | |
| # while silently never publishing. Fail visibly instead. | |
| env: | |
| PAT: ${{ secrets.AUTO_TAG_PAT }} | |
| run: | | |
| if [ -z "$PAT" ]; then | |
| echo "::error::AUTO_TAG_PAT is not set. Create a repository secret with a PAT that has contents:write, or the release chain cannot fire." | |
| exit 1 | |
| fi | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: main | |
| fetch-depth: 2 | |
| token: ${{ secrets.AUTO_TAG_PAT }} | |
| - name: Fetch tags | |
| # checkout does not fetch tags, and the guards below need them. | |
| run: git fetch --tags origin | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v5 | |
| - name: Detect .devN suffix | |
| id: strip | |
| run: | | |
| CURRENT=$(grep -m1 '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/') | |
| echo "original=$CURRENT" >> "$GITHUB_OUTPUT" | |
| # Strip .devN only. Explicit prereleases (rcN, aN, bN) are deliberate | |
| # and pass through to be tagged as prereleases. | |
| STRIPPED="${CURRENT%.dev*}" | |
| echo "stripped=$STRIPPED" >> "$GITHUB_OUTPUT" | |
| if [ "$STRIPPED" != "$CURRENT" ]; then | |
| echo "did_strip=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "did_strip=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Apply strip and push | |
| if: steps.strip.outputs.did_strip == 'true' | |
| run: | | |
| set -euo pipefail | |
| STRIPPED="${{ steps.strip.outputs.stripped }}" | |
| # Tag exists but main still carries .devN: releasing the literal dev | |
| # version would be worse than stopping, so stop. | |
| if git rev-parse "v$STRIPPED" >/dev/null 2>&1; then | |
| echo "::error::Tag v$STRIPPED exists but pyproject.toml is still ${{ steps.strip.outputs.original }}. Refusing to tag a .dev version. Investigate manually." | |
| exit 1 | |
| fi | |
| git config user.name "pamica-bot" | |
| git config user.email "pamica-bot@users.noreply.github.com" | |
| BEFORE=$(git rev-parse HEAD) | |
| uv run python scripts/sync_version.py sync "$STRIPPED" | |
| uv lock | |
| git add pyproject.toml CITATION.cff .zenodo.json uv.lock | |
| git commit -m "Bump version to $STRIPPED" | |
| if [ "$BEFORE" = "$(git rev-parse HEAD)" ]; then | |
| echo "::error::no commit created despite did_strip=true; state raced between detect and apply." | |
| exit 1 | |
| fi | |
| git push origin main | |
| - name: Determine version to tag | |
| id: final | |
| run: | | |
| set -euo pipefail | |
| # Pick up the strip commit. No `|| true`: a non-fast-forward means | |
| # main diverged, and tagging a stale local tree would mis-release. | |
| git pull --ff-only origin main | |
| FINAL=$(grep -m1 '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/') | |
| echo "version=$FINAL" >> "$GITHUB_OUTPUT" | |
| # PEP 440 prerelease markers: aN / bN / rcN / .devN / .postN is not one. | |
| if [[ "$FINAL" =~ (a|b|rc|\.dev)[0-9]+$ ]]; then | |
| echo "prerelease=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "prerelease=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Create tag | |
| id: create_tag | |
| run: | | |
| set -euo pipefail | |
| TAG="v${{ steps.final.outputs.version }}" | |
| if git rev-parse "$TAG" >/dev/null 2>&1; then | |
| echo "::warning::$TAG already exists (re-run on an already-tagged commit); skipping." | |
| echo "created=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git tag -a "$TAG" -m "Release $TAG" | |
| git push origin "$TAG" | |
| echo "created=true" >> "$GITHUB_OUTPUT" | |
| - name: Create GitHub release | |
| if: steps.create_tag.outputs.created == 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.AUTO_TAG_PAT }} | |
| run: | | |
| set -euo pipefail | |
| TAG="v${{ steps.final.outputs.version }}" | |
| ARGS=("$TAG" --title "$TAG" --generate-notes) | |
| if [ "${{ steps.final.outputs.prerelease }}" = "true" ]; then | |
| ARGS+=(--prerelease) | |
| else | |
| ARGS+=(--latest) | |
| fi | |
| gh release create "${ARGS[@]}" |