131 lines
6.4 KiB
Markdown
131 lines
6.4 KiB
Markdown
---
|
|
name: changelog-release
|
|
description: The user's release methodology - how CHANGELOG.md (or debian/changelog) is written, how its newest untagged entry collects every change, and how release.mjs / release.py commit, tag and push from it. Use this skill before EVERY edit of a CHANGELOG.md or debian/changelog, whenever a change is finished and has to be recorded, when a version has to be chosen (patch or minor), when the user mentions a release, a tag, a version bump, release.mjs or release.py, or when a repository needs a changelog and a release script set up - even for a one-line addition to a changelog.
|
|
---
|
|
|
|
# Changelog and release
|
|
|
|
The user releases with a release script (`release.mjs` or `release.py`) that reads the newest
|
|
changelog entry, commits everything with the entry's summary line as the message, tags the commit
|
|
with the entry's version and pushes. The changelog is therefore both the history of the project and
|
|
the source of every commit message and tag. Getting the entry right is what makes a release a
|
|
one-command step for the user.
|
|
|
|
## The rule that matters most: check the tag and the commit before every changelog edit
|
|
|
|
The user commits, tags and pushes between your edits, often without telling you. An entry whose
|
|
version is tagged is released: changing it rewrites history that is already published, and the next
|
|
release fails because the tag exists. This mistake has happened many times and the user finds it
|
|
very annoying, so treat the check as part of the edit itself, not as a once-per-session step.
|
|
|
|
Right before each edit of the changelog, also for a one-line addition, run:
|
|
|
|
```sh
|
|
git tag --sort=-v:refname | head -5
|
|
git log --oneline -3
|
|
git status --short
|
|
```
|
|
|
|
Then decide:
|
|
|
|
- **The newest entry's version is not in the tags**: the entry is open. Add your items to it, and
|
|
raise its version if your change needs a bigger bump (a feature in an open patch entry makes it a
|
|
minor entry; keep one entry per release).
|
|
- **The newest entry's version is tagged**: it is released. Add a new entry above it with the next
|
|
version and your items. Never change the tagged entry, not even its wording.
|
|
- **The newest commit is a release commit** (its message is the summary line of the newest entry):
|
|
the user has just released; the same holds as for a tagged entry.
|
|
|
|
A check made earlier in the session does not count, because a release may have happened since.
|
|
|
|
## CHANGELOG.md format
|
|
|
|
```markdown
|
|
# Changelog
|
|
|
|
## 1.4.1
|
|
|
|
fix: the export page saves the chosen date range instead of the default one.
|
|
|
|
- The date range of the export page is kept when the export starts.
|
|
- The file name of an export names the date range.
|
|
|
|
## 1.4.0
|
|
|
|
feature: the reports are exported as CSV files from the export page.
|
|
|
|
- ...
|
|
```
|
|
|
|
- `# Changelog` once at the top; entries newest first.
|
|
- An entry is a `## x.y.z` heading, a blank line, one summary line, a blank line, and the changes
|
|
as `-` items. The blank lines around the summary are what the release script looks for.
|
|
- **The summary line is the commit message.** It starts with a type prefix as in the git history,
|
|
`feature:`, `fix:` or `update:` (check `git log --oneline` of the repository for the prefixes it
|
|
uses), then one sentence. It must not start with `-` or `#`, or the script finds no summary.
|
|
When an entry collects several changes, rewrite the summary so it covers all of them.
|
|
- **Items describe what changed for the user** of the software: behaviour, options, paths, pages,
|
|
commands. Not the reasoning and not the implementation story. Reasons and design decisions go to
|
|
`ADR.md`, which describes only the decisions behind the current code; the changelog is the only
|
|
place for history.
|
|
- Wrap items at about 100 columns, continuation lines indented by two spaces. Separate the list
|
|
from the summary line with a blank line. Plain ASCII only.
|
|
|
|
### Versions
|
|
|
|
- Patch (`x.y.Z`) for fixes, minor (`x.Y.0`) for features; the user decides about major versions.
|
|
- `x.y.z-n`, `n` from 2, is a later release of the same code with changes that are not code
|
|
changes (documentation, packaging); `release.mjs` accepts it.
|
|
- Every change you make goes into the newest untagged entry; when there is none, add one. A
|
|
repository's `CLAUDE.md` may add rules; follow them.
|
|
|
|
## debian/changelog format (repositories that build a Debian package)
|
|
|
|
Some repositories keep the changelog in `debian-package/debian/changelog` and release with
|
|
`release.py`. The same rules apply in Debian's format:
|
|
|
|
```text
|
|
example-app (1.4.0-1) unstable; urgency=medium
|
|
|
|
feature: the service reads and changes its settings at /settings.
|
|
|
|
* GET and PUT /settings show and change the settings; a password is
|
|
never shown, and an empty one keeps it.
|
|
* The service starts after the database is reachable.
|
|
|
|
-- Maintainer Name <maintainer@example.org> Mon, 02 Mar 2026 10:00:00 +0100
|
|
```
|
|
|
|
- The summary is the first text line, indented by exactly two spaces, between blank lines, not a
|
|
`*` item. Items are ` * ` with continuation lines indented by four spaces.
|
|
- The tag is the version without the Debian revision `1`: `(1.4.0-1)` is tagged `1.4.0`,
|
|
`(1.4.0-2)` is tagged `1.4.0-2`.
|
|
- The trailer line ` -- name <email> date` follows Debian policy (`date -R` format); keep the
|
|
maintainer of the previous entries.
|
|
|
|
## Releasing
|
|
|
|
The user runs the release. Committing, tagging and pushing are the user's decisions, so:
|
|
|
|
- Do not run a release script without `-n` (`--dry-run`), which prints the git commands instead of
|
|
running them. Use the dry run to show the user what a release would do.
|
|
- Do not commit, tag or push on your own; only when the user asks.
|
|
- The script refuses when the tag exists ("add a new CHANGELOG.md entry first"); that means an
|
|
entry was released and the next change needs a new entry.
|
|
|
|
What the scripts do, in order: read the newest entry's version and summary, refuse when the tag
|
|
exists, `git add -A`, `git commit -m <summary>`, `git tag <version>`, and
|
|
`git push origin main --tags` unless `--no-push`. `-m` overrides the message.
|
|
|
|
## Setting up a new repository
|
|
|
|
When a repository needs this setup, copy the script that fits its language and changelog:
|
|
|
|
- `scripts/release.mjs` - Node.js, `CHANGELOG.md` in the repository root.
|
|
- `scripts/release.py` - Python, `debian-package/debian/changelog`; change `CHANGELOG` for another
|
|
path.
|
|
|
|
Make it executable, add a `## Releasing` section to the README describing the commands
|
|
(`./release.mjs`, `-m`, `-n`, `--no-push`), and add a repository `CLAUDE.md` section with the
|
|
changelog rules above when the user wants one.
|