ci: fail the build when a commit will be dropped from the changelog - #449
Merged
Conversation
release-please parses every commit with `@conventional-commits/parser`, and when the parse throws it catches, writes two `logger.debug` lines and moves on. Nothing turns red: CI is green, the release PR renders normally, and the commit is simply absent. dcb3bac — `test(components): build path expectations with pathe, not node:path (#427)` — went missing from the 2.12.0 notes that way; the range holds seven `test:` commits and the notes list six. Fidelity is measured, not assumed. Over all 3240 commits on `main`, this oracle and release-please's own `parseConventionalCommits` agree on every one: 67 rejected by both, zero disagreements in either direction. The hazard is live at roughly one commit in a hundred in the current style, and every one of the 29 line-leading call tokens in the whole history sits in the last 400 commits. The trigger is broader than nested parentheses, which is what this commit first claimed. A body line beginning with a call-like token arms the parser's scope rule, which then rejects unless the very next `(`, `)` or line-end is a `)`. So an unclosed call — `writeTemplates(config.root` running to end of line, or a call wrapped across two lines — fails identically. Any `:` or `!` before the paren disarms it, as does one space of indentation, and only the first group on the line is scanned. The reliable repairs are indenting or prefixing; reflowing works only where the parentheses balance once joined. More importantly, 64 of the 67 real rejections are on the SUBJECT, not in the body — missing colons, `Revert "…"`, a space between type and scope. The first version of the error text sent all of them hunting for nested parens in a body that does not have them, while the parser's own message pointed at line 1. The guard now branches on the reported position and lists the shapes for each. Merge commits are exempt. Their subject is `Merge pull request …`, which the parser rejects and release-please is right to drop — failing there would redden `main` for correct behaviour. Thirteen exist in this history, the last from 2026-05-07. The parser is an ordinary devDependency. `pnpm add`-ing it downgraded TypeScript 6.0.3 to 5.9.3, which this commit first blamed on the package; the cause is that `typescript` is a non-optional peer not listed in devDependencies while `@nuxt/module-builder` peers `^5.9.3`, so any root `pnpm add` does it (#451). Appending to `package.json` by hand and running `pnpm install` gives a 33-line pure-addition lockfile diff with `typescript` untouched — so the CI-time install, the only npm fetch in this repository outside `--frozen-lockfile`, is gone. Tests moved from `test/workflows/` to `test/utils/commit-parses.spec.ts`, because `run.sh` executes before `pnpm install` and the parser now lives in `node_modules`. Eighteen accepted shapes are pinned, weighted by what this repository actually writes — `Co-authored-by:` appears in 83% of recent commits, `Claude-Session:` in 74%, a `(#NNN)` subject in 98%, an indented body line in 28% — because a guard that reddened `main` on ordinary messages would be removed within the week. Closes #436.
IgorShevchik
force-pushed
the
ci/guard-unparseable-commits
branch
from
August 20, 2026 06:58
2502e66 to
1e4066b
Compare
8 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Linked issue
Closes #436.
Type of change
Description
release-please parses every commit with
@conventional-commits/parser. When the parse throws it catches, writes twologger.debuglines and moves on — CI stays green, the release PR renders normally, and the commit is gone.dcb3bacfwent missing from the 2.12.0 notes that way: seventest:commits in range, six listed.Fidelity — measured
Over all 3240 commits on
main, this guard's oracle and release-please's ownparseConventionalCommitswere run side by side:Perfect agreement, both directions. And the hazard is live rather than historic: all 29 line-leading call tokens in the entire history are in the last 400 commits, and the rate over the release-please era is ~1 in 100.
The trigger — corrected from the first revision
The first revision said "a body line that BEGINS with a call-like token whose parentheses nest". That is half of it.
A line beginning with a call-like token arms the parser's scope rule, which then rejects unless the very next
(,)or line-end is a). So both of these fail:`.toEqual([join(a, b)])`writeTemplates(config.rootAnd it is narrower than stated too: any
:or!before the paren disarms it, one space of indentation disarms it, and only the first group on the line is scanned —foo(a)(b(c))parses.The reliable repairs are indenting the line or putting a word,
:or!before the call. Reflowing — the only remedy the first revision offered — works only where the parentheses balance once joined, so it fails on intrinsic nesting, on a paragraph-initial line, and inside a code fence.The error message was pointing most readers the wrong way
64 of the 67 real rejections are on the SUBJECT line, not in the body — missing colons (40), merge commits (13),
Revert "…", a space between type and scope. Only 3 are the body shape the first revision described.So someone whose subject is malformed was told to hunt for nested parentheses in a body that has none, while the parser's own message printed right above pointed at line 1. The guard now branches on the reported position:
Merge commits are exempt
Their subject is
Merge pull request …, which the parser rejects and release-please is right to drop — there is nothing to put in a changelog. Failing there would reddenmainfor correct behaviour. Thirteen exist in this history, the last from 2026-05-07, so it is rare rather than impossible.The parser is now an ordinary devDependency
The first revision installed it into
$RUNNER_TEMPat CI time, on the grounds thatpnpm addhad downgraded TypeScript 6.0.3 → 5.9.3. The downgrade is real and reproduces — but the package is not the cause.typescriptis a non-optional peer that is not listed indevDependencies, while@nuxt/module-builderpeers^5.9.3, so any rootpnpm adddoes it. Filed separately as #451.Appending to
package.jsonby hand and runningpnpm installgives a 33-line pure-addition lockfile diff withtypescriptuntouched, an integrity hash recorded, and--frozen-lockfilepassing before and after.That removes, in one move: the network install on every CI run, the only npm fetch in this repository outside
--frozen-lockfile, the unpinned transitive tree, and the new coupling between registry availability andnpm-publish.yml'sawait-cigate on the release SHA.pushonlyci.yml's push trigger isbranches: [main], so every push event reaching this workflow is the message that actually lands, and the release PR's squash is checked.Correcting the first revision, which said "a PR's own commits are never the ones parsed". They usually are — for #427 the branch commit's body was byte-identical to the squash. The honest reason for
pushis that the squash subject and body can be rewritten in the merge dialog, and #440's were, so only the message onmainis certain.Tests
Moved from
test/workflows/totest/utils/commit-parses.spec.ts—run.shexecutes beforepnpm install, and the parser now lives innode_modules. It also drops the old suite's install fallback, which printedSKIPand exited 0 when offline.33 assertions. Eighteen accepted shapes, weighted by what this repository actually writes, because a guard that reddened
mainon ordinary messages would be removed within the week:(#NNN)subjectCo-authored-by:trailerClaude-Session:URL trailerPort of …trailerrevert(Scope): …Plus twelve rejected shapes across both positions, including the reduced
dcb3bacfcase, and three assertions that drive the real script for exit codes and for the subject/body branch.What this does not do
It does not recover
dcb3bacf. That is possible — 2.12.0 is not cut, and release-please reads aBEGIN_COMMIT_OVERRIDEblock on the merged PR body in place of the commit message — but the missing entry describes a test-only change, and adding a machine directive to PR #427's description to recover one line was judged not worth the friction on a release that has been open for nine days.Correcting the first revision, which said "a release already cut cannot be corrected retroactively": it is not cut, and it can be.
Verification
vitest run test/— 6870 passed, 6 skipped.eslintclean ·vue-tsc --noEmitclean ·shellcheck -x -S warningclean ·assert-actions-pinned.pyclean ·test/workflows/run.sh66 passed ·pnpm install --frozen-lockfileclean before and after.Checklist