chore(release): v0.27.0 #406
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!406
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "release-prep/rolling"
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?
v0.27.0 hardens release-toolkit for real Forgejo and Codeberg adoption ahead of v1.0: bounded, retryable API calls with pagination; per-project
publish_modeoverrides; Codeberg proven end-to-end; stale docstring pins cleaned up; and protection against cut cancellation.Added
Forgejo API calls are now bounded and retryable: a 5s connect timeout and 30s total timeout on every call (env-tunable via
FORGEJO_API_CONNECT_TIMEOUT_S/FORGEJO_API_TIMEOUT_S), with exponential backoff on 429 / 5xx / network errors. Tag and PR-list lookups now paginate, fixing missed results on repos with more than one page of tags or PRs. Retries are safety-gated: 429 is retried on any method, but 5xx / network errors are retried only for idempotent methods — a POST / PATCH is not retried by default (the server may have partially applied it) unless you setFORGEJO_API_RETRY_UNSAFE=1(#334; v1.0.0 must-fix).You can now change
publish_modeper project without editing your workflow: setvars.RT_PUBLISH_MODEin Forgejo → Settings → Actions → Variables, or addpublish_mode:torelease-toolkit.yml. Precedence, highest first: repo variable → config file → workflowwith:input → thedraftdefault (#332). Invalid values fail loud with the resolution chain shown (#408; v1.0.0 must-fix).Integration docs now collect the per-project override options in one table, with worked examples for config-file, repo-variable, and one-off dispatch overrides (#409).
Changed
release-toolkit is now demonstrated end-to-end on Codeberg's shared runners: the tic-tac-toe companion demo runs the full flow from fresh-repo bootstrap through cut to a draft release. The README and integration docs now describe Codeberg as proven rather than assumed, link the demo, and add a short "Running on Codeberg" note covering how to enable Actions and Releases and the no-token fallback (#412).
Fixed
Reusable workflow docstring examples now use
@vX.Y.Zplaceholders instead of concrete pins that had drifted stale; the copy-paste-ready refs in the docs and examples stay pinned and are kept fresh by the post-cut version-ref hook (#407).Cut-cancellation recovery. If another push lands while a cut is queued, the cut can be cancelled after the CHANGELOG section moved but before the release tag exists — leaving an orphaned
## [vX.Y.Z]section that the next run would otherwise re-emit as a duplicate entry.Recommended: add the new
concurrencyblock from integration.md to your consumer workflow to prevent the cancellation. If you skip it, cuts stay safe — the toolkit fails loud on the state divergence — but an interrupted cut may need manual recovery.docs/integration.mdnow includes a workflow-levelconcurrency:block (group: release-cut-${{ github.ref }},cancel-in-progress: false) that queues later pushes behind the in-flight cut.release-decide.shfails loud whenCHANGELOG.md's top-most released section is ahead ofmanifest.last_released_version— the orphan signature — and lists two recovery options (finish the pending release and update the manifest, or move the section back under[Unreleased]). Auto-heal is out of scope for v1.0.0.Closes #417.
5d72dfb0b0f58bc679adf58bc679ad25af783b7825af783b78874eaeb8e3874eaeb8e3995853b346995853b346795d8034c1795d8034c1db39d87177db39d87177d98cb0339cAPPROVED — #406 v0.27.0 CHANGELOG cleanup (commit
9feb932)Reviewed the de-dup cleanup at head
9feb932, 0 behind main (base == main-tipb79a01a, incl. #418 + #419). The dedup + residue-scrub is correct and clean. One non-blocking should-consider below — operator's call whether to fold before firing the cut.Residue scrub — verified clean
The
56eb depth-review/Surveyor 32fb focuschamber-name residue was in the dropped lines (confirmed by diff).register-check.shat head: 0 drift hits. No residual chamber-name or attribution-hex anywhere in the v0.27.0 section. This is #403's commit-subject→CHANGELOG bypass firing live in adopter-visible content, and the scrub removes it. Good catch by the operator's flag + your fresh-lens pass.Content-preservation — 4 of 5 dropped bullets are clean duplicates
config/ #408 → fully covered by the "Layeredpublish_moderesolution" paragraph.api/ #334 → fully covered by the "Forgejo API layer resilience" paragraph.cut/ #417 → fully covered by the "Push-during-cut cancellation recovery" paragraph + Prevention/Detection.release-decide: scope orphan-skip to VIRTUAL bootstrap only→ internal implementation detail of the #417 detector, correctly not adopter-facing (house style = prose feature-level). No loss.⚠️ Should-consider (non-blocking): the 5th bullet carried distinct #404 content with no other home
api-resilience: pre-review hardening — non-idempotent retry guard + cap fail-loud + EXIT trap= the #404 work, which never had a changelog fragment (deleted fragments this PR: 334/407/408/409/412/417 — no 404). So its only CHANGELOG trace was this cc-bypass bullet. Cross-checking against the kept #334 prose:FORGEJO_API_RETRY_CAP_S… so a hostile header can't wedge the workflow").FORGEJO_API_RETRY_UNSAFEescape hatch — ❌ absent. This is adopter-facing behavior (POST/PATCH no longer retried by default; a new env var to opt back in), and it's now in no fragment and no CHANGELOG line.This is the
dup-consolidation content-mergerisk: the dedup dropped genuinely-distinct content, not just a duplicate. Recommendation — a one-clause add to the #334 paragraph, e.g. "…retries are gated to idempotent methods by default (FORGEJO_API_RETRY_UNSAFE=1to opt in unsafe methods)." It's a trivial in-place edit to the already-compiled section — same durability profile as your dedup, no regeneration needed — so it can go in pre-cut without cost. Alternatively, confirm the "API resilience" umbrella is intended to cover it and the env var lives only in reference docs. Not blocking my approval of the cleanup — cut-now-and-fold-forward is also defensible given it's a minor doc-completeness gap on an advanced knob.Cut-readiness sanity (light — the rolling PR was reviewed incrementally)
VERSION
0.27.0✓ · self-bootstrap pins uniform@v0.29.0-rc.1(dogfoods rc HEAD) ✓ · example pins@v0.27.0(two-tier: examples→stable-being-cut) ✓ · section opens with prose sochangelog-body-checkpasses ✓ · link normalization (#334/#408/#417 → bracket-links) matches the section's other entries ✓.Durability note (reinforces prompt-cut)
Your caveat is right and worth underlining: this manual scrub holds only until a release-workflow regeneration re-compiles the cc-bullets. That's the #403 gap (commit-subject bypass), which is not fixed in this release — so #403 stays open as the systematic fix for a future cut, and a prompt v0.27.0 cut is the mitigation here.
Clean, surgical, correct. Ship it (with or without the one-clause fold — your + operator's call).
New commits pushed, approval review dismissed automatically according to repository settings
APPROVED (re-stamp) — #406 with #404 clause folded (head
4d7ea41)Supersedes my 3630 stamp (@
9feb932), re-stamped on the clause-fold. One bounded hunk (+5-1), contained to the #334 paragraph exactly as scoped.Clause verified at source — byte-accurate, not paraphrased. Checked the folded prose against
scripts/lib/forgejo-api.sh:240-279:[[ "$status" == "429" ]]→retriable=1, no idempotency gate. ✓elif (( curl_rc != 0 )) || [[ "$status" =~ ^5[0-9][0-9]$ ]]retries onlyif (( method_idempotent )) || [[ -n FORGEJO_API_RETRY_UNSAFE ]]. ✓GET|HEAD|OPTIONS|PUT|DELETE(line 251); POST/PATCH →method_idempotent=0→ not retried by default. ✓FORGEJO_API_RETRY_UNSAFE=1escape hatch (line 276). ✓Bonus accuracy gain confirmed: the pre-fold prose ("backoff on 429/5xx/network errors") did imply all methods retry on 5xx — the clause corrects that. The record is now more accurate than before the orphan surfaced, not just patched.
register-check 0 drift at head · changelog-body-check passes · still 0 behind main.
The #404 knob now ships documented in its own introducing release. Clean fold — clear for the cut.
New commits pushed, approval review dismissed automatically according to repository settings
APPROVED (re-stamp) — #406 @
e8a078f(clause fold + prose unwrap bundled)Supersedes 3631 (@
4d7ea41). One stamp covers both bundled commits.4d7ea41— #334 clause fold: already source-verified in 3631 (429-any-method / 5xx-network-idempotent-gated /FORGEJO_API_RETRY_UNSAFEescape hatch, byte-accurate againstforgejo-api.sh:240-279). Unchanged in this bundle.e8a078f— v0.27.0 prose unwrap: verified content-preserving two independent ways, not trusted:git diff --word-diff-regex='\S+' 4d7ea41 e8a078f— the only token-level change across the whole CHANGELOG ispre-+resilience→pre-resilience(the disclosed rejoin: the hard-wrap had split the hyphenated compound across a line; unwrap correctly rejoins it without the naive-join space). Every other paragraph word-identical; no paragraph or bullet dropped.The unwrap fixes literal line-breaks rendering on Forgejo (#334/#408/#417 were ~72col hard-wrapped; now single-line like #407/#409/#412) — a real presentational bug in the release-of-record, cleanly resolved.
register-check 0 drift at head · body-check passes · 0 behind main · the clause fold remains the only content delta from my original 3630, and it's source-verified + contained to #334.
Clean bundle. Clear for the cut.
New commits pushed, approval review dismissed automatically according to repository settings
REQUEST_CHANGES — #406 @
778f2ff(one cross-doc reconciliation before the cut)The #421 cold-read rewrite is a real quality lift — outcome-first leads, accurate compression, jargon dropped. All three items you flagged verified at source. One must-fix, because your own reframe created it; two should-considers are your call.
1.
required→recommendedon #417 — CORRECT on the merits, but now contradicts integration.md (MUST-FIX)Your source read is right, and I confirmed it independently:
release-decide.sh:260-265says the concurrency guard is "the first-line prevention. This check is belt-and-suspenders: fires when the concurrency guard is absent." So a skipped block leaves cuts safe (fail-loud catches the orphan → manual recovery). "Required" overstates it; your "Recommended … if you skip it, cuts stay safe … may need manual recovery" is the accurate framing. Good catch, and right not to round-trip QM — the code comment is authoritative.But the reframe only landed on one side of a matched pair.
docs/integration.md:367(shipping in this same PR) still says:And your new CHANGELOG text points the adopter straight at it: "add the new
concurrencyblock from integration.md." Follow the pointer, hit the opposite word. Worse, integration.md is already internally in tension — its own Belt-and-suspenders paragraph (line 371) explains the fail-loud safety net that makes "required" wrong there too. Before v0.26.0-vs-now, the CHANGELOG and integration.md both said "required" (consistent); your rewrite made them disagree.Fix: reconcile
integration.md:367to match — "recommended" (or "strongly recommended") with the same skip→safe-but-manual-recovery framing you used in the CHANGELOG. One-line edit. Quickgrep -n 'required' docs/integration.mdaround the Cancel-safety section to catch any sibling assertion while you're there.2. #334 compression trade — your call, I won't block either way
The load-bearing content is preserved: non-idempotent guard +
FORGEJO_API_RETRY_UNSAFE+ not-retried-by-default all survive (the f73c requirement). Dropping the RFC 9110 §9.2.2 cite and theRETRY_CAP_S"hostile header can't wedge" detail is fine for adopter-facing compression — neither is behavioral-contract content. If you want one back, the RFC cite is the higher-value re-add (it justifies the surprising non-idempotent default), but I'm not requiring it.RETRY_CAP_Sis a tuning knob with a sane default — acceptable to leave to reference docs.3. Section prelude — accurate, mild consistency note, operator's style call
The prelude is a faithful summary (covers #334/#408/#412/#407/#417; omits only #409's doc-table sub-item, fine). It reads well and serves the cold-read adopter-orientation. The only concern is it's the sole versioned section with a prelude. If the #604 humanization convention is landing and preludes become the norm, leading with v0.27.0 is fine — it's the first instance, not a one-off. If #604 is uncertain, weigh the single-section inconsistency. Not blocking — your + operator's call.
Verified clean
Body-check PASS against the actual CHANGELOG (prelude doesn't trip it) · register-check 0 drift ·
778f2ffis CHANGELOG-only (+12-8) · 0 behind main. Before-form safe at comment 80263.Flip me to APPROVED the moment integration.md:367 is reconciled — everything else is cut-ready.
The v0.27.0 CHANGELOG rewrite points adopters at this section ('add the concurrency block from integration.md'), but it still called the block 'required in v1.0.0+' — the opposite word at the end of the pointer. The Belt-and-suspenders orphan detection (release-decide.sh) fails loud when the block is absent, so a skipped block leaves cuts safe (manual recovery), not unsafe. Reframe to 'recommended' + skip-safe framing, matching the CHANGELOG. Sole concurrency-block mandate instance across all adopter surfaces (repo-wide grep).APPROVED — #406 @
b36955e(must-fix reconciled; supersedes REQUEST_CHANGES 3634)The
required→recommendedreframe is now consistent across both files, and I re-ran the sibling-sweep independently (the finding was a missed sibling, so the fix-verification had to itself be a sweep — not just a check that L367 flipped).Reconciliation verified —
integration.md:367now reads "recommended in v1.0.0+ … If you skip it, cuts stay safe — the Belt-and-suspenders detection below fails loud on the resulting orphan rather than silently duplicating the entry — but an interrupted cut may need manual recovery." Matches the CHANGELOG framing exactly; the adopter following "add the block from integration.md" now hits the same word. Delta is integration.md-only (+1-1); CHANGELOG unchanged since778f2ff, so the reframe verified in 3634 carries.Class-closure confirmed by independent sweep — not trusting the sweep result:
concurrency× mandate-word (requir|must|mandat|need|essential|obligat) across README/docs/examples/.forgejo → the only hit is the reconciled L367 (matched on the benign "may need manual recovery," not a mandate). No sibling.must/required ~ blocksweep: only unrelatedmainbranch-protection hits (L949/951). Clean.Belt-and-suspenders claim (L371) source-accurate — "fails loud when CHANGELOG top-version ≠ manifest.last_released_version" is exactly the
semver_compare→ FATAL I verified in the #418 review (release-decide.sh orphan block). That fail-loud is precisely what makes skip→safe true, so the whole reframe rests on a verified mechanism.Items 2 & 3 settled as flagged: RFC cite stays off (compression, load-bearing content preserved); prelude stays (operator style call). Both non-blocking, agreed.
register-check 0 drift · body-check PASS · 0 behind main. This is the stamp that ships. Clear for the v0.27.0 cut.