Tegami

CI Setup

Run Tegami in your release pipeline.

The tegami ci command is designed for a release pipeline on your main branch. It runs versioning first; only when there is nothing to version does it publish from the existing publish lock.

GitHub Actions

A minimal publish workflow:

.github/workflows/publish.yml
name: Publish

on:
  push:
    branches:
      - main

jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      id-token: write
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: pnpm/action-setup@v6

      - uses: actions/setup-node@v6
        with:
          node-version: 24
          cache: pnpm

      - run: pnpm install --frozen-lockfile

      # see the "Build packages" section below for details
      - name: Build packages
        run: pnpm build

      - name: Version & publish packages
        run: pnpm tegami ci
        env:
          GITHUB_TOKEN: ${{ github.token }}

Set id-token: write so npm trusted publishing (OIDC) works without an NPM_TOKEN.

The GitHub plugin requires GITHUB_TOKEN to open pull requests and create GitHub releases after publish.

Build packages

Tegami does not build your packages, registries publish whatever is on disk.

The example above runs pnpm build before every tegami ci invocation, this will always build all packages before they are published.

To build only packages that are about to be published, use a willPublish plugin. For example, using tinyexec and Turborepo:

scripts/tegami.mts
import { x } from "tinyexec";
import { tegami } from "tegami";
import type { TegamiPlugin } from "tegami";

function buildOnPublish(): TegamiPlugin {
  return {
    name: "build-on-publish",
    async willPublish({ pkg }) {
      const result = await x("turbo", ["run", "build", "--filter", pkg.name], {
        nodeOptions: { cwd: this.cwd },
      });

      if (result.exitCode !== 0) {
        throw new Error(`Failed to build ${pkg.name}`);
      }
    },
  };
}

const paper = tegami({
  // [!code +]
  plugins: [buildOnPublish()],
});

willPublish runs once per package right before it is published. Turborepo's --filter scopes the build to that package and its dependencies.

With this plugin, you can remove the standalone build step from your workflow and rely on tegami ci to build each package at publish time.

With the GitHub plugin enabled, CI follows a two-step flow:

  1. Changelogs committed
    • Maintainers push commits that add .tegami/*.md changelog files.
    • CI runs tegami ci, writes the publish lock.
    • CI opens a Version Packages pull request (at tegami/version-packages by default).
  2. Version PR merges
    • The merged commit contains bumped versions and .tegami/publish-lock.yaml.
    • The next CI run publishes packages and creates GitHub releases.

Because the publish lock lives in git, failed publish jobs can be retried safely without duplicating releases.

Pull request preview

Use tegami pr preview and tegami pr comment on pull requests to post a release preview comment. The preview renders markdown that:

  1. Introduces Tegami and tells contributors to add changelog files.
  2. Links to create a new changelog file on GitHub.
  3. Shows pending package bumps and changelogs added in the PR.

Split this into two workflows for a better security model.

.github/workflows/tegami-pr.yml
name: Tegami PR

on:
  pull_request:
    types: [opened, synchronize, reopened]

permissions:
  contents: read

jobs:
  preview:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: pnpm/action-setup@v6

      - uses: actions/setup-node@v6
        with:
          node-version: 24
          cache: pnpm

      - run: pnpm install --frozen-lockfile

      - name: Release preview
        run: pnpm tegami pr preview --artifact tegami-pr-preview.md

      - uses: actions/upload-artifact@v4
        with:
          name: tegami-pr-preview
          path: tegami-pr-preview.md

The "Generate Comment" step must use pull_request over pull_request_target, it will run untrusted code.

Custom CLI hooks

createCli accepts optional hooks to replace the default draft() or publish() behavior:

createCli(paper, {
  version: async () => {
    const draft = await paper.draft();
    // customize the draft before apply
    return draft;
  },
  publish: async () => {
    return paper.publish();
  },
});

See the programmatic API for lower-level control.

On this page