Files
skills/.gitea/workflows/release.yml
T
mathiasandClaude Opus 5 e2a312d05e
release / tag (push) Successful in 1s
fix(ci): anchor the release-skip marker to a trailer, and test it
The tagger searched the entire commit message for a bracketed marker as a
substring. The commit that introduced the manifest-bump step described that
marker in its own body — so the tagger read its own documentation as an
instruction and skipped the release. Run 15 reported success with no tag, no
manifest bump and no error. Same failure family as everything else in this
sweep: the wrong thing happened silently.

The rule is now a git trailer matched anchored to its own line, consistent
with Bump-Type, and it lives in bin/should-skip-release rather than untested
inside YAML. tests/release_skip_test.sh pins six cases, including the exact
regression: a message that spells the marker out in prose must still tag.

A marker you cannot mention without triggering is a footgun, not a mechanism.

Bump-Type: patch

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BeAp5LTscnz2W7Ubt4eJ6m
2026-08-23 14:39:31 +02:00

88 lines
3.5 KiB
YAML

name: release
# Auto-tag every push to main with a semver bump derived from the latest
# commit footer:
# Bump-Type: major → vX.0.0
# Bump-Type: minor → v0.Y.0
# (default) → v0.0.Z (patch)
#
# Skips when the latest commit is itself the auto-tagger (commit message
# contains "[skip-release]") to prevent recursion. Tag is pushed back
# to the repo using the GITEA_TOKEN provided by act_runner.
on:
push:
branches:
- main
jobs:
tag:
runs-on: self-hosted
steps:
- name: checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITEA_TOKEN }}
- name: derive next version
id: bump
run: |
set -euo pipefail
last_commit=$(git log -1 --pretty=%B)
# Delegated to bin/should-skip-release so the rule is covered by
# tests/release_skip_test.sh rather than living untested in YAML.
if printf '%s\n' "$last_commit" | sh bin/should-skip-release; then
echo "skipping — commit carries the Skip-Release trailer"
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
latest=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
major=$(echo "$latest" | sed -E 's/^v([0-9]+)\.([0-9]+)\.([0-9]+)$/\1/')
minor=$(echo "$latest" | sed -E 's/^v([0-9]+)\.([0-9]+)\.([0-9]+)$/\2/')
patch=$(echo "$latest" | sed -E 's/^v([0-9]+)\.([0-9]+)\.([0-9]+)$/\3/')
bump=$(printf '%s' "$last_commit" | grep -iE '^Bump-Type:' | head -1 | awk '{print tolower($2)}' || true)
case "$bump" in
major) major=$((major + 1)); minor=0; patch=0 ;;
minor) minor=$((minor + 1)); patch=0 ;;
*) patch=$((patch + 1)) ;;
esac
next="v${major}.${minor}.${patch}"
echo "previous=$latest"
echo "bump=${bump:-patch}"
echo "next=$next"
echo "tag=$next" >> "$GITHUB_OUTPUT"
echo "skip=false" >> "$GITHUB_OUTPUT"
- name: tag + push
if: steps.bump.outputs.skip != 'true'
env:
TAG: ${{ steps.bump.outputs.tag }}
GITEA_ACTOR: ${{ gitea.actor }}
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
run: |
set -euo pipefail
git config user.name "${GITEA_ACTOR}"
git config user.email "${GITEA_ACTOR}@noreply.gitea.d-ma.be"
# Claude Code gates plugin updates on plugin.json's version field:
# if it does not move, installed hosts never see the new tag. Bump
# it in the same commit train so the tag and the manifest agree —
# `task check` enforces that equality on every run.
manifest=.claude-plugin/plugin.json
version="${TAG#v}"
current=$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$manifest" | head -1)
if [ "$current" != "$version" ]; then
sed -i "s/\"version\"[[:space:]]*:[[:space:]]*\"$current\"/\"version\": \"$version\"/" "$manifest"
git add "$manifest"
# The trailer below stops this push from re-triggering the tagger.
git commit -m "chore(release): plugin.json version $version" -m "Skip-Release: true"
git push origin HEAD:main
echo "bumped $manifest to $version"
fi
git tag -a "$TAG" -m "release $TAG"
# gitea-actions checkout uses an x-access-token URL; reuse it.
git push origin "$TAG"
echo "pushed $TAG"