feat: scaffold Arcline OS build system

Scaffold the Arcline OS build system ("the wires"): a transparent,
auditable pipeline that turns a Debian bookworm base into hardened OS
images for the server, workstation, and cloud editions.

- Makefile orchestrates everything (make iso-<edition>, check, test,
  toolchain, clean); versions.mk is the single source of truth for
  versions and paths.
- scripts/ is the plain-bash pipeline: debootstrap -> install packages
  -> apply overlays -> in-chroot configure -> live ISO, plus a rootfs
  archive along the way.
- ARCLINE_TOOLCHAIN=auto|skip|require controls whether the 11 Go tools
  are bundled into an image (auto by default; minimal builds available
  via make iso-<edition>-minimal).
- GPL-3.0 licensed, sponsored by Arcline IT LLC.
This commit is contained in:
Blake Ridgway
2026-08-21 13:15:43 -05:00
commit 14e5ea9e1e
12 changed files with 765 additions and 0 deletions

112
scripts/configure-system.sh Executable file
View File

@@ -0,0 +1,112 @@
#!/usr/bin/env bash
# ─────────────────────────────────────────────────────────────────────────────
# Arcline OS — in-chroot system configuration
#
# scripts/configure-system.sh <edition>
#
# This script is executed INSIDE the chroot (build-rootfs.sh copies it in and
# runs it via chroot). It turns a raw debootstrap tree into an Arcline system:
# hostname, locale, kernel cmdline, enabled/masked services, optional live-boot,
# optional Arcline toolchain install, optional upstream repos (grafana/loki).
#
# It is idempotent and safe to re-run.
# ─────────────────────────────────────────────────────────────────────────────
set -euo pipefail
EDITION="${1:?usage: configure-system.sh <edition>}"
DISTRO_NAME="${DISTRO_NAME:-Arcline OS}"
VERSION="${VERSION:-0.1.0}"
RELEASE_NAME="${RELEASE_NAME:-arclines}"
LIVE="${ARCLINE_LIVE:-0}"
LOCK_ROOT="${ARCLINE_LOCK_ROOT:-0}"
EXTRA_REPOS="${ARCLINE_EXTRA_REPOS:-0}"
# Absolute path of the edition dir on the host is injected by build-rootfs.sh.
EDIR="${ARCLINE_EDITION_DIR:?ARCLINE_EDITION_DIR must be set}"
log() { printf '\033[1;34m[arcline:chroot]\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m[arcline:chroot]\033[0m %s\n' "$*" >&2; }
# Read a flat YAML list (key: then indented "- item" lines). Keys may be
# nested under a section (e.g. services: → enabled:).
yaml_list() {
awk -v key="$2" '
$0 ~ "^[[:space:]]*" key ":" { insec=1; next }
insec && /^[[:space:]]*-/ { sub(/^[[:space:]]*-[[:space:]]*/, ""); print; next }
insec && !/^[[:space:]]*-/ && !/^[[:space:]]*$/ { exit }
' "$1"
}
# ── identity ────────────────────────────────────────────────────────────────
echo "$RELEASE_NAME" > /etc/hostname
cat > /etc/arcline-release <<EOF
$DISTRO_NAME $VERSION ($RELEASE_NAME)
Edition: $EDITION
Debian base: $(. /etc/os-release && echo "$PRETTY_NAME")
EOF
ln -sf /etc/arcline-release /etc/os-release-arcline
# ── locale / timezone ───────────────────────────────────────────────────────
sed -i 's/^# *en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen
locale-gen >/dev/null 2>&1 || true
echo "LANG=en_US.UTF-8" > /etc/default/locale
ln -sf /usr/share/zoneinfo/UTC /etc/localtime
# ── kernel cmdline (used when this rootfs is installed to disk) ─────────────
KCMD="$(tr '\n' ' ' < "$EDIR/kernel.cmdline" | sed 's/ */ /g; s/^ *//; s/ *$//')"
cat > /etc/default/grub <<EOF
GRUB_DEFAULT=0
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$DISTRO_NAME"
GRUB_CMDLINE_LINUX_DEFAULT="$KCMD"
GRUB_CMDLINE_LINUX=""
GRUB_DISABLE_OS_PROBER=false
EOF
# ── systemd services ────────────────────────────────────────────────────────
META="$EDIR/metadata.yaml"
while IFS= read -r svc; do
[[ -n "$svc" ]] && systemctl enable "$svc" 2>/dev/null || true
done < <(yaml_list "$META" "enabled" || true)
while IFS= read -r svc; do
[[ -n "$svc" ]] && systemctl mask "$svc" 2>/dev/null || true
done < <(yaml_list "$META" "masked" || true)
# Zero-telemetry: apt's automatic update calls are masked in metadata; also
# ensure no package telemetry survives.
rm -f /var/log/apt/*.log /var/cache/apt/archives/*.deb
# ── live-boot (ISO builds) ──────────────────────────────────────────────────
if [[ "$LIVE" == "1" ]]; then
log "installing live-boot support"
export DEBIAN_FRONTEND=noninteractive
apt-get install -y --no-install-recommends live-boot live-config-systemd live-tools || warn "live-boot install failed"
fi
# ── Arcline toolchain (host-built .debs) ────────────────────────────────────
if [[ -d /arcline-debs ]] && ls /arcline-debs/*.deb >/dev/null 2>&1; then
log "installing Arcline toolchain packages"
export DEBIAN_FRONTEND=noninteractive
apt-get install -y /arcline-debs/*.deb 2>/dev/null \
|| dpkg -i /arcline-debs/*.deb 2>/dev/null \
|| warn "toolchain install incomplete (fix with: apt-get -f install)"
fi
# ── optional upstream repos (grafana, loki) ─────────────────────────────────
if [[ "$EXTRA_REPOS" == "1" ]]; then
log "adding upstream observability repos (grafana, loki)"
install -d /usr/share/keyrings
curl -fsSL https://apt.grafana.com/gpg.key -o /usr/share/keyrings/grafana.asc 2>/dev/null || warn "grafana key fetch failed"
echo "deb [signed-by=/usr/share/keyrings/grafana.asc] https://apt.grafana.com stable main" > /etc/apt/sources.list.d/grafana.list
# Loki ships as a static binary tarball; the packaging lives in toolchain/.
# (No-op here; documented in docs/observability.md.)
fi
# ── root account policy ─────────────────────────────────────────────────────
if [[ "$LOCK_ROOT" == "1" ]]; then
passwd -l root
log "root account locked (sudo/ssh-key access only)"
fi
log "configure-system.sh complete for edition '$EDITION'"