ci: add release workflow (manual dispatch: cz bump + tag + Gitea release)

Cutting a release is now a workflow, not a local command. Uses uv/commitizen
(actions/setup-python is broken on the current runners). Supports auto-detect or
forced increment, and a dry_run mode.
This commit is contained in:
2026-07-20 08:49:04 -04:00
parent f766e68d0d
commit 407239296b
+88
View File
@@ -0,0 +1,88 @@
name: release
# Cut a release as an action, not a local command. Manually triggered ("Run
# workflow"): bumps the version from conventional commits (or a forced level),
# updates CHANGELOG.md, tags vX.Y.Z, pushes both, and creates a Gitea release.
# Uses uv/commitizen because actions/setup-python is broken on these runners.
on:
workflow_dispatch:
inputs:
increment:
description: "Bump level (auto = detect from conventional commits)"
required: false
default: auto
type: choice
options: [auto, PATCH, MINOR, MAJOR]
dry_run:
description: "Dry run — compute + show, but do NOT push or release"
required: false
default: false
type: boolean
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout (full history + tags)
uses: actions/checkout@v4
with:
fetch-depth: 0
# Needs push + release rights. The auto token works with contents:write;
# if your runner's token can't push, set a PAT secret RELEASE_TOKEN.
token: ${{ secrets.RELEASE_TOKEN || github.token }}
- name: Install uv (self-contained; avoids the broken setup-python)
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: Git identity
run: |
git config user.name "release-bot"
git config user.email "release-bot@farhoodlabs.com"
- name: Bump version + changelog + tag
id: bump
run: |
before=$(uvx --from commitizen cz version --project)
args="--yes"
[ "${{ inputs.increment }}" != "auto" ] && args="$args --increment ${{ inputs.increment }}"
if [ "${{ inputs.dry_run }}" = "true" ]; then
echo "== DRY RUN =="
uvx --from commitizen cz bump $args --dry-run || true
echo "do_release=false" >> "$GITHUB_OUTPUT"
exit 0
fi
if uvx --from commitizen cz bump $args; then
after=$(uvx --from commitizen cz version --project)
echo "version=$after" >> "$GITHUB_OUTPUT"
echo "do_release=true" >> "$GITHUB_OUTPUT"
echo "Bumped $before -> $after"
else
echo "No releasable commits since the last tag — nothing to do."
echo "do_release=false" >> "$GITHUB_OUTPUT"
fi
- name: Push commit + tag
if: ${{ steps.bump.outputs.do_release == 'true' }}
run: git push origin HEAD:${{ github.ref_name }} --follow-tags
- name: Create Gitea release
if: ${{ steps.bump.outputs.do_release == 'true' }}
run: |
v="${{ steps.bump.outputs.version }}"
# Body = this version's section from CHANGELOG.md (fallback to a stub).
body=$(awk "/^## \\[$v\\]/{f=1;next} /^## \\[/{f=0} f" CHANGELOG.md)
[ -z "$body" ] && body="Release v$v"
jq -n --arg tag "v$v" --arg name "v$v" --arg body "$body" \
'{tag_name:$tag, name:$name, body:$body, draft:false, prerelease:false}' \
| curl -sf -X POST \
-H "Authorization: token ${{ secrets.RELEASE_TOKEN || github.token }}" \
-H "Content-Type: application/json" \
--data @- \
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases"
echo "Created release v$v"