Every release deserves a héraut.
Every team that tags releases ends up with a script. It bumps a version, writes a changelog entry, creates the tag, pushes a release to GitHub. It works — until someone adds a second environment, or the changelog format needs a tweak, or a new teammate has to figure out what it does. That script is now yours to maintain.
Héraut replaces it. One command — heraut release — resolves the next version,
generates the changelog and release notes, tags, and publishes to GitHub and/or GitLab.
It wraps the tools you already use (git, gh, glab) and handles what a hand-rolled
script usually gets wrong: version resolution for prefixed-tag strategies, multi-forge
publishing, and config that's validated before it runs, not discovered broken in CI.
Config-driven, not script-driven — change behavior by editing .heraut.yml, not by
reading bash.
The name's a French pun — héraut (herald) sounds
like héros (hero), which is the idea.
brew install --cask adaouat/tap/herautAlso installs bash/zsh/fish completions and the heraut man page.
mise use packslip:github.com/adaouat/herautVerifies the release against heraut's GitHub Actions signing identity via a
packslip manifest before installing — requires mise v2026.9.2 or newer.
Declare it in your .mise.toml / mise.toml:
[tools]
"packslip:github.com/adaouat/heraut" = "latest"On an older mise, or if you'd rather skip verification, use the plain GitHub-releases backend:
mise use github:adaouat/herautDownload the raw binary for your platform from the
releases page. Assets are named
heraut_<version>_<os>_<arch> — no .tar.gz/.zip wrapper, just the binary — alongside
a checksums.txt for verification.
# example: macOS arm64 — replace <version> with the release tag
curl -L -o heraut "https://github.com/adaouat/heraut/releases/download/<version>/heraut_<version>_darwin_arm64"
chmod +x heraut
./heraut --versionOnce installed, heraut prints a one-line upgrade hint when a newer release exists; re-run
your install method (mise upgrade heraut, go install …@latest, or the curl command) to
upgrade.
macOS / Gatekeeper: heraut's binaries aren't notarized by Apple (yet), so macOS quarantines them on download and refuses to run them. Until that changes, clear the quarantine flag yourself before running the binary:
xattr -d com.apple.quarantine heraut
docker run --rm ghcr.io/adaouat/heraut:latest --version
# run against the current repo
docker run --rm -v "$PWD":/repo -w /repo ghcr.io/adaouat/heraut:latest release --dry-runAvailable tags (Docker images do not carry the v prefix that git tags use):
| Tag | Meaning |
|---|---|
latest |
Latest release |
X.Y.Z |
Exact version, e.g. 0.58.0 |
X.Y |
Latest patch of that minor, e.g. 0.58 |
X |
Latest release of that major, e.g. 0 |
go install github.com/adaouat/heraut/cmd/heraut@latestWhen running via binary or go install, heraut does not bundle the external CLIs it
orchestrates — install the ones your config uses and make sure they are on PATH.
The Docker image bundles all of them at pinned versions; no extra setup needed.
| Tool | Needed for |
|---|---|
git |
always |
gh |
publishing to a GitHub release.targets[] entry |
glab |
publishing to a GitLab release.targets[] entry |
Neither is needed just to enrich a changelog with PR/MR data from a github/gitlab
forge — that talks to each platform's API directly over HTTP, no CLI involved. Changelog
and release-notes generation itself needs no external binary either — native (heraut's
built-in generator) ships in the heraut binary.
Run heraut check runtime to verify the tools and tokens for your config are available.
# 1. Generate a .heraut.yml interactively (or `heraut init --defaults` for an opinionated default)
heraut init
# 2. Validate the config offline — parse + semantic checks, no network
heraut check config
# 3. Preview the full pipeline without side effects
heraut release --dry-run
# 4. Cut the release: resolve version → changelog → commit → tag → publish
heraut release- Versioning — SemVer or CalVer, plus a per-environment variant of each for projects
with independently-versioned lines (e.g.
stagingvs.prod). See Spec 04 — Versioning. - Changelog & release notes — built in (
native), no external binary to install or pin a version of. See Spec 05. - Publishing — GitHub and GitLab releases; Azure DevOps is supported for commit enrichment (PR/MR data in the changelog) but has no release API of its own to publish to. See Spec 05.
The Quickstart above covers the core loop. Beyond that: heraut changelog
(changelog only, optionally --commit/--tag), heraut version next/current (print
without side effects), heraut commit verify/create (Conventional Commits tooling).
--help works on every command; --dry-run on the ones with side effects (release,
changelog, commit create, version sprint bump). See
Spec 03 — Commands for the full per-command flag reference.
Configuration lives in .heraut.yml (or .config/heraut.yml). Add the schema header for
IDE autocomplete and inline validation in any editor with YAML Language Server support:
# yaml-language-server: $schema=https://raw.githubusercontent.com/adaouat/heraut/main/schema.json
version: "1"
versioning:
strategy: semver
tag_prefix: "v"
initial_version: "0.1.0"
bump:
mode: auto
changelog:
output: CHANGELOG.md
forges:
- name: github
platform: github
repository: acme/widget
token_env: GH_TOKEN
release:
notes: {}
targets:
- forge: github| Block | Purpose |
|---|---|
versioning |
Which strategy to use and how it computes the next version |
forges |
Code-hosting connections heraut talks to — often not needed at all, since it auto-detects from CI or your git origin |
release |
What to publish and where; release.targets references a forges[].name |
That's the shape, not the whole schema — changelog output, per-environment overrides
via environments, and every other field are in
Spec 02 — Configuration. For a fully annotated example
covering every field, see docs/heraut.sample.yml.
After any command, heraut runs a non-blocking, once-per-day check against the GitHub
Releases API and prints a one-line hint — with the matching upgrade command — when a newer
version exists. Disable the check with HERAUT_CHECK_UPDATE=false.
heraut does not self-replace its binary: upgrades go through your install method
(mise upgrade heraut, go install …@latest, Homebrew, or re-running the curl command).
docs/specs/— behavioural specification (the authority for users)docs/adr/— architecture decision recordsdocs/guides/— task-oriented how-tos
