chore(release): v0.20.0 #278
No reviewers
Labels
No labels
bump
major
bump
minor
bump
patch
kind/bug
kind/chore
kind/docs
kind/feature
priority/critical
priority/high
priority/low
priority/medium
size/L
size/M
size/S
size/XL
No milestone
No project
No assignees
3 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
frankenbit/release-toolkit!278
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "i/271-fragment-check-reusable"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Added
A push-whitelist pre-flight now runs at the start of a path-α cut, before
draft-release.shdoes any work. It probes the consumer repo's default-branch protection and fails fast with an actionable message if the release-bot identity (the push token's owner) isn't inpush_whitelist_usernames— converting the late, mid-cutNot allowed to push to protected branchrejection (which leaves the manifest desynced behind the tag/release, as the tmux-tell first toolkit cut hit) into an early, clear failure naming the missing identity. It fails only on a definitive misconfig (whitelist enabled + identity absent + no covering team) and warns-and-proceeds on anything it can't determine, so a probe limitation never blocks a cut that would actually succeed. Path-γ cuts (PR-mediated manifest, no direct-push) skip the check.docs/integration.mdnow states thepush_whitelistmembership as an explicit path-α-only precondition.Fixed
The rolling-PR
chore(release): prepare vX.Y.Zpush now authenticates withFORGEJO_TOKEN(the path-αRELEASE_TOOLKIT_TOKENPAT) instead of theactions/checkout-persisted system token. Previously the prepare-commit push was a plaingit pushthat reused the workflow's system token, which Forgejo Actions' anti-recursion safeguard suppresses — so no CI ran on the rolling PR and its required checks could never be satisfied, blocking the merge (observed on the first toolkit-driven tmux-tell cut). The push now injects the token as an HTTPAuthorizationheader (mirroring the manifest push), so a real-user PAT triggers downstream CI; local/standalone runs with noFORGEJO_TOKENset push plainly as before. Note: this is necessary but not sufficient — the consumer must still setRELEASE_TOOLKIT_TOKENto a real-user PAT (the documented path-α setup) for CI to fire.changelog.dfragment and a conventional-commit subject reference the same issue number ((#NNN)),release-prep.shnow suppresses the CC-subject bullet in favour of the fragment prose. Previously both entries appeared (the v0.1 behaviour), and any manual dedup was silently wiped when the rolling prep PR regenerated on rebase (tmux-tell#630 — deduped twice in one session; surfaces #258). Fragment prose is richer; the CC-subject bullet is a mechanical extract — the richer source wins. A[changelog-dedup]line is logged to stderr for auditability. (#258)release-decidenow detects thechore(release): prepare vX.Y.Zcommit anywhere in the merged rolling-PR range, not only at HEAD. Previously, any commit landing on top of the prepare commit before the rolling PR merged (a post-rebaseci: triggernudge, a CHANGELOG dedupe) shifted HEAD off the prepare subject, so the cut was silently skipped: the manifest stayed at the old version, no tag or release was produced, and a stale rolling PR was opened against the old baseline (observed on the first toolkit-driven tmux-tell cut). The range scan is a full git-log walk, so a merge-commit's second-parent prepare commit is detected too. The existing Layer 2/3 branch-source + author safeguards still gate the cut, so a stale or unrelated prepare commit in range falls through tomode=updateloudly (safeguard_fail=...) rather than silently. Squash-merge of the rolling PR (which drops the prepare subject entirely) is not yet covered; that needs the Layer 2head.refsignal promoted to the primary cut signal (tracked in #259 as the follow-up fix).Post-cut bake-reset commit no longer fails with "nothing to commit" on external consumers. The reusable-release.yml's bake-reset blocks (path-α and path-γ) now wrap the
git commit+ push in agit diff --cached --quietguard — when the consumer doesn't have.forgejo/workflows/reusable-release.yml/reusable-manifest-check.ymlin its own checkout (true for every external consumer; the bake-reset is toolkit-self-only by design), the staging area is empty after thegit addloop and the commit/push are cleanly skipped instead of halting the workflow. This was the substrate-bug behind every external cut since v0.22.0 needing manual manifest recovery: the bake-reset step's failure cascaded into the manifest update never landing, sorelease-decide.shcouldn't advance its walk window on the next run. Toolkit-self continues to operate unchanged — the toolkit's own workflow files ARE present in its own checkout, sogit diff --cachedreports staged changes and the commit + push proceed normally.release-prep.shPR body assembly now re-normalizes throughchangelog_merge_sectionsso any surviving duplicate### Kindheadings collapse to one section per kind. Belt-and-suspenders on top of the merge already done inchangelog_transition; ensures the rolling-PR body stays idempotent-clean at the assembly step regardless of upstream drift. Closes #272.forgejo_find_pr_by_headnow filters response client-side by exacthead.label/head.refmatch. Forgejo'shead=query filter is empirically ignored (returns every open PR); without the client-side guard,.[0].numberpicked whichever PR the API sorted first — mutating a random feature PR's title + body instead of the actual rolling PR (release-toolkit#274, reproduced on tmux-tell v0.29.0 cycle 2026-07-01). No consumer-side change required. Closes #274.REQUEST_CHANGES — reviewed at head
a78c0eeThe reusable, wrapper, script, docs, and tests are well-built and the dogfood shape is right. But there's one must-fix that ships a latent immutable-pin violation with this PR's own artifact.
Must-fix: the new reusable is not registered in the build-bake machinery
reusable-changelog-fragment-check.ymlcarries the baked-ref stanza:That comment's claim is false for this file — the auto-update is driven by hardcoded file lists, not marker-glob discovery, and this new reusable is absent from all four of them:
scripts/release-prep.sh:541reusable-release/reusable-manifest-checkto the tag but leaves fragment-check at'main'.scripts/release-prep.sh:657scripts/check-self-bootstrap.sh:80scripts/lib/build_bake.sh:118create_detached_bake_commit(repin.sh rc path)Consequence: when v0.20.0 is cut, the tagged commit's
reusable-changelog-fragment-check.ymlstill saysBUILD_BAKED_TOOLKIT_REF: 'main'. A consumer pinning@v0.20.0(tmux-tell/cellblock per AC #3) gets a reusable whose self-checkout step pullsfrankenbit/release-toolkit@main— running whatever is on main at execution time, not the pinned tag. That is exactly the driftbuild_bake.sh:105documents as "the 483d regression" and mutation-tests against for the other reusables. This PR reintroduces that class for its own file.Fix: add
.forgejo/workflows/reusable-changelog-fragment-check.ymlto all four lists.Strongly recommend (companion, to stop the recurrence): the root cause is four scattered hardcoded lists with no construction-enforcement that they stay in sync with the set of marker-carrying reusables. Either (a) hoist the list to a single shared array sourced by all four sites, or (b) add a
workflows.batsguard asserting every.forgejo/workflows/reusable-*.ymlcarrying therelease-toolkit-build-refmarker is present in the bake list. Without one of these, the next new reusable silently reintroduces this — andcheck-self-bootstrapwon't catch it (it didn't catch this one).Should-consider (non-blocking)
@v0.19.0while the current self-pin is@v0.20.0-rc.2. Illustrative, but bump it to the current tag to avoid copy-paste of a stale ref.Verified clean
fragment-check.sh:set -euo pipefail;assert_no_unknown_kindsexplicitlyreturn 1s (safe inside theif, which disablesset -e) and enumerates all offenders; missing dir → exit 0. shellcheck-xclean fromscripts/.fragment-check.bats7/7 green;workflows.bats13/13 green (incl. the dogfood-guard).paths:filter scopes cost). Good calls.Happy to re-review fast on the bake-list fix — the rest is merge-ready.
feat(fragment-check): reusable PR-gate for changelog-fragment kinds (#271)to chore(release): v0.20.0.forgejo/workflows/fragment-check.ymlto release-toolkit (post-v0.20.0-cut) #281APPROVED — re-review at head
9c6dc0e(delta froma78c0ee)Must-fix resolved and the recurrence-guard is real. Delta verified at source on live state:
Bake-list registration — all 4 sites now carry the reusable:
release-prep.sh:541(build-bake→tag) ✓ ·:657(bake-reset) ✓check-self-bootstrap.sh:81(DEFAULT_COMPOSE_SCRIPTS) ✓build_bake.sh:118(create_detached_bake_commitdefaults) ✓No 5th gap. Confirmed the content-comparison in
check-self-bootstrapwon't false-red at cut time:NORM_SEDis marker-generic ('[^']*' # release-toolkit-build-ref), and the new reusable's marker line has the exact'main' #two-space spacing, so it normalizes to'__BAKE_NORMALIZED__'at both HEAD and the rc tag → equal. The dropped wrapper is coherent becauseWRAPPERSis glob-discovered (.forgejo/workflows/*.ymlminusreusable-*), so removingfragment-check.ymljust drops it from the wrapper set — no dangling ref (the only remaining workflows.bats mention is the reusable-presence check at line 136).Regression guard mutation-verified. I dropped the reusable from
build_bake.sh:118, re-ran — the guard reds with the precise diagnosticreusable-changelog-fragment-check.yml:...build_bake.sh(missing); reverted (empty diff); guard passes again (workflows.bats 13/13). It catches exactly the class: any futureBUILD_BAKED_TOOLKIT_REF:-bearing reusable missing from a list fails at PR time. This is the construction-enforcement that closes the "four scattered lists" root cause — the right fix.Deferrals properly tracked: AC#3 wiring → #280 (open), self-dogfood wrapper → #281 (open). Dropping the wrapper from this PR is the correct bootstrap call (can't pin
@v0.20.0-rc.2before the reusable exists at that tag;@mainbreaks single-ref alignment). #271's core deliverable (pre-merge gate + script + tests + docs) lands here; #280/#281 carry the genuinely post-cut-bootstrap-blocked remainder.Minor (non-blocking, no action needed): the guard's
grep -c "$base"counts line-occurrences rather than list-membership, so a stray comment mention could satisfy the ≥2 on release-prep.sh — but it reliably catches total omission, which is the point. Docstring bumped to@v0.20.0.Merge-ready. Nice fold.
9c6dc0eba0fccedcfa81New commits pushed, approval review dismissed automatically according to repository settings