The tenzir-changelog harness curates
changelog entries, ships release notes, and assembles the public changelog
across Tenzir repositories. Use this page as a reference for concepts,
configuration, and CLI details. For step-by-step walkthroughs, see the guide for
maintaining a changelog.
Install
Section titled “Install”tenzir-changelog ships as a Python package that requires Python 3.12 or later.
Install it with uv (or pip) and verify the
console script:
uv add tenzir-changeloguvx tenzir-changelog --helpPython API
Section titled “Python API”Drive the CLI flows from another Python process by importing the
Changelog helper:
from pathlib import Pathfrom tenzir_changelog import Changelog
client = Changelog(root=Path("changelog"))client.add( title="API entry", entry_type="feature", description="Body text", co_authors=["claude"],)client.show(identifiers=["unreleased"], view="markdown", explicit_links=True)client.release_notes("v1.0.0", explicit_links=True)For advanced scenarios, reuse tenzir_changelog.create_cli_context and call
the helper functions from tenzir_changelog.cli directly.
Core concepts
Section titled “Core concepts”- Project – Identifies which documentation stream the changelog belongs to. Every entry and release references the same project string.
- Entry – A changelog consists of entries. Each entry uses one of four
hard-coded types:
breaking,feature,bugfix, orchange. - Unreleased bucket – Pending entries live in
unreleased/until you move them to a release. - Release – Versioned milestone under
releases/<version>/containingmanifest.yaml,notes.md, and archived entry files inentries/. - Configuration file – Settings live in
config.yamlby default, or you can store them inpackage.yamlalongside thechangelog/directory.config.yamltakes precedence when both exist. - Export style – Controls whether release notes use a detailed card layout
or a compact bullet-list layout. Set
export_style: compactin configuration to prefer the bullet-list format without passing--compacteach time. - Components – You may constrain the optional
componentfield on entries by listing allowed labels in configuration. Component filters apply to the CLI table, exports, and validation. - Modules – Nested changelog projects discovered via a glob pattern. Use
modules for monorepos where each subproject has its own changelog and independent
versioning. The
showcommand aggregates entries from all modules by default.
A typical project layout looks like this:
Directoryproject-root/
Directorychangelog/
- config.yaml
Directoryunreleased/
- add-feature.md
- fix-bug.md
Directoryreleases/
Directoryv1.0.0/
- manifest.yaml
- notes.md
Directoryentries/
- add-feature.md
- fix-bug.md
For a package layout (with package.yaml), the
structure may look like:
Directorymy-package/
- package.yaml
Directorychangelog/
Directoryunreleased/
- …
Directoryreleases/
- …
Commands
Section titled “Commands”Run changelog commands from the project root or the changelog/ directory:
uvx tenzir-changelog [command] [options]All commands accept --config to point at an explicit configuration file (YAML
format, defaulting to config.yaml) and --root to operate on another
repository. When you omit both options, the CLI looks for config.yaml inside
the changelog root or, failing that, for a package.yaml one directory above
the changelog folder. The CLI also automatically uses a changelog/
subdirectory as the project root when running from a parent directory that
contains one. This package mode lets you run commands from either the package
root or the changelog/ directory without repeating --root.
Display changelog entries in multiple views.
tenzir-changelog show [identifiers...] [options]| Option | Description |
|---|---|
identifiers | Row numbers, entry IDs, release versions, or - (optional) |
-c/--card | Show detailed cards for matching entries |
-m/--markdown | Export as Markdown |
-j/--json | Export as JSON |
--compact | Use compact bullet-list layout |
--no-compact | Use detailed card layout |
--no-emoji | Remove type emoji from output |
--explicit-links | Render @mentions and PR refs as explicit Markdown links |
--project <id> | Filter to specific project |
--component <label> | Filter to specific component |
--banner <text> | Add banner text to table output |
--include-modules/--no-modules | Include or exclude entries from discovered modules |
The table view (default) lists entries with ID, title, type, project, PRs, and
authors. Row numbers count backward from the newest entry, so #1 always
targets the latest change.
Create a new changelog entry in unreleased/.
tenzir-changelog add [options]| Option | Description |
|---|---|
--title <text> | Entry title |
--type <type> | breaking, feature, bugfix, or change |
--description <text> | Entry body |
--author <name> | Contributor name (repeatable) |
--co-author <name> | Additional contributor (repeatable) |
--pr <number> | Pull request number (repeatable) |
--component <label> | Component label (repeatable) |
--web | Open prefilled GitHub file creation URL |
The command prompts for any information you do not pass explicitly. The first
invocation scaffolds the project automatically, creating a changelog/
subdirectory with config.yaml and unreleased/. When you provide an explicit
--root flag, the CLI uses that directory directly instead of creating a
subdirectory. The CLI names entry files using the slugified title (e.g.,
my-feature.md).
By default, the CLI infers the primary author from environment variables
(TENZIR_CHANGELOG_AUTHOR, GH_USERNAME) or the GitHub CLI (gh api user).
Using --author overrides this inference entirely. The --co-author option
adds to the inferred or explicit author list without replacing it, making it
ideal for AI-assisted development, pair programming, or collaborative
contributions. Duplicates are removed automatically while preserving order.
release create
Section titled “release create”Stage a release under releases/<version>/.
tenzir-changelog release create [version] [options]| Option | Description |
|---|---|
version | Release version (e.g., v1.0.0) |
--patch | Bump patch version from latest release |
--minor | Bump minor version from latest release |
--major | Bump major version from latest release |
--yes | Commit changes (default is dry run) |
--intro <text> | Inline intro text (mutually exclusive with --intro-file) |
--intro-file <path> | Path to intro file (mutually exclusive with --intro) |
--compact | Use bullet-list layout for notes.md |
--date <YYYY-MM-DD> | Override release date |
The command renders notes.md, updates manifest.yaml, and moves entry files
into entries/. It performs a dry run by default. When the release already
exists, the CLI appends additional unreleased entries.
release notes
Section titled “release notes”Re-export release notes without modifying files.
tenzir-changelog release notes <identifier> [options]| Option | Description |
|---|---|
identifier | Release version or - for unreleased |
-m | Export as Markdown (default) |
-j | Export as JSON |
--compact | Use bullet-list layout |
--no-emoji | Drop type icons |
--explicit-links | Render @mentions and PR refs as explicit Markdown links |
Use --explicit-links when rendering Markdown outside of GitHub. By default,
the output uses @username and #123 references that GitHub auto-links. With
--explicit-links, these become explicit Markdown links like
[@username](https://github.com/username) and
[#123](https://github.com/owner/repo/pull/123), making the output portable to
documentation sites, blogs, or other Markdown renderers.
release publish
Section titled “release publish”Publish a release to GitHub via gh.
tenzir-changelog release publish <version> [options]| Option | Description |
|---|---|
version | Release version |
--yes | Skip confirmation prompts |
--draft | Mark as draft |
--prerelease | Mark as prerelease |
--tag | Create and push annotated Git tag |
The command reads project metadata from config.yaml or package.yaml for the
repository slug and uses notes.md as the release body.
validate
Section titled “validate”Run structural checks across entry files, release manifests, and exported documentation.
tenzir-changelog validateThe validator reports missing metadata, unused entries, duplicate entry IDs, and configuration drift across repositories. When modules are configured, validation runs against the parent project and all discovered modules. Issues from modules are prefixed with the module ID.
modules
Section titled “modules”List discovered modules when a modules glob pattern is configured.
tenzir-changelog modulesThe command displays a table with module ID, name, relative path, and count of
unreleased entries. Use this to discover module paths for --root operations.
Configuration
Section titled “Configuration”Configuration settings live in config.yaml by default, or you can store them
in package.yaml alongside the changelog/ directory. config.yaml takes
precedence when both exist.
Configuration fields:
| Field | Description |
|---|---|
id | Canonical project slug written into entry frontmatter (required) |
name | Human-friendly label surfaced in release titles and CLI output |
description | Optional project description included in release manifests |
repository | Optional GitHub slug (e.g., owner/repo) used by release publish |
export_style | Default layout: compact (bullet-list) or omit for detailed cards |
components | Optional list of component labels that entries must satisfy |
modules | Optional glob pattern for discovering nested changelog projects |
Example:
id: tenzir-corename: Tenzir Coredescription: Core pipeline enginerepository: tenzir/tenzirexport_style: compactcomponents: - cli - engine - operatorsThe first invocation of tenzir-changelog add scaffolds a changelog/
subdirectory with config.yaml, inferring defaults from the parent directory
name. When you provide an explicit --root flag, the CLI uses that directory
directly. Projects with package.yaml next to changelog/ reuse the package
id and name automatically.
Entry file format
Section titled “Entry file format”Entry files live in unreleased/ or releases/<version>/entries/ as Markdown
files with YAML frontmatter. The CLI names entry files using the slugified title
(e.g., my-feature.md, fix-bug.md).
Example entry:
---title: Add pipeline buildertype: featureauthor: alicecreated: 2025-10-16T14:30:00Zpr: 101component: cli---
Introduces the new pipeline builder UI with drag-and-drop support.You can use either singular (author, pr, component) or plural (authors,
prs, components) keys. The singular form is shorthand for single values and
is normalized to the plural form internally.
Frontmatter fields:
| Field | Type | Required | Description |
|---|---|---|---|
title | string | yes | Entry title |
type | string | yes | breaking, feature, bugfix, or change |
author / authors | list[string] | no | Contributor names (singular or plural form) |
created | string | yes | Creation datetime in ISO 8601 UTC format |
pr / prs | list[int] | no | Pull request numbers (singular or plural form) |
component / components | list[string] | no | Labels matching configured components |
Release manifest format
Section titled “Release manifest format”Release manifests live at releases/<version>/manifest.yaml and record metadata
about a release.
Example manifest:
created: 2025-10-18intro: | Welcome to version 1.0.0!
This release includes significant performance improvements.Manifest fields:
| Field | Type | Required | Description |
|---|---|---|---|
created | string | yes | Release date in YYYY-MM-DD format |
intro | string | no | Introductory content (supports Markdown) |
The CLI generates notes.md by stitching together the intro and grouped entry
sections. Intro content comes from either --intro (inline text) or
--intro-file (file path) when creating a release.
Environment variables
Section titled “Environment variables”The CLI recognizes these environment variables:
| Variable | Description |
|---|---|
GITHUB_TOKEN | GitHub token with repo scope for private repos |
Multi-project mode
Section titled “Multi-project mode”For managing multiple related projects, tenzir-changelog supports coordinated
changelog operations across multiple project roots using the --root flag
repeatedly.
When you specify multiple --root flags, the CLI enters multi-project mode:
- The
showcommand displays entries from all projects in a unified table with a Project column, sorting entries by project order (as specified by--rootflags) and then by date within each project. - Markdown and JSON exports group entries by project using a hierarchical format: version → project → entry type → entries.
- The
release createcommand performs atomic coordinated releases across all projects. If any project fails validation (e.g., the version already exists), the CLI makes no changes to any project.
Use --project <id> to filter output to specific projects. Entry identifiers
may repeat across projects, and the CLI keeps them correctly scoped to their
origin project when rendering tables or exports.
Multi-project markdown exports follow this hierarchical structure:
# v5.0.0
## Tenzir Core
### Features
- **Add pipeline builder**: Introduces the new pipeline builder UI.
## Tenzir Cloud
### Features
- **Add SSO support**: Integrates enterprise SSO for authentication.Modules
Section titled “Modules”Modules are nested changelog projects discovered automatically via a configurable glob pattern. This enables repositories with multiple subprojects—each with its own changelog and independent versioning—to manage them from a single parent project.
Configuration
Section titled “Configuration”Add a modules field to your parent project’s config.yaml:
id: libraryname: Tenzir Librarymodules: "../packages/*/changelog"The glob pattern resolves relative to the changelog root directory. Each matched
directory must contain a valid config.yaml with its own id and name.
Example module config (packages/amazon_vpc_flow/changelog/config.yaml):
id: amazon_vpc_flowname: Amazon VPC Flow PackageAggregated views
Section titled “Aggregated views”When modules is configured, show includes entries from all modules by
default. The table displays a Project column showing which project (parent
or module) each entry belongs to:
# Show all unreleased entries (parent + modules)tenzir-changelog show
# Show only parent entriestenzir-changelog show --no-modulesOperating on modules
Section titled “Operating on modules”Modules are fully standalone changelog projects. Use --root to operate on a
specific module:
# Add entry to a moduletenzir-changelog --root ../packages/amazon_vpc_flow/changelog add --title "Add parser"
# Create a release for a moduletenzir-changelog --root ../packages/amazon_vpc_flow/changelog release create v1.0.0The modules command displays paths for convenient copy-paste into --root
flags.
Validation
Section titled “Validation”When modules is configured, tenzir-changelog validate checks:
- Module ID uniqueness across all discovered modules
- Valid configuration in each module directory
Troubleshooting
Section titled “Troubleshooting”- Validation errors – Run
tenzir-changelog validateto identify missing metadata, unused entries, or duplicate IDs. - Component mismatch – When
componentsis configured, ensure every entry either omitscomponentor uses an allowed label. - Configuration not found – Ensure
config.yamlexists in the changelog root orpackage.yamlsits next to thechangelog/directory. Runtenzir-changelog addonce to scaffold the configuration. - Version bump fails – Bump flags read the latest release manifest on disk.
Create an initial release with an explicit version before using
--patch/--minor/--major.