Skip to main content

Changelog guard

A GitHub Action that fails a pull request which changes a project and writes nothing down about it.

GitHub Repository

Why it exists​

What that means in a diff: the project's changelog has to carry a ## heading naming the version its manifest declares. A change a reader can observe moves that version, so the heading is a new one, and writing what goes under it is the pull request's job rather than the release's.

A fix shipped with nothing written down loses the reasoning while somebody still remembers it, and the reader who needs it is on the previous version deciding whether this one affects them. Written at release time instead, the entry is written by whoever cut the release, from a diff, about work they may not have done.

Adding it​

name: Validate
on:
pull_request:

jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0

- uses: releasetools/actions/changelog-guard@v0

A group that names no changelog owes none, so the check asks nothing until one does. Name the file in .releasetools.yaml at the repository root:

projects:
- path:
- packages/api
- packages/web
manifest: package.json
changelog: CHANGELOG.md

changelog is a name inside each project's own directory, and every project is named rather than matched. ignore-files and case-sensitive decide what counts as a project changing, and the versions guard documents them. Both guards read the same file.

What counts as a heading​

A ## carrying the version the project's manifest now declares.

headingagainst version 0.2.0
## 0.2.0passes
## 0.2.0 - 2026-09-19passes, which is what the release notes plugin writes
## [0.2.0] - 2026-09-19passes, which is what Keep a Changelog writes
## v0.2.0passes
## 0.2.0-rc1fails
### 0.2.0fails
Changelog not updated
packages/api/CHANGELOG.md has no section for 2.1.0. Add one above the
older releases: what changed, and the reason not to act that a later
change cannot get from the diff.

A project with no changelog file at all is told to start one.

Why it is not enough on its own​

A project whose version never moved is satisfied by the section written for its last release, because that section carries the version the manifest still claims. Catching that is the versions guard's job, and it is why the two ship together.

Letting one pull request through​

Label it skip-changelog-guard.

What it reports​

One line per project whose changelog carries the version it claims, then a verdict:

packages/api 0.2.1
Every changed project recorded what changed

Failures arrive as annotations on the pull request, titled Changelog not updated.

Inputs​

inputdefault
basethe pull request's basethe ref to compare against, resolved to where the branch forked from it. Read from the event on a pull request, and has to be set anywhere else

The full reference is in the action's guide.