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.8.3
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.8.3
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-docs-lock |
manual | Regenerate the source-owned docs lockfile (.rrt/docs.lock.toml) |
rrt-docs-publish |
manual | Regenerate CLI reference documentation and topic pages |
rrt-docs-inject |
manual | Synchronize shared anchor blocks across documentation |
rrt-docstring-suggest |
manual | Apply scaffolded docstrings to missing or thin module docstrings |
rrt-folder-check |
pre-commit / pre-push | Validate repository folder structure against [tool.rrt.folders] config |
rrt-artifacts-check |
pre-commit / pre-push | Verify artifact hashes match the committed lock (strict by default) |
rrt-artifacts-snapshot |
manual | Hash all configured artifact_targets and write .rrt/artifacts.lock.toml |
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.8.3
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
Husky setup
Husky v9 runs shell scripts from .husky/
and works with any installed CLI, including rrt-hooks.
Install repo-release-tools so rrt-hooks is on PATH, then initialise Husky
once and add the script files that match your workflow:
pip install repo-release-tools # or: uv pip install repo-release-tools
npx husky init
Husky v9 hook file format:
npx husky initcreates.husky/pre-commitwith a shell shebang. Keep the header it generates and replace only the command body. Each hook file must be executable — runchmod +x .husky/<hook>after creating it.
Incremental workflow example
#!/usr/bin/env sh
# .husky/pre-commit
rrt-hooks pre-commit
#!/usr/bin/env sh
# .husky/commit-msg
rrt-hooks update-unreleased --message-file "$1"
rrt-hooks commit-msg "$1"
#!/usr/bin/env sh
# .husky/pre-push (optional — add when you want an unreleased guard)
rrt-hooks check-changelog --subject "$(git log -1 --format=%s)" --strategy unreleased
After creating each file, make it executable:
chmod +x .husky/pre-commit .husky/commit-msg # add .husky/pre-push if used
Squash workflow example
#!/usr/bin/env sh
# .husky/pre-commit
rrt-hooks pre-commit
#!/usr/bin/env sh
# .husky/commit-msg
rrt-hooks commit-msg "$1"
Comparison: pre-commit · lefthook · husky
| Policy | pre-commit | lefthook | husky |
|---|---|---|---|
| Auto-write changelog | rrt-update-unreleased (commit-msg) |
rrt-update-unreleased --message-file {1} |
rrt-hooks update-unreleased --message-file "$1" |
| Validate commit subject | rrt-commit-subject (commit-msg) |
rrt-commit-subject {1} |
rrt-hooks commit-msg "$1" |
| Validate branch name | rrt-branch-name (pre-commit) |
rrt-hooks pre-commit |
rrt-hooks pre-commit |
| Pre-push unreleased guard | rrt-changelog or rrt-dirty-tree |
rrt-hooks check-changelog --subject "$(git log -1 --format=%s)" --strategy unreleased |
rrt-hooks check-changelog --subject "$(git log -1 --format=%s)" --strategy unreleased |