Skip to content

feat: auto-update CHANGELOG.md on each release (option A) - #795

Closed
fiunchinho wants to merge 1 commit into
mainfrom
auto-update-changelog-md
Closed

feat: auto-update CHANGELOG.md on each release (option A)#795
fiunchinho wants to merge 1 commit into
mainfrom
auto-update-changelog-md

Conversation

@fiunchinho

Copy link
Copy Markdown
Member

Summary

Extends the push-based auto-release workflow added in #792 to also keep CHANGELOG.md in sync with each release. After gh release create succeeds, the workflow splices the rendered release notes into CHANGELOG.md and pushes the resulting commit back to the branch.

This implements option A from the design discussion — direct commit-back from the workflow (not a follow-up PR).

Design

Splice, not regenerate. The existing CHANGELOG.md carries ~650 lines of hand-curated pre-conventional-commit history (the ## [0.1.0] - 2026-02-23 section and everything below it). A full git-cliff --output regenerate would destroy that, so this PR uses an insertion-marker approach instead:

# Changelog

All notable changes to this project will be documented in this file.

<!-- auto-release: insert below -->        <-- new sentinel

## [Unreleased]                            <-- stale release-please residue, untouched
...                                        <-- all pre-existing content preserved verbatim

On each release, the workflow:

  1. Renders the new release section (version header + the same Added / Fixed / Changed / Security bullets that go into the GitHub Release) into a temp file.
  2. awk-splices it into CHANGELOG.md directly under the marker, so newest releases land at the top — matching the existing file's ordering.
  3. Commits with chore: update CHANGELOG.md for vX.Y.Z [skip ci] and pushes.

Loop protection (two layers):

  • [skip ci] in the commit message prevents the auto-release workflow (and CircleCI) from running on the chore commit itself.
  • A new cliff.toml commit_parsers rule (^chore: update CHANGELOG\.md -> skip) ensures that even if the chore commit ends up inside a future --unreleased range, git-cliff filters it out of both the version bump computation and the rendered notes. Placed before the general ^chore rule (first match wins).

Race protection: The concurrency group serializes auto-release jobs against each other but not against human pushes to the branch. The commit-back step does git pull --rebase origin "$BRANCH" before git push to handle the narrow window between job start and push. If the rebase fails (genuine conflict on CHANGELOG.md itself, exotic), the step errors out — the release tag and GitHub Release are already created, so nothing is half-finished; the next release re-splices.

Required branch-protection change before this works in production

The main-branch protection currently requires 1 PR review, and the github-actions[bot] is not in any bypass list. Until that's adjusted, the commit-back step will fail at git push with a 403.

Two options:

  1. Add github-actions[bot] (or a dedicated App) to "Allow specified actors to bypass required pull requests" in the branch protection rule for main. Lowest-friction.
  2. Switch the workflow's checkout to use a token from a dedicated GitHub App (re-use the release-please-approver app, or a new one) with bypass permissions. More setup; reuses existing infra.

The release itself is unaffected if the commit-back fails — gh release create happens before the commit-back step, and the file self-heals on the next release once the protection allows the push.

Out of scope (called out for awareness)

  • The ## [Unreleased] section in CHANGELOG.md (release-please residue) is left untouched. It will become permanently stale once this lands — worth deleting separately, but not bundled here to keep the diff focused.

Test plan

  • Branch-protection bypass for github-actions[bot] (or App token) configured on main
  • After merge: next feat: / fix: lands on main -> workflow runs -> new release tag created -> new section appears in CHANGELOG.md directly under the marker -> chore commit lands with [skip ci]
  • Subsequent feature push does NOT include the chore in the bumped version or rendered notes (cliff.toml skip rule works)
  • If the commit-back ever fails, the release itself still succeeded (tag + GitHub Release exist) and the next release recovers

After `gh release create` succeeds, splice the same release-notes body
into CHANGELOG.md and push the resulting commit back to the branch.

- New sentinel comment `<!-- auto-release: insert below -->` in
  CHANGELOG.md marks the insertion point. Pre-conventional-commit
  history (everything below the marker) is preserved verbatim.
- cliff.toml gains a commit_parser skip rule for the auto-generated
  `chore: update CHANGELOG.md` commit so it doesn't trigger spurious
  patch bumps or appear as noise in subsequent releases' notes.
- The commit-back uses `[skip ci]` to prevent re-triggering the
  workflow on itself (GitHub Actions + CircleCI both honor it).
- Pull-rebase before push handles the narrow race window where a
  human merges between job start and commit-back.
@fiunchinho fiunchinho self-assigned this Jun 3, 2026
@fiunchinho fiunchinho closed this Jun 4, 2026
@fiunchinho
fiunchinho deleted the auto-update-changelog-md branch June 4, 2026 08:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant