fix: preflight checks the running binary, not a dev .build path

boot_host_preflight.sh hardcoded RELEASE_BIN=$PROJECT_ROOT/.build/release/
vphone-cli. Inside the bundled .app, PROJECT_ROOT resolves to Contents/
Resources, so it looked for Contents/Resources/.build/release/vphone-cli —
which doesn't exist (the binary is at Contents/MacOS/vphone-cli). Under
--assert-bootable that made the preflight fail with "missing release binary",
blocking `vm launch` from a brew-installed or copied .app.

`vm launch` now passes the running executable (CommandLine.arguments[0]) to the
preflight via VPHONE_CLI_BIN and the script checks that binary; it still falls
back to the dev .build/release path for standalone/`make` invocation.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
zqxwce
2026-07-28 14:48:19 +03:00
committed by zqxwce
co-authored by Claude Opus 4.8
parent f79c897dd1
commit 70ff75067a
2 changed files with 23 additions and 15 deletions
+18 -14
View File
@@ -25,20 +25,8 @@ struct VPhoneVMLaunchCommand: ParsableCommand {
let resources = projectRoot.map { VPhoneResources(base: URL(fileURLWithPath: $0)) } ?? .resolve()
let layout = VPhoneLaunchLayout(resources: resources)
// Host preflight same gate make boot applies.
var preflightArgs = ["--assert-bootable"]
if variant == "less" { preflightArgs.append("--less") }
let pre = try VPhoneProcessRunner.runCapturing(
URL(fileURLWithPath: "/bin/zsh"), [layout.preflightScript.path] + preflightArgs,
cwd: resources.base)
if !pre.stdout.isEmpty { print(pre.stdout, terminator: "") }
guard pre.succeeded else {
FileHandle.standardError.write(Data(pre.stderr.utf8))
throw ExitCode(pre.exitCode == 0 ? 1 : pre.exitCode)
}
// A bundled .app is its own boot binary self-spawn the running executable
// for both the GUI and --dfu (headless) boot paths.
// The running executable is BOTH what we boot from and what preflight
// should check a bundled .app is its own boot binary.
let bootBinary = URL(fileURLWithPath: CommandLine.arguments[0]).resolvingSymlinksInPath()
guard FileManager.default.isExecutableFile(atPath: bootBinary.path) else {
FileHandle.standardError.write(Data(
@@ -46,6 +34,22 @@ struct VPhoneVMLaunchCommand: ParsableCommand {
throw ExitCode(1)
}
// Host preflight same gate make boot applies. Point it at THIS binary
// (VPHONE_CLI_BIN) so it checks the vphone-cli we're running, not a dev
// .build/release path that doesn't exist inside the bundled .app.
var preflightArgs = ["--assert-bootable"]
if variant == "less" { preflightArgs.append("--less") }
var preflightEnv = ProcessInfo.processInfo.environment
preflightEnv["VPHONE_CLI_BIN"] = bootBinary.path
let pre = try VPhoneProcessRunner.runCapturing(
URL(fileURLWithPath: "/bin/zsh"), [layout.preflightScript.path] + preflightArgs,
cwd: resources.base, env: preflightEnv)
if !pre.stdout.isEmpty { print(pre.stdout, terminator: "") }
guard pre.succeeded else {
FileHandle.standardError.write(Data(pre.stderr.utf8))
throw ExitCode(pre.exitCode == 0 ? 1 : pre.exitCode)
}
if !dfu && !noVphoned {
do {
_ = try layout.stageVphoned(into: bundle)