Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions packages/codev-sandbox/script/build-vzhelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ const universal = process.argv.includes("--universal")

const slices = await (async () => {
if (!universal) {
await $`swift build -c release --package-path ${root}`
await swiftBuild([])
return [`${root}.build/release/codev-vmm`]
}
const triples = ["arm64-apple-macosx", "x86_64-apple-macosx"]
for (const triple of triples) {
await $`swift build -c release --package-path ${root} --triple ${triple}`
await swiftBuild(["--triple", triple])
}
return triples.map((triple) => `${root}.build/${triple}/release/codev-vmm`)
})()
Expand All @@ -31,3 +31,17 @@ else await $`lipo -create ${slices} -output ${root}dist/codev-vmm`
// fat Mach-O, and lipo would strip a per-slice signature anyway.
await $`codesign --force --sign - --entitlements ${root}vzhelper.entitlements ${root}dist/codev-vmm`
console.log(`built ${root}dist/codev-vmm${universal ? " (universal)" : ""}`)

// Swift 6.4 made the swiftbuild backend the default, and on Command Line
// Tools setups it fails before compiling anything ("Could not initialize
// build system ... Unknown error parsing property list"). The native backend
// still builds this package, so a failed build retries with it: toolchains
// whose default works never see the retry, and the flag stays out of the
// first attempt in case a later Swift drops it.
async function swiftBuild(extra: string[]) {
const args = ["build", "-c", "release", "--package-path", root, ...extra]
const first = await $`swift ${args}`.nothrow()
if (first.exitCode === 0) return
console.warn("swift build failed with the default build system; retrying with --build-system native")
await $`swift ${args} --build-system native`
}
12 changes: 12 additions & 0 deletions packages/codev-sandbox/src/vz/provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,18 @@ test("surfaces helper error events with the stderr tail", async () => {
await rm(dir, { recursive: true, force: true })
})

test("fails the start, instead of throwing, when the helper binary is missing", async () => {
const dir = await mkdtemp(join(tmpdir(), "codev-vz-"))
const helperPath = join(dir, "codev-vmm")
// The real spawn: a missing binary emits "error" and never "exit".
const { options, projectDir } = await preparedOptions(dir, FAKE_HELPER, { helperPath, spawnImpl: spawn })
const provider = createVzProvider(options)
await expect(provider.start({ ...config, projectDir })).rejects.toThrow(
`sandbox VM helper is not installed (expected at ${helperPath})`,
)
await rm(dir, { recursive: true, force: true })
})

test("times out when the helper never reaches running", async () => {
const dir = await mkdtemp(join(tmpdir(), "codev-vz-"))
const prepared = await preparedOptions(dir, SILENT_HELPER, { startTimeoutMs: 300 })
Expand Down
11 changes: 11 additions & 0 deletions packages/codev-sandbox/src/vz/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,17 @@ export function createVzProvider(options: VzProviderOptions): SandboxProvider {
const running = new Promise<"running">((resolve) => {
handlers.running = () => resolve("running")
})
// A helper that cannot be spawned (not built, deleted, not executable)
// emits "error" and never "exit". Unhandled, that event is an uncaught
// exception — in the desktop's main process, a crash dialog at launch
// when a sandbox autostarts — so it fails this start instead.
child.once("error", (err: NodeJS.ErrnoException) => {
handlers.error?.(
err.code === "ENOENT"
? `sandbox VM helper is not installed (expected at ${options.helperPath})`
: `could not run the sandbox VM helper: ${err.message}`,
)
})
child.stdout?.on(
"data",
createVzHelperEventReader((event: VzHelperEvent) => {
Expand Down
Loading