#!/usr/bin/env bash source "$(dirname -- "${BASH_SOURCE[0]}")/lib.sh" [[ $# == 0 ]] || die 'Usage: PROFILE=cli tools/build-rootfs' profile=${PROFILE:-cli} [[ $profile == cli || $profile == development || $profile == recovery ]] || die 'Supported profiles: cli, development, recovery' use_xbps check_void_pin need python3 python3 -c 'import sys; assert sys.version_info >= (3, 14), "Rootfs assembly requires Python 3.14+ for zstd XBPS archives"' cd "$FDS_ROOT" mkdir -p out/logs out/manifests out/packages out/cache/rootfs exec 9>out/.rootfs.lock flock -n 9 || die 'Another rootfs build is already running' missing=() for package in qemu-user-aarch64 util-linux; do if ! xbps-query -r "$FDS_VOID/masterdir-x86_64" "$package" >/dev/null 2>&1; then missing+=("$package") fi done if (( ${#missing[@]} )); then tools/in-void xbps-install -y "${missing[@]}" fi tools/build-base-packages work=$(mktemp -d "$FDS_ROOT/out/rootfs-build.XXXXXX") root="$work/root" printf 'Rootfs build workspace: %s\n' "$work" # Keep failed workspaces for diagnosis. No partial result replaces a good archive. mkdir -p "$root" python3 tools/rootfs-layout "$root" packages/fds-base-files/files/layout.json mkdir -p "$root/var/db/xbps/keys" "$work/config" cp "$FDS_VOID/common/repo-keys/"*.plist "$root/var/db/xbps/keys/" cp packages/fds-base-files/files/10-fds.conf "$work/config/" mapfile -t packages < <(sed -e 's/#.*//' -e '/^[[:space:]]*$/d' image/base-packages.list "profiles/$profile.list") (( ${#packages[@]} )) || die 'Empty image/base-packages.list' for package in "${packages[@]}"; do [[ $package =~ ^[a-zA-Z0-9][a-zA-Z0-9+_.-]*$ ]] || die "Invalid base package: $package" done # Extract with native XBPS in the build user namespace. The on-disk tree belongs # to the host user; rootfs-archive restores ownership from package headers. repository=https://repo-default.voidlinux.org/current/aarch64 if [[ ${FDS_OFFLINE:-0} == 1 ]]; then repository="$FDS_ROOT/.host/frozen/$profile" [[ -f $repository/aarch64-repodata ]] || die "Missing frozen repository for $profile" fi tools/in-void env XBPS_ARCH=aarch64 XBPS_TARGET_ARCH=aarch64 \ xbps-install -r "$root" -C "$work/config" \ -c "$FDS_ROOT/out/cache/rootfs" -i \ -R "$repository" \ -R "$FDS_ROOT/out/packages" -SyU --reproducible "${packages[@]}" printf '%s\n' "$profile" >"$root/usr/share/fds/image-profile" install -Dm644 docs/internal-storage.md "$root/usr/share/doc/fds/internal-storage.md" install -Dm644 docs/recovery.md "$root/usr/share/doc/fds/recovery.md" install -Dm644 docs/releases.md "$root/usr/share/doc/fds/releases.md" install -Dm644 tools/capture-hardware "$root/usr/share/fds/capture-hardware" python3 tools/rootfs-audit "$root" --unconfigured epoch=$(git -C "$FDS_VOID" show -s --format=%ct HEAD) tools/in-rootfs "$root" /usr/bin/env SOURCE_DATE_EPOCH="$epoch" /bin/bash /tmp/fds-finalize # Apply the upstream iputils policy with direct QEMU and a namespace-local # CAP_SETFCAP; the exported capability must be an actual persisted xattr. tools/in-rootfs "$root" --direct /usr/bin/setcap CAP_NET_RAW+p /usr/bin/iputils-ping tools/in-rootfs "$root" --direct /usr/bin/getcap /usr/bin/iputils-ping | grep -qx '/usr/bin/iputils-ping cap_net_raw=p' python3 tools/rootfs-audit "$root" tools/rootfs-runtime-check "$root" # Preserve the exact selected package inputs, independently of future repository # updates. Local FDS package versions can be rebuilt, so copy them per build. archive_inputs=(out/cache/rootfs out/packages) if [[ ${FDS_OFFLINE:-0} == 1 ]]; then archive_inputs+=("$repository") fi python3 tools/rootfs-archive "$root" "$work" "${archive_inputs[@]}" { printf 'profile=%s\nvoid_commit=%s\n' "$profile" "$(cat VOID_PACKAGES_COMMIT)" printf 'emulation=private-user-namespace-binfmt\n' tools/in-void xbps-query -p pkgver qemu-user-aarch64 date -u '+built_at=%Y-%m-%dT%H:%M:%SZ' } >"$work/build.txt" find packages/fds-base packages/fds-base-files packages/fds-init packages/fds-cli packages/fds-cartridged packages/fds-kernel packages/fds-eink packages/fds-dhcpcd profiles s6/source -type f -print0 | sort -z | \ xargs -0 sha256sum >"$work/build-inputs.sha256" sha256sum image/base-packages.list tools/{build-rootfs,build-base-packages,build-fds,build-fds-package,build-kernel,in-void,in-rootfs,rootfs-namespace,finalize-rootfs,rootfs-audit,rootfs-archive,rootfs-runtime-check,rootfs-layout,rootfs_lib.py,lib.sh} \ >>"$work/build-inputs.sha256" sha256sum docs/recovery.md docs/internal-storage.md docs/releases.md tools/capture-hardware tools/rust-notices tools/cargo-build .cargo/config.toml >>"$work/build-inputs.sha256" cp out/manifests/fds-tools*.sha256 "$work/" # The versioned build directory is the authoritative result; stable links are # convenience pointers. Rename the archive last, after every acceptance check. ln -sfn "${work##*/}/root" out/rootfs-aarch64 ln -sfn "../${work##*/}" out/manifests/rootfs-latest ln -s "${work##*/}/rootfs-aarch64.tar" out/rootfs-aarch64.tar.next mv -Tf out/rootfs-aarch64.tar.next out/rootfs-aarch64.tar ln -s "${work##*/}/rootfs-aarch64.tar" "out/rootfs-$profile.tar.next" mv -Tf "out/rootfs-$profile.tar.next" "out/rootfs-$profile.tar" printf 'PASS: FDS rootfs built and verified: out/rootfs-aarch64.tar\n'