From be4f2b83f7243eac6d756acdafef05a0bc9a4d23 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 19 Jun 2026 17:16:17 +0000 Subject: [PATCH] ci: auto-tag + publish a GitHub Release on version bump MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The v1.5.0 PR merged to main but no git tag or GitHub Release was ever created to mark it, unlike v1.4.0 / v1.4.1. Add a release workflow that, when the kit version in openapi.json (info.version, "v") is not yet released, creates the tag — anchored to main's tip — and publishes a Release with notes drawn from the matching CHANGELOG.md section. The job is idempotent, runs on every push to main (so future version bumps self-tag), and is bootstrapped on the claude/** branch so the already-merged v1.5.0 gets its missing tag/Release. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01NhzBUBpmFLVPAdBAJn8iAs --- .github/workflows/release.yml | 80 +++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..829d990 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,80 @@ +name: Release on version bump + +# Tags + publishes a GitHub Release for the kit version (openapi.json info.version, "v") +# whenever it isn't already released. This closes the gap where a version-bump PR merges to main but +# no git tag / Release is ever created. The release notes are taken from the matching CHANGELOG.md +# section, and the tag is always anchored to the tip of main. The job is idempotent: it is a no-op +# once the tag/Release for the current version exists. + +on: + push: + # main: auto-release every future version bump the moment it lands. + # claude/**: bootstrap — lets the automation branch that introduces this workflow create the + # already-merged-but-untagged v1.5.0 release. Harmless to keep (guarded + idempotent). + branches: + - main + - "claude/**" + workflow_dispatch: + +permissions: + contents: write + +concurrency: + group: release + cancel-in-progress: false + +jobs: + release: + name: Tag + publish the carried contract version + runs-on: ubuntu-latest + steps: + - name: Check out main (the tag is always anchored to main's tip) + uses: actions/checkout@v4 + with: + ref: main + fetch-depth: 0 + + - name: Read carried version from openapi.json + id: ver + run: | + set -euo pipefail + version="$(node -e "process.stdout.write(require('./openapi.json').info.version)")" + echo "version=$version" >> "$GITHUB_OUTPUT" + echo "tag=v$version" >> "$GITHUB_OUTPUT" + echo "Carried API contract version: $version" + + - name: Create the tag + Release if it does not exist yet + env: + GH_TOKEN: ${{ github.token }} + VERSION: ${{ steps.ver.outputs.version }} + TAG: ${{ steps.ver.outputs.tag }} + run: | + set -euo pipefail + + if gh release view "$TAG" >/dev/null 2>&1; then + echo "Release $TAG already exists — nothing to do." + exit 0 + fi + + # Title from the CHANGELOG heading "## [x.y.z]" → "vx.y.z" (keeps the heading's + # own " — " separator verbatim; byte-safe, so the multibyte em-dash is preserved). + title="$(awk -v v="$VERSION" ' + index($0, "## [" v "]") == 1 { + printf "v%s%s", v, substr($0, index($0, "]") + 1); + exit + }' CHANGELOG.md)" + [ -n "$title" ] || title="$TAG" + + # Body = the CHANGELOG section for this version (up to the next "## [" heading). + awk -v v="$VERSION" ' + index($0, "## [" v "]") == 1 { grab = 1; next } + grab && /^## \[/ { exit } + grab { print } + ' CHANGELOG.md > notes.md + [ -s notes.md ] || echo "Release ${TAG}." > notes.md + + echo "Creating $TAG at $(git rev-parse HEAD) with title: $title" + gh release create "$TAG" \ + --target "$(git rev-parse HEAD)" \ + --title "$title" \ + --notes-file notes.md