Skip to content

feat(release): one command to lock a released branch, and prove it locked - #46

Merged
LMPrado-DZ23 merged 1 commit into
release/v3.8.55from
feat/release-lock-script
Sep 19, 2026
Merged

LMPrado-DZ23 merged 1 commit into
release/v3.8.55from
feat/release-lock-script

Conversation

@LMPrado-DZ23

Copy link
Copy Markdown
Owner

lock-released-branch.yml cannot run. It needs a BRANCH_LOCK_TOKEN secret; GITHUB_TOKEN cannot be granted the Administration scope, and only a PAT or fine-grained token can hold it. The secret does not exist, so the workflow has failed on every release and no release branch has ever been locked automatically — v3.8.54 shipped with its branch still writable and was locked by hand afterwards.

Creating that token is the repository owner's action. This is the fallback until it exists.

Why a script and not a line in the runbook

The failure Hard Rule #18 exists to prevent was a silent one. In the v3.8.3 incident six commits landed on an already-shipped version because nobody checked the lock had applied. A raw gh api PUT that half-works looks exactly like one that worked.

So the script always re-reads the protection from GitHub afterwards and exits non-zero unless lock_branch and enforce_admins both come back true. A green exit means the branch is genuinely read-only — not that a request was accepted.

npm run release:lock -- 3.8.54            # lock and verify
npm run release:lock -- 3.8.54 --check    # verify only
npm run release:lock -- 3.8.54 --unlock   # reopen

It accepts 3.8.54, v3.8.54 or release/v3.8.54, refuses anything that is not a version rather than guessing at a branch name, and percent-encodes the slash — an unencoded one makes GitHub read the branch as a nested path.

Tests

They assert the behaviour that matters, not that a PUT was sent:

  • a branch GitHub reports as unlocked is never reported as locked;
  • the read-back actually queries the protection endpoint instead of echoing what was sent;
  • non-versions (latest, main, 3.8, empty) are refused.
Gate Result
tests/unit/release-lock-branch-script.test.ts 7 pass / 0 fail
eslint --max-warnings=0 exit 0
prettier clean

Run with isolated DATA_DIR / HOME / USERPROFILE / APPDATA.

🤖 Generated with Claude Code

…cked

`lock-released-branch.yml` cannot run: it needs a `BRANCH_LOCK_TOKEN` secret,
`GITHUB_TOKEN` cannot be granted the `Administration` scope, and only a PAT or
fine-grained token can hold it. The secret does not exist, so the workflow has
failed on every release and no release branch has ever been locked
automatically — v3.8.54 shipped with its branch still writable and was locked by
hand afterwards.

Creating that token is the repository owner's action, so this is the fallback,
and it is a script rather than a line in a runbook for one reason: the failure
Hard Rule #18 exists to prevent was a SILENT one. In the v3.8.3 incident six
commits landed on an already-shipped version because nobody checked the lock had
applied. A raw `gh api` PUT that half-works looks exactly like one that worked.

So the script always re-reads the protection from GitHub afterwards and exits
non-zero unless `lock_branch` and `enforce_admins` both come back true. A green
exit means the branch is genuinely read-only — not that a request was accepted.

  npm run release:lock -- 3.8.54            # lock and verify
  npm run release:lock -- 3.8.54 --check    # verify only
  npm run release:lock -- 3.8.54 --unlock   # reopen

It accepts `3.8.54`, `v3.8.54` or `release/v3.8.54`, refuses anything that is
not a version rather than guessing at a branch name, and percent-encodes the
slash — an unencoded one makes GitHub read the branch as a nested path.

The tests assert the behaviour that matters: that a branch GitHub reports as
unlocked is never reported as locked, and that the read-back actually queries
the protection endpoint instead of echoing what was sent.

Verification (isolated DATA_DIR/HOME/USERPROFILE/APPDATA):
  tests/unit/release-lock-branch-script.test.ts   7 pass / 0 fail
  eslint --max-warnings=0                         exit 0
  prettier                                        clean

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 19, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 10c7e821-af8e-4e15-8488-a1e3a0ac7e86


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@LMPrado-DZ23
LMPrado-DZ23 merged commit 02a8926 into release/v3.8.55 Sep 19, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants