2026-03-12 23:41:12 +01:00
2026-03-12 23:41:12 +01:00
2026-03-12 23:41:12 +01:00
2026-03-12 22:59:20 +01:00
2026-03-12 23:41:12 +01:00
2026-03-08 20:56:23 +01:00
2026-03-12 22:59:20 +01:00
2026-03-12 22:59:20 +01:00
2026-03-08 20:56:23 +01:00
2026-03-12 23:41:12 +01:00

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 version and stamp publish locally or in CI on the base branch.
  • Release PR — run stamp release-pr in 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:

  1. Reads all pending stamp files. If there are none, it exits cleanly with no side effects.
  2. Fetches origin and 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.
  3. Applies all version bumps and changelog updates to that branch (identical logic to stamp version --no-commit), then commits and force-pushes it.
  4. 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.32.0.0
minor New feature — 1.2.31.3.0
patch Bug fix — 1.2.31.2.4
premajor Pre-release major — 1.2.32.0.0-alpha.0
preminor Pre-release minor — 1.2.31.3.0-alpha.0
prepatch Pre-release patch — 1.2.31.2.4-alpha.0
prerelease Increment pre-release — 1.2.4-rc.01.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-pr is a no-op once there are no more pending stamp files (i.e. after the release PR has been merged and stamp publish has consumed them). stamp publish is 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

Description
No description provided
Readme 126 KiB
Languages
Go 100%