#!/usr/bin/env bash # Per-flavor rootfs smoke test (NAROS.md §10.3). # # smoke-rootfs.sh # # is the build-rootfs.sh tier: base | vm | vm-desktop. # is an ALREADY-IMPORTED docker image (the caller runs `docker import` on the # tarball, because it usually wants the named image for later steps too). # amd64 | arm64 — passed to --platform. # # Assertions run inside the image under nash. They live here rather than inline in # naros.yml because the vm and vm-desktop jobs are one matrix over a single set of steps: # the flavors differ ONLY in what is asserted, and inlining that difference is what forced # the two near-identical jobs the matrix replaced. # # Exit: 0 all assertions hold, non-zero on the first failure (the container shell is `set -e`). set -euo pipefail FLAVOR="${1:?usage: smoke-rootfs.sh }" IMAGE="${2:?missing image}" ARCH="${3:?missing arch}" run() { docker run --rm --platform "linux/$ARCH" "$IMAGE" /usr/bin/nash -lc "$1"; } # Sourced by every flavor: narOS identity plus the forced-nash /bin/sh with the real bash kept # aside. Leaves os-release variables ($VARIANT_ID, $VERSION_ID) in scope for the flavor blocks. COMMON=' set -e . /etc/os-release; test "$ID" = naros readlink /bin/sh | grep -q nash test -x /usr/bin/bash.real ' # A runtime-clean sources.list (no build-time copy:// pool left in), scoped away from # sources.list.d so the not-yet-live hosted-repo entry (naros-keyring) cannot fail this # before the apt domain is provisioned. APT_CLEAN=' apt-get update -q -o Dir::Etc::SourceParts=- ' case "$FLAVOR" in base) echo "smoke: base rootfs ($ARCH)" run "$COMMON"' naros version naros info --json > /dev/null # Kernel identity shim (NAROS.md §2.3). Every command in this smoke test already runs # under the global preload, so a broken .so fails the job long before here; these assert # the tag is actually applied and correctly scoped. grep -qxF /usr/lib/naros/libnaros-uname.so /etc/ld.so.preload # VERSION_ID comes from the os-release sourced above. uname -r | grep -q -- "-naros$VERSION_ID" # sysname must stay "Linux" — build tooling switches on it. test "$(uname -s)" = Linux # The escape hatch /lib/modules consumers depend on must really bypass. Compare against # procfs, which the shim cannot touch (a file read, not a syscall). test "$(NAROS_UNAME_PASSTHROUGH=1 uname -r)" = "$(cat /proc/sys/kernel/osrelease)" '"$APT_CLEAN" ;; vm) echo "smoke: vm rootfs — identity + forced shell + systemd present ($ARCH)" # nash directly (systemd is PID 1 only under a real boot); assert the bootable base is sane. run "$COMMON"' test "$VARIANT_ID" = vm test -x /usr/lib/systemd/systemd || test -x /lib/systemd/systemd command -v systemctl > /dev/null naros version # The control-plane agent IS baked (from the local pool): it is the only way the host can # reach the guest, and the phase-1 bootstrap no longer overlays a GHCR copy on top. The # rest of the tier still arrives at firstboot via `apt install naros-tier-vm`. test -x /usr/local/bin/nucleic-linux-agent test -L /etc/systemd/system/multi-user.target.wants/nucleic-linux-agent.service '"$APT_CLEAN" ;; vm-desktop) echo "smoke: vm-desktop rootfs — GNOME 50 + agent user + semantic surface ($ARCH)" run "$COMMON"' test "$VARIANT_ID" = vm naros info --json | jq -e ".variant == \"vm\" and .flavor == \"desktop\"" > /dev/null test "$(id -u agent)" = 501 test -x /usr/lib/systemd/systemd || test -x /lib/systemd/systemd command -v gnome-shell > /dev/null gnome-shell --version | grep -qE "GNOME Shell 5[0-9]" command -v gdm3 > /dev/null || test -x /usr/sbin/gdm3 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' ;; *) echo "unknown flavor: $FLAVOR (expected base, vm, or vm-desktop)" >&2 exit 2 ;; esac echo "smoke OK ($FLAVOR/$ARCH)"