From bc11afc63dfa9963391fe7a263e767c4f4783838 Mon Sep 17 00:00:00 2001 From: Nucleic Date: Mon, 27 Jul 2026 21:20:25 -0700 Subject: [PATCH] Merge nucleic/olive-willow-newt-96nb into dev --- images/agent/Dockerfile | 49 +++++++++++++++++-- images/agent/files/etc/profile.d/naros-env.sh | 8 +-- mkimage/profiles/vm-desktop.pkgs | 10 ++++ packages/naros-tier-agent/control | 16 ++++-- tests/smoke-rootfs.sh | 6 ++- 5 files changed, 77 insertions(+), 12 deletions(-) diff --git a/images/agent/Dockerfile b/images/agent/Dockerfile index 0500258..338b414 100644 --- a/images/agent/Dockerfile +++ b/images/agent/Dockerfile @@ -30,6 +30,9 @@ ARG GO_SHA256_ARM64=fe4789e92b1f33358680864bbe8704289e7bb5fc207d80623c308935bd69 ARG MISE_VERSION=v2026.7.7 ARG MISE_SHA256_AMD64=0953810c2785eb4a75159f67f8b5721c4f3c80b8a6a812015d5af7d7fbd1b8a4 ARG MISE_SHA256_ARM64=c4e542b53a15d2ec641e072f7b2d9da8a0554b92fd2c09a51febde32c6080ab8 +ARG SWIFT_VERSION=6.3.3 +ARG SWIFT_SHA256_AMD64=19e0c78cad5418ad48bfa87aa20c53ac9ac9996d1695d04dd94f7c7ea4eb133f +ARG SWIFT_SHA256_ARM64=ecba8ef87b54a5048d466af500f3169c939a6b8a2cb7c600f76b5184457f293a # Warm shared caches at fixed world-readable paths (§6.3). Exported here so the BUILD's # own installs warm them; the runtime equivalent for agent shells is @@ -42,7 +45,7 @@ ENV npm_config_cache=/opt/cache/npm \ RUSTUP_HOME=/opt/rustup \ GOMODCACHE=/opt/cache/gomod \ PLAYWRIGHT_BROWSERS_PATH=/opt/playwright-browsers \ - PATH=/opt/mise/shims:/opt/cache/cargo/bin:/usr/local/go/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin + PATH=/opt/mise/shims:/opt/cache/cargo/bin:/usr/local/go/bin:/opt/swift/usr/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin # The local apt pool (CI's just-built Nucleic packages) rides in only for this stage's # installs; the hosted signed repo (naros-keyring, already in the base) is the runtime @@ -114,6 +117,43 @@ RUN set -eu; \ mkdir -p /opt/mise/shims; \ MISE_DATA_DIR=/opt/mise mise --version +# Swift — the swift.org release toolchain (pinned + checksummed, same shape as Go above). +# Baked because Nucleic itself is a Swift package and agents work on Swift repos all day: +# swiftc, SwiftPM, swift-testing, sourcekit-lsp and swift-format all run in-container now. +# It does NOT replace host_exec / the macOS VM for anything Apple-SDK — there is no +# SwiftUI/AppKit/UIKit or xcodebuild on Linux — but pure-Swift targets build and test here. +# +# The `debian12` slice is the newest Debian build swift.org publishes; it runs unmodified on +# trixie (verified: swift build/test/swiftc on the 26.07 image) once libncurses6 is present, +# which naros-tier-agent now pulls along with the rest of the toolchain's runtime libs +# (libxml2, libcurl4, libsqlite3-0, libuuid1, libtinfo6, zlib1g). Debian's own `swiftlang` +# is deliberately NOT used here: trixie carries 6.0.3, too old for a swift-tools-version 6.2 +# package like this repo's. (The VM desktop rootfs is the mirror-image case — forky's +# libxml2 soname bump to .so.16 makes the swift.org build unloadable there, so it takes +# forky's swiftlang 6.2.3 instead; see os/mkimage/profiles/vm-desktop.pkgs.) +# +# LLDB is stripped: upstream links it against libpython3.11, which trixie does not ship, so +# lldb/lldb-dap/liblldb could never load — dropping them keeps 338 MB out of the image and +# makes `swift repl` fail as a plain "not found" instead of a loader error. Nothing else in +# the toolchain depends on liblldb (checked via NEEDED). +RUN set -eu; \ + case "$TARGETARCH" in \ + amd64) swift_slice=debian12; swift_sha="$SWIFT_SHA256_AMD64";; \ + arm64) swift_slice=debian12-aarch64; swift_sha="$SWIFT_SHA256_ARM64";; \ + *) echo "unsupported TARGETARCH: $TARGETARCH" >&2; exit 1;; \ + esac; \ + swift_tag="swift-${SWIFT_VERSION}-RELEASE"; \ + curl -fsSL "https://download.swift.org/swift-${SWIFT_VERSION}-release/${swift_slice}/${swift_tag}/${swift_tag}-${swift_slice}.tar.gz" \ + -o /tmp/swift.tgz; \ + echo "$swift_sha /tmp/swift.tgz" | sha256sum -c -; \ + mkdir -p /opt/swift; \ + tar -C /opt/swift --strip-components=1 -xzf /tmp/swift.tgz; rm /tmp/swift.tgz; \ + rm -f /opt/swift/usr/bin/lldb /opt/swift/usr/bin/lldb-dap \ + /opt/swift/usr/bin/lldb-server /opt/swift/usr/bin/lldb-argdumper \ + /opt/swift/usr/lib/liblldb.so*; \ + chmod -R a+rX /opt/swift; \ + swift --version | grep -q "Swift version ${SWIFT_VERSION}" + # Runtime environment for agent shells: cache paths, toolchain PATH entries, mise # activation. nash -l sources /etc/profile.d, which is how every containerized exec # (ContainerEngine exec → nash -lc) sees this without OCI env. @@ -192,11 +232,14 @@ RUN set -eu; \ go_v="$(go version | awk '{print $3}')"; \ mise_v="$(mise --version 2>/dev/null | awk '{print $1}')"; \ gcc_v="$(gcc -dumpfullversion)"; \ + swift_v="$(swift --version | awk '/Swift version/ {print $3; exit}')"; \ pw_v="$(playwright --version | awk '{print $2}')"; \ jq --arg node "$node_v" --arg python "$python_v" --arg rust "$rust_v" \ - --arg go "$go_v" --arg mise "$mise_v" --arg gcc "$gcc_v" --arg playwright "$pw_v" \ + --arg go "$go_v" --arg mise "$mise_v" --arg gcc "$gcc_v" --arg swift "$swift_v" \ + --arg playwright "$pw_v" \ '.tier = "agent" | .variant = "agent" \ - | .toolchains = {node: $node, python: $python, rust: $rust, go: $go, mise: $mise, gcc: $gcc} \ + | .toolchains = {node: $node, python: $python, rust: $rust, go: $go, mise: $mise, \ + gcc: $gcc, swift: $swift} \ | .caches = {npm: "/opt/cache/npm", pip: "/opt/cache/pip", uv: "/opt/cache/uv", \ cargo: "/opt/cache/cargo", gomod: "/opt/cache/gomod"} \ | .playwright = {version: $playwright, browsers: "/opt/playwright-browsers", chromium: true}' \ diff --git a/images/agent/files/etc/profile.d/naros-env.sh b/images/agent/files/etc/profile.d/naros-env.sh index 2a4523d..1fda6ee 100644 --- a/images/agent/files/etc/profile.d/naros-env.sh +++ b/images/agent/files/etc/profile.d/naros-env.sh @@ -16,11 +16,11 @@ export PLAYWRIGHT_BROWSERS_PATH=/opt/playwright-browsers # where the interactive `mise activate` hook would not run. export MISE_DATA_DIR=/opt/mise -# Baked toolchains (§6.2): cargo/rustup bins, Go, and the mise shims ahead of them all -# so `mise use` versions win per-project. Idempotent (guarded) — profile.d can be -# sourced more than once per session. +# Baked toolchains (§6.2): cargo/rustup bins, Go, the swift.org toolchain at +# /opt/swift/usr/bin, and the mise shims ahead of them all so `mise use` versions win +# per-project. Idempotent (guarded) — profile.d can be sourced more than once per session. case ":$PATH:" in *:/opt/mise/shims:*) ;; - *) PATH="/opt/mise/shims:/opt/cache/cargo/bin:/usr/local/go/bin:$PATH" ;; + *) PATH="/opt/mise/shims:/opt/cache/cargo/bin:/usr/local/go/bin:/opt/swift/usr/bin:$PATH" ;; esac export PATH diff --git a/mkimage/profiles/vm-desktop.pkgs b/mkimage/profiles/vm-desktop.pkgs index af4e380..8deaacb 100644 --- a/mkimage/profiles/vm-desktop.pkgs +++ b/mkimage/profiles/vm-desktop.pkgs @@ -70,3 +70,13 @@ python3-venv python3-pip nodejs npm + +# Swift, from forky's own swiftlang (6.2.3 in the pinned snapshot) rather than the swift.org +# release tarball the container image bakes (6.3.3, os/images/agent/Dockerfile). Not a +# preference for older — the upstream builds are simply unloadable here: every swift.org slice +# links libxml2.so.2, and forky replaced libxml2 with libxml2-16 (soname .so.16, no versioned +# symbols to alias), so swift-build/swift-test die in the loader. Debian's package is built +# against forky's own libxml2-16 and python3.14, which also means its LLDB works — the piece +# the container layer has to strip. Same rule as the GNOME stack above: one coherent pocket, +# no trixie/forky ABI mixing. ~2.6 GB installed. +swiftlang diff --git a/packages/naros-tier-agent/control b/packages/naros-tier-agent/control index 3bbe5c0..37d26f5 100644 --- a/packages/naros-tier-agent/control +++ b/packages/naros-tier-agent/control @@ -8,10 +8,18 @@ Depends: naros-tier-base, nucleic-bridge, build-essential, pkg-config, python3, python3-pip, python3-venv, sudo, gh, ripgrep, fd-find, jq, yq, sqlite3, htop, tree, zip, unzip, zstd, xz-utils, moreutils, rsync, less, procps, file, bsdextrautils, e2fsprogs, util-linux-extra, mount, login, - gpgv + gpgv, libncurses6, libtinfo6, libxml2 | libxml2-16, libsqlite3-0, libuuid1, + libcurl4t64 | libcurl4, zlib1g Description: narOS agent tier — apt-resolvable half (NAROS.md §4, §6) The dev toolchain and modern CLI kit that come from Debian, plus the control bridge. The non-apt half of the agent tier — Node (NodeSource), rustup, Go, - mise, warm caches, agent CLIs, Playwright — is layered by the naros-agent - image build (milestone N2); this meta is what `apt install` can deliver into - any Debian-family container (NAROS.md §7.3 conversion path). + Swift (swift.org toolchain), mise, warm caches, agent CLIs, Playwright — is + layered by the naros-agent image build (milestone N2); this meta is what + `apt install` can deliver into any Debian-family container (NAROS.md §7.3 + conversion path). + . + The trailing lib* entries are the swift.org toolchain's runtime dependencies + (its full external NEEDED set beyond libc/libstdc++/libgcc, which + build-essential already carries). Most arrive transitively today — libncurses6 + does not, and without it every Swift driver binary dies in the loader — so all + of them are declared here rather than left to another package's whim. diff --git a/tests/smoke-rootfs.sh b/tests/smoke-rootfs.sh index 1541df4..5726141 100755 --- a/tests/smoke-rootfs.sh +++ b/tests/smoke-rootfs.sh @@ -95,7 +95,11 @@ case "$FLAVOR" in test -x /usr/local/bin/nucleic-a11y-agent test -x /usr/local/bin/nucleic-linux-agent test -f /usr/share/gnome-shell/extensions/nucleic-geometry@nucleic.xyz/metadata.json - grep -q "AutomaticLogin=agent" /etc/gdm3/daemon.conf' + grep -q "AutomaticLogin=agent" /etc/gdm3/daemon.conf + # Swift comes from forky swiftlang here (vm-desktop.pkgs), so assert the compiler + # actually loads — a soname drift in the pocket (the libxml2 .so.2→.so.16 kind) would + # otherwise ship a Swift that only fails the first time an agent invokes it. + swiftc --version > /dev/null' ;; *)