Merge nucleic/mellow-dewy-falcon-rjhr into main

This commit is contained in:
2026-08-07 02:30:26 -07:00
parent d64b3f3e9b
commit e7163de22f
9 changed files with 403 additions and 47 deletions
+31 -12
View File
@@ -91,14 +91,15 @@ struct ImageCommand: AsyncParsableCommand {
name: imageName,
ipswPath: ipswPath,
config: frozenConfig,
progress: { stage in printer.update(ImageCommand.describe(stage)) }
progress: { stage in ImageCommand.report(stage, to: printer) }
)
} catch {
printer.finish()
CLI.error("\(error)")
// Fully qualified: inside a ParsableCommand a bare `exit`
// resolves to ParsableCommand.exit(withError:).
await MainActor.run { Foundation.exit(1) }
// Not `MainActor.run`: the main actor is parked inside
// `app.run()` for the life of the process, so hopping onto
// it to exit is its own deadlock. See `VZAppRuntime.run`.
VZAppRuntime.flushAndExit(1)
}
printer.finish("done")
@@ -254,14 +255,15 @@ struct ImageCommand: AsyncParsableCommand {
name: imageName,
config: frozenConfig,
xcodeXIPPath: xipPath,
progress: { stage in printer.update(ImageCommand.describe(stage)) }
progress: { stage in ImageCommand.report(stage, to: printer) }
)
} catch {
printer.finish()
CLI.error("\(error)")
// Fully qualified: inside a ParsableCommand a bare `exit`
// resolves to ParsableCommand.exit(withError:).
await MainActor.run { Foundation.exit(1) }
// Not `MainActor.run`: the main actor is parked inside
// `app.run()` for the life of the process, so hopping onto
// it to exit is its own deadlock. See `VZAppRuntime.run`.
VZAppRuntime.flushAndExit(1)
}
printer.finish("done")
print("provisioned image '\(imageName)'")
@@ -270,19 +272,36 @@ struct ImageCommand: AsyncParsableCommand {
}
}
/// Routes a stage to the progress printer.
///
/// Notes get a line of their own: they are the reason the operator is still
/// watching, and a status line that is about to be overwritten is no place
/// to put "this may be a truncated download".
static func report(_ stage: ImageBuildStage, to printer: ProgressPrinter) {
if case .note(let text) = stage {
printer.line(text)
} else {
printer.update(describe(stage))
}
}
/// Renders a build stage as one status line.
static func describe(_ stage: ImageBuildStage) -> String {
switch stage {
case .downloadingIPSW(let fraction):
return "downloading IPSW " + CLI.progressBar(fraction)
case .preparing:
return "preparing"
case .creatingBundle:
return "creating bundle"
return "resolving restore image…"
case .loadingRestoreImage:
return "loading restore image metadata…"
case .creatingBundle(let diskGB):
return "creating VM bundle (disk \(diskGB) GB)…"
case .note(let text):
return text
case .installing(let fraction):
return "installing macOS " + CLI.progressBar(fraction)
case .firstBoot:
return "first boot (Setup Assistant)"
return "first boot + guest provisioning…"
case .provisioning(let step):
return "provisioning: \(step)"
case .finalizing: