fix: pull-requests scope for the door's two PR-API calls; docs catch up to the shipped shape
claude-bot's round-4 blockers: (1) a declared permissions: block zeroes every unspecified scope, so the decide step's label read (commits/<sha>/pulls) and the bump fallback's gh pr create could only 403 — every genuine ceremony would end red at the label check, the exact failure shape this feature exists to kill, one layer down. labels.yml already carries the precedent; pull-requests: write added with the two consumers named. (2) CONTRIBUTING still prescribed the follow-up bump PR the workflow now performs itself, and the changelog entry described the old PR-base interlock — both now tell the shipped story (event.before interlock, self-re-arm, manual-path bump stays the operator's). Nits taken: fetch-depth 2 for the all-zeros fallback's first parent, re-runs refuse-loudly wording, the bump-window arithmetic comment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
a5d108cbf2
commit
b92919d4d0
3 changed files with 43 additions and 20 deletions
21
.github/workflows/release.yml
vendored
21
.github/workflows/release.yml
vendored
|
|
@ -39,7 +39,11 @@ on:
|
|||
branches: [main]
|
||||
|
||||
permissions:
|
||||
contents: write # tag create via the API + gh release create
|
||||
contents: write # tag create via the API + gh release create + the bump push
|
||||
# Two consumers (labels.yml precedent — a declared permissions: block
|
||||
# zeroes every unspecified scope): the decide step's label read
|
||||
# (commits/<sha>/pulls) and the bump fallback's `gh pr create --label`.
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
release:
|
||||
|
|
@ -58,6 +62,10 @@ jobs:
|
|||
# merge commit the maintainer shipped, which the tag created
|
||||
# below will name).
|
||||
ref: ${{ github.sha }}
|
||||
# Depth 2: the pushed head's first parent must be resolvable for
|
||||
# the decide step's all-zeros fallback (event.before on a
|
||||
# branch-creation push).
|
||||
fetch-depth: 2
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "22"
|
||||
|
|
@ -159,9 +167,10 @@ jobs:
|
|||
MERGE_SHA: ${{ github.sha }}
|
||||
run: |
|
||||
# Assert 4 — no tag and no release exist for this version. Re-runs
|
||||
# stay idempotent, and a manual race (an operator who tagged by
|
||||
# hand between merge and here) fails loudly instead of
|
||||
# double-publishing.
|
||||
# of a completed ceremony REFUSE LOUDLY (red, creating nothing —
|
||||
# the correct direction), and a manual race (an operator who
|
||||
# tagged by hand between merge and here) fails the same way
|
||||
# instead of double-publishing.
|
||||
if git ls-remote --exit-code origin "refs/tags/$RELEASE_VERSION" > /dev/null; then
|
||||
echo "tag '$RELEASE_VERSION' already exists — creating nothing (already released, or a manual tag won the race)." >&2
|
||||
exit 1
|
||||
|
|
@ -214,6 +223,10 @@ jobs:
|
|||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
# next is computed from the RELEASE tree (the checkout), then
|
||||
# applied to whatever main is by the time of the push — if main
|
||||
# moved in the window, release+1 still lands on the newer head,
|
||||
# which is the intended arithmetic either way.
|
||||
next="$(node -p 'const v = require("./package.json").version.split("."); v[2] = String(Number(v[2]) + 1) + "-dev"; v.join(".")')"
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
|
|
|||
29
CHANGELOG.md
29
CHANGELOG.md
|
|
@ -9,17 +9,24 @@ actually cutting it, and this file starts there.
|
|||
|
||||
### Added
|
||||
|
||||
- **Merging a release-labeled PR is the release** (#111; box#96's design) —
|
||||
`release.yml` now also fires when a `release`-labeled PR merges into
|
||||
main: it asserts fail-loud (non-`-dev` version, version changed in the
|
||||
PR — the `-dev` interlock, changelog section extracts non-empty, no
|
||||
existing tag or release), then tags the merge commit, builds the
|
||||
`cast-X.Y.Z.tgz` asset once, and publishes — the maintainer's merge is
|
||||
the ship decision, no silent-when-forgotten manual tag step. The
|
||||
tag-push path stays as the documented fallback and backfill, and both
|
||||
paths run the same steps so they cannot drift. First-release edge:
|
||||
0.1.0 never carried `-dev`, so its ceremony (#110) ships by manual tag;
|
||||
the automation applies from 0.1.1 on.
|
||||
- **Merging a release-labeled PR is the release — and the release re-arms
|
||||
main itself** (#111; box#96's design) — `release.yml` now also fires on
|
||||
pushes to main (not `pull_request` events: fork-sourced ceremony PRs get
|
||||
a read-only token there — the round-1 catch). A decide step reads the
|
||||
version transition from the push (`event.before` → the pushed head) and
|
||||
answers four states: release-flow *work* merged under the `release`
|
||||
label — `-dev` endstates, and the post-release window — no-ops green
|
||||
with a NOTICE; the two genuinely ambiguous bare states refuse loudly;
|
||||
a true transition then requires a merged, `release`-labeled PR behind
|
||||
the commit (read via the API — the label is the operator's declared
|
||||
intent) before the door opens. It then tags the merge commit, builds
|
||||
the `cast-X.Y.Z.tgz` asset once, publishes — and bumps main to
|
||||
`X.Y.(Z+1)-dev` itself, direct push with a loud open-a-PR fallback, so
|
||||
no follow-up bump PR exists on the paved road. The tag-push path stays
|
||||
as the documented fallback and backfill, and both paths run the same
|
||||
steps so they cannot drift. First-release edge: 0.1.0 never carried
|
||||
`-dev`, so its ceremony (#110) ships by manual tag; the automation
|
||||
applies from 0.1.1 on.
|
||||
|
||||
### Fixed
|
||||
|
||||
|
|
|
|||
|
|
@ -69,12 +69,15 @@ on box#83's shape):
|
|||
*Manual fallback and backfill:* push a bare `X.Y.Z` tag on the merge
|
||||
commit yourself — the same workflow runs the same asserts, build, and
|
||||
publish from the tag.
|
||||
3. **Right after the release, a follow-up PR bumps `package.json` to
|
||||
`X.Y.(Z+1)-dev`** (and `package-lock.json` with it) — box#90's step of
|
||||
the family ritual. Installs are versioned by the tree's `package.json`
|
||||
version, so a `CAST_REF=main` install between releases must land as
|
||||
3. **The release re-arms main itself**: the same workflow run bumps
|
||||
`package.json` (and `package-lock.json`) to `X.Y.(Z+1)-dev` and pushes
|
||||
the commit straight to main — no follow-up PR (it opens one only if
|
||||
branch protection refuses the direct push, and says so loudly).
|
||||
Installs are versioned by the tree's `package.json` version, so a
|
||||
`CAST_REF=main` install between releases must land as
|
||||
`versions/X.Y.(Z+1)-dev`, never as `versions/X.Y.Z` — main's tree must
|
||||
not impersonate the release it merely descends from.
|
||||
not impersonate the release it merely descends from. On the *manual*
|
||||
tag path the bump stays yours: open the one-line PR after publishing.
|
||||
|
||||
## Labels — who sets what
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue