# 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 ``` ### Release PR (recommended for automation) `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: ```sh # 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.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-beta.0` | | `preminor` | Pre-release minor | `1.2.3` → `1.3.0-beta.0` | | `prepatch` | Pre-release patch | `1.2.3` → `1.2.4-beta.0` | | `prerelease` | Increment pre-release counter | `1.2.4-rc.0` → `1.2.4-rc.1` | --- ## Checking Pending Stamps ```sh 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. ```sh # 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`): ```sh 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: ```sh 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/`. 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 (``) 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 | ``` 6. **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` ```yaml # .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: 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: 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` ```yaml # .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: 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: 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** ```yaml # .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: Configure git run: | git config user.name "stamp-bot" git config user.email "stamp-bot@users.noreply.gitea.com" - 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** ```yaml # .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: Configure git run: | git config user.name "stamp-bot" git config user.email "stamp-bot@users.noreply.gitea.com" - 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 ```yaml # .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 ```yaml # .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. ```sh # 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 ```sh # 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 ```toml # .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: `@v` for monorepos (e.g. `my-app@v1.3.0`), or `v` 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 |