#!/usr/bin/env bash # ───────────────────────────────────────────────────────────────────────────── # Arcline OS — rootfs builder # # scripts/build-rootfs.sh # # Pipeline: # 1. debootstrap a minimal Debian base # 2. configure apt sources (main + security) # 3. install the edition package set # 4. apply overlays (hardening, configs, service units) # 5. run configure-system.sh inside the chroot # 6. clean and tar the result → build/artifacts/arcline--.tar.xz # # Root is required (re-execs under sudo when needed). # ───────────────────────────────────────────────────────────────────────────── set -euo pipefail source "$(dirname "${BASH_SOURCE[0]}")/common.sh" EDITION="${1:?usage: build-rootfs.sh }" validate_edition "$EDITION" # Re-exec under sudo if we aren't root (debootstrap/chroot/mount need it). require_root "$0" "$@" # Fail fast on a bad toolchain policy before debootstrap does any work. case "$ARCLINE_TOOLCHAIN" in auto|skip|require) ;; *) die "ARCLINE_TOOLCHAIN must be auto|skip|require (got '$ARCLINE_TOOLCHAIN')" ;; esac # Fail fast on a bad boot flavour. grub-pc and grub-efi-amd64 conflict, so we # install exactly the one matching BOOT (never both). case "$BOOT" in bios) BOOT_PKGS="grub-pc" ;; efi) BOOT_PKGS="grub-efi-amd64 shim-signed mokutil" ;; *) die "BOOT must be bios|efi (got '$BOOT')" ;; esac EDIR="$(edition_dir "$EDITION")" ROOTFS="$ROOTFS_DIR/$EDITION" ARTIFACT="$ARTIFACT_DIR/arcline-$EDITION-$VERSION-$ARCH.tar.xz" mkdir -p "$ROOTFS_DIR" "$LOG_DIR" "$ARTIFACT_DIR" # ── staleness check ───────────────────────────────────────────────────────── # Incremental builds: skip rebuilding when no input changed since the last # artifact. This is what makes `make iso-` fast when nothing changed, # AND it catches stale archives during development (the bug class that shipped # a rootfs with no grub-install). Force with FORCE=1 or `make clean`. newer_input() { # → 0 if any input is newer than the artifact local art="$1" [[ -f "$art" ]] || return 0 find "$ROOT/scripts" "$ROOT/btrfs" "$ROOT/overlays" "$EDIR" \ -type f -newer "$art" -print -quit 2>/dev/null | grep -q . && return 0 [[ -d "$DEB_DIR" ]] && find "$DEB_DIR" -name '*.deb' -newer "$art" -print -quit 2>/dev/null | grep -q . && return 0 [[ "$ROOT/versions.mk" -nt "$art" ]] && return 0 return 1 } if [[ "${FORCE:-0}" != "1" && -d "$ROOTFS" && -f "$ARTIFACT" ]] && ! newer_input "$ARTIFACT"; then log "rootfs '$EDITION' is up to date — skipping rebuild ($ARTIFACT)" log " (FORCE=1 rebuilds anyway)" exit 0 fi rm -rf "$ROOTFS" "$ARTIFACT" log "═══ building Arcline $EDITION rootfs ($DISTRO_NAME $VERSION) ═══" # ── 1. bootstrap ──────────────────────────────────────────────────────────── log "[1/6] debootstrap $DEBIAN_SUITE ($ARCH)" debootstrap \ --arch="$ARCH" \ --variant=minbase \ --include=apt-transport-https,ca-certificates,gnupg,curl \ "$DEBIAN_SUITE" "$ROOTFS" "$DEBIAN_MIRROR" \ | tee "$LOG_DIR/bootstrap-$EDITION.log" # ── 2. apt sources ────────────────────────────────────────────────────────── log "[2/6] configuring apt sources" cat > "$ROOTFS/etc/apt/sources.list" </dev/null || true } unmount_pseudo() { umount -l "$ROOTFS/dev/pts" 2>/dev/null || true umount -l "$ROOTFS/sys" 2>/dev/null || true umount -l "$ROOTFS/proc" 2>/dev/null || true umount -l "$ROOTFS/dev" 2>/dev/null || true } trap 'unmount_pseudo' EXIT chroot_run() { chroot "$ROOTFS" /bin/bash -c "$*"; } # ── 3. install edition packages ───────────────────────────────────────────── log "[3/6] installing edition packages (${EDITION}, boot: $BOOT)" PKGS="$(grep -vE '^\s*(#|$)' "$EDIR/packages.list" | tr '\n' ' ')$BOOT_PKGS" mount_pseudo chroot_run "export DEBIAN_FRONTEND=noninteractive; apt-get update -qq && apt-get install -y --no-install-recommends $PKGS" \ | tee "$LOG_DIR/packages-$EDITION.log" unmount_pseudo # ── 4. overlays ───────────────────────────────────────────────────────────── log "[4/6] applying overlays" "$ROOT/scripts/apply-overlays.sh" "$ROOTFS" "$EDITION" # copy btrfs snapshot/rollback tooling into the image install -Dm0755 "$ROOT/btrfs/snapshot.sh" "$ROOTFS/usr/local/sbin/arcline-snapshot" install -Dm0755 "$ROOT/btrfs/rollback.sh" "$ROOTFS/usr/local/sbin/arcline-rollback" # ── 5. configure system in chroot ─────────────────────────────────────────── log "[5/6] configuring system in chroot" install -m0755 "$ROOT/scripts/configure-system.sh" "$ROOTFS/root/configure-system.sh" # Stage the edition manifest INSIDE the chroot (the hook runs in there and # cannot see host paths). mkdir -p "$ROOTFS/root/arcline-edition" cp "$EDIR/kernel.cmdline" "$EDIR/metadata.yaml" "$ROOTFS/root/arcline-edition/" # Toolchain policy — the Arcline tools are optional in an image build. See # ARCLINE_TOOLCHAIN in scripts/common.sh (auto | skip | require). HAVE_DEBS=0 if [[ -d "$DEB_DIR" ]] && ls "$DEB_DIR"/*.deb >/dev/null 2>&1; then HAVE_DEBS=1 fi case "$ARCLINE_TOOLCHAIN" in require) [[ $HAVE_DEBS -eq 1 ]] || die "ARCLINE_TOOLCHAIN=require but no .deb files in $DEB_DIR (run: make toolchain)" mkdir -p "$ROOTFS/arcline-debs" cp "$DEB_DIR"/*.deb "$ROOTFS/arcline-debs/" log "toolchain: staging $(ls "$DEB_DIR"/*.deb | wc -l) packages (required)" ;; skip) log "toolchain: skipped (ARCLINE_TOOLCHAIN=skip) — building without Arcline tools" ;; auto) if [[ $HAVE_DEBS -eq 1 ]]; then mkdir -p "$ROOTFS/arcline-debs" cp "$DEB_DIR"/*.deb "$ROOTFS/arcline-debs/" log "toolchain: staging $(ls "$DEB_DIR"/*.deb | wc -l) packages" else warn "no toolchain .debs in $DEB_DIR — building WITHOUT Arcline tools (run: make toolchain)" fi ;; *) die "ARCLINE_TOOLCHAIN must be auto|skip|require (got '$ARCLINE_TOOLCHAIN')" ;; esac mount_pseudo chroot_run "ARCLINE_EDITION_DIR='/root/arcline-edition' ARCLINE_LOCK_ROOT='${ARCLINE_LOCK_ROOT:-0}' ARCLINE_EXTRA_REPOS='${ARCLINE_EXTRA_REPOS:-0}' /root/configure-system.sh '$EDITION'" rm -f "$ROOTFS/root/configure-system.sh" rm -rf "$ROOTFS/root/arcline-edition" "$ROOTFS/arcline-debs" unmount_pseudo # ── 6. clean + archive ────────────────────────────────────────────────────── log "[6/6] cleaning and archiving" chroot_run "apt-get clean 2>/dev/null; rm -rf /var/lib/apt/lists/* /var/cache/apt/* /tmp/* /root/.bash_history" rm -f "$ROOTFS/etc/machine-id" : > "$ROOTFS/etc/machine-id" tar -C "$ROOTFS" -cJf "$ARTIFACT" . log "rootfs artifact: $ARTIFACT" sha256sum "$ARTIFACT" | tee "$ARTIFACT.sha256"