Files
stamp/docs/workflow.md
2026-03-12 23:41:12 +01:00

14 KiB

stamp — Workflow Guide

stamp is a language-agnostic, changesets-style versioning and changelog tool. It works with any project type and supports monorepos.


Concepts

Term Meaning
Stamp file A .stamp/*.md file describing a change and which projects it affects. Committed alongside the code change that caused it.
Bump type How to increment a version: major, minor, patch, or a pre-release variant.
Pending stamps Stamp files that have been committed but not yet consumed by a version run.
stamp version Consumes pending stamp files → bumps versions in stamp.toml → prepends entries to CHANGELOG.md files → commits the result.
stamp release-pr Same versioning logic as stamp version, but commits the changes onto a dedicated branch and opens (or updates) a pull request. The actual release happens when that PR is merged.
stamp publish Creates git tags and publishes releases (with optional artifact uploads) to GitHub or Gitea.

Two Release Workflows

Direct release

Run stamp version and stamp publish directly on the base branch. Good for small projects, CLIs, or repos where a human is doing releases manually.

feature branch ──► merge to main ──► stamp version ──► git push ──► stamp publish

stamp release-pr runs in CI whenever a change is merged to the base branch. It opens a pull request that contains all the version bumps and changelog updates. Merging that PR is the explicit, reviewable release trigger. A second CI job runs stamp publish after the merge.

feature branch ──► merge to main ──► stamp release-pr ──► release PR
                                                               │
                                                         merge to main
                                                               │
                                                        stamp publish ──► tags + releases

The two CI jobs (create PR and publish) both run on every push to main. They are each no-ops when there is nothing for them to do — stamp release-pr exits silently if there are no pending stamp files, and stamp publish skips any tag that already exists.


Day-to-day: Adding a Stamp File

When making a code change that should result in a release, add a stamp file alongside it:

# Interactive wizard
stamp add

# Non-interactive — good for scripts and AI agents
stamp add --project=my-app --bump=minor --message="Add X feature" --no-interactive

This creates a file like .stamp/brave-river.md:

---
bumps:
  my-app: minor
---

Add X feature

Commit and push the stamp file together with your code. Your CI PR-check job (see below) will update its comment automatically.

Bump type reference

Type What it does Example
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-beta.0
preminor Pre-release minor 1.2.31.3.0-beta.0
prepatch Pre-release patch 1.2.31.2.4-beta.0
prerelease Increment pre-release counter 1.2.4-rc.01.2.4-rc.1

Checking Pending Stamps

stamp status
📦 Pending stamps: 2

  PROJECT   CURRENT  NEXT   BUMP   STAMPS
  -------   -------  ----   ----   ------
  my-app    1.2.3    1.3.0  minor  2
  my-lib    0.4.1    0.4.1  —      0

Workflow A: Direct Release

Use this when you release manually or from CI without a release PR.

# 1. Bump versions, update changelogs, and commit
stamp version

# 2. Push the version commit and tags
git push

# 3. Create GitHub / Gitea releases and upload artifacts
STAMP_REPO=owner/repo stamp publish

For a pre-release snapshot (does not require stamp preview):

stamp version --snapshot=rc
git push
stamp publish

--snapshot overrides the bump type for every affected project so that it produces a pre-release version with the given identifier. It is independent of stamp preview mode.

Skipping the git commit

If you want to inspect or further modify the files before committing:

stamp version --no-commit
# review the diff, make additional edits…
git add -A && git commit -m "chore: version packages"

Workflow B: Release PR

Use this for fully automated, reviewable releases in CI.

How it works — step by step

  1. Check for pending stamps. If none exist, stamp release-pr exits immediately with no side effects. This makes it safe to run on every push.

  2. Fetch origin and reset the release branch (default: stamp/release) from the tip of origin/<base>. The branch is always recreated cleanly, so it is always fast-forwardable and never diverges from the base branch.

  3. Apply version changes — bump versions in stamp.toml, prepend changelog entries, delete consumed stamp files. This is identical to what stamp version --no-commit does.

  4. Commit and force-push the release branch to origin.

  5. Upsert the pull request. A hidden marker (<!-- stamp-release-pr -->) is embedded in the PR body. On the first run a new PR is opened; on subsequent runs the existing PR's title and body are updated to reflect any newly merged stamp files. Only one stamp release PR is ever open at a time.

The PR body contains a version table:

## 📦 Pending releases

| Project | Current | Next  | Bump  |
|---------|---------|-------|-------|
| my-app  | 1.2.3   | 1.3.0 | minor |
| my-lib  | 0.4.1   | 0.4.2 | patch |
  1. Merge the PR. Merging triggers the publish job (see below) which creates git tags and GitHub/Gitea releases.

CI setup — GitHub Actions

Job 1: open / update the release PR — runs on every push to main

# .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:
          # Full clone 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 }}

Job 2: publish after the release PR is merged — also runs on every push to main

# .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 }}

Both jobs run on every push to main. They are each idempotent and safe to run concurrently: stamp release-pr is a no-op when there are no pending stamps; stamp publish skips tags that already exist.

CI setup — Gitea Actions

Job 1: open / update the release PR

# .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 }}

Job 2: publish after the release PR is merged

# .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 }}

Note on GITEA_TOKEN: Gitea Actions does not automatically inject a token the way GitHub Actions does. You must create a personal access token (or a bot account token) in your Gitea account settings with repository read/write permissions, then store it as a repository secret named GITEA_TOKEN.


PR Commenting (stamp comment)

Use stamp comment in CI to post a summary comment on every pull request. It keeps reviewers and contributors informed about what will be released.

  • No stamp file → the comment warns the author and shows how to add one.
  • Stamps found → the comment shows a table of affected projects and their projected next versions.

The comment is created on first run and updated in place on subsequent runs (identified by a hidden marker in the body).

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 }}

Pre-release Mode (stamp preview)

stamp preview lets you put an individual project into pre-release mode. While in this mode, every stamp version or stamp release-pr run produces pre-release versions instead of normal ones, regardless of the bump types in the stamp files.

# Enter pre-release mode — all future versions for my-app will be pre-releases
stamp preview enter my-app beta
# → next stamp version will produce e.g. 1.3.0-beta.0

# If a pre-release version already exists, subsequent runs increment the counter
# → 1.3.0-beta.0 → 1.3.0-beta.1 → 1.3.0-beta.2 …

# Exit pre-release mode — the next stamp version will produce a normal release
stamp preview exit my-app
# → next stamp version will produce e.g. 1.3.0

stamp preview enter sets the pre_tag field on the project in stamp.toml and commits nothing — it is a local config change you commit yourself. stamp preview exit clears the field.

Typical pre-release workflow

# 1. Enter beta mode
stamp preview enter my-app beta
git add .stamp/stamp.toml && git commit -m "chore: enter beta mode for my-app"

# 2. Normal development — add stamp files as usual, merge PRs
#    stamp release-pr will produce beta versions automatically

# 3. When ready to release, exit beta mode
stamp preview exit my-app
git add .stamp/stamp.toml && git commit -m "chore: exit beta mode for my-app"

# 4. The next release-pr or stamp version will produce the full release (e.g. 1.3.0)

Stamp File Format Reference

Stamp files live in .stamp/ alongside stamp.toml, and use Markdown with YAML or TOML frontmatter.

YAML frontmatter (default)

---
bumps:
  my-app: minor
  my-lib: patch
---

Short description used as the changelog entry.

- Optional bullet points
- for more detail

TOML frontmatter

+++
[bumps]
my-app = "minor"
+++

Short description.

A stamp file can affect multiple projects in a single file (as shown above). Each project gets its own changelog entry with the description from the file.


Configuration Reference

# .stamp/stamp.toml

[config]
base_branch = "main"   # Base branch targeted by release PRs (default: main)

[[projects]]
name      = "my-app"
path      = "apps/my-app"     # Relative to repository root
version   = "1.2.3"           # Current version — managed by stamp, do not edit manually
changelog = "CHANGELOG.md"    # Relative to `path` (default: CHANGELOG.md)
pre_tag   = ""                # Set by `stamp preview enter`; blank means normal releases

  [projects.publish]
  tags      = true            # Create an annotated git tag on publish
  releases  = true            # Create a GitHub / Gitea release on publish
  artifacts = [               # Glob patterns for files to upload to the release
    "apps/my-app/dist/my-app-linux-amd64",
    "apps/my-app/dist/my-app-darwin-arm64",
  ]

Tag format: <name>@v<version> for monorepos (e.g. my-app@v1.3.0), or v<version> for single-project repositories where name is empty.


Environment Variables

Variable Purpose
STAMP_REPO Repository slug owner/repo — required for publish, comment, and release-pr
GITHUB_TOKEN GitHub token — automatically injected by GitHub Actions; no setup needed
GITEA_TOKEN Gitea access token — must be created manually and stored as a secret
GITEA_BASE_URL Gitea instance URL (e.g. https://gitea.example.com) — presence of this variable also activates Gitea mode