From 56973386fb1df447d82f9e5c4c9f1ba5c6c8a554 Mon Sep 17 00:00:00 2001 From: lukasWuttke <54042461+LukasWodka@users.noreply.github.com> Date: Tue, 28 Jul 2026 17:03:00 +0200 Subject: [PATCH 1/3] chore: declare next release in VERSION (0.10.1) for the release train The train reads this file to cut tags: vX.Y.Z-rc.N on every staging promotion (pre-release binaries for FR; 'latest' never sees them) and vX.Y.Z on the prod promotion. The binary's own version still derives from the tag at build time (release.yml ldflags) -- this file only declares intent, uniform with tracebloc-py-package's pyproject version. 0.10.1 ships the cosign identity anchor fix (#415). Co-Authored-By: Claude Opus 4.8 --- VERSION | 1 + 1 file changed, 1 insertion(+) create mode 100644 VERSION diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..5712157 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.10.1 From aef84a6c02c33470c22eed998a182ccec3eb1681 Mon Sep 17 00:00:00 2001 From: lukasWuttke <54042461+LukasWodka@users.noreply.github.com> Date: Tue, 28 Jul 2026 17:19:42 +0200 Subject: [PATCH 2/3] feat(release): enforce tag==VERSION + strict prerelease detection Two guards raised in release-train review: (1) any tag's base X.Y.Z must match the VERSION file (train-cut or manual), so the file can never go silently stale after an out-of-train release; pre-VERSION tags are grandfathered for rebuilds. (2) STRICT stability: only plain vX.Y.Z is a stable release -- rc tags AND malformed variants (v1.2.3rc1, no dash) are prereleases, so 'releases/latest' (the installer bootstrap) can only ever resolve a real production build. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/release.yml | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b18b496..d72d5a7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -100,6 +100,21 @@ jobs: REF="${{ inputs.ref || github.ref_name }}" # Strip the leading v for use in -X main.version VERSION="${REF#v}" + # The VERSION file declares the next release (read by the release + # train to cut rc/final tags). Any tag -- train-cut OR manual -- + # must agree with it, so the file can never go silently stale + # after an out-of-train release. Refs from before the file existed + # (rebuilds of old tags) are grandfathered with a warning. + BASE=$(printf '%s' "$VERSION" | grep -oE '^[0-9]+\.[0-9]+\.[0-9]+') + if [ -f VERSION ]; then + FILE_VERSION=$(tr -d '[:space:]' < VERSION) + if [ "$BASE" != "$FILE_VERSION" ]; then + echo "::error::tag $REF (base $BASE) does not match VERSION ($FILE_VERSION) - bump VERSION on develop first." + exit 1 + fi + else + echo "::warning::no VERSION file at this ref (pre-train tag) - skipping the consistency check." + fi echo "tag=$REF" >> $GITHUB_OUTPUT echo "version=$VERSION" >> $GITHUB_OUTPUT echo "Building $REF (version=$VERSION)" @@ -202,6 +217,15 @@ jobs: id: tag run: | REF="${{ inputs.ref || github.ref_name }}" + # STRICT stability rule: only a plain vX.Y.Z tag is a stable release. + # Anything else (v1.2.3-rc.1, and typos like v1.2.3rc1) is marked + # prerelease, so it can never become 'latest' -- which is what the + # installer bootstrap (releases/latest/download/...) resolves. + if printf '%s' "$REF" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+$'; then + echo "prerelease=false" >> $GITHUB_OUTPUT + else + echo "prerelease=true" >> $GITHUB_OUTPUT + fi echo "tag=$REF" >> $GITHUB_OUTPUT - name: Create GitHub Release @@ -210,9 +234,9 @@ jobs: tag_name: ${{ steps.tag.outputs.tag }} name: ${{ steps.tag.outputs.tag }} generate_release_notes: true - # Mark as prerelease for v*.*.* tags containing - (e.g. - # v0.1.0-rc1). Plain semver releases are stable. - prerelease: ${{ contains(steps.tag.outputs.tag, '-') }} + # Strict: only plain vX.Y.Z is stable (computed above); rc tags and + # malformed variants are prereleases and never become 'latest'. + prerelease: ${{ steps.tag.outputs.prerelease == 'true' }} files: | dist/tracebloc-* dist/SHA256SUMS From 17826e66af6dc08df1313365b0f671f21ed55172 Mon Sep 17 00:00:00 2001 From: lukasWuttke <54042461+LukasWodka@users.noreply.github.com> Date: Tue, 28 Jul 2026 17:20:45 +0200 Subject: [PATCH 3/3] style: quote GITHUB_OUTPUT redirects (shellcheck SC2086) Co-Authored-By: Claude Opus 4.8 --- .github/workflows/release.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d72d5a7..bb79a56 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -115,8 +115,8 @@ jobs: else echo "::warning::no VERSION file at this ref (pre-train tag) - skipping the consistency check." fi - echo "tag=$REF" >> $GITHUB_OUTPUT - echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "tag=$REF" >> "$GITHUB_OUTPUT" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" echo "Building $REF (version=$VERSION)" - name: Build binary @@ -222,11 +222,11 @@ jobs: # prerelease, so it can never become 'latest' -- which is what the # installer bootstrap (releases/latest/download/...) resolves. if printf '%s' "$REF" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+$'; then - echo "prerelease=false" >> $GITHUB_OUTPUT + echo "prerelease=false" >> "$GITHUB_OUTPUT" else - echo "prerelease=true" >> $GITHUB_OUTPUT + echo "prerelease=true" >> "$GITHUB_OUTPUT" fi - echo "tag=$REF" >> $GITHUB_OUTPUT + echo "tag=$REF" >> "$GITHUB_OUTPUT" - name: Create GitHub Release uses: softprops/action-gh-release@v3