From 6e3cddae41f8ac0089f55d6b89b0a8e6afc6c583 Mon Sep 17 00:00:00 2001 From: Taras Mankovski <74687+taras@users.noreply.github.com> Date: Sat, 18 Jul 2026 20:42:02 -0400 Subject: [PATCH] release: surface a forgotten version bump on the Releases page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Publishing v0.3.1 without bumping the manifests failed invisibly — the guard fired in a workflow log while the Releases page showed a healthy release. draft-release.yml now syncs the draft with the manifests after every merge: an already-released manifest version puts a warning banner in the draft's notes, and a bumped version clears it and sets the draft's tag/title to the guard-passing v. release.yml moves the tag guard into a preflight job (once, not per matrix target) that on failure edits the just-published release: caution note, failed title, prerelease marker. --- .github/workflows/draft-release.yml | 38 ++++++++++++++++++++++ .github/workflows/release.yml | 49 ++++++++++++++++++++++------- specs/release-process-spec.md | 19 ++++++----- 3 files changed, 88 insertions(+), 18 deletions(-) diff --git a/.github/workflows/draft-release.yml b/.github/workflows/draft-release.yml index 14baa42..2b746c9 100644 --- a/.github/workflows/draft-release.yml +++ b/.github/workflows/draft-release.yml @@ -19,3 +19,41 @@ jobs: - uses: release-drafter/release-drafter@4d75298e00d9e34c483e5ff8c68d0ea1c1940c1e # v7.5.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + # The Releases page must tell the truth before Publish is clicked: when + # the manifest version is already released, the draft carries a warning + # banner; once the manifests are bumped, the banner clears and the + # draft's tag/title default to the guard-passing v. + - name: Sync the draft with the manifest version + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + VERSION="$(jq -r .version cli/deno.json)" + DRAFT_ID="$(gh api "repos/${{ github.repository }}/releases?per_page=10" --jq '[.[] | select(.draft)][0].id // empty')" + if [ -z "$DRAFT_ID" ]; then + echo "no draft release found" + exit 0 + fi + gh api "repos/${{ github.repository }}/releases/$DRAFT_ID" --jq .body > /tmp/body.md + sed '//,//d' /tmp/body.md > /tmp/clean.md + if git ls-remote --exit-code origin "refs/tags/v$VERSION" >/dev/null 2>&1; then + { + echo "" + echo "> [!WARNING]" + echo "> The manifests still declare \`$VERSION\`, which is already released." + echo "> Bump every manifest before publishing this draft (release spec §2)." + echo "" + echo + cat /tmp/clean.md + } > /tmp/new.md + gh api -X PATCH "repos/${{ github.repository }}/releases/$DRAFT_ID" \ + -F body=@/tmp/new.md >/dev/null + echo "draft flagged: manifests still at already-released $VERSION" + else + gh api -X PATCH "repos/${{ github.repository }}/releases/$DRAFT_ID" \ + -f tag_name="v$VERSION" -f name="v$VERSION" -F body=@/tmp/clean.md >/dev/null + echo "draft synced to v$VERSION" + fi diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e947ee7..73b42ed 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,8 +8,46 @@ permissions: contents: write jobs: + # Refuse a tag the manifests do not declare (spec §2) — the binary's + # --version comes from cli/deno.json. On failure, the mistake is made + # visible on the release itself, not just in this log. + preflight: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: Tag matches the manifest version + run: | + TAG="${{ github.ref_name }}" + declared="$(jq -r .version cli/deno.json)" + if [ "v$declared" != "$TAG" ]; then + echo "::error::cli/deno.json declares $declared but the tag is $TAG — bump the manifests first" + exit 1 + fi + + - name: Flag the release when the tag cannot build + if: failure() + env: + GH_TOKEN: ${{ github.token }} + run: | + set -u + TAG="${{ github.ref_name }}" + gh release view "$TAG" --json body --jq .body > /tmp/body.md 2>/dev/null || exit 0 + { + echo "> [!CAUTION]" + echo "> This release did not build: the manifests do not declare \`${TAG#v}\`." + echo "> Delete this release and its tag, bump every manifest, and release again" + echo "> (release spec §2)." + echo + cat /tmp/body.md + } > /tmp/new.md + gh release edit "$TAG" --prerelease \ + --title "$TAG — failed: manifests not bumped" \ + --notes-file /tmp/new.md || true + build: name: Compile ${{ matrix.target }} + needs: preflight runs-on: ubuntu-latest strategy: fail-fast: false @@ -28,17 +66,6 @@ jobs: steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - # Refuse a tag the manifests do not declare (spec §2) — the binary's - # --version comes from cli/deno.json. - - name: Tag matches the manifest version - run: | - TAG="${{ github.ref_name }}" - declared="$(jq -r .version cli/deno.json)" - if [ "v$declared" != "$TAG" ]; then - echo "::error::cli/deno.json declares $declared but the tag is $TAG — bump the manifests first" - exit 1 - fi - - uses: denoland/setup-deno@e95548e56dfa95d4e1a28d6f422fafe75c4c26fb # v2.0.3 with: deno-version: v2.9.1 diff --git a/specs/release-process-spec.md b/specs/release-process-spec.md index 25ead4f..e049c9c 100644 --- a/specs/release-process-spec.md +++ b/specs/release-process-spec.md @@ -62,13 +62,18 @@ create the `vX.Y.Z` tag. - **`draft-release.yml`** (`push: main`): maintains the rolling draft release with release-drafter (config: `.github/release-drafter.yml`). Each merged PR - appends a changelog line; publishing the draft cuts the release. Before - publishing, set the tag to the version the manifests declare — the guards - below refuse a mismatched tag. -- **`release.yml`** (`push: tags v*`): validates the tag against - `cli/deno.json`, compiles `xmd` per target with - `--include packages/code-review-agent`, and attaches the binaries and sha256 - checksums to the tag's GitHub Release. + appends a changelog line; publishing the draft cuts the release. After every + merge the workflow syncs the draft with the manifests: when the manifest + version is already released, the draft's notes carry a warning banner saying + the manifests need bumping; once bumped, the banner clears and the draft's + tag and title default to the guard-passing `v`. +- **`release.yml`** (`push: tags v*`): a preflight job validates the tag + against `cli/deno.json`; on mismatch it flags the just-published release on + the Releases page — caution note in the notes, a failed title, and the + prerelease marker — so a forgotten bump is visible where the release was + made, then refuses to build. On a valid tag it compiles `xmd` per target + with `--include packages/code-review-agent` and attaches the binaries and + sha256 checksums to the tag's GitHub Release. - **`publish-packages.yml`** (`push: tags v*`): GENERATED by `scripts/gen-publish-workflow.md` — an executable markdown document that derives the jobs from the workspace manifests — never edited by hand. Run