fix(release): restore release-note generation and stop masking failures - #2083
Conversation
semantic-release has been failing on every push since the v2.15.0-beta.6 tag, while the Release workflow reported success and published nothing -- including the v2.15.0 promotion (merge commit df45a58). Root cause is a two-engine split in the changelog toolchain. conventional-changelog-writer@8 is handlebars-based and expects partials as template strings; writer 9 replaced handlebars with @conventional-changelog/template, whose partials are JS functions. conventional-changelog-conventionalcommits 10.x emits function partials, but @semantic-release/release-notes-generator@14 still pins writer ^8. Handing functions to handlebars does not throw -- it renders an empty changelog. That is why v2.15.0-beta.6's release body is 116 bytes of compare-link header and nothing else, and why every release back to at least v2.14.0 has a hollow auto-generated section. ccc 10.3.0 added a deliberate poison pill to turn that silent failure into a loud one, so the dev-dependencies bump in 038a943 (10.2.1 -> 10.4.0) did not create this bug; it made an already-broken pipeline audible. Pinning back to 10.2.1 would therefore have restored the silence, not the notes. Pin to 9.3.1 instead -- the last release on the writer-8 line, verified to emit handlebars template strings. A writer ^9 override was rejected: upstream's own migration (release-notes-generator 15.0.0-beta.2) moves conventional-commits-parser 6->7 and -filter 5->6 alongside the writer, so overriding the writer alone would ship a combination upstream never tests, repo-wide. Verified against the repo's exact locked graph: generateNotes now completes and produces full grouped notes (Documentation / Chores / Continuous Integration) instead of crashing. Also stop the workflow from hiding this class of failure. The step ran `npx semantic-release 2>&1 || true` and then decided what happened by grepping stdout for "Created tag v", so a crash and a clean "no relevant commits" run were indistinguishable: the job went green with new-release-published=false and every downstream job skipped. Capture the exit status, fail loudly on non-zero, and give the job summary a distinct "Release Failed" branch so it can no longer claim "commits since last tag did not trigger a version bump" after a crash. Lockfile regenerated with a full npm install; the delta is 35 lines, confined to the ccc downgrade plus the now-orphaned @conventional-changelog/template. npm audit reports no advisory against conventional-changelog-* or compare-func. Fixes #2082 Co-Authored-By: Claude product-architect <noreply@anthropic.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
[security-engineer] VERDICT: APPROVE Reviewed 1.
|
|
🎉 This PR is included in version 2.15.0-beta.7 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
|
🎉 This PR is included in version 2.15.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
Summary
Fixes #2082 —
semantic-releasehas been failing on every push since thev2.15.0-beta.6tag while theReleaseworkflow reported success and published nothing, including the v2.15.0 promotion (merge commitdf45a58b).The bug is older and worse than the crash suggested
Investigating turned up a third defect the issue did not describe: release notes have been silently empty for months.
v2.15.0-beta.6v2.15.0-beta.5v2.14.0RELEASE_SUMMARY.mdis prepended; its auto-generated half is also just the headerMechanism. Two incompatible rendering engines:
conventional-changelog-writer@8is handlebars-based and expects partials as template strings.@conventional-changelog/template, whose partials are JS functions.conventional-changelog-conventionalcommits10.x emits function partials, but@semantic-release/release-notes-generator@14still pins writer^8.Handing functions to handlebars doesn't throw — it renders an empty changelog. From ccc
10.3.0upstream added a deliberate poison pill, whose own doc comment names the disease: "make legacy handlebars-based writers fail loudly instead of silently rendering an empty changelog … Update the tooling or use an older major version of the preset."So
038a9431(ccc10.2.1→10.4.0) did not create this bug — it made a long-silent one audible.Remedy: pin the preset to
9.3.1, not10.2.1Pinning back to
10.2.1— the version that "successfully" cutv2.15.0-beta.6— would have restored the silence, not the notes.9.3.1is the last release on the writer-8 line and was verified to emit handlebars template strings.Tested against the repo's exact locked graph (
semantic-release@25.0.9,release-notes-generator@14.1.0,writer@8.4.0):generateNotes10.4.0(current)10.2.1^9override9.3.1(this PR)A writer
^9override was rejected on evidence despite working: upstream's own migration (release-notes-generator@15.0.0-beta.2) bumpsconventional-commits-parser6→7 andconventional-commits-filter5→6 alongside the writer. Overriding only the writer ships a parser-6/filter-5/writer-9 triple upstream never tests — and rootoverridesapply repo-wide.9.3.1keeps every package inside its declared range with no overrides.Verified output after the fix:
Defect 2 — the workflow no longer hides this
The step ran
npx semantic-release 2>&1 || trueand then decided what had happened by grepping stdout forCreated tag v, so a crash and a clean "no relevant commits" run were indistinguishable. The job went green withnew-release-published=falseand every downstream job (Merge Back to Beta,Docker,Docker Scout,DockerHub README,Docs Screenshots,Docs Deploy) skipped.Now the exit status is captured and fails the job loudly with an
::errorannotation, and the job summary gained a distinct "Release Failed" branch so it can no longer claim "commits since last tag did not trigger a version bump" after a crash. The outputs contract (new-release-published,new-release-version,is-prerelease) is unchanged — all 8 downstream jobs'if:conditions still parse. All four states were simulated with a stub binary.Lockfile
Regenerated with a full
npm install(not--package-lock-only).npm installfails withENOTDIRon the session worktree's mount (98% full), so it was regenerated in a clean tree on a different filesystem and copied in. The delta is 35 lines, confined to the ccc downgrade plus the now-orphaned@conventional-changelog/template.For the security reviewer
npm auditon the regenerated tree reports no advisory againstconventional-changelog-*orcompare-func; the 20 pre-existing findings are Docusaurus-related and unchanged frombeta.9.3.1's only dependency,compare-func@2.0.0, was already in the tree viaconventional-changelog-angular.::errorannotation echoes only an exit status;$OUTPUTwas already printed. semantic-release redacts tokens.Known follow-ups (not addressed here)
v2.14.0has a hollow auto-changelog. Needs a decision on whether to backfill.grep -q "Created tag v"misses (upstream wording change), the job still reports "no release" silently. Same class as defect 2; left alone to keep this PR scoped.🤖 Generated with Claude Code