From 98a2d077683cd51272b8759589679532b2f8dc11 Mon Sep 17 00:00:00 2001 From: Ildar Nurislamov Date: Wed, 26 Aug 2026 18:37:23 +0400 Subject: [PATCH] feat(release): publish each tag as an immutable GitHub release The repo now has immutable releases enabled; a bare tag is not protected, only tags attached to a published release. release.yml publishes one per tag so consumers pinning @ get SHA-pin guarantees. Co-Authored-By: Claude Fable 5 --- .github/workflows/release.yml | 17 +++++++++++++---- README.md | 10 ++++++---- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b0cb552..cc04178 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,17 +16,26 @@ jobs: with: fetch-depth: 0 - - name: "🏷️ Create and push tag" + # The repo has "immutable releases" enabled, so publishing a release + # locks its tag to this commit: it can't be moved or deleted while the + # release exists, and the tag name can never be reused. Consumers that + # pin `@` therefore get SHA-pin guarantees. A bare tag is NOT + # protected — only tags attached to a published release are — so the + # tag is created BY the release (--target) rather than pushed first: + # there is never a window with an unprotected tag on origin. + - name: "🏷️ Publish immutable release" id: tag + env: + GH_TOKEN: ${{ github.token }} run: | set -euo pipefail COMMIT_COUNT=$(git rev-list --count HEAD) DATE=$(date -u +%Y%m%d) TAG="1.${COMMIT_COUNT}.${DATE}" - git tag "$TAG" - git push origin "$TAG" + gh release create "$TAG" --target "$GITHUB_SHA" --title "$TAG" \ + --notes "$(git log -1 --pretty=%s)" echo "tag=$TAG" >> "$GITHUB_OUTPUT" - echo "Created tag: $TAG" + echo "Released $TAG at $GITHUB_SHA" - name: "📝 Write summary" run: echo "Released tag \`${{ steps.tag.outputs.tag }}\`" >> "$GITHUB_STEP_SUMMARY" diff --git a/README.md b/README.md index c23f2ac..4c25798 100644 --- a/README.md +++ b/README.md @@ -83,10 +83,12 @@ Use the `review_instructions` input to focus the review on what matters for your #### Versioning -Every merge to `main` is tagged `1..` by -[`release.yml`](.github/workflows/release.yml). Consuming repos pin a release tag and bump -it deliberately — there is no floating tag, so a github-workflows release never changes a -consumer's review behaviour until that repo opts in. +Every merge to `main` is tagged `1..` and published as a GitHub +release by [`release.yml`](.github/workflows/release.yml). The repo has **immutable +releases** enabled, so a released tag can't be moved or deleted and its name can never be +reused — pinning `@` is as strong as pinning a SHA. Consuming repos pin a release tag +and bump it deliberately; there is no floating tag, so a github-workflows release never +changes a consumer's review behaviour until that repo opts in. ## Composite actions