Files
intervalsicu-mcp/.gitea/workflows/release.yaml
T
Chris Farhood 95d7681b03 ci: extract release notes from commitizen-style changelog headings
The release-notes awk matched Keep-a-Changelog "## [X.Y.Z]" brackets, but
commitizen writes "## vX.Y.Z (date)", so every release body fell back to the
"Release vX.Y.Z" stub. Match the "## v" heading form instead so the release
carries its actual changelog section.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NGzHtDvJur9U7ysgRKRUTN
2026-07-20 16:07:40 -04:00

100 lines
4.0 KiB
YAML

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)
# cz bump updates pyproject's version but not uv.lock's own-package
# entry, which would leave `uv sync --locked` failing at the release
# tag. Refresh the lock and fold it into the bump commit + tag.
uv lock
if ! git diff --quiet uv.lock; then
git add uv.lock
git commit --amend --no-edit
git tag -f "v$after"
fi
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).
# Commitizen writes headings as "## vX.Y.Z (date)", so match that form
# (not the Keep-a-Changelog "## [X.Y.Z]" brackets) and stop at the next.
body=$(awk "/^## v$v/{f=1;next} /^## v/{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"