diff --git a/PATCHES.md b/PATCHES.md index 0f193d3..1c0cdd1 100644 --- a/PATCHES.md +++ b/PATCHES.md @@ -35,7 +35,10 @@ in-tree means the patch can't be lost to a dependency re-resolve. readability handler is never wired and the agent's stdin is never delivered (it hangs) or its stdout is never read (the "no output, just a spinner" symptom in Nucleic Control containers). Behavior is unchanged; it only surfaces the failing stream. Marked `[Nucleic vendored patch]` - (the `import os`, the `nucleicIOLog` static, and the per-stream check in `setupIO`). + (the `import os`, the `nucleicIOLog` static, and the per-stream check in `setupIO`). All three are + wrapped in `#if canImport(os)` — the swiftly toolchain used by `.github/workflows/vminit-image.yml` + resolves Foundation/Virtualization but not the `os` overlay, so the diagnostic degrades to a no-op + there instead of failing the build; Xcode (local) builds keep it. 4. **Trimmed for footprint (no behavior change).** `Tests/`, `docs/`, `examples/`, and `images/` were dropped, and the corresponding `.testTarget(...)` entries removed from `Package.swift`. The diff --git a/Sources/Containerization/LinuxProcess.swift b/Sources/Containerization/LinuxProcess.swift index 7f21306..bbc15f9 100644 --- a/Sources/Containerization/LinuxProcess.swift +++ b/Sources/Containerization/LinuxProcess.swift @@ -21,13 +21,22 @@ import ContainerizationOS import Foundation import Logging import Synchronization -import os // [Nucleic vendored patch] stdio-connection diagnostics + +// [Nucleic vendored patch] stdio-connection diagnostics. Guarded: the `os` overlay isn't importable +// under every toolchain that builds this package (e.g. the swiftly Swift used by the vminit-image CI, +// which resolves Foundation/Virtualization but not `os`), so the diagnostic degrades to a no-op there +// rather than failing the build. Local (Xcode) builds keep it. +#if canImport(os) +import os +#endif /// `LinuxProcess` represents a Linux process and is used to /// setup and control the full lifecycle for the process. public final class LinuxProcess: Sendable { /// [Nucleic vendored patch] Diagnostic log for stdio stream-connection failures (see `setupIO`). + #if canImport(os) static let nucleicIOLog = os.Logger(subsystem: "com.nucleic", category: "container-io") + #endif /// The ID of the process. This is purely metadata for the caller. public let id: String @@ -191,12 +200,15 @@ extension LinuxProcess { // never wired — the agent's stdin is then never delivered (it hangs waiting for input) or // its stdout is never read ("no output, just a spinner"). Log that specific failure (Console // / `log show`, subsystem com.nucleic, category container-io) so a stall pinpoints the stream - // instead of proceeding silently. Log-only; behavior is unchanged. + // instead of proceeding silently. Log-only; behavior is unchanged. Guarded on `canImport(os)` + // (see the import) so a toolchain without the `os` overlay still builds. + #if canImport(os) let configured = [self.ioSetup.stdin != nil, self.ioSetup.stdout != nil, self.ioSetup.stderr != nil] for (index, label) in [(0, "stdin"), (1, "stdout"), (2, "stderr")] where configured[index] && handles[index] == nil { Self.nucleicIOLog.error( "setupIO[\(self.id, privacy: .public)]: \(label, privacy: .public) stream never connected from the guest — agent stdio will stall") } + #endif // Note: stdin relay is started separately via startStdinRelay() after // the process has started, to avoid a deadlock where closeStdin is