Skip to content

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.

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:

Terminal window
uv add tenzir-changelog
uvx tenzir-changelog --help

Drive the CLI flows from another Python process by importing the Changelog helper:

from pathlib import Path
from 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.

  • 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, or change.
  • Unreleased bucket – Pending entries live in unreleased/ until you move them to a release.
  • Release – Versioned milestone under releases/<version>/ containing manifest.yaml, notes.md, and archived entry files in entries/.
  • Configuration file – 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.
  • Export style – Controls whether release notes use a detailed card layout or a compact bullet-list layout. Set export_style: compact in configuration to prefer the bullet-list format without passing --compact each time.
  • Components – You may constrain the optional component field 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 show command 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/

Run changelog commands from the project root or the changelog/ directory:

Terminal window
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]
OptionDescription
identifiersRow numbers, entry IDs, release versions, or - (optional)
-c/--cardShow detailed cards for matching entries
-m/--markdownExport as Markdown
-j/--jsonExport as JSON
--compactUse compact bullet-list layout
--no-compactUse detailed card layout
--no-emojiRemove type emoji from output
--explicit-linksRender @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-modulesInclude 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]
OptionDescription
--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)
--webOpen 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.

Stage a release under releases/<version>/.

tenzir-changelog release create [version] [options]
OptionDescription
versionRelease version (e.g., v1.0.0)
--patchBump patch version from latest release
--minorBump minor version from latest release
--majorBump major version from latest release
--yesCommit 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)
--compactUse 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.

Re-export release notes without modifying files.

tenzir-changelog release notes <identifier> [options]
OptionDescription
identifierRelease version or - for unreleased
-mExport as Markdown (default)
-jExport as JSON
--compactUse bullet-list layout
--no-emojiDrop type icons
--explicit-linksRender @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.

Publish a release to GitHub via gh.

tenzir-changelog release publish <version> [options]
OptionDescription
versionRelease version
--yesSkip confirmation prompts
--draftMark as draft
--prereleaseMark as prerelease
--tagCreate 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.

Run structural checks across entry files, release manifests, and exported documentation.

tenzir-changelog validate

The 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.

List discovered modules when a modules glob pattern is configured.

tenzir-changelog modules

The 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 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:

FieldDescription
idCanonical project slug written into entry frontmatter (required)
nameHuman-friendly label surfaced in release titles and CLI output
descriptionOptional project description included in release manifests
repositoryOptional GitHub slug (e.g., owner/repo) used by release publish
export_styleDefault layout: compact (bullet-list) or omit for detailed cards
componentsOptional list of component labels that entries must satisfy
modulesOptional glob pattern for discovering nested changelog projects

Example:

id: tenzir-core
name: Tenzir Core
description: Core pipeline engine
repository: tenzir/tenzir
export_style: compact
components:
- cli
- engine
- operators

The 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 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 builder
type: feature
author: alice
created: 2025-10-16T14:30:00Z
pr: 101
component: 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:

FieldTypeRequiredDescription
titlestringyesEntry title
typestringyesbreaking, feature, bugfix, or change
author / authorslist[string]noContributor names (singular or plural form)
createdstringyesCreation datetime in ISO 8601 UTC format
pr / prslist[int]noPull request numbers (singular or plural form)
component / componentslist[string]noLabels matching configured components

Release manifests live at releases/<version>/manifest.yaml and record metadata about a release.

Example manifest:

created: 2025-10-18
intro: |
Welcome to version 1.0.0!
This release includes significant performance improvements.

Manifest fields:

FieldTypeRequiredDescription
createdstringyesRelease date in YYYY-MM-DD format
introstringnoIntroductory 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.

The CLI recognizes these environment variables:

VariableDescription
GITHUB_TOKENGitHub token with repo scope for private repos

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 show command displays entries from all projects in a unified table with a Project column, sorting entries by project order (as specified by --root flags) 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 create command 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 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.

Add a modules field to your parent project’s config.yaml:

id: library
name: Tenzir Library
modules: "../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_flow
name: Amazon VPC Flow Package

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:

Terminal window
# Show all unreleased entries (parent + modules)
tenzir-changelog show
# Show only parent entries
tenzir-changelog show --no-modules

Modules are fully standalone changelog projects. Use --root to operate on a specific module:

Terminal window
# Add entry to a module
tenzir-changelog --root ../packages/amazon_vpc_flow/changelog add --title "Add parser"
# Create a release for a module
tenzir-changelog --root ../packages/amazon_vpc_flow/changelog release create v1.0.0

The modules command displays paths for convenient copy-paste into --root flags.

When modules is configured, tenzir-changelog validate checks:

  • Module ID uniqueness across all discovered modules
  • Valid configuration in each module directory
  • Validation errors – Run tenzir-changelog validate to identify missing metadata, unused entries, or duplicate IDs.
  • Component mismatch – When components is configured, ensure every entry either omits component or uses an allowed label.
  • Configuration not found – Ensure config.yaml exists in the changelog root or package.yaml sits next to the changelog/ directory. Run tenzir-changelog add once 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.

Last updated: