Skip to content

Key Workflows

Day-to-day development

Add a change interactively

Run add without arguments to get a guided prompt:

changelogmanager add

You will be asked to choose a change type and type your message, then confirm before the file is written.

Add a change non-interactively

Suitable for scripts and CI:

changelogmanager add --change-type fixed --message "Prevent crash on empty input"

Valid change types are: added, changed, deprecated, removed, fixed, security.

Preview without writing

Every command accepts --dry-run. It runs all validation and prints what would happen, but does not modify any files:

changelogmanager add --change-type added --message "New feature" --dry-run
changelogmanager release --dry-run

Maintaining [Unreleased]

List entries before editing

changelogmanager remove --list

This prints each [Unreleased] entry as [change-type] index: message, which is the index format used by both edit and remove.

Edit an existing entry

Update the text in place:

changelogmanager edit --change-type added --index 0 --message "Document the non-interactive release flow"

Recategorise an entry:

changelogmanager edit --change-type changed --index 1 --new-change-type fixed

You can combine --message and --new-change-type in the same command.

Remove an entry

changelogmanager remove --change-type fixed --index 0

If removing the last entry in a section empties that change type, the section is removed automatically.

Seed entries from commit history

changelogmanager from-commits

By default the command starts from the most recent git tag, then parses commit subjects using Conventional Commit-style prefixes:

Commit prefix Changelog bucket
feat, feature added
fix, bug fixed
deprecate deprecated
remove removed
security, sec security
docs, refactor, test, chore, etc. changed

Breaking commits like feat!: are treated as removed, which produces a major version bump.

Useful variants:

changelogmanager from-commits --since v1.2.0
changelogmanager from-commits --all-history
changelogmanager from-commits --strict

--strict skips subjects that do not match the selected commit schema. Without it, unmatched subjects are added as changed.


Backfilling historical releases

Backfill missing version sections from local history

changelogmanager backfill --source all --dry-run
changelogmanager backfill --source all
changelogmanager backfill --source tags --dry-run
changelogmanager backfill --source tags

This is aimed at repositories that already have release tags but either no CHANGELOG.md yet or gaps in the released sections. The command discovers local git tags, normalizes a leading v, filters them through the changelog's active versioning scheme, and adds only versions that are missing. With --source all or --source commits, it also reads commit subjects between tag intervals before falling back to tag placeholders.

Commit parsing supports --commit-schema auto, conventional, gitmoji, and keepachangelog. Auto tries all built-in schemas, so subjects like feat: add export, :bug: fix parser, and Fixed: restore ordering can all become typed changelog entries.

For each imported version, the tool uses an intentionally honest placeholder:

### Changed

- Release notes unavailable; backfilled from tag `v1.2.3`.

That keeps the generated changelog valid without inventing release notes.

Limit the range

changelogmanager backfill --source tags --since v1.0.0 --until v2.0.0

--since and --until accept either the exact tag name or the normalized version string.

What happens today

  • --source tags is the implemented path
  • --source commits and --source all are local-only and use commits grouped by tag interval
  • existing versions are skipped by default via --missing-only
  • --strategy merge --no-missing-only additively backfills entries into existing versions while preserving their text; it is idempotent on re-runs
  • --include-unreleased seeds [Unreleased] from commits since the latest release tag
  • non-version tags are reported and skipped
  • --strategy replace and the remote backfill sources (github-releases, github-prs, pypi) are not implemented; replace is intentionally unsupported because changelog entries have no stable identity

Releasing

Where your version number lives — an important design choice

Before using release, decide where your project's authoritative version number lives. This choice changes which release workflow you need.

Option A — changelog is the single source of truth. pyproject.toml (and any __version__ string in your source) is never checked by your build tool, or your build tool is told to read the version dynamically from the installed package metadata. In this case the release command alone is sufficient: it promotes [Unreleased] to a version entry, and nothing else needs to change before you build.

Option B — version appears in multiple places. Your pyproject.toml has version = "x.y.z" and/or your source has __version__ = "x.y.z". These must match the released version before you run uv build or the package will carry the wrong version number. You have two sub-options:

  • Guess the version first: compute the next version yourself (e.g. from changelogmanager version --reference future), set it in pyproject.toml manually, push that change, then trigger the release job. This is error-prone — if your guess is wrong, or a new commit changes the bump level between your edit and the job running, the versions drift.
  • Use --bump-versions: let the release command bump pyproject.toml (and Python source __version__ strings) to the released version atomically, in the same step. This is the recommended approach when you need version strings in multiple places. See Syncing version strings with --bump-versions below.

Automatic version bump

release inspects the change types in [Unreleased] and bumps the version according to the configured scheme (semver, pep440, or calver):

Change type present Bump
removed Major
added or security Minor
changed, deprecated, fixed only Patch/micro
changelogmanager release

Override the version

changelogmanager release --override-version 2.0.0

The v prefix is accepted and stripped automatically (v2.0.0 becomes 2.0.0).

Non-interactive releases

For scripts, CI, or any non-interactive run, add --yes:

changelogmanager release --yes

Without --yes, non-interactive release runs are refused. Pair it with --dry-run first if you want a preview.

Syncing version strings with --bump-versions

If your project stores a version number outside the changelog — in pyproject.toml, in a __version__ variable, or both — use --bump-versions to keep them in sync.

Prerequisite: install the jiggle optional extra:

# uv project dependency
uv add "keepachangelog-manager-fork[jiggle]"

# or standalone tool install
uv tool install "keepachangelog-manager-fork[jiggle]"

# or pip
pip install "keepachangelog-manager-fork[jiggle]"

Release and sync in one step:

changelogmanager release --bump-versions --yes

This does, in order:

  1. Promotes [Unreleased] to the inferred next version in CHANGELOG.md.
  2. Writes that same version string into [project] version in pyproject.toml.
  3. Updates any __version__ = "..." variables found in your Python source tree.

Limit to pyproject.toml only (skip Python source files):

changelogmanager release --bump-versions --pyproject-only --yes

Preview without writing anything:

changelogmanager release --bump-versions --dry-run

JSON output includes the list of modified files:

changelogmanager --json release --bump-versions --yes
{
  "released": "1.3.0",
  "bumped_version": "1.3.0",
  "bumped_files": ["pyproject.toml", "mypackage/__about__.py"]
}

The typical build sequence after running --bump-versions is:

changelogmanager release --bump-versions --yes
uv build
uv publish

Querying versions

# most recently released version
changelogmanager version

# the version before that
changelogmanager version --reference previous

# what the next release would be, based on Unreleased changes
changelogmanager version --reference future

Validation

Basic validation

changelogmanager validate

The validator checks:

  • Heading depth (maximum 3 levels)
  • Version headings follow ## [version] - yyyy-mm-dd, where version matches the configured scheme
  • Change headings are one of the six allowed types
  • Entries do not use sub-lists, numbered lists, or block quotes
  • Versions are in descending order
  • [Unreleased] is at the top

Warnings are also reported for:

  • Empty version sections
  • Empty change-type sections
  • Duplicate entries within the same change-type section

Autofix common issues

changelogmanager validate --fix

This can:

  • repair safe layout issues before parsing, such as ## Unreleased, ## Added, miscased or near-miss change headings, simple entry wrappers, a leading v in release headings, and ISO date separator variants
  • reorder released versions into descending configured-version order
  • lowercase change-type headings such as Added -> added
  • remove empty change-type sections
  • deduplicate identical entries within a section

Validate all configured components

changelogmanager --config changelogmanager.toml validate --all
changelogmanager --config changelogmanager.toml validate --all --changed-only

--changed-only uses git status --porcelain and skips configured components whose changelog files are unchanged.

Initialize or update config interactively

changelogmanager config
changelogmanager config init

config shows the effective config plus where it came from. config init writes changelogmanager.toml or pyproject.toml using interactive prompts, defaulting to pyproject.toml and semver. Re-running it updates the active config with the current answers.

Export the bundled CLI skill

changelogmanager skill export
changelogmanager skill export --path .github/skills

Without --path, the CLI prompts for a common Copilot or Claude skills location and writes the keepachangelog-manager-cli folder there.

Enforce the canonical preamble

You can require the standard Keep a Changelog preamble from configuration:

[versioning]
scheme = "semver"

[validation]
enforce_preamble = true

If versioning.scheme is set to pep440 or calver, create writes that scheme into the changelog preamble and validation expects the same wording.

GitHub Actions format

changelogmanager --error-format github validate

Errors are printed in GitHub Actions annotation format (::error file=...), making them appear inline in pull request diffs.


GitHub releases

Create a draft release

changelogmanager github-release \
  --repository owner/repo

This deletes any existing draft releases and creates a new draft from the [Unreleased] section. The release tag is set to the inferred future version.

If --github-token is omitted, the command falls back to the GITHUB_TOKEN environment variable.

Publish the release immediately

changelogmanager github-release \
  --github-token "$GITHUB_TOKEN" \
  --repository owner/repo \
  --release

Typical CI pattern

- name: Create GitHub release
  run: |
    changelogmanager github-release \
      --repository "${{ github.repository }}" \
      --release

Run github-release while [Unreleased] still exists. If you also want to rewrite CHANGELOG.md, do that in a later step or workflow with changelogmanager release --override-version "$TAG" after the GitHub release tag is known.


GitLab releases

changelogmanager gitlab-release --project group/project

GitLab has no draft-release state, so the command is an upsert: it updates the release if the computed tag already exists, otherwise it creates it and lets GitLab create the tag from --ref.

Useful variants:

changelogmanager gitlab-release --project group/project --ref "$CI_COMMIT_SHA"
changelogmanager gitlab-release --project group/project --gitlab-url https://gitlab.example.com

The command looks for credentials in --gitlab-token, then GITLAB_TOKEN, then CI_JOB_TOKEN. In practice the default CI_JOB_TOKEN usually cannot create releases, so CI jobs should normally supply a project/group/personal access token via GITLAB_TOKEN.

See GitLab automation for a concrete .gitlab-ci.yml example.


GitHub release PR automation

changelogmanager github-pr \
  --repository owner/repo \
  --head release/bump-123 \
  --base main \
  --title "chore: release 1.2.3"

This opens a pull request for a release branch, or updates the title/body if the PR already exists. It is mainly intended for GitHub Actions workflows that cut a release branch and then want an auditable PR back to the target branch.

See GitHub automation for the repository's end-to-end release workflow.


Exports

changelogmanager to-json
changelogmanager to-json --schema-version v1
changelogmanager to-html

Default output files:

Command Default output
to-json CHANGELOG.json
to-html CHANGELOG.html

to-json writes one object per release. Example output:

[
    {
        "metadata": {
            "version": "1.2.0",
            "release_date": "2024-05-01",
            "semantic_version": {
                "major": 1,
                "minor": 2,
                "patch": 0,
                "prerelease": null,
                "buildmetadata": null
            }
        },
        "added": [
            "New export command"
        ],
        "fixed": [
            "Handle missing release date gracefully"
        ]
    }
]

Use a custom filename:

changelogmanager to-json --file-name changelog-export.json
changelogmanager to-html --file-name changelog-export.html

to-json also accepts --schema-version so automation can pin the expected export contract.


Multi-component repositories

When a single repository contains multiple packages, each with its own CHANGELOG.md, create a configuration file:

[versioning]
scheme = "pep440"

[[components]]
name = "Service Component"
changelog = "service/CHANGELOG.md"
match = ["service/**"]

[[components]]
name = "Client Interface"
changelog = "client/CHANGELOG.md"
match = ["client/**"]

[[components]]
name = "default"
changelog = "CHANGELOG.md"

Then pass --config and --component to any command:

changelogmanager --config changelogmanager.toml --component "Client Interface" version
changelogmanager --config changelogmanager.toml --component "Service Component" release

from-commits --all uses each component's optional match globs to route commits by touched files. A component with no match acts as the fallback bucket for commits that do not match any explicit component.

If --config is omitted, the CLI auto-detects changelogmanager.toml, .changelogmanager.toml, or [tool.changelogmanager] in pyproject.toml from the current directory.


Specifying a changelog file directly

If you do not use a config file, you can point at any file with --input-file:

changelogmanager --input-file packages/api/CHANGELOG.md validate

Automation-friendly output

Suppress human-friendly output:

changelogmanager --quiet validate

Emit a single JSON object to stdout:

changelogmanager --json version --reference future
changelogmanager --json remove --list

--json is useful for CI or wrapper scripts that need structured output. For destructive non-interactive workflows such as release, combine it with the command's explicit confirmation flags, for example changelogmanager --json release --yes.