Changelog management for Rust, Python, Go, and TypeScript¹ workspaces.
¹ TypeScript support is coming soon.
# Install changelogs
curl -sSL changelogs.sh | sh
# Initialize changelogs in your workspace
changelogs init
# Add a changelog for your changes
changelogs add
# See what would be released
changelogs status
# Apply version bumps and generate changelogs
changelogs versionflowchart LR
subgraph Development
A[Make Changes] --> B[Open PR]
B --> C{Bot comments<br/>with changelog link}
C --> D[Add changelog]
D --> E[Merge PR]
end
subgraph Release
E --> F[/RC PR created/]
F --> G[Merge RC PR]
G --> H[/📦 Packages released/]
end
| # | Step | Description |
|---|---|---|
| 1 | Make changes & open PR | Implement your feature or fix |
| 2 | Bot comments on PR | Links to add/edit changelog (AI pre-fills if enabled) |
| 3 | Add changelog & merge | Changelog gets staged in .changelog/ |
| # | Step | Description |
|---|---|---|
| 1 | Push to main | Triggers release workflow |
| 2 | RC PR created | Version bumps and changelog updates |
| 3 | Merge RC PR | Packages published, GitHub releases created |
curl -sSL https://changelogs.sh | shOr download directly from GitHub Releases:
| Platform | Download |
|---|---|
| Linux (x86_64) | changelogs-linux-amd64 |
| macOS (Intel) | changelogs-darwin-amd64 |
| macOS (Apple Silicon) | changelogs-darwin-arm64 |
| Command | Description |
|---|---|
init |
Initialize .changelog/ directory |
add |
Create a new changelog interactively |
add --ai "<command>" |
Generate changelog using AI (see Supported AI Providers) |
status |
Show pending changelogs and releases |
version |
Apply version bumps and update changelogs |
version --prerelease rc |
Apply prerelease bumps like 1.6.0-rc1, then 1.6.0-rc2 |
publish |
Publish unpublished packages to crates.io |
.changelog/config.toml:
# How to bump packages that depend on changed packages
dependent_bump = "patch" # patch, minor, or none
[changelog]
format = "per-crate" # or "root"
# Fixed groups: all always share the same version
[[fixed]]
members = ["crate-a", "crate-b"]
# Linked groups: versions sync when released together
[[linked]]
members = ["sdk-core", "sdk-macros"]
# Packages to ignore
ignore = [].changelog/brave-lions-dance.md:
---
my-crate: minor
other-crate: patch
---
Added new feature X that does Y.
Fixed bug Z in the parser.Override the default AI prompt by placing an instructions.md file in your .changelog/ directory:
.changelog/instructions.md:
The file contents are used as the prompt template. Use {packages} and {diff} as placeholders:
Generate a changelog entry for this diff.
Available packages: {packages}
---
<package-name>: patch
---
Description.
Version rules:
- "major": any removal or rename of public API
- "minor": new features
- "patch": bug fixes, internal changes
{diff}Priority: --instructions flag > .changelog/instructions.md > built-in default.
The --ai flag and GitHub Action ai input accept any CLI command that reads from stdin and outputs text. The diff is piped to the command, and the output becomes the changelog entry.
| Provider | Command | Required Secret | Install |
|---|---|---|---|
| Amp | amp -x |
AMP_API_KEY |
npm install -g @sourcegraph/amp |
| Claude Code | claude -p |
ANTHROPIC_API_KEY |
npm install -g @anthropic-ai/claude-code |
| OpenAI | openai api chat.completions.create -m gpt-4o |
OPENAI_API_KEY |
pip install openai |
| Gemini | gemini |
GOOGLE_API_KEY |
npm install -g @anthropic-ai/gemini-cli |
# Using Amp
changelogs add --ai "amp -x"
# Using Claude
changelogs add --ai "claude -p"
Comments on PRs with changelog status. If no changelog exists and ai is provided, generates one and pre-fills the "Add changelog" link.
name: Changelog
on:
pull_request:
types: [opened, synchronize]
jobs:
changelog:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- run: npm install -g @sourcegraph/amp
- uses: wevm/changelogs/check@master
with:
ai: 'amp -x'
env:
AMP_API_KEY: ${{ secrets.AMP_API_KEY }}Creates a release candidate PR when changelogs exist, or publishes packages when the RC PR is merged.
name: Release
on:
push:
branches:
- main
- 'release/**'
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: wevm/changelogs@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}The release action automatically handles both versioning and publishing:
- If changelogs exist → Creates/updates a "Version Packages" PR
- If no changelogs (PR was just merged) → Publishes unpublished packages to crates.io
Use post-version-command to run a command after version bumps but before the PR is created (e.g. refreshing lockfiles):
- uses: wevm/changelogs@master
with:
post-version-command: 'cargo metadata --format-version=1 > /dev/null'Use prerelease to create release candidate PRs before a stable release:
- uses: wevm/changelogs@master
with:
prerelease: rcThis runs changelogs version --prerelease rc, creating versions like
1.6.0-rc1 and incrementing an existing 1.6.0-rc1 to 1.6.0-rc2.
Running changelogs version without --prerelease promotes an existing
prerelease version to its stable version, such as 1.6.0.
In the GitHub Action, leave prerelease unset for the stable release workflow.
When there are no pending changelog files but package versions are still
prereleases, the action opens a stable-promotion PR instead of publishing
immediately.
| Input | Description | Default |
|---|---|---|
branch |
Branch name for the version PR. Defaults to changelog-release/{trigger-branch}, enabling independent release PRs per branch. |
changelog-release/{trigger-branch} |
commit |
Commit message for version bump | Version Packages |
conventional-commit |
Use conventional commit format | false |
prerelease |
Prerelease identifier for release candidates, such as rc |
- |
post-version-command |
Command to run after version bumps but before PR creation | - |
crate-token |
Crates.io API token for publishing (Rust) | - |
pypi-token |
PyPI API token for publishing (Python) | - |
publish-mode |
"registry" publishes to crates.io/PyPI (default). "tags-only" intentionally skips registry authentication; only git tags and GitHub releases are created. When no registry authentication is configured, registry upload is skipped automatically. |
registry |
Projects that release binaries rather than publishing to a package registry
can use changelogs without providing any registry token. When
CARGO_REGISTRY_TOKEN (Rust) or TWINE_PASSWORD (Python) is absent,
changelogs publish silently skips the registry upload, creates git tags for
each package, and exits successfully — so the GitHub release step that follows
runs normally.
When no registry authentication is configured, no extra configuration is required:
- uses: wevm/changelogs@master
# No registry authentication configured -> tags-only mode automatically
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}Set publish-mode: tags-only to make the intent explicit and force registry
authentication to be skipped even if registry credentials or PyPI Trusted
Publishing are available:
- uses: wevm/changelogs@master
with:
publish-mode: tags-only
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}To maintain independent release trains per branch (e.g. patch releases on
release/v1.5 alongside new features on master), configure your release
workflow to run on each release branch. The action then automatically creates a
separate release PR per trigger branch:
| Trigger branch | Release PR branch |
|---|---|
| master | changelog-release/master |
| release/v1.5 | changelog-release/release/v1.5 |
| release/v2.0 | changelog-release/release/v2.0 |
Changelog entries on each branch are independent. To override the release PR
branch name, set the branch input explicitly:
- uses: wevm/changelogs@master
with:
branch: my-custom-release-branch| Output | Description |
|---|---|
hasChangelogs |
Whether there are pending changelogs |
pullRequestNumber |
The PR number if created/updated |
published |
Whether packages were published |
publishedPackages |
JSON array of published packages |
Changelogs supports Python packages using PEP 621 pyproject.toml files.
Requirements:
pyproject.tomlwith[project]section containingnameandversion- Static version (dynamic versions not supported)
- Semantic versioning (no PEP 440 epochs or local versions)
python -m buildandtwineinstalled (pip install build twine)
Limitations:
- Single-package repos only (no Python monorepo support)
- PEP 621 only (no
setup.pyorsetup.cfg)
Authentication:
You can authenticate to PyPI with either a static API token or Trusted
Publishing (OIDC). The action picks
OIDC automatically when pypi-token is empty and the workflow has
id-token: write. PyPI auth setup only runs when ecosystem: python is set, so
include that input for both static-token and Trusted Publishing workflows.
Static API token:
- uses: wevm/changelogs@master
with:
ecosystem: python
pypi-token: ${{ secrets.PYPI_API_TOKEN }}Trusted Publishing (recommended — no long-lived secrets):
jobs:
release:
runs-on: ubuntu-latest
environment: release
permissions:
contents: write
pull-requests: write
id-token: write # required for OIDC trusted publishing
steps:
- uses: actions/checkout@v5
with:
persist-credentials: true
- uses: wevm/changelogs@master
with:
ecosystem: python
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}You also need to register a Trusted Publisher on PyPI under the project's Publishing settings. The repository, workflow filename, and (optionally) environment must match the workflow above.
Changelogs supports Go modules using go.mod files. Go is unusual: versions
live in git tags, not in the manifest. To remember the bumped version
between the version and publish CI runs, changelogs writes it inline as a
comment in go.mod:
// changelogs:version 1.2.3
module github.com/owner/repoRequirements:
go.modat the repository root with amoduledirective- Semantic versioning for git tags (
v1.2.3)
Package name: Go uses the full module path as the package name (e.g.
github.com/owner/repo). This is what you write in changelog frontmatter:
---
github.com/owner/repo: minor
---Publishing:
- No registry token required — pushing the git tag publishes to
proxy.golang.org - Tag format is
vX.Y.Z(no package-name prefix) - Already-published versions are skipped via a
proxy.golang.org/<module>/@v/v<ver>.infolookup
Limitations:
- Single-module repos only (no Go monorepo / multi-
go.modsupport) - Major version bumps (≥ v2) do not currently rewrite the
module .../vNsuffix
Changelogs supports Swift Package Manager packages using root Package.swift
files. SwiftPM versions live in git tags, not in the manifest. To remember
the bumped version between the version and publish CI runs, changelogs writes
it inline as a comment in Package.swift:
// swift-tools-version: 5.10
// changelogs:version 1.2.3
import PackageDescriptionRequirements:
Package.swiftat the repository root withPackage(name: "...")- Semantic versioning for git tags (
v1.2.3)
Package name: Swift uses the package's Package(name:) value as the package
name. This is what you write in changelog frontmatter:
---
TempoKit: minor
---Publishing:
- No registry token required — pushing the git tag publishes the SwiftPM package
- Tag format is
vX.Y.Z(no package-name prefix)
Limitations:
- Single-package repos only (no Swift package monorepo support)
- Dependency version updates currently support
.package(url: ..., from: "...")and.package(url: ..., exact: "...")
MIT OR Apache-2.0