rrt hooks
rrt hooks
repo-release-tools publishes reusable hooks in .pre-commit-hooks.yaml.
The first decision is not which hook do I add? — it is which changelog workflow does this repo follow?
Choose the workflow first
| Workflow | Recommended hooks | Best for |
|---|---|---|
incremental (default) |
rrt-branch-name, rrt-commit-subject, plus rrt-update-unreleased or rrt-changelog |
teams that maintain changelog state while developing |
squash |
rrt-branch-name, rrt-commit-subject, optional rrt-dirty-tree / rrt-doctor / rrt-release-check |
repos that squash many commits and do changelog work at release time |
With changelog_workflow = "squash", the changelog-writing and changelog-check
hooks intentionally skip changelog enforcement. You can leave them configured
during migration, but the cleaner setup is to remove them and keep only the
non-changelog policy hooks.
Incremental workflow: keep [Unreleased] current
default_install_hook_types: [pre-commit, commit-msg]
repos:
- repo: https://github.com/Anselmoo/repo-release-tools
rev: v1.4.0
hooks:
- id: rrt-branch-name
- id: rrt-update-unreleased
- id: rrt-commit-subject
Install both hook types:
pre-commit install --hook-type pre-commit --hook-type commit-msg
This setup keeps CHANGELOG.md moving with development. rrt-update-unreleased
auto-writes changelog bullets for changelog-relevant commit types, while
rrt-commit-subject enforces Conventional Commits.
If you prefer manual changelog edits instead of auto-writing them, replace
rrt-update-unreleased with rrt-changelog.
Squash workflow: keep local policy, skip per-commit changelog noise
default_install_hook_types: [pre-commit, commit-msg]
repos:
- repo: https://github.com/Anselmoo/repo-release-tools
rev: v1.4.0
hooks:
- id: rrt-branch-name
- id: rrt-commit-subject
Use this when pull requests are squash-merged and you do not want ten tiny commit-level changelog bullets to become one giant release footnote monster. Pair it with:
changelog_workflow = "squash"in repo config- GitHub Action
changelog-strategy: "auto"or"release-only" rrt bumpto generate release-time changelog content
Hook overview
| Hook | Stage | Description |
|---|---|---|
rrt-branch-name |
pre-commit | Validate branch naming convention |
rrt-update-unreleased |
commit-msg | Auto-write a bullet under [Unreleased] for changelog-relevant commits |
rrt-changelog |
pre-commit | Require a staged changelog update for changelog-relevant work |
rrt-commit-subject |
commit-msg | Validate Conventional Commit subjects |
rrt-dirty-tree |
pre-push / manual | Fail on uncommitted changes |
rrt-doctor |
manual | Run rrt doctor core automation checks on rrt config |
rrt-release-check |
manual | Run rrt release check for version targets, pin targets, and changelog files |
rrt-update-unreleased and rrt-changelog are alternatives for the
incremental workflow. You usually want one or the other, not both.
Optional guards
Dirty tree check
rrt-dirty-tree is not enabled in the minimal configs because a normal
pre-commit run happens while the working tree is intentionally dirty. It is
better suited for pre-push or manual execution when you want to enforce a
clean repository before publishing work:
repos:
- repo: https://github.com/Anselmoo/repo-release-tools
rev: v1.4.0
hooks:
- id: rrt-dirty-tree
stages: [pre-push]
Doctor check
rrt-doctor runs rrt doctor against the repository’s core automation wiring
so you can confirm hook and CI surfaces are configured before a release or
automation rollout:
pre-commit run rrt-doctor --hook-stage manual
# or directly:
rrt doctor
Release check
rrt-release-check runs rrt release check against version targets, pin
targets, and changelog files in [tool.rrt]. It is also registered at the
manual stage so you can invoke it on demand before releases:
pre-commit run rrt-release-check --hook-stage manual
# or directly:
rrt release check
You can also run the same dirty-tree logic directly:
rrt-hooks check-dirty-tree
Post-correction mode (squash-merge workflows)
When a pull request is merged via squash merge, GitHub condenses all
per-commit changelog entries into the squash commit. The result can be
fragmented micro-commit noise in CHANGELOG.md — for example several
"CI: add Node 26" / "CI: remove Node 26" pairs that cancel each other out.
rrt-hooks changelog post-correct consolidates those entries by:
- Inspecting the diff that the squash commit introduced to
CHANGELOG.md. - Removing exact duplicate bullet entries (case-insensitive).
- Removing semantically-cancelling pairs — e.g.
"CI: add Node 26"followed by"CI: remove Node 26", or bare"add X"/"remove X". Scope prefixes (e.g.CI:,Deps:) must match for entries to be considered a pair. - Rewriting
CHANGELOG.mdin-place with the cleaned content, restricting removals to the exact diff hunk so older release sections are never touched. - Optionally creating a follow-up commit (
--commit).
Quick usage
# auto mode — use HEAD as the squash commit (default when no SHA given)
rrt-hooks changelog post-correct
# explicit squash commit SHA
rrt-hooks changelog post-correct --squash-commit abc1234
# write a follow-up commit automatically
rrt-hooks changelog post-correct --commit
As a GitHub Actions step (post-merge on default branch)
- name: Consolidate changelog after squash merge
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
run: uvx --from repo-release-tools rrt-hooks changelog post-correct --commit
Options
| Flag | Description |
|---|---|
--squash-commit SHA |
Explicit commit SHA to inspect (defaults to HEAD) |
--output PATH |
Changelog file to rewrite (default: CHANGELOG.md) |
--commit |
Create a follow-up commit with the corrected changelog |
Lefthook setup
Lefthook can run the same local
policy with an installed rrt-hooks binary.
Install repo-release-tools so rrt-hooks is on PATH, then add the commands
that match your workflow.
Incremental workflow example
# lefthook.yml
commit-msg:
commands:
rrt-update-unreleased:
run: rrt-hooks update-unreleased --message-file {1}
rrt-commit-subject:
run: rrt-hooks commit-msg {1}
pre-commit:
commands:
rrt-branch-name:
run: rrt-hooks pre-commit
pre-push:
commands:
rrt-changelog:
run: rrt-hooks check-changelog --subject "$(git log -1 --format=%s)" --strategy unreleased
Squash workflow example
# lefthook.yml
commit-msg:
commands:
rrt-commit-subject:
run: rrt-hooks commit-msg {1}
pre-commit:
commands:
rrt-branch-name:
run: rrt-hooks pre-commit
Comparison: pre-commit vs. lefthook
| Policy | pre-commit | lefthook |
|---|---|---|
| Auto-write changelog | rrt-update-unreleased (commit-msg) |
rrt-update-unreleased --message-file {1} |
| Validate commit subject | rrt-commit-subject (commit-msg) |
rrt-commit-subject {1} |
| Validate branch name | rrt-branch-name (pre-commit) |
rrt-hooks pre-commit |
| Pre-push unreleased guard | rrt-changelog or rrt-dirty-tree |
rrt-hooks check-changelog --strategy unreleased |