ci: strip git trailers from changelog and default to dry-run release - #1099
Merged
nielspardon merged 2 commits intoJun 18, 2026
Merged
Conversation
The conventional-commits parser folds trailing git trailers (e.g. Signed-off-by:) into BREAKING CHANGE notes because it only ends a note at a recognized reference or note keyword, not at a trailer. This leaked a Signed-off-by line into the v0.88.0 changelog and GitHub release notes. Convert .releaserc.json to .releaserc.mjs so the release-notes-generator can use a writerOpts.transform that strips trailing trailer lines from note text (JSON cannot carry functions). The transform wraps the preset's transform and only overrides note text, so grouping/templates/sorting are preserved. Also make the config default to a dry run as a fail-safe: the publishing plugins (@semantic-release/github, @semantic-release/git) and the publish/notify exec commands are only loaded when RELEASE_DRY_RUN=false. This matters because semantic-release still runs prepare/publish/success in dry-run mode (it only skips tag and push), so those side-effecting plugins must be omitted entirely rather than merely guarded by --dry-run. ci/release/run.sh opts in to a real release via RELEASE_DRY_RUN=false; ci/release/dry_run.sh inherits the safe default and now reads the config file so its preview reflects the real changelog. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
nielspardon
requested review from
EpsilonPrime,
benbellick,
cpcloud,
jacques-n,
vbarua,
westonpace and
yongchul
as code owners
June 8, 2026 10:56
The release scripts run semantic-release via `npx -p ...`, which installs the conventionalcommits preset as a sibling of semantic-release in a temporary node_modules. A bare `import` in .releaserc.mjs resolves relative to the config file's directory (the repo, which has no node_modules) and fails with ERR_MODULE_NOT_FOUND in the dry-run CI job. Fall back to resolving the preset relative to the running semantic-release binary (process.argv[1]) when the plain import fails; the plain import still covers local dev where the preset is installed alongside the project. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Member
|
Hmm it looks like the "signed off by" issue is not present in other releases. What was actually different about that merge that caused this issue? Are we likely to see it again? |
Member
Author
|
Yes, when you have a |
yongchul
reviewed
Jun 16, 2026
| // recognized reference (closes #..., fixes #...) or another note keyword, not | ||
| // at a git trailer -- so a trailing `Signed-off-by:` gets absorbed into the | ||
| // note text. Strip such trailing trailer lines. | ||
| const stripTrailers = (text) => { |
Contributor
There was a problem hiding this comment.
wow... wish the commit message is actually structured, rather than relying on adhoc parsing 😮💨
yongchul
approved these changes
Jun 16, 2026
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.
Problem
In v0.88.0, a
Signed-off-byline leaked into the changelog and GitHub release notes under BREAKING CHANGES.Root cause: the conventional-commits parser only ends a
BREAKING CHANGEnote at a recognized reference (closes #…) or another note keyword — not at a git trailer. So when aSigned-off-by:trailer immediately follows the breaking-change description (with no interveningcloses/fixesline), it gets absorbed into the note text. That is exactly why #1019 leaked but #994 (which had acloses #980line before its sign-off) did not.Because
@semantic-release/changelogand@semantic-release/githubboth rendernextRelease.notes, the single fix point is@semantic-release/release-notes-generator.Changes
.releaserc.json→.releaserc.mjs. JSON can't carry functions, and the fix needs awriterOpts.transform. The new ESM config is a faithful translation of the old one plus the transform.commit.notes[].text, removing trailing trailer lines (Signed-off-by,Co-authored-by, etc.). Grouping, templates, and sorting from the preset are preserved (the generator merges{ ...preset.writer, ...writerOpts }).@semantic-release/github,@semantic-release/git) and the publish/notify exec commands are only loaded whenRELEASE_DRY_RUN=false. This matters because semantic-release still runsprepare/publish/successin dry-run mode (it only skips tag and push), so those side-effecting plugins must be omitted entirely rather than merely guarded by--dry-run.ci/release/run.shopts in to a real release viaRELEASE_DRY_RUN=false.ci/release/dry_run.shnow reads the config file (instead of overriding plugins on the CLI), so its preview reflects the real changelog. It inherits the safe default.Verification
Tested against the actual
conventional-changelog-conventionalcommits@8.0.0+@semantic-release/release-notes-generator@14packages:.releaserc.mjs, pulled itstransform, and re-rendered the actualv0.87.0..v0.88.0commits: theSigned-off-byis gone, while the breaking-change text, thecloses #980reference, and all other entries are intact.RELEASE_DRY_RUNunset /true/false(safe reduced plugin set by default; full publishing set only onfalse).bash -nandnode --checkpass on all three files.Note
Tradeoff of the dry-run default: if CI ever fails to set
RELEASE_DRY_RUN=false, a release silently becomes a no-op (green build, no new tag). That is visible and cheap to recover, versus an accidental release which is not.🤖 Generated with Claude Code
This change is