Skip to content

commy plugin: release 0.16.0 (#121) #11

commy plugin: release 0.16.0 (#121)

commy plugin: release 0.16.0 (#121) #11

Workflow file for this run

name: Release
# B2 release automation. The maintainer's merge of a release-bump commit to
# `main` is the single trigger; CI does everything outward-facing from there. No
# agent ever pushes a tag or publishes anything — see docs/releasing.md for the
# worker flow that produces the bump PR this workflow consumes.
#
# In one run, when a release commit lands on `main`:
# 1. detect — read the canonical version (plugin.json) and decide whether
# this push is a release: a checked-in RELEASE-NOTES/<version>.md must be
# present (the bump PR's opt-in) AND the commy-v<version> tag must not exist
# yet (idempotency — every later push to main re-runs detect, and the tag is
# what stops a re-publish). Logic lives in scripts/release-detection.ts and
# is unit-tested (scripts/release-detection.test.ts).
# 2. verify parity — re-run the seven-site lockstep test so the artifact, tag,
# and Release can't ship a partially-bumped version.
# 3. tag — create+push the commy-v<version> tag as the record.
# 4. publish-npm — build and publish @codeforbreakfast/commy-mcp to npm via
# OIDC trusted publishing (no NPM_TOKEN).
# 5. release — cut the GitHub Release from the checked-in notes file.
#
# Two load-bearing constraints:
# * npm's Trusted Publisher (OIDC) is pinned to this workflow's filename
# (`release.yml`). The publish step must stay in this file — renaming or
# moving it makes npm reject the OIDC publish (filename mismatch), which
# needs a maintainer-side npm-config change to fix.
# * A tag pushed by the default GITHUB_TOKEN does not trigger another
# `on: push: tags` workflow. So this workflow triggers directly on the
# release-commit-to-main (guarded by `detect`); it never relies on the tag
# it pushes to fire a second run. `workflow_dispatch` is the manual fallback.
#
# Trusted publishing means npm trusts this workflow (org/repo/filename), not a
# stored token: each run mints a short-lived signed OIDC token the registry
# verifies against the package's trusted-publisher config. Because the repo and
# package are public, npm attaches a provenance attestation automatically — no
# `--provenance` flag, no secret to rotate or leak. Configure the publisher once
# at npmjs.com → package settings → Trusted Publisher (GitHub Actions, org
# `CodeForBreakfast`, repo `commy`, workflow `release.yml`).
on:
push:
branches: [main]
# Cheap pre-filter: a version bump always edits the canonical version site,
# so only bump pushes start the workflow at all. `detect` is the
# authoritative guard; this just keeps ordinary main pushes from spinning a
# runner.
paths:
- 'clients/claude-code/.claude-plugin/plugin.json'
workflow_dispatch:
inputs:
force:
description: 'Re-run even if the version is already tagged (recover a release that tagged but failed before publishing). The RELEASE-NOTES file must still be present.'
type: boolean
default: false
permissions:
contents: read
# Serialise releases: never let two runs race on the tag/publish/Release.
concurrency:
group: release
cancel-in-progress: false
jobs:
detect:
runs-on: ubuntu-24.04
outputs:
release: ${{ steps.detect.outputs.release }}
version: ${{ steps.detect.outputs.version }}
tag: ${{ steps.detect.outputs.tag }}
steps:
# SHA-pinned like ci.yml; Renovate (.github/renovate.json5) bumps it.
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: DeterminateSystems/nix-installer-action@ef8a148080ab6020fd15196c2084a2eea5ff2d25 # v22
# Same module the unit tests cover, run with the flake-pinned bun. It reads
# plugin.json, checks the notes file, queries the remote for the tag, and
# writes release/version/tag to $GITHUB_OUTPUT.
- id: detect
env:
FORCE: ${{ github.event_name == 'workflow_dispatch' && inputs.force }}
run: nix develop .#ci --command bun scripts/release-detection.ts
release:
needs: detect
if: needs.detect.outputs.release == 'true'
runs-on: ubuntu-24.04
permissions:
contents: write # push the record tag and cut the GitHub Release
id-token: write # mint the OIDC token npm verifies for trusted publishing
env:
TAG: ${{ needs.detect.outputs.tag }}
VERSION: ${{ needs.detect.outputs.version }}
steps:
# SHA-pinned like ci.yml; Renovate (.github/renovate.json5) bumps them.
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: DeterminateSystems/nix-installer-action@ef8a148080ab6020fd15196c2084a2eea5ff2d25 # v22
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '24.18.0'
registry-url: 'https://registry.npmjs.org'
# Re-check the seven-site version lockstep on the commit being released,
# before any outward act — a partially-bumped commit must never reach the
# registry. manifests.test.ts asserts all seven sites agree with
# plugin.json (the six hand-edited sites plus the uv.lock self-entry).
- name: Verify seven-site version parity
run: nix develop .#ci --command bash -euo pipefail -c 'bun install --frozen-lockfile && bun test clients/claude-code/manifests.test.ts'
# The record tag, created from the released commit. checkout persists the
# GITHUB_TOKEN in the remote, so the push authenticates via contents:write.
# Idempotent so a forced re-run after a partial failure doesn't fail here.
- name: Create and push the record tag
run: |
set -euo pipefail
if git ls-remote --tags origin "refs/tags/${TAG}" | grep -q .; then
echo "Tag ${TAG} already exists on origin; skipping tag creation (forced re-run)."
else
git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
git tag -a "${TAG}" -m "commy ${VERSION}"
git push origin "${TAG}"
echo "Pushed ${TAG}."
fi
# Build the publishable bundle with the flake-pinned bun, the same way
# ci.yml runs the gate — `bun run pack:npm` stages the node-target,
# dependency-inlined server.js plus a generated package.json (version read
# from plugin.json) at packages/mcp/dist.
- name: Stage the npm package
run: nix develop .#ci --command bash -euo pipefail -c 'bun install --frozen-lockfile && bun run pack:npm'
# Trusted publishing (OIDC) needs npm >= 11.5.1; Node 24 still ships npm
# 10.x, so upgrade the publish CLI explicitly. Pinned to an exact version
# rather than `@latest`: a publish run is exactly when grabbing the newest
# release blind would defeat the supply-chain caution this repo's pinning
# buys — and any npm >= 11.5.1 satisfies OIDC, so this floor never needs
# to move on its own. Bump it deliberately when a newer npm is wanted.
- name: Upgrade npm for trusted publishing
run: npm install -g npm@11.17.0
# No NODE_AUTH_TOKEN: with id-token write + a registered trusted publisher,
# npm exchanges the OIDC token itself and attaches provenance by default.
# This step stays in release.yml (filename unchanged) — the OIDC pin is on
# the workflow filename.
- name: Publish to npm via OIDC trusted publishing
run: npm publish packages/mcp/dist
# Cut the curated GitHub Release from the notes the bump PR checked in —
# not raw git-log notes. --verify-tag refuses if the record tag is missing.
- name: Cut the GitHub Release
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
gh release create "${TAG}" \
--title "commy ${VERSION}" \
--notes-file "RELEASE-NOTES/${VERSION}.md" \
--verify-tag