Files
nucleic/Package.swift
T
2026-06-26 14:28:43 -07:00

126 lines
7.5 KiB
Swift

// swift-tools-version: 6.2
import PackageDescription
// Build channel, selected at manifest-evaluation time via the NUCLEIC_CHANNEL
// environment variable (dev | beta | stable); defaults to `dev`. It drives two
// things in lockstep:
// • the app executable's PRODUCT name — which becomes the on-disk binary and
// therefore the OS process name (Activity Monitor / `ps`):
// dev -> nucleic-local beta -> nucleic-beta rc -> nucleic-rc stable -> nucleic
// • a compile-time define (`NUCLEIC_STABLE/RC/BETA/DEV`) read by NucleicApp for its build
// identity + warning banner (red local / blue beta / gold release-candidate / none stable),
// and by NucleicCore's `ContainerManager` to suffix non-release container names.
// Branch mapping (see BUILD.md): dev builds from `dev`, beta from `staging`,
// rc from `rc`, stable from `main`.
let nucleicChannel = (Context.environment["NUCLEIC_CHANNEL"] ?? "dev").lowercased()
let (appProductName, channelDefine): (String, String) = {
switch nucleicChannel {
case "stable", "prod", "release": return ("nucleic", "NUCLEIC_STABLE")
case "rc", "candidate": return ("nucleic-rc", "NUCLEIC_RC")
case "beta": return ("nucleic-beta", "NUCLEIC_BETA")
default: return ("nucleic-local", "NUCLEIC_DEV")
}
}()
let package = Package(
name: "Nucleic",
// NucleicProtocol (the shared sync wire layer) must build for iOS too — that's what the
// NucleicRemote iPhone client links. The host-only targets (NucleicCore/App/spike) use
// macOS-only APIs and are simply never built for iOS.
//
// macOS floor is 26 (Tahoe): the container sandbox is built directly on Apple's
// `containerization` framework, whose in-process vmnet networking requires macOS 26 + Apple
// silicon. `containerization` is a dependency of NucleicCore ONLY (never of the iOS-linked
// NucleicProtocol), so the iOS build resolves the package graph but never compiles it.
platforms: [.macOS(.v26), .iOS(.v17)],
products: [
.library(name: "NucleicProtocol", targets: ["NucleicProtocol"]),
.library(name: "NucleicCore", targets: ["NucleicCore"]),
// Product (binary / process) name varies by channel — see `appProductName`.
.executable(name: appProductName, targets: ["NucleicApp"]),
.executable(name: "nucleic-spike", targets: ["nucleic-spike"]),
.executable(name: "fake-claude", targets: ["fake-claude"]),
.executable(name: "fake-grok", targets: ["fake-grok"]),
.executable(name: "container-spike", targets: ["container-spike"]),
],
dependencies: [
.package(url: "https://github.com/groue/GRDB.swift.git", from: "7.0.0"),
.package(url: "https://github.com/migueldeicaza/SwiftTerm.git", from: "1.2.0"),
// Apple's containerization framework — the in-process runtime the `container` CLI is built
// on. **Vendored** (third_party/containerization) at upstream commit 6b7b42ca rather than
// pulled from github, because we carry a small patch: `LinuxContainer.Configuration` forwards
// `vmExtensions` into `VMConfiguration.extensions` so we can attach a memory-balloon device
// (see third_party/containerization/PATCHES.md). The package is pre-1.0 (source stability
// holds only within a minor version); to bump, re-vendor the new commit and re-apply the
// patch. All framework calls are centralized in `ContainerEngine`/`MemoryBalloon`.
.package(path: "third_party/containerization"),
// Sparkle — in-app auto-update for the directly-distributed (non-App-Store) macOS
// builds. macOS-only and used ONLY by the NucleicApp executable target, so the iOS
// resolution of the package graph never compiles it. Updates are delivered as signed,
// notarized DMGs described by a per-channel appcast (see BUILD.md "Auto-update").
.package(url: "https://github.com/sparkle-project/Sparkle", from: "2.6.0"),
],
targets: [
// Pure, platform-agnostic: identifiers, the AgentEvent model, the sync wire
// protocol (ClientMsg/HostMsg), CBOR framing, and the Noise SecureChannel.
// No GRDB / Process / Network — compiles for macOS and iOS alike.
.target(name: "NucleicProtocol"),
.target(
name: "NucleicCore",
dependencies: [
"NucleicProtocol",
.product(name: "GRDB", package: "GRDB.swift"),
// In-process container runtime (host-only). EXT4 unpacking + vmnet networking are
// reached transitively through `Containerization`; `ContainerizationOCI` provides
// the `User`/`Platform` types used directly by `ContainerEngine`.
.product(name: "Containerization", package: "containerization"),
.product(name: "ContainerizationOCI", package: "containerization"),
// For `AddressAllocator` — named in the `VZInstanceExtension.configureVZ` signature
// our `MemoryBalloon` conforms to (it lives here, not in `Containerization`).
.product(name: "ContainerizationExtras", package: "containerization"),
],
// The build channel reaches the container-naming code here: `ContainerManager` appends a
// per-channel suffix (e.g. `-beta`, `-local`) to non-release container names so a beta /
// local-dev build's containers don't collide with a release's on the shared on-disk
// container store (ContainerEngine.defaultStorageRoot). Same define NucleicApp's banner uses.
swiftSettings: [.define(channelDefine)]),
.executableTarget(
name: "NucleicApp",
dependencies: [
"NucleicCore",
.product(name: "SwiftTerm", package: "SwiftTerm"),
// Auto-update for direct (non-App-Store) distribution. macOS-only.
.product(name: "Sparkle", package: "Sparkle"),
],
swiftSettings: [.define(channelDefine)],
plugins: ["EmbedGitCommit"]),
// Prebuild plugin: stamps the built-from commit into a generated `GitCommit.swift`
// at build time, so the hash is baked into the binary (correct even on a
// packaged beta / rc / stable archive, where git isn't present at runtime).
.plugin(name: "EmbedGitCommit", capability: .buildTool()),
.executableTarget(name: "nucleic-spike", dependencies: ["NucleicCore"]),
.executableTarget(name: "fake-claude", dependencies: ["NucleicCore"]),
.executableTarget(name: "fake-grok", dependencies: ["NucleicCore"]),
// Standalone de-risking spike for the container rework: drives the real `ContainerEngine`
// end-to-end — pulls an image from a registry, boots the VM, execs a process, and streams
// its stdout through `ContainerizedProcessHandle` (the agent's path). Run signed with
// signing/spike.entitlements; not shipped.
.executableTarget(
name: "container-spike",
dependencies: [
"NucleicCore",
.product(name: "Containerization", package: "containerization"),
.product(name: "ContainerizationOCI", package: "containerization"),
]),
.testTarget(
name: "NucleicProtocolTests",
dependencies: ["NucleicProtocol"]
),
.testTarget(
name: "NucleicCoreTests",
dependencies: ["NucleicCore"],
resources: [.copy("Fixtures")]
),
]
)