stamp
A language-agnostic, changesets-style versioning and changelog tool. Works with any project type and supports monorepos.
Overview
stamp is inspired by the Changesets workflow, but without the Node.js dependency. It works by having contributors add small stamp files (.stamp/*.md) alongside their code changes. When it's time to release, stamp consumes those files, bumps versions, updates changelogs, creates git tags, and publishes releases to GitHub or Gitea.
There are two supported release workflows:
- Direct release — run
stamp versionandstamp publishlocally or in CI on the base branch. - Release PR — run
stamp release-prin CI to open a pull request that contains the version bumps. Merging the PR triggers the actual publish step.
Installation
Via Mise:
# mise.toml
[tools]
"go:git.thokra.dev/thokra/stamp/cmd/stamp" = "latest"
Or with go install:
go install git.thokra.dev/thokra/stamp/cmd/stamp@latest
Or build from source:
git clone https://git.thokra.dev/thokra/stamp
cd stamp
go build -o bin/stamp ./cmd/stamp
Quick Start
1. Create .stamp/stamp.toml
[[projects]]
name = "my-app"
path = "."
version = "0.1.0"
[projects.publish]
tags = true
releases = true
artifacts = ["dist/my-app-*"]
Because stamp.toml lives inside .stamp/, git will track the directory without needing a .gitignore file in it.
See examples/stamp.toml for a fully annotated example.
2. Add a stamp file when making a change
# Interactive
stamp add
# Non-interactive (great for scripts and AI agents)
stamp add --project=my-app --bump=minor --message="Added new feature"
This creates a .stamp/<random-slug>.md file. Commit it alongside your code.
3. Check pending stamps
stamp status
📦 Pending stamps: 2
PROJECT CURRENT NEXT BUMP STAMPS
------- ------- ---- ---- ------
my-app 1.2.3 1.3.0 minor 2
4. Release
# Bump versions and update changelogs
stamp version
# Create git tags, GitHub/Gitea releases, and upload artifacts
STAMP_REPO=owner/repo stamp publish
Commands
stamp add
Creates a new stamp file describing a change.
| Flag | Description |
|---|---|
--project |
Project name to include (repeatable) |
--bump |
Bump type: major, minor, patch, premajor, preminor, prepatch, prerelease |
--message |
Description of the change |
--no-interactive |
Disable interactive prompts (requires --project, --bump, --message) |
--slug |
Custom filename slug (default: auto-generated, e.g. brave-river) |
stamp status
Shows all pending stamp files and the projected next version for each project.
| Flag | Description |
|---|---|
--json |
Output as JSON |
stamp version
Consumes all pending stamp files, bumps versions in .stamp/stamp.toml, and prepends a new entry to each project's CHANGELOG.md. Then stages and commits the changes via git.
| Flag | Description |
|---|---|
--snapshot <id> |
Create a pre-release with the given identifier (e.g. alpha, rc) |
--no-commit |
Apply changes without creating a git commit |
stamp publish
Creates git tags and publishes releases (with optional artifact uploads) to GitHub or Gitea.
| Flag | Description |
|---|---|
--dry-run / -n |
Print what would happen without executing |
--project |
Only publish a specific project |
Requires STAMP_REPO=owner/repo to be set. Detects GitHub vs Gitea automatically via the GITEA_BASE_URL environment variable.
Tag format: <name>@v<version> (e.g. my-app@v1.3.0).
stamp release-pr
Creates or updates a release pull request — a PR that contains all the version bumps and changelog updates that would be applied by stamp version. Merging it acts as the explicit release trigger.
The command:
- Reads all pending stamp files. If there are none, it exits cleanly with no side effects.
- Fetches
originand resets a well-known branch (default:stamp/release) from the tip of the base branch, so the branch is always a clean, fast-forwardable head. - Applies all version bumps and changelog updates to that branch (identical logic to
stamp version --no-commit), then commits and force-pushes it. - Opens a new PR — or updates the existing one — targeting the base branch. The PR is identified across runs by a hidden marker in the body, so only one stamp release PR exists at a time.
Once the PR is merged, run stamp publish to tag and release.
| Flag | Description |
|---|---|
--branch |
Release branch name (default: stamp/release) |
--base |
Base branch the PR targets (default: from stamp.toml, fallback: main) |
--repo |
Repository slug owner/repo (defaults to STAMP_REPO or GITHUB_REPOSITORY) |
--snapshot <id> |
Pre-release identifier (e.g. alpha, rc) |
--dry-run / -n |
Print what would be done without pushing or opening a PR |
stamp comment
Posts or updates a PR comment summarising pending stamps. Useful in CI to remind contributors to add stamp files or to show reviewers what versions will change.
- No stamps found → warns the author with instructions on how to add one.
- Stamps found → shows a table of affected projects and their next versions.
| Flag | Description |
|---|---|
--pr |
Pull request number (required) |
--repo |
Repository slug owner/repo (defaults to STAMP_REPO or GITHUB_REPOSITORY) |
stamp preview
Manages pre-release mode for a project. While a project is in pre-release mode, every stamp version (and stamp release-pr) run produces pre-release versions (e.g. 1.3.0-beta.0, 1.3.0-beta.1) instead of normal ones. Exiting preview mode causes the next run to produce a regular release.
# Enter pre-release mode — all future version bumps will be pre-releases
stamp preview enter my-app beta
# Leave pre-release mode — the next version bump will be a normal release
stamp preview exit my-app
Stamp File Format
Stamp files live in .stamp/ alongside stamp.toml, and use Markdown with YAML or TOML frontmatter.
YAML (default)
---
bumps:
my-app: minor
my-lib: patch
---
Short description used as the changelog entry.
- Optional bullet points for more detail.
TOML
+++
[bumps]
my-app = "minor"
+++
Short description.
Bump types
| Type | Description |
|---|---|
major |
Breaking change — 1.2.3 → 2.0.0 |
minor |
New feature — 1.2.3 → 1.3.0 |
patch |
Bug fix — 1.2.3 → 1.2.4 |
premajor |
Pre-release major — 1.2.3 → 2.0.0-alpha.0 |
preminor |
Pre-release minor — 1.2.3 → 1.3.0-alpha.0 |
prepatch |
Pre-release patch — 1.2.3 → 1.2.4-alpha.0 |
prerelease |
Increment pre-release — 1.2.4-rc.0 → 1.2.4-rc.1 |
Configuration Reference (.stamp/stamp.toml)
# Global settings (all optional)
[config]
base_branch = "main" # Base branch for PR change detection (default: main)
[[projects]]
name = "my-app"
path = "apps/my-app" # Path relative to repo root
version = "1.2.3" # Current version — updated by `stamp version`
changelog = "CHANGELOG.md" # Relative to path (default: CHANGELOG.md)
pre_tag = "" # Set by `stamp preview enter`; leave blank for normal releases
[projects.publish]
tags = true
releases = true
artifacts = [
"apps/my-app/dist/my-app-linux-amd64",
"apps/my-app/dist/my-app-darwin-arm64",
]
Environment Variables
| Variable | Purpose |
|---|---|
STAMP_REPO |
Repository slug owner/repo — required for publish, comment, and release-pr |
GITHUB_TOKEN |
GitHub token for releases and PR comments — automatically provided by the GitHub Actions runner; no manual setup needed |
GITEA_TOKEN |
Gitea access token — must be created manually (Gitea Actions does not inject one automatically); create a token in your Gitea account settings and store it as a repository secret |
GITEA_BASE_URL |
Gitea instance URL (e.g. https://gitea.example.com) — also enables Gitea mode |
CI Integration
Workflow: PR check (stamp comment)
Posts a comment on every pull request showing which projects will be bumped and to what version. Warns if no stamp file was added.
GitHub Actions
# .github/workflows/stamp-check.yml
name: stamp check
on:
pull_request:
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install stamp
run: go install git.thokra.dev/thokra/stamp/cmd/stamp@latest
- name: Comment on PR
run: stamp comment --pr=${{ github.event.pull_request.number }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
STAMP_REPO: ${{ github.repository }}
Gitea Actions
# .gitea/workflows/stamp-check.yml
name: stamp check
on:
pull_request:
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install stamp
run: go install git.thokra.dev/thokra/stamp/cmd/stamp@latest
- name: Comment on PR
run: stamp comment --pr=${{ gitea.event.pull_request.number }}
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
GITEA_BASE_URL: ${{ gitea.server_url }}
STAMP_REPO: ${{ gitea.repository }}
Workflow: Release PR (stamp release-pr + stamp publish)
This is the recommended automation workflow. Whenever a PR with stamp files is merged to the base branch, CI opens (or updates) a release PR. Merging the release PR triggers the actual publish.
feature branch ──► merge to main ──► stamp release-pr ──► release PR
│
merge to main
│
stamp publish ──► tags + releases
Step 1 — Open or update the release PR on every push to main
GitHub Actions
# .github/workflows/stamp-release-pr.yml
name: release PR
on:
push:
branches: [main]
jobs:
release-pr:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
# A full clone is needed so stamp can fetch and reset the release branch.
fetch-depth: 0
- name: Install stamp
run: go install git.thokra.dev/thokra/stamp/cmd/stamp@latest
- name: Create or update release PR
run: stamp release-pr --base=main
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
STAMP_REPO: ${{ github.repository }}
Gitea Actions
# .gitea/workflows/stamp-release-pr.yml
name: release PR
on:
push:
branches: [main]
jobs:
release-pr:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install stamp
run: go install git.thokra.dev/thokra/stamp/cmd/stamp@latest
- name: Create or update release PR
run: stamp release-pr --base=main
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
GITEA_BASE_URL: ${{ gitea.server_url }}
STAMP_REPO: ${{ gitea.repository }}
Step 2 — Publish after the release PR is merged
GitHub Actions
# .github/workflows/stamp-publish.yml
name: publish
on:
push:
branches: [main]
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install stamp
run: go install git.thokra.dev/thokra/stamp/cmd/stamp@latest
- name: Publish releases
run: stamp publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
STAMP_REPO: ${{ github.repository }}
Gitea Actions
# .gitea/workflows/stamp-publish.yml
name: publish
on:
push:
branches: [main]
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install stamp
run: go install git.thokra.dev/thokra/stamp/cmd/stamp@latest
- name: Publish releases
run: stamp publish
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
GITEA_BASE_URL: ${{ gitea.server_url }}
STAMP_REPO: ${{ gitea.repository }}
Tip: Both jobs run on every push to
main.stamp release-pris a no-op once there are no more pending stamp files (i.e. after the release PR has been merged andstamp publishhas consumed them).stamp publishis a no-op if there are no new tags to create. The two jobs are safe to run in parallel or in sequence.
Workflow: Direct release (no PR)
If you prefer to version and publish in a single step without a release PR, run both commands directly on the base branch:
# .github/workflows/stamp-release.yml
name: release
on:
push:
branches: [main]
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Configure git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Install stamp
run: go install git.thokra.dev/thokra/stamp/cmd/stamp@latest
- name: Version and publish
run: |
stamp version
git push
stamp publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
STAMP_REPO: ${{ github.repository }}
Development
This project uses Mise to manage the Go toolchain and common tasks.
# Build
mise run build
# Run tests
mise run test
# Lint
mise run lint
# Install locally
mise run install
Project Structure
stamp/
├── cmd/stamp/ # CLI entry point and command definitions
├── internal/
│ ├── changeset/ # Stamp file parsing, writing, and slug generation
│ ├── changelog/ # CHANGELOG.md generation and appending
│ ├── config/ # stamp.toml loading, validation, and saving
│ ├── git/ # Git operations (tag, commit, push, branch management)
│ ├── gitea/ # Gitea API client (releases, PR comments, release PRs)
│ ├── github/ # GitHub API client (releases, PR comments, release PRs)
│ ├── publish/ # Orchestrates tagging and release publishing
│ ├── releasepr/ # Orchestrates release PR creation and updates
│ └── semver/ # Semantic version bumping logic
├── examples/
│ └── stamp.toml # Annotated configuration example
└── docs/
└── workflow.md # Detailed workflow guide
License
MIT