Repository navigation
fix(release): repair the release PR heading normaliser and lockfile sync - #274
Conversation
|
Fixed and pushed commit Task list (5/5 completed)
|
Signed-off-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Important
The release branch is not guaranteed to be rebased on every master push as documented; hidden-only pushes can still leave the existing release PR stale.
Reviewed changes
This review covers the CI version-gate fixes, release-please workflow/configuration changes, release documentation, and the new changelog-heading guard.
- Version gate — reads the Tauri version through JSON, rejects empty extractions, and avoids
grep/headpipeline failures. - Release synchronization — resolves the open release PR by label, synchronizes lockfiles, normalizes both changelog heading dialects, and pushes the repair commit.
- Release cadence — adds root-level
always-updateand documents the release branch as being rebuilt on everymasterpush. - Regression guard — tests the heading matcher, date capture, near-miss rejection, idempotency, and CI gate compatibility.
Targeted verification run: node test/check-changelog-heading.mjs, node test/check-release-config.mjs, and YAML/JSON parsing all passed. The full frontend/Rust chains were not run because this PR changes only CI/release automation and documentation.
gpt-6-luna | 𝕏
There was a problem hiding this comment.
Important
The incremental fix is correct, but the prior release-branch freshness issue remains unresolved: always-update does not cause release-please to process an existing PR when a master push contains only hidden commit types, so the documented every-push rebase guarantee is still not met.
Reviewed changes
This incremental review covers the changelog guard correction landed since the prior Pullfrog review.
- Escaped dotted versions — Updated the guard's substituted version to preserve literal dots when it constructs the CI gate
RegExp, preventing false matches for sibling version headings. - Validated the guard — Confirmed
node test/check-changelog-heading.mjspasses with the corrected pattern.
⚠️ Hidden-only pushes can leave the release branch stale
always-update only affects release-please's update decision after it has a release candidate to process. Because docs, test, and chore are hidden, a push containing only those commit types can leave an existing release branch behind master, despite the workflow and documentation claiming unconditional synchronization.
Technical details
# Hidden-only pushes can leave the release branch stale
## Affected sites
- `release-please-config.json:9` — `always-update` does not create a candidate for hidden-only pushes.
- `.github/workflows/release-please.yml:28-30` — the workflow triggers for every `master` push but does not itself rebase the existing branch when release-please has no candidate.
- `CONTRIBUTING.md:178-186` — documents unconditional branch rebuilding.
## Required outcome
- Either explicitly synchronize the existing release branch on every successful `master` push, including hidden-only pushes, or narrow the configuration and documentation claim to pushes for which release-please builds a candidate.gpt-6-luna | 𝕏
There was a problem hiding this comment.
✅ Pullfrog addressed all of its review feedback on this PR.
Addressed the review’s hidden-only push gap in 1bb23ea. The release synchronization job now rebases the labeled release branch onto origin/master on every successful run, force-updates only when needed, and the documentation plus release-config guard describe and verify the behavior. Targeted release guards, changelog guard, YAML/JSON validation, and whitespace checks passed; the commit was pushed and the review thread was replied to and resolved.
Task list (4/4 completed)
- Check repository guidance and durable learnings
- Checkout PR 274 and inspect review 5543353758
- Implement and verify all actionable review fixes
- Commit, push, reply to threads, and report results
gpt-6-luna | 𝕏
There was a problem hiding this comment.
✅ Pullfrog addressed all of its review feedback on this PR.
Review 5543385701 has no open inline threads. Its required hidden-only push fix is already present on PR 274 in commit 1bb23ea: the workflow resolves the labeled release branch, rebases it onto origin/master on every successful run, force-updates only when needed, and includes config/documentation regression coverage. Targeted changelog and release-config guards, workflow/config parsing, and whitespace validation pass; the branch is clean and no additional push was necessary.
Task list (4/4 completed)
- Check repository guidance and review metadata
- Checkout PR 274 and inspect all review threads
- Implement and test every actionable review comment
- Commit, push, reply to threads, and verify clean tree
gpt-6-luna | 𝕏

Summary
fix: repair the release-PR heading normaliser so the release PR stops failing CI, and rebase the release branch on every master push
The open release PR (#273) was failing the
lintjob with:The cause was not the changelog content — it was that the step meant to normalise the heading had never matched anything, and failed silently. Fixing that exposed two adjacent bugs that had been masking each other, plus a structural gap in how the release branch is kept in sync with
master.🐛 Bug Fixes
githubdialect here —## [0.2.0](https://…/compare/v0.1.8...v0.2.0) (2026-10-16)— but the step searched for the literal## 0.2.0 (, from thedefaultdialect. It never matched, so it printed "heading already normalised (or not found)" and exited 0. The un-normalised heading then shipped toci.yml, whose gate keys on thevprefix and rejected it. The pattern is now dialect-agnostic: the link target is matched loosely because the compare URL contains its own parentheses, and the version boundary is anchored strictly so a sibling section such as## 0.2.01or## 0.2.0-rc.1is not mistaken for this release.tauri.conf.jsonthrough JSON instead ofgrep -A2 '^version'. The version key is indented ("version": "0.1.8"), so the anchored grep never matched and silently produced an empty string;[ "$npm_v" = "" ]was simply false, so that file was skipped without any signal. The same bug existed in thesync-lockfilesjob'ssync_root_version, where the empty result was substituted into anawkrewrite.sync-lockfileswas gated onneeds.release-please.outputs.prs_created == 'true', but that output is only true when a PR was opened — on a rebase of an existing PR the action reportsfalseand emits noproutput at all, so the job that repairs the branch was skipped in exactly the case it exists to handle. It now runs after every successful release-please run and resolves the branch viaautorelease: pending.grep -m1rather thangrep … | head -1for version extraction, avoiding aSIGPIPEunderset -o pipefail.🚀 Features
masterviaalways-update. Release-please only rewrites the release PR when its generated body changes, so any master commit producing no changelog line —docs,testandchoreare allhiddeninchangelog-sections— left the PR stale against master with no signal. Withalways-updateset, release-please rebuilds the branch on the new master head and force-updates the ref. This is a root-level manifest option and is deliberately not read frompackages.🧪 Testing
node test/check-changelog-heading.mjs— new guard. It extracts the heading pattern out of the workflow source (so it cannot drift from what actually runs) and asserts both dialects, the near-misses that must not match, date capture, idempotency, the loud-failure marker, and that theci.ymlgate accepts the normalised output. Confirmed the guard fails when the original buggy pattern is restored, and passes when the fix is in place.node test/check-release-config.mjs— still passes; the newalways-updatekey is accepted and the manifest/package.jsonagreement check is unaffected.node test/check-acl.mjsandnode test/check-docker-paths.mjs— both pass.npx oxlint frontend/ test/__fixtures__/— 0 warnings, 0 errors across 197 files.npx tsc -b— clean.npx js-yaml) and that the config file still satisfiesJSON.parse, sincerelease-please-config.jsonis read with a strict JSON parser.## v0.2.0 — 2026-10-16, the older## v0.1.8section is untouched, theci.ymlgate passes, and a second run is a no-op.master(all seven values0.1.8) and the release-PR branch (all seven0.2.0) — and that it now rejects an empty extraction.🔄 Refactor
CONTRIBUTING.md, which claimed release-please updates the PR "on every push tomaster" — it did not, and now does because ofalways-update. Documents that the rebase is agit tree+updateRefforce push rather than a merge, thatalways-updateis a root-level option, and that the heading rewrite fails loudly if release-please changes its format.ci.ymlwhy the changelog gate accepts more than one dialect, and that the release PR is normalised before it reaches the gate.Notes
Two deliberate design decisions worth reviewing:
--force-with-leasewas intentionally not used for thesync-lockfilespush. That commit is always built on the head just checked out, so a plain push is a fast-forward.--force-with-leasecompares against the checkout's stale remote-tracking ref rather than the live head, so against release-please's own force-push it is a no-op guard — and it would only ever be "protecting" a commit we did not write. A rejected push is the correct outcome there: it means release-please rebased underneath us and re-running the job rebuilds the commit. The reasoning is recorded in the workflow comments.always-updatechanges the release PR into a moving target. Every master push now force-rewrites the release branch. That is what makes thesync-lockfilesrepair reliable, but if a stable release PR is preferred instead, this is the one line to drop.Known limitation (unchanged, pre-existing)
A push made with
GITHUB_TOKENdoes not trigger other workflows, so thesync-lockfilescommit still will not re-runci.ymlon the release PR — the checks run on the PR as release-please opened it. This was already documented atrelease-please.ymlL57–63 and is not made worse here, but the loop stays only partly verified until a PAT or GitHub App token is supplied viasecrets.RELEASE_PLEASE_TOKEN.