ReviewOS

also looking at this

craft-native/craft

Test the built native CLI startup in CI

#317
Merged glennmichael123 wants to merge codex/native-startup-rehearsal into main
3 files +54 -2

Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.

.github/workflows/ci.ymlmodified+7-2
Changes to .github/workflows/ci.yml
@@ -57,8 +57,13 @@ jobs:
5757 uses: ./.github/actions/first-party-zig-deps
5858
5959 - name: Build Zig core
60 working-directory: packages/zig
61 run: zig build
60 run: bun run build:core
61
62 - name: Verify built native CLI startup
63 run: |
64 bun test scripts/verify-native-startup.test.ts
65 VERSION="$(bun -p "require('./package.json').version")"
66 bun scripts/verify-native-startup.ts packages/zig/zig-out/bin/craft "$VERSION"
6267
6368 - name: First-party Zig dependencies
6469 uses: ./.github/actions/first-party-zig-deps
scripts/verify-native-startup.test.tsadded+27-0
Changes to scripts/verify-native-startup.test.ts
@@ -0,0 +1,27 @@
1import { afterEach, expect, test } from 'bun:test'
2import { chmodSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
3import { tmpdir } from 'node:os'
4import { join } from 'node:path'
5import { verifyNativeStartup } from './verify-native-startup'
6
7const roots: string[] = []
8afterEach(() => { for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true }) })
9
10function executable(body: string): string {
11 const root = mkdtempSync(join(tmpdir(), 'craft-native-startup-test-'))
12 roots.push(root)
13 const path = join(root, 'craft')
14 writeFileSync(path, `#!/bin/sh\n${body}\n`)
15 chmodSync(path, 0o755)
16 return path
17}
18
19test('accepts a running native CLI with the requested version', () => {
20 expect(() => verifyNativeStartup(executable("printf 'craft version 1.2.3\\nBuilt with Zig\\n'"), '1.2.3')).not.toThrow()
21})
22
23test('rejects a stale binary, startup failure, and missing executable', () => {
24 expect(() => verifyNativeStartup(executable("printf 'craft version 1.2.2\\n'"), '1.2.3')).toThrow('reported')
25 expect(() => verifyNativeStartup(executable('exit 7'), '1.2.3')).toThrow('failed to start')
26 expect(() => verifyNativeStartup('/path/that/does/not/exist', '1.2.3')).toThrow()
27})
scripts/verify-native-startup.tsadded+20-0
Changes to scripts/verify-native-startup.ts
@@ -0,0 +1,20 @@
1import { resolve } from 'node:path'
2
3export function verifyNativeStartup(binary: string, version: string): void {
4 const result = Bun.spawnSync([resolve(binary), '--version'], {
5 stdout: 'pipe', stderr: 'pipe', timeout: 30_000,
6 })
7 if (result.exitCode !== 0)
8 throw new Error(`Native CLI failed to start (${result.signalCode ?? result.exitCode}): ${result.stderr.toString()}`)
9 const firstLine = result.stdout.toString().trim().split('\n')[0]
10 if (firstLine !== `craft version ${version}`)
11 throw new Error(`Native CLI reported ${JSON.stringify(firstLine)}, expected craft version ${version}`)
12 console.log(`Verified native CLI startup: craft version ${version}`)
13}
14
15if (import.meta.main) {
16 const [binary, version] = Bun.argv.slice(2)
17 if (!binary || !version)
18 throw new Error('Usage: bun scripts/verify-native-startup.ts <binary> <version>')
19 verifyNativeStartup(binary, version)
20}