Skip to content

Bump version to 0.3.3 #3

Bump version to 0.3.3

Bump version to 0.3.3 #3

Workflow file for this run

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[@]}"