-
Notifications
You must be signed in to change notification settings - Fork 0
224 lines (212 loc) · 9.62 KB
/
Copy pathrelease.yml
File metadata and controls
224 lines (212 loc) · 9.62 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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
name: release
# Tag-driven publishing for @leadbay/mcp (npm).
#
# Tag scheme:
# - mcp-v<ver> → publishes @leadbay/mcp
# - v<ver> → legacy alias for mcp-v<ver>
#
# One-time setup (done in repo settings):
# - NPM_TOKEN → npm automation token, scope-owner on @leadbay
#
# The live HTTP endpoint (https://mcp.leadbay.app/mcp) is deployed separately by
# build-image.yml → Azure Container Registry → Argo CD in leadbay/infra; this
# workflow only publishes to npm + the MCP Registry and uploads the .dxt/.mcpb.
#
# No auto-version-bump, no auto-changelog. Bump `packages/mcp/package.json`
# in a normal PR. Tagging is automatic: `auto-tag.yml` watches main, sees the
# bumped version, and pushes the corresponding tag, which triggers this workflow.
# Manual `git tag` is only needed for emergency re-publish of a version already on main.
on:
push:
tags:
- "mcp-v*.*.*"
- "v*.*.*"
workflow_dispatch:
inputs:
package:
description: "Which package to publish (mcp)"
required: true
default: "mcp"
type: choice
options: [mcp]
dry_run:
description: "If true, npm publish uses --dry-run"
required: false
default: "false"
jobs:
preflight-npm:
name: Preflight — verify npm auth + @leadbay scope
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v4
with:
node-version: 22
registry-url: "https://registry.npmjs.org"
always-auth: true
- name: whoami + scope probe
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
set -euo pipefail
echo "Logged in as: $(npm whoami --registry=https://registry.npmjs.org)"
# Scope-empty on first publish is OK; fall through. Fatal errors (401/403)
# come from npm whoami above.
npm access list packages @leadbay 2>&1 || echo "scope empty or no packages yet — first publish will create"
publish-mcp:
name: Publish @leadbay/mcp to npm
needs: preflight-npm
if: |
startsWith(github.ref, 'refs/tags/mcp-v') ||
startsWith(github.ref, 'refs/tags/v') ||
(github.event_name == 'workflow_dispatch' && github.event.inputs.package == 'mcp')
runs-on: ubuntu-latest
permissions:
contents: write # gh release create/upload for the .dxt bundle
id-token: write # npm provenance
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v4
with:
# Node 24 ships with npm 11.5+ which speaks Trusted Publishers
# OIDC natively. Node 22's bundled npm 10.x cannot do the OIDC
# handshake, and in-place self-upgrade (`npm i -g npm@latest`)
# consistently breaks with MODULE_NOT_FOUND on the runner image.
node-version: 24
registry-url: "https://registry.npmjs.org"
# NO `always-auth: true` — that writes a .npmrc `_authToken=${NODE_AUTH_TOKEN}`
# line which preempts the Trusted Publishers OIDC handshake.
- name: Install
run: pnpm install --frozen-lockfile
- name: Build
run: pnpm -r build
- name: Test
run: pnpm -r test
- name: Verify tag matches packages/mcp/package.json version
# Runs whenever the workflow is keyed off a tag ref — both real
# tag-push events and auto-tag's workflow_dispatch on a tag ref.
if: startsWith(github.ref, 'refs/tags/')
run: |
set -euo pipefail
REF="${GITHUB_REF#refs/tags/}"
# Strip either "mcp-v" or plain "v" prefix.
TAG="${REF#mcp-v}"
TAG="${TAG#v}"
PKG_VERSION=$(node -p "require('./packages/mcp/package.json').version")
if [ "$TAG" != "$PKG_VERSION" ]; then
echo "Tag $REF → version $TAG does not match packages/mcp/package.json $PKG_VERSION"
exit 1
fi
echo "Aligned: $REF = $PKG_VERSION"
- name: Publish
# Uses npm Trusted Publishers (OIDC). Requirements (all must hold):
# 1. `id-token: write` permission (granted at job level above)
# 2. Trusted Publisher binding configured on npmjs.com for
# @leadbay/mcp pointing at leadbay/mcp + release.yml
# 3. npm CLI ≥ 11.5.1 (we `npm install -g npm@latest` below;
# the runner's bundled npm 10.x doesn't speak OIDC)
# 4. NO NODE_AUTH_TOKEN env var — not even empty string. Per
# the npm migration guide, an empty value is treated as
# "use this token", which preempts OIDC. We deliberately
# omit the env block entirely.
# 5. NO `_authToken=` line in any .npmrc (we don't pass
# `always-auth: true` to setup-node above, so no auth line
# is written into $RUNNER_TEMP/.npmrc).
working-directory: packages/mcp
run: |
set -euo pipefail
# Node 24's bundled npm is ≥11.5 — sufficient for Trusted
# Publishers OIDC. No self-upgrade dance required.
echo "npm $(npm --version) at $(which npm)"
VERSION=$(node -p "require('./package.json').version")
# Idempotent: if @leadbay/mcp@$VERSION is already on npm, skip.
if npm view "@leadbay/mcp@$VERSION" version --silent 2>/dev/null; then
echo "@leadbay/mcp@$VERSION already on npm — skipping"
exit 0
fi
if [ "${{ github.event.inputs.dry_run }}" = "true" ]; then
npm publish --access public --provenance --dry-run
else
npm publish --access public --provenance
fi
- name: Build .dxt bundle
# Same gate as the tag-verify step: runs on any tag ref, including
# auto-tag's workflow_dispatch. Skipped when running off main.
if: startsWith(github.ref, 'refs/tags/') && github.event.inputs.dry_run != 'true'
run: pnpm --filter @leadbay/dxt build
- name: Upload .dxt and .mcpb to GitHub Release
if: startsWith(github.ref, 'refs/tags/') && github.event.inputs.dry_run != 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
TAG="${GITHUB_REF#refs/tags/}"
# gh release upload needs a release to exist. Create it if the tag
# wasn't already promoted to a release (auto-tag.yml only pushes tags).
if ! gh release view "$TAG" >/dev/null 2>&1; then
# Extract the changelog section for this version (text between
# the matching "## X.Y.Z" header and the next "## " header).
VERSION="${TAG#mcp-v}"
NOTES=$(awk "/^## ${VERSION}/{found=1; next} found && /^## /{exit} found{print}" packages/mcp/CHANGELOG.md | sed '/^[[:space:]]*$/{ N; /^\n$/d }' | head -60)
if [ -z "$NOTES" ]; then
NOTES="See [CHANGELOG.md](https://github.com/leadbay/mcp/blob/main/packages/mcp/CHANGELOG.md) for details."
fi
gh release create "$TAG" \
--title "$TAG" \
--notes "$NOTES"
fi
gh release upload "$TAG" packages/dxt/dist/*.dxt packages/dxt/dist/*.mcpb --clobber
echo "Uploaded .dxt and .mcpb to release $TAG"
# Also publish fixed-name copies so docs can hardcode a stable URL.
# The newest release is GitHub's "Latest", so
# …/releases/latest/download/leadbay-latest.dxt
# always resolves to the current version without per-release edits.
DXT=$(ls packages/dxt/dist/*.dxt | grep -v 'leadbay-latest' | head -1)
MCPB=$(ls packages/dxt/dist/*.mcpb | grep -v 'leadbay-latest' | head -1)
cp "$DXT" packages/dxt/dist/leadbay-latest.dxt
cp "$MCPB" packages/dxt/dist/leadbay-latest.mcpb
gh release upload "$TAG" \
packages/dxt/dist/leadbay-latest.dxt \
packages/dxt/dist/leadbay-latest.mcpb --clobber
echo "Uploaded fixed-name leadbay-latest.{dxt,mcpb} to release $TAG"
publish-mcp-registry:
name: Publish to MCP Registry
needs: publish-mcp
if: |
(startsWith(github.ref, 'refs/tags/mcp-v') || startsWith(github.ref, 'refs/tags/v')) &&
github.event.inputs.dry_run != 'true'
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- name: Install mcp-publisher
run: |
set -euo pipefail
curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher
./mcp-publisher --version
- name: Authenticate to MCP Registry
run: ./mcp-publisher login github-oidc
- name: Verify server.json version matches package.json
run: |
set -euo pipefail
PKG=$(node -p "require('./packages/mcp/package.json').version")
SRV=$(node -p "require('./packages/mcp/server.json').version")
NAME=$(node -p "require('./packages/mcp/server.json').name")
MCP_NAME=$(node -p "require('./packages/mcp/package.json').mcpName")
if [ "$PKG" != "$SRV" ]; then
echo "Drift: package.json=$PKG server.json=$SRV"
exit 1
fi
if [ "$NAME" != "$MCP_NAME" ]; then
echo "Drift: server.json name=$NAME package.json mcpName=$MCP_NAME"
exit 1
fi
echo "Aligned: $PKG / $NAME"
- name: Validate server.json
run: ./mcp-publisher validate packages/mcp/server.json
- name: Publish
run: ./mcp-publisher publish packages/mcp/server.json