Skip to content

build(deps): make goreleaser own the release changelog with prefix-grouped sections#118

Merged
rsharath merged 3 commits intomainfrom
release-changelog-robustness
May 6, 2026
Merged

build(deps): make goreleaser own the release changelog with prefix-grouped sections#118
rsharath merged 3 commits intomainfrom
release-changelog-robustness

Conversation

@rsharath
Copy link
Copy Markdown
Contributor

@rsharath rsharath commented May 5, 2026

Summary

Releases v1.1.4 → v1.1.15 all shipped with GitHub's default "Generate release notes" body (ungrouped PR list, blind to our feat: / fix: / build(deps) / devops: prefix convention) because goreleaser's release block defaulted to mode: keep-existing. CHANGELOG.md froze at v1.1.3 and stayed stale across 13 subsequent releases.

This PR wires goreleaser to actually own the release body going forward, and resets CHANGELOG.md to a brief pointer at GitHub Releases.

Changes

.goreleaser.yml

Field Why
release.mode: replace The release workflow trigger is release: published — a human creates the release first (UI button or gh release create), then goreleaser runs. With keep-existing (default), goreleaser leaves the human's body alone, which is how we ended up with 12 ungrouped releases. replace makes goreleaser overwrite the body with its grouped changelog every time.
changelog.groups Six sections keyed off the same prefix convention highflame-commit-check enforces: Features, Bug Fixes, Dependencies, DevOps, Refactoring, Other Changes. YAML list order is significant — goreleaser is first-match-wins; order field is display order only.
filters.exclude Extended with ^style:, ^ci:, ^Revert to match the existing `^docs:

No "Security" group: to claim fix(security): commits it would need to sit in front of Bug Fixes in YAML, and the marginal taxonomy gain wasn't worth the regex risk. Security fixes land under Bug Fixes, which is semantically correct.

CHANGELOG.md

Trimmed to a one-screen header pointing at GitHub Releases as the source of truth, with the v1.0.0 → v1.1.3 entries preserved for context. Nothing back-fills the v1.1.4 → v1.1.16 gap — goreleaser is now responsible for current versions.

Validation

$ docker run --rm -v "$PWD":/work -w /work ghcr.io/goreleaser/goreleaser:latest check
  • checking                                  path=.goreleaser.yml
  • 1 configuration file(s) validated
  • thanks for using GoReleaser!

Regex grouping sanity-checked against git log v1.1.15..main — 18 commits:

Group Count Examples
Features 1 feat: zeroid cli, first commit (#35)
Bug Fixes 6 fix(security): scope API key get/revoke …, fix: validate SPIFFE path segments …, fix: set typ=JWT …
Dependencies 2 build(deps): upgrade lestrrat-go/jwx v2 → v4 + Go 1.26 (#114), build(deps): bump pgdriver, mapstructure, … (#115)
DevOps 4 devops: Pushing the V2 changes of workflow (#108), …
Refactoring 1 refactor: go fix ./... — modernize to Go 1.25 idioms (#116)
Other Changes 4 Add identity audit logging (#72), Issue16 (#78), deactivation must stop token use (#90), replace attestation trust-promotion stub …(#93) — pre-prefix-gate commits, fall through cleanly
Excluded 1 ci: add golangci-lint job (#83)

Verification on next release

Once this lands, the next release (likely v1.1.17 after #115 + #117 merge — the orphan v1.1.16 tag has no published release) will exercise the new flow:

  1. gh release create v1.1.17 --generate-notes — GitHub seeds the body with its default content.
  2. release: published event fires, release.yml runs.
  3. Goreleaser regenerates the body with the grouped changelog above. The Docker image, multi-arch binaries, and pkg/authjwt/v1.1.17 submodule tag are produced as before.

If the next release body still looks ungrouped, the issue is not in this config — it would point at goreleaser's release.mode: replace failing to take effect (e.g., GitHub permissions on the goreleaser GitHub App token), and we should investigate that path.

Scope

  • .goreleaser.yml and CHANGELOG.md only.
  • No workflow changes — release.yml keeps its existing release: published trigger.
  • No semantic Go changes; no test impact.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request automates the release process by configuring GoReleaser to generate release notes from git commit messages and updating the CHANGELOG.md to point to GitHub Releases as the source of truth. Feedback includes resolving a contradiction between excluded commit prefixes and the documentation, broadening the regex for dependency updates, and ensuring consistency in regex patterns for scoped DevOps commits.

Comment thread .goreleaser.yml
Comment thread .goreleaser.yml
Comment thread .goreleaser.yml Outdated
rsharath added a commit that referenced this pull request May 5, 2026
Match the pattern already used by the feat/fix/refactor groups so a
hypothetical 'devops(ci):' style commit is grouped under DevOps instead
of falling through to Other Changes. Caught by Gemini review on #118.
@rsharath rsharath self-assigned this May 5, 2026
@rsharath rsharath added the bug Something isn't working label May 5, 2026
rsharath added 3 commits May 5, 2026 13:20
…ouped sections

Releases v1.1.4 through v1.1.15 all carried GitHub's default
"Generate release notes" body (an ungrouped, prefix-blind PR list)
because goreleaser's `release` block defaulted to `mode: keep-existing`,
leaving whatever the human pasted intact. CHANGELOG.md was last touched
at v1.1.3 and went stale across 13 subsequent releases.

This wires goreleaser to actually own the release body going forward:

- `release.mode: replace` — goreleaser overwrites the GitHub
  --generate-notes content with its own grouped changelog when the
  `release: published` workflow fires. Future releases stop carrying
  the ungrouped body.
- `changelog.groups` — six sections keyed off the same commit-prefix
  convention enforced by `highflame-commit-check` (Features / Bug Fixes /
  Dependencies / DevOps / Refactoring / Other Changes). YAML list order
  is significant for goreleaser: it's first-match-wins, with the `order`
  field controlling display order only. No "Security" group: it would
  need to sit in front of "Bug Fixes" in YAML to claim `fix(security):`
  commits, and the marginal taxonomy gain wasn't worth the regex risk —
  security fixes land under Bug Fixes, which is semantically correct.
- `filters.exclude` extended with `^style:`, `^ci:`, `^Revert ` —
  matches the same drop-list the existing `^docs:|^test:|^chore:` was
  already using.

CHANGELOG.md is rewritten to its v1.1.3 historical floor with a header
pointing at GitHub Releases as the source of truth. Pre-v1.1.4 entries
preserved in case anyone wants to read them; nothing back-fills the
v1.1.4 → v1.1.16 gap because goreleaser is now responsible for current
releases.

Validated locally with `goreleaser check` (v2 image) — config is valid.
Regex grouping was sanity-checked against `git log v1.1.15..main`: every
prefix-tagged commit lands in the right bucket; four pre-prefix-gate
commits fall through to "Other Changes" as intended.

## Test plan

- [x] `goreleaser check` — green
- [x] Regex dry-run against actual commit log — every prefixed commit
      grouped correctly, untaggedcommits land in Other Changes
- [ ] Verify on the next release: cut a tag, watch `release.yml` run,
      confirm goreleaser rewrites the body with grouped sections
Pairs with the goreleaser changelog work in the previous commit on this
branch: that commit makes release notes deterministic per commit prefix;
this one makes the version number deterministic from the same prefixes.

## What it does

`svu next` (caarlos0/svu) reads commits since the last `v*` tag and
prints the implied semver bump using Conventional Commits rules:

  feat:                         minor
  fix: / build(deps) / devops:  patch  (the latter two via "no rule
                                        matched -> patch")
  feat!: / BREAKING CHANGE:     major

The release workflow now runs `svu next` after the tag-format check
and compares the published tag to svu's recommendation:

  - tag == svu next     -> pass (the common case)
  - tag >  svu next     -> pass with ::notice:: (manual bump up; e.g.
                           force a minor on a fix-only window)
  - tag <  svu next     -> ::error:: + exit 1. Almost always means a
                           feat: was missed and we'd silently ship a
                           feature as a patch.

The Makefile adds `make next-version` so the same suggestion is
available locally before someone clicks "Publish release".

## Bug fix bundled in

The old `Validate Release Tag` step was guarded by
`if: ${{ steps.validate_branch.outputs.enable_branch_build == 'true' }}`,
referencing a `validate_branch` step that never existed in this job.
The condition evaluated to false on every release, so the regex format
check has been silently skipping for as long as it's been there. Removed
the dead conditional so it (and the new svu step) actually run.

## Why this PR and not separate

The user (Highflame engineer) asked for the svu guardrail as part of
"making releases more robust", which is exactly what the existing
commit on this branch does for the changelog. Bundling keeps the
review surface small and the rationale on one PR.

## Verification

- `svu next` against current main reports `v1.1.17` (current orphan tag
  v1.1.16, no feat: commits since; patch bump). That matches the next
  expected release.
- Comparison-via-sort -V tested in shell against three cases (match,
  manual bump-up, missed feat:); all branches behave as documented.
- The new step in release.yml needs `actions/setup-go@v6` because the
  validate job didn't previously install Go; added.
Match the pattern already used by the feat/fix/refactor groups so a
hypothetical 'devops(ci):' style commit is grouped under DevOps instead
of falling through to Other Changes. Caught by Gemini review on #118.
@rsharath rsharath force-pushed the release-changelog-robustness branch from d1298ef to 7420f44 Compare May 5, 2026 20:20
Copy link
Copy Markdown
Contributor

@saucam saucam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving — fix matches the diagnosis cleanly. release.mode: replace is the exact flip needed to make goreleaser own the body instead of leaving GitHub's seeded default in place, and the prefix groups mirror the highflame-commit-check gate so CI and release notes will stay in sync going forward.

Gemini rejections look sound:

  • The CHANGELOG "contradiction" was a misread — those lines document the CI commit-prefix allow-list, not the changelog visibility policy. Merge/Revert being CI-allowed but goreleaser-excluded is intentional.
  • Bump trailing space is correct anchoring against Bumped/Bumping false positives; real dependabot subjects on this repo use build(deps): which the same group already covers.

The applied ^devops(\(.*\))?: consistency tweak is a nice catch.

Failure mode is benign — if replace somehow doesn't take effect on the next release, worst case is "body still looks ungrouped," which is the current state. Zero regression risk.

@rsharath rsharath merged commit ca7f262 into main May 6, 2026
10 checks passed
@rsharath rsharath deleted the release-changelog-robustness branch May 6, 2026 03:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants