ci: automate SemVer releases with semantic-release - #7
Conversation
Add semantic-release config and a release workflow that cuts vX.Y.Z tags and GitHub Releases from Conventional Commits on main, plus PR linting for commit messages and PR titles. Refs: #6
jvcorredor
left a comment
There was a problem hiding this comment.
Spec-compliance review — PR #7 vs issue #6
Verdict: faithful, working implementation. One behavioral spec deviation (medium) and one PR-description inaccuracy (low). Not blocking; the deviation is a small .releaserc.json change or an amendment to issue #6.
Verified
.releaserc.jsonmatches the Design snippet exactly and parses.release.ymlmatches the design line-for-line: trigger, permissions,concurrency: releasewithcancel-in-progress: false,fetch-depth: 0,persist-credentials: false, Node 22,npm install --no-save semantic-release@25,npx semantic-releasewithGITHUB_TOKEN. Stripping credentials does not break tag push: semantic-release 25.0.9 rebuilds an authenticatedx-access-token:URL fromGITHUB_TOKENwhenGITHUB_ACTIONis set (lib/get-git-auth-url.js).lint.ymlmatches the design. wagoid v6 defaults tocommitlint.config.mjsand falls back to@commitlint/config-conventionalwhen absent. Confirmed live on this PR:Commit messagesran and passed (run 34707461590, job 103589998107);PR titlecorrectly skips until the workflow exists onmain.actionlint .github/workflows/release.yml .github/workflows/lint.yml→ exit 0, no findings.- Bootstrap:
v0.1.0is a published release whose tag points at64db650= currentmainHEAD; PR head is the singleci:commit. Independently reproduced with semantic-release 25.0.9 against the real seed tag:ci:→ no release,fix:→0.1.1with generated notes,refactor:→ no release. - Out-of-scope check: the diff adds only the three in-scope files — no
CHANGELOG.md, nogo test/go vetci.yml, no GoReleaser, no npm/Docker publishing.
Findings
- Medium —
.releaserc.json:4:revertcommits cut a patch release, contradicting issue #6's no-release mapping. Inline comment. - Low —
.github/workflows/lint.yml:29: PR body overstates the fallback behavior of the PR-title action. Inline comment.
Could not verify pre-merge
- End-to-end tag + Release creation on a push to
main(first possible only after this merges; dry-run covers commit analysis and note generation). - A non-conventional PR title actually failing:
pull_request_targetloads the workflow from the default branch, so it activates after merge (acknowledged in the PR body). The commitlint failure path is likewise covered by the action docs, not exercised here. - Making the lint checks required merge gates — explicitly a settings/ruleset task, out of scope.
jvcorredor
left a comment
There was a problem hiding this comment.
Verdict: no functional defects found — approve with hardening nits. I could not break the release path.
What I verified (sources + live experiment):
actionlintis clean on both workflows.- I ran real
semantic-release@25 --dry-runagainst scratch repos with afile://remote using this exact.releaserc.jsonand seed tag:v0.1.0+fix-> 0.1.1;perf-> 0.1.1;ci/docs/chore/refactor/style/revert:and merge commits -> no release; no tag with historicalfeat-> 1.0.0. A note-less, API-created lightweight seed tag is recognised (lib/branches/get-tags.jsdefaults it to channel[null]). persist-credentials: falseis correct: core pushes the tag itself, butlib/get-git-auth-url.jsrewrites the remote tox-access-token:$GITHUB_TOKENbecauseGITHUB_ACTIONis set.fetch-depth: 0is required and present.- No
package.jsonis needed:npm install --no-save semantic-release@25in an empty dir installed 301 packages, left nopackage.json/package-lock.json, andnpxfound the local binary. Node 22 satisfies^22.14.0 || >=24.10.0. - commitlint gets the right commits: wagoid v6 uses
pulls.listCommits(API), not local history, so the checkout depth is irrelevant. config-conventional v19 includesci/build; amannn v6 defaults toconventional-commit-types, which includesci. This PR's ownci:merge passes both and cuts no release. pull_request_targethere is the safe variant: read-only permissions, no PR checkout, no PR code execution. Skipped sibling jobs reportskipped, which does not block required checks.
Residual risks / process notes (not blockers): the v0.1.0 seed is a manual prerequisite the workflow cannot enforce — if this merges before the tag is seeded, the first run analyses all history and ships 1.0.0; a failed run between the tag push and release creation leaves a tag with no release (retry computes the next version, so manual tag cleanup is needed); and with no lockfile each push installs a floating dependency tree. Line comments cover the actionable hardening.
|
🎉 This PR is included in version 0.1.1 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
Summary
Adds the repository's first
.github/workflows/and automates SemVer releases from Conventional Commits:.releaserc.json— explicitcommit-analyzer,release-notes-generator, andgithubplugin list. Required because semantic-release otherwise loads@semantic-release/npmand fails without apackage.json; the repo stays a pure Go module.parserOpts.breakingHeaderPatternmakes the!shorthand register as breaking, and areleaseRulesentry keepsrevertcommits non-releasing..github/workflows/release.yml— on push tomain, runssemantic-release@25on Node 22 (fetch-depth: 0,persist-credentials: false,concurrency: release) and cutsvX.Y.Ztags + GitHub Releases with generated notes..github/workflows/lint.yml— commitlint on pull-request commits (pull_request; falls back to@commitlint/config-conventional) and Conventional Commit PR titles (pull_request_target; uses the action's conventional-commit-types defaults).Closes: #6
Verification
actionlintpasses on both workflows;.releaserc.jsonand both YAML files parse.semantic-release@25 --dry-run --no-ciagainst a scratch clone seeded withv0.1.0:fix:→0.1.1feat:→0.2.0feat!:,fix!:,feat(scope)!:, and aBREAKING CHANGE:footer →1.0.0revert:/ git-styleRevert "…"/docs:/ci:/refactor:/chore:→ no releasev0.1.0GitHub Release created at the currentmainHEAD (64db650), so the first automated release isv0.1.x, notv1.0.0.Notes
pull_request_targetworkflows are loaded from the default branch, so the PR-title check activates only after this merges; commitlint runs on this PR now.CHANGELOG.mdis committed (GitHub Releases carry the notes), per the ticket.