This guide covers automated changelog generation and the hook system for running custom logic before and after version bumps.
Changelog
Releaser generates changelogs from git commits using conventional commit prefixes.
Commit format
Write your commits like this:
feat: add CartaPorte 3.1 complement support
fix: correct UTF-8 encoding in XML attributes
refactor: extract version parsing to Version struct
docs: update publishing guide with org examples
perf: cache XSD schema parsing results
breaking: remove deprecated cer/key modulesThe prefix before : maps to a changelog section.
Default mappings
| Prefix | Changelog section |
|---|---|
feat | Added |
fix | Fixed |
refactor | Changed |
docs | Documentation |
perf | Performance |
breaking | Breaking Changes |
Generate a changelog
# Preview without writing
$ mix releaser.changelog cfdi_xml --dry-run
Changelog for cfdi_xml:
## [4.0.19] - 2026-04-20
### Added
- add CartaPorte 3.1 complement support
- add Certificate.toBase64() method
### Fixed
- correct UTF-8 encoding in XML attributes
### Changed
- migrate certificar() to use Certificate class
--dry-run: no files written
# Write to CHANGELOG.md
$ mix releaser.changelog cfdi_xml
Updated apps/cfdi/xml/CHANGELOG.md
Scope by git ref
# Changes since a specific tag
$ mix releaser.changelog cfdi_xml --from cfdi_xml-v4.0.18
# Changes in a range
$ mix releaser.changelog --from v1.0.0 --to v2.0.0
Custom anchors
Override the default prefix → section mapping:
releaser: [
changelog: [
anchors: %{
"feat" => "New Features",
"fix" => "Bug Fixes",
"security" => "Security",
"deprecate" => "Deprecated",
"remove" => "Removed"
}
]
]Hooks
Hooks let you run custom code before and after version bumps.
Built-in hooks
Releaser includes two ready-to-use hooks:
Releaser.Hooks.GitTag
After a bump, stages the changed mix.exs files, creates a commit, and
tags it:
Commit: "bump: version update"
Tag: "cfdi_xml-v4.0.19"Releaser.Hooks.ChangelogHook
After a bump, generates/updates the CHANGELOG.md in the app's directory.
Enable hooks
Add them to your config:
releaser: [
hooks: [
post: [
Releaser.Hooks.GitTag,
Releaser.Hooks.ChangelogHook
]
]
]Now when you bump:
$ mix releaser.bump cfdi_xml patch
Version changes:
cfdi_xml 4.0.18 → 4.0.19 (direct)
1 app(s) updated
changelog apps/cfdi/xml/CHANGELOG.md
tagged cfdi_xml-v4.0.19
Skip hooks for a single run
$ mix releaser.bump cfdi_xml patch --no-hooks
Writing custom hooks
Pre-hook example: ensure clean working tree
defmodule MyProject.Hooks.EnsureClean do
@behaviour Releaser.Hooks.PreHook
@impl true
def run(_context) do
if Releaser.Git.dirty?() do
{:error, "Working tree is dirty. Commit or stash changes first."}
else
:ok
end
end
endPost-hook example: notify Slack
defmodule MyProject.Hooks.NotifySlack do
@behaviour Releaser.Hooks.PostHook
@impl true
def run(%{app: app, new_version: version, changes: changes}) do
count = length(changes)
message = "Released #{app} v#{version} (#{count} package(s) updated)"
# Your Slack API call here...
Slack.post_message("#releases", message)
:ok
end
endPost-hook example: run tests before tagging
defmodule MyProject.Hooks.RunTests do
@behaviour Releaser.Hooks.PreHook
@impl true
def run(%{app: app, path: path}) do
case System.cmd("mix", ["test"], cd: path, stderr_to_stdout: true) do
{_output, 0} -> :ok
{output, _} -> {:error, "Tests failed for #{app}:\n#{output}"}
end
end
endHook context
Both pre and post hooks receive a context map:
%{
app: "cfdi_xml", # app being bumped
path: "apps/cfdi/xml", # path to the app
old_version: "4.0.18", # version before bump
new_version: "4.0.19", # version after bump
bump_type: :patch, # :patch | :minor | :major | :release | :explicit
changes: [ # all planned changes (including cascade)
%{app: "cfdi_xml", path: "...", old: "4.0.18", new: "4.0.19", reason: :direct},
%{app: "cfdi_designs", path: "...", old: "1.0.0", new: "1.0.1", reason: :cascade}
],
apps: [%Releaser.App{}, ...] # all discovered apps
}Hook execution order
- All pre-hooks run (in config order)
- If any pre-hook returns
{:error, reason}, the bump is aborted - Version files are updated
- All post-hooks run (in config order)
- If a post-hook fails, a warning is printed but the bump is not rolled back
Full hook configuration example
releaser: [
hooks: [
pre: [
MyProject.Hooks.EnsureClean,
MyProject.Hooks.RunTests
],
post: [
Releaser.Hooks.ChangelogHook,
Releaser.Hooks.GitTag,
MyProject.Hooks.NotifySlack
]
]
]