ci(release): add one-button release cut from main - #20
Conversation
Releases were tag-driven: a maintainer hand-bumped pom.xml, hand-moved the
CHANGELOG [Unreleased] entries under a new heading, committed, then pushed a
SemVer tag. Three manual steps, each able to disagree with the others.
Add a manually-dispatched Release Cut workflow that runs semantic-release
against main: it analyzes the Conventional Commits since the last tag, bumps
pom.xml via `mvn versions:set`, regenerates CHANGELOG.md, commits both, tags
(tagFormat ${version}), and creates the GitHub Release. The tag push then
fires release.yml exactly as a manual tag push did, so the binary build and
homebrew-bump chain is untouched. A dry-run input previews the next version
and notes without releasing.
The cut needs a fine-grained PAT (GH_TOKEN, contents:write) rather than the
default GITHUB_TOKEN, whose tag pushes do not trigger other workflows — with
it, release.yml would never run. The semantic-release toolchain is pinned by
its own package-lock.json and installed with `npm ci --ignore-scripts`, so
the job holding that PAT never resolves mutable versions.
CHANGELOG.md becomes generated output. The hand-maintained [Unreleased]
section and its compare link are removed, the header explains the new
contract, and historic sections stay as they were. Changelog Guard fails any
PR that edits the file by hand unless a maintainer applies the
`changelog-edit` label.
Since commit messages are now the changelog, add a Conventional Commits
workflow that lints every non-merge commit in a PR against the types
semantic-release recognizes.
Also drop the hand-written installation body from release.yml — the release
now carries just the attachments, and semantic-release writes the notes.
|
| Filename | Overview |
|---|---|
| .github/workflows/release-cut.yml | Adds a serialized, manually dispatched semantic-release job using a write PAT and SHA-pinned actions. |
| .releaserc.json | Configures version analysis, Maven version updates, generated changelog commits, numeric tags, and GitHub release notes. |
| .github/workflows/conventional-commits.yml | Adds commit-message validation but invokes checkout through a mutable major-version tag. |
| .github/workflows/changelog-guard.yml | Prevents direct changelog edits unless maintainers apply the documented override label. |
| .github/workflows/release.yml | Leaves artifact publication intact while allowing semantic-release’s generated notes to remain on the existing release. |
| .github/release-tooling/package.json | Declares exact semantic-release tooling versions backed by the committed lockfile. |
Sequence Diagram
sequenceDiagram
participant M as Maintainer
participant C as Release Cut
participant S as semantic-release
participant R as Release workflow
participant G as GitHub Release
participant H as Homebrew bump
M->>C: Dispatch from main
C->>S: Analyze Conventional Commits
S->>S: Update pom.xml and CHANGELOG.md
S->>G: Commit, tag, and create release notes
S-->>R: Push SemVer tag
R->>R: Build binaries, JAR, and checksums
R->>G: Attach release assets
R-->>H: Successful workflow completion
H->>H: Open tap update PR
Reviews (1): Last reviewed commit: "ci(release): add one-button release cut ..." | Re-trigger Greptile
|
🎉 This PR is included in version 0.2.1 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
Summary
Releases were tag-driven and manual in three separate steps: hand-bump
<version>inpom.xml, hand-move theCHANGELOG.md[Unreleased]entries under a new heading, commit, then push a SemVer tag. Each step could disagree with the others.This adds a manually-dispatched Release Cut workflow that runs semantic-release against
main: it analyzes the Conventional Commits since the last tag, bumpspom.xmlviamvn versions:set, regeneratesCHANGELOG.md, commits both, tags (tagFormat: ${version}), and creates the GitHub Release. The tag push then firesrelease.ymlexactly as a manual tag push did — the binary build andhomebrew-bump.ymlchain is untouched. Adry-runinput previews the next version and release notes without releasing anything.Supporting changes:
CHANGELOG.mdbecomes generated output. The hand-maintained[Unreleased]section and its compare link are removed and the header states the new contract; historic sections stay as they were. Changelog Guard fails any PR that edits the file by hand unless a maintainer applies thechangelog-editlabel.release.yml— dropped the hand-written installation body; the release now carries just the attachments and semantic-release writes the notes.Token and supply-chain notes
The cut requires a fine-grained PAT (
GH_TOKEN,contents:write), not the defaultGITHUB_TOKEN: tags pushed withGITHUB_TOKENdo not trigger other workflows (GitHub's recursion guard), sorelease.ymlwould never run.The semantic-release toolchain is pinned by its own
package-lock.jsonand installed withnpm ci --ignore-scripts, so the job holding that write PAT never resolves mutable versions. Action refs inrelease-cut.ymlare SHA-pinned.Type of change
fix:)feat:)feat!:orfix!:)ci:(release automation only; nosrc/changes, so no version bump of its own)Product trees affected
commands/gcp/)commands/az/)Checklist
mvn testpasses locally — unchanged; this PR touches nosrc/, and CI runs the suiteCHANGELOG.mdentry — N/A by design: this PR is what makes the file generated. The hand edit here is the point of the PR, hence thechangelog-editlabelREADME.mdupdated — not affectedBefore merging
GH_TOKENsecret hascontents:writeon this repo. The existingGH_TOKENused byhomebrew-bump.ymlneeds scope onfloci-io/homebrew-floci, which is a different grant — if it is tap-only, the release cut fails at the push step.dry-run: trueto preview the computed version and notes. With this PR landing asci:, the next cut releases whatever realfeat:/fix:commits sit on main — currently the fix(env): single-quote exported values in every product tree #19 env-quoting fix, i.e. a patch.