-
Notifications
You must be signed in to change notification settings - Fork 0
156 lines (146 loc) · 7.86 KB
/
Copy pathrelease.yml
File metadata and controls
156 lines (146 loc) · 7.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- 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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- 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