-
Notifications
You must be signed in to change notification settings - Fork 6
473 lines (395 loc) · 15.4 KB
/
release.yml
File metadata and controls
473 lines (395 loc) · 15.4 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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
name: Release
on:
push:
tags:
- "v*"
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: write
id-token: write
env:
CARGO_TERM_COLOR: always
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
# All unit-level coverage (Linux, macOS, Windows cargo, Windows bash e2e).
# Defined as a reusable workflow so tests.yml shares the exact same
# workload. Release mode (strict=true) makes EVERY job blocking — including
# the Windows jobs that are non-blocking at PR time. If a Windows-only
# regression slipped past PR-time CI, this gate catches it before publish.
unit:
name: Unit
uses: ./.github/workflows/_unit-suite.yml
with:
strict: true
# Run the full E2E matrix (Linux Docker + Pi RPC + Windows native +
# macOS native) at release time. Reuses the same workflow tests.yml runs
# at PR time — single source of truth, so PR-time and release-time e2e
# can never drift. Build/publish jobs below `needs:` this job, so an e2e
# regression blocks the release.
e2e:
name: E2E
needs: unit
uses: ./.github/workflows/_e2e-suite.yml
publish-crates:
name: Publish to crates.io
runs-on: ubuntu-latest
needs: [unit, e2e]
steps:
- uses: actions/checkout@v5
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- uses: dtolnay/rust-toolchain@stable
# Sync Cargo.toml version to match the tag. Without this, `cargo publish`
# tries to publish whatever version is in HEAD's Cargo.toml — which is
# last-released-version on tag pushes (the tag does NOT carry a separate
# version commit; release.sh commits the version bump locally, but if
# someone retags HEAD without running release.sh, Cargo.toml stays
# stale). Mirrors the same step in publish-npm-platforms.
- name: Sync versions from tag
run: node scripts/version-sync.mjs --from-tag
- name: Publish to crates.io
# Allow re-runs of the same tag: treat "already exists" as success
# ONLY if the version we're publishing matches the tag. Otherwise the
# fallback silently masks the version-mismatch bug (we try to publish
# vN, it already exists, we say success even though we wanted vN+1).
run: |
TAG_VERSION="${GITHUB_REF_NAME#v}"
CARGO_VERSION=$(grep '^version' crates/aft/Cargo.toml | head -1 | sed -E 's/version = "([^"]+)"/\1/')
if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then
echo "::error::Tag $GITHUB_REF_NAME wants $TAG_VERSION but Cargo.toml has $CARGO_VERSION after version-sync. version-sync.mjs broken?"
exit 1
fi
cargo publish --package agent-file-tools \
|| { ec=$?; cargo publish --package agent-file-tools --dry-run 2>&1 | grep -q "already exists" && exit 0 || exit $ec; }
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
build-darwin-arm64:
name: Build macOS ARM64
runs-on: macos-latest
needs: [unit, e2e]
steps:
- uses: actions/checkout@v5
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- uses: dtolnay/rust-toolchain@stable
with:
targets: aarch64-apple-darwin
# Safety net: if someone retags HEAD without running release.sh,
# Cargo.toml.version stays stale. Bake the tag's version into the
# binary before cargo build so `aft --version` matches what the
# plugin expects.
- name: Sync versions from tag
run: node scripts/version-sync.mjs --from-tag
- name: Build
run: cargo build --release --target aarch64-apple-darwin
- name: Strip binary
run: strip target/aarch64-apple-darwin/release/aft
- uses: actions/upload-artifact@v4
with:
name: darwin-arm64
path: target/aarch64-apple-darwin/release/aft
if-no-files-found: error
build-darwin-x64:
name: Build macOS x64
runs-on: macos-latest
needs: [unit, e2e]
steps:
- uses: actions/checkout@v5
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- uses: dtolnay/rust-toolchain@stable
with:
targets: x86_64-apple-darwin
- name: Sync versions from tag
run: node scripts/version-sync.mjs --from-tag
- name: Build
run: cargo build --release --target x86_64-apple-darwin
- name: Strip binary
run: strip target/x86_64-apple-darwin/release/aft
- uses: actions/upload-artifact@v4
with:
name: darwin-x64
path: target/x86_64-apple-darwin/release/aft
if-no-files-found: error
build-linux-arm64:
name: Build Linux ARM64
runs-on: ubuntu-latest
needs: [unit, e2e]
steps:
- uses: actions/checkout@v5
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- uses: dtolnay/rust-toolchain@stable
with:
targets: aarch64-unknown-linux-gnu
- name: Sync versions from tag
run: node scripts/version-sync.mjs --from-tag
- name: Install cross
run: cargo install cross --version 0.2.5 --locked
# Use gnu target (not musl) so dlopen works for ONNX Runtime loading.
# musl produces static binaries where dlopen is a stub that always fails.
- name: Build
run: cross build --release --target aarch64-unknown-linux-gnu
- uses: actions/upload-artifact@v4
with:
name: linux-arm64
path: target/aarch64-unknown-linux-gnu/release/aft
if-no-files-found: error
build-linux-x64:
name: Build Linux x64
# Build on Ubuntu 22.04 to link against GLIBC 2.35 — compatible with
# Ubuntu 22.04 LTS (supported until 2027), Pop!OS 22.04, Debian 12+,
# and any distro with GLIBC >= 2.35. Using ubuntu-latest (24.04) would
# require GLIBC 2.39 and break older LTS users.
runs-on: ubuntu-22.04
needs: [unit, e2e]
steps:
- uses: actions/checkout@v5
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- uses: dtolnay/rust-toolchain@stable
- name: Sync versions from tag
run: node scripts/version-sync.mjs --from-tag
# Native x64 build on Ubuntu runner — no cross-compilation needed.
# Uses gnu target (not musl) so dlopen works for ONNX Runtime loading.
- name: Build
run: cargo build --release
- uses: actions/upload-artifact@v4
with:
name: linux-x64
path: target/release/aft
if-no-files-found: error
build-win32-x64:
name: Build Windows x64
runs-on: windows-latest
needs: [unit, e2e]
steps:
- uses: actions/checkout@v5
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- uses: dtolnay/rust-toolchain@stable
with:
targets: x86_64-pc-windows-msvc
- name: Sync versions from tag
run: node scripts/version-sync.mjs --from-tag
shell: pwsh
- name: Build
run: cargo build --release --target x86_64-pc-windows-msvc
- uses: actions/upload-artifact@v4
with:
name: win32-x64
path: target/x86_64-pc-windows-msvc/release/aft.exe
if-no-files-found: error
# npm publish runs AFTER github-release so that checksums.sha256 is already
# available on the GitHub release page when ensureBinary() runs during
# @cortexkit/aft-opencode@latest install.
publish-npm:
name: Publish to npm
runs-on: ubuntu-latest
needs:
- build-darwin-arm64
- build-darwin-x64
- build-linux-arm64
- build-linux-x64
- build-win32-x64
- github-release
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: "24"
registry-url: "https://registry.npmjs.org"
- name: Ensure latest npm (for trusted publishing)
run: npm install -g npm@latest
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Download darwin-arm64 binary
uses: actions/download-artifact@v4
with:
name: darwin-arm64
path: packages/npm/darwin-arm64/bin
- name: Download darwin-x64 binary
uses: actions/download-artifact@v4
with:
name: darwin-x64
path: packages/npm/darwin-x64/bin
- name: Download linux-arm64 binary
uses: actions/download-artifact@v4
with:
name: linux-arm64
path: packages/npm/linux-arm64/bin
- name: Download linux-x64 binary
uses: actions/download-artifact@v4
with:
name: linux-x64
path: packages/npm/linux-x64/bin
- name: Download win32-x64 binary
uses: actions/download-artifact@v4
with:
name: win32-x64
path: packages/npm/win32-x64/bin
- name: Set binary permissions
run: |
chmod +x packages/npm/darwin-arm64/bin/aft
chmod +x packages/npm/darwin-x64/bin/aft
chmod +x packages/npm/linux-arm64/bin/aft
chmod +x packages/npm/linux-x64/bin/aft
- name: Sync versions from tag
run: node scripts/version-sync.mjs --from-tag
- name: Validate packages
run: node scripts/validate-packages.mjs
- name: Install bridge dependencies
run: bun install
working-directory: packages/aft-bridge
- name: Build aft-bridge
run: bun run build
working-directory: packages/aft-bridge
- name: Install plugin dependencies
run: bun install
working-directory: packages/opencode-plugin
- name: Build OpenCode plugin
run: bun run build
working-directory: packages/opencode-plugin
- name: Install Pi plugin dependencies
run: bun install
working-directory: packages/pi-plugin
- name: Build Pi plugin
run: bun run build
working-directory: packages/pi-plugin
- name: Install CLI dependencies
run: bun install
working-directory: packages/aft-cli
- name: Build CLI
run: bun run build
working-directory: packages/aft-cli
# Uses npm Trusted Publishing (OIDC) — configured per-package on npmjs.com
- name: Publish platform packages
run: |
set -euo pipefail
publish_or_skip() {
local pkg_dir="$1"
local pkg_name
local version
pkg_name=$(node -p "require('./${pkg_dir}/package.json').name")
version=$(node -p "require('./${pkg_dir}/package.json').version")
if npm view "${pkg_name}@${version}" version >/dev/null 2>&1; then
echo "::notice::${pkg_name}@${version} already published; skipping"
return 0
fi
npm publish --access public --provenance "$pkg_dir"
}
for pkg in darwin-arm64 darwin-x64 linux-arm64 linux-x64 win32-x64; do
publish_or_skip "packages/npm/$pkg"
done
# aft-bridge MUST publish before the plugins, because @cortexkit/aft-opencode
# and @cortexkit/aft-pi depend on it. Plugins publish from local builds, so
# there's no install-time race here, but consumers installing @latest will
# need aft-bridge resolvable on npm.
#
# Tolerate `already published` so a re-run of the same tag (or the
# initial bootstrap publish that claims the package name with a
# token before Trusted Publishing is configured) doesn't fail the
# whole release. Mirrors the crates.io publish step above.
- name: Publish @cortexkit/aft-bridge
run: |
set -euo pipefail
PKG_NAME=$(node -p "require('./package.json').name")
VERSION=$(node -p "require('./package.json').version")
if npm view "${PKG_NAME}@${VERSION}" version >/dev/null 2>&1; then
echo "::notice::${PKG_NAME}@${VERSION} already published; skipping"
exit 0
fi
npm publish --access public --provenance
working-directory: packages/aft-bridge
- name: Publish @cortexkit/aft-opencode
run: |
set -euo pipefail
PKG_NAME=$(node -p "require('./package.json').name")
VERSION=$(node -p "require('./package.json').version")
if npm view "${PKG_NAME}@${VERSION}" version >/dev/null 2>&1; then
echo "::notice::${PKG_NAME}@${VERSION} already published; skipping"
exit 0
fi
npm publish --access public --provenance
working-directory: packages/opencode-plugin
- name: Publish @cortexkit/aft-pi
run: |
set -euo pipefail
PKG_NAME=$(node -p "require('./package.json').name")
VERSION=$(node -p "require('./package.json').version")
if npm view "${PKG_NAME}@${VERSION}" version >/dev/null 2>&1; then
echo "::notice::${PKG_NAME}@${VERSION} already published; skipping"
exit 0
fi
npm publish --access public --provenance
working-directory: packages/pi-plugin
- name: Publish @cortexkit/aft (unified CLI)
run: |
set -euo pipefail
PKG_NAME=$(node -p "require('./package.json').name")
VERSION=$(node -p "require('./package.json').version")
if npm view "${PKG_NAME}@${VERSION}" version >/dev/null 2>&1; then
echo "::notice::${PKG_NAME}@${VERSION} already published; skipping"
exit 0
fi
npm publish --access public --provenance
working-directory: packages/aft-cli
# GitHub release must complete before npm publish so that checksums.sha256
# is available when freshly-installed @cortexkit/aft-opencode@latest triggers
# ensureBinary(). crates.io publish is also gated so that a failed crate
# publish blocks the release — no point shipping binaries if the Rust crate
# isn't available yet.
github-release:
name: Create GitHub Release
runs-on: ubuntu-latest
needs:
- build-darwin-arm64
- build-darwin-x64
- build-linux-arm64
- build-linux-x64
- build-win32-x64
- publish-crates
steps:
- uses: actions/checkout@v5
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Prepare release assets
run: |
mkdir -p release-assets
cp artifacts/darwin-arm64/aft release-assets/aft-darwin-arm64
cp artifacts/darwin-x64/aft release-assets/aft-darwin-x64
cp artifacts/linux-arm64/aft release-assets/aft-linux-arm64
cp artifacts/linux-x64/aft release-assets/aft-linux-x64
cp artifacts/win32-x64/aft.exe release-assets/aft-win32-x64.exe
chmod +x release-assets/aft-*
- name: Generate checksums
run: |
cd release-assets
sha256sum aft-* > checksums.sha256
cat checksums.sha256
- name: Verify curated release notes exist
run: |
notes_file=".alfonso/release-notes/${GITHUB_REF_NAME}.md"
if [ ! -f "$notes_file" ]; then
echo "::error::Curated release notes missing: $notes_file"
echo "Draft them under .alfonso/release-notes/ before tagging the release."
exit 1
fi
echo "Using $notes_file as the release body."
wc -l "$notes_file"
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
body_path: .alfonso/release-notes/${{ github.ref_name }}.md
files: release-assets/*