Add dpack sign command, test runner, ISO builders, fix build errors

dpack fixes:
- Fixed missing SourceInfo fields in CRUX/Gentoo converters (git, branch,
  tag, commit, update_check fields added to struct initializers)
- Added 'sign' command: downloads source tarballs and computes real SHA256
  checksums, updating .toml definitions in-place. Replaces placeholder
  checksums. Usage: dpack sign zlib  or  dpack sign all

Testing:
- tests/run-tests.sh: comprehensive integration test runner for Arch Linux
  host. 7 test suites covering host env, dpack build/tests, package defs,
  toolchain scripts, kernel config, init system, and QEMU boot.
  Generates JSON + text reports for automated debugging.
  Usage: bash tests/run-tests.sh [--quick]

ISO builders:
- src/iso/build-iso-arch.sh: builds live ISO from Arch Linux host
  Creates rootfs from pre-built base system or busybox fallback,
  includes installer + dpack + package repos, UEFI-only boot
- src/iso/build-iso-darkforge.sh: builds live ISO from running DarkForge
  Snapshots the live system via rsync, creates redistributable ISO

Package repository (submodule updated):
- 14 new self-hosting packages: qemu, edk2-ovmf, squashfs-tools,
  xorriso, mtools, efibootmgr, efivar, rsync, lz4, nasm,
  neovim, htop, tmux, libevent
- Total: 138 packages across 4 repos

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-19 12:11:59 +01:00
parent 5fb597e2d6
commit 3a5c200a28
7 changed files with 910 additions and 1 deletions

234
src/iso/build-iso-arch.sh Executable file
View File

@@ -0,0 +1,234 @@
#!/bin/bash
# ============================================================================
# DarkForge Linux — ISO Builder (Arch Linux Host)
# ============================================================================
# Builds a bootable DarkForge live ISO from an Arch Linux host.
# This is the script you run on your workstation to create the installer media.
#
# Requirements (Arch Linux):
# sudo pacman -S squashfs-tools xorriso dosfstools mtools arch-install-scripts
# sudo pacman -S base-devel gcc make git wget curl
#
# What this does:
# 1. Creates a minimal root filesystem in a temp directory
# 2. Installs the DarkForge base system (from pre-built packages or chroot)
# 3. Includes the installer scripts, dpack binary, and package repos
# 4. Compresses to squashfs
# 5. Creates a UEFI-bootable hybrid ISO
#
# Usage:
# sudo bash src/iso/build-iso-arch.sh
#
# Output:
# darkforge-live.iso in the project root
# ============================================================================
set -euo pipefail
# Must be root for chroot/mount operations
if [ "$(id -u)" -ne 0 ]; then
echo "ERROR: This script must be run as root (for chroot/mount operations)."
echo "Usage: sudo bash $0"
exit 1
fi
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
BUILD_DIR="/tmp/darkforge-iso-build"
ROOTFS="${BUILD_DIR}/rootfs"
ISO_DIR="${BUILD_DIR}/iso"
ISO_OUTPUT="${PROJECT_ROOT}/darkforge-live.iso"
ISO_LABEL="DARKFORGE"
SQFS_COMP="zstd"
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m'
info() { echo -e "${CYAN}>>> $1${NC}"; }
ok() { echo -e "${GREEN}>>> $1${NC}"; }
warn() { echo -e "${YELLOW}!!! $1${NC}"; }
die() { echo -e "${RED}!!! $1${NC}"; exit 1; }
# --- Preflight --------------------------------------------------------------
info "DarkForge ISO Builder (Arch Linux host)"
echo ""
for tool in mksquashfs xorriso mkfs.fat mcopy; do
command -v "$tool" >/dev/null 2>&1 || die "Missing: $tool — install with pacman"
done
# --- Clean previous build ----------------------------------------------------
info "Cleaning previous build..."
rm -rf "${BUILD_DIR}"
mkdir -p "${ROOTFS}" "${ISO_DIR}"/{EFI/BOOT,LiveOS,boot}
# --- Create the live root filesystem -----------------------------------------
info "Creating live root filesystem..."
# Create FHS directory structure
mkdir -p "${ROOTFS}"/{bin,boot,dev,etc/{rc.d,sysconfig},home,lib,lib64,mnt,opt}
mkdir -p "${ROOTFS}"/{proc,root,run,sbin,srv,sys,tmp}
mkdir -p "${ROOTFS}"/usr/{bin,include,lib,lib64,sbin,share/{man,doc}}
mkdir -p "${ROOTFS}"/var/{cache,lib/{dpack/{db,repos}},log,lock,run,spool,tmp}
mkdir -p "${ROOTFS}"/install
# --- Check if we have a pre-built base system --------------------------------
BASE_SYSTEM="${PROJECT_ROOT}/build/base-system"
TOOLCHAIN_CHROOT="${LFS:-/mnt/darkforge}"
if [ -d "${BASE_SYSTEM}" ] && [ -f "${BASE_SYSTEM}/usr/bin/bash" ]; then
info "Copying pre-built base system from ${BASE_SYSTEM}..."
cp -a "${BASE_SYSTEM}"/* "${ROOTFS}"/
elif [ -d "${TOOLCHAIN_CHROOT}" ] && [ -f "${TOOLCHAIN_CHROOT}/usr/bin/bash" ]; then
info "Copying from toolchain chroot at ${TOOLCHAIN_CHROOT}..."
cp -a "${TOOLCHAIN_CHROOT}"/{usr,lib,lib64,bin,sbin,etc} "${ROOTFS}"/
else
warn "No pre-built base system found."
warn "Creating minimal live environment with busybox..."
# Fallback: use static busybox for a minimal live shell
if command -v busybox >/dev/null 2>&1; then
cp "$(which busybox)" "${ROOTFS}/bin/busybox"
# Create essential symlinks
for cmd in sh ash ls cat cp mv rm mkdir mount umount grep sed awk vi; do
ln -sf busybox "${ROOTFS}/bin/$cmd"
done
else
# Download static busybox
info "Downloading busybox..."
curl -fLo "${ROOTFS}/bin/busybox" \
"https://busybox.net/downloads/binaries/1.35.0-x86_64-linux-musl/busybox"
chmod +x "${ROOTFS}/bin/busybox"
for cmd in sh ls cat cp mv rm mkdir mount umount; do
ln -sf busybox "${ROOTFS}/bin/$cmd"
done
fi
# Copy essential libs from host
for lib in ld-linux-x86-64.so.2 libc.so.6 libm.so.6 libdl.so.2 libpthread.so.0; do
if [ -f "/usr/lib/$lib" ]; then
cp "/usr/lib/$lib" "${ROOTFS}/usr/lib/"
fi
done
fi
# --- Install DarkForge-specific files ----------------------------------------
info "Installing DarkForge configuration and tools..."
# Configs
cp "${PROJECT_ROOT}/configs/rc.conf" "${ROOTFS}/etc/"
cp "${PROJECT_ROOT}/configs/inittab" "${ROOTFS}/etc/"
cp "${PROJECT_ROOT}/configs/fstab.template" "${ROOTFS}/etc/fstab"
cp -a "${PROJECT_ROOT}/configs/rc.d/"* "${ROOTFS}/etc/rc.d/" 2>/dev/null || true
cp "${PROJECT_ROOT}/configs/zprofile" "${ROOTFS}/etc/skel/.zprofile" 2>/dev/null || true
# Override inittab for live mode (auto-login root)
cat > "${ROOTFS}/etc/inittab" << 'EOF'
id:3:initdefault:
si::sysinit:/etc/rc.d/rc.sysinit
l3:3:wait:/etc/rc.d/rc.multi
1:2345:respawn:/sbin/agetty --autologin root --noclear 38400 tty1 linux
2:2345:respawn:/sbin/agetty 38400 tty2 linux
ca::ctrlaltdel:/sbin/shutdown -r now
EOF
# Installer scripts
cp -a "${PROJECT_ROOT}/src/install/"* "${ROOTFS}/install/" 2>/dev/null || true
cp "${PROJECT_ROOT}/configs/zprofile" "${ROOTFS}/install/configs/zprofile" 2>/dev/null || true
mkdir -p "${ROOTFS}/install/configs"
# Live shell profile with installer prompt
cat > "${ROOTFS}/root/.bash_profile" << 'PROFILE'
echo ""
echo " ╔══════════════════════════════════════════╗"
echo " ║ DarkForge Linux Installer ║"
echo " ║ ║"
echo " ║ Type 'install' to begin installation ║"
echo " ║ Type 'shell' for a live shell ║"
echo " ╚══════════════════════════════════════════╝"
echo ""
alias install='/install/install.sh'
alias shell='exec /bin/bash --login'
PROFILE
# dpack binary
DPACK_BIN="${PROJECT_ROOT}/src/dpack/target/release/dpack"
if [ -f "$DPACK_BIN" ]; then
install -m755 "$DPACK_BIN" "${ROOTFS}/usr/bin/dpack"
ok "dpack binary installed"
else
warn "dpack binary not found — build it first: cd src/dpack && cargo build --release"
fi
# Package repos
cp -a "${PROJECT_ROOT}/src/repos/core" "${ROOTFS}/var/lib/dpack/repos/" 2>/dev/null || true
cp -a "${PROJECT_ROOT}/src/repos/extra" "${ROOTFS}/var/lib/dpack/repos/" 2>/dev/null || true
cp -a "${PROJECT_ROOT}/src/repos/desktop" "${ROOTFS}/var/lib/dpack/repos/" 2>/dev/null || true
cp -a "${PROJECT_ROOT}/src/repos/gaming" "${ROOTFS}/var/lib/dpack/repos/" 2>/dev/null || true
# --- Install kernel ----------------------------------------------------------
KERNEL_PATH=""
for kp in "${PROJECT_ROOT}/kernel/vmlinuz" "${PROJECT_ROOT}/build/vmlinuz" /boot/vmlinuz-linux; do
if [ -f "$kp" ]; then
KERNEL_PATH="$kp"
break
fi
done
if [ -n "$KERNEL_PATH" ]; then
cp "$KERNEL_PATH" "${ISO_DIR}/EFI/BOOT/BOOTX64.EFI"
ok "Kernel: ${KERNEL_PATH}"
else
warn "No kernel found — ISO will not be bootable!"
warn "Build the kernel first (Phase 4) or copy vmlinuz to kernel/vmlinuz"
echo "PLACEHOLDER" > "${ISO_DIR}/EFI/BOOT/BOOTX64.EFI"
fi
# --- Create squashfs ----------------------------------------------------------
info "Creating squashfs image..."
mksquashfs "${ROOTFS}" "${ISO_DIR}/LiveOS/rootfs.img" \
-comp "${SQFS_COMP}" -Xcompression-level 19 -b 1M \
-noappend -wildcards \
-e 'proc/*' 'sys/*' 'dev/*' 'run/*' 'tmp/*'
ok "squashfs: $(du -sh "${ISO_DIR}/LiveOS/rootfs.img" | cut -f1)"
# --- Create EFI boot image ---------------------------------------------------
info "Creating EFI boot image..."
ESP_IMG="${BUILD_DIR}/efiboot.img"
ESP_SIZE=8192 # 8MB
dd if=/dev/zero of="${ESP_IMG}" bs=1K count=${ESP_SIZE} 2>/dev/null
mkfs.fat -F 12 "${ESP_IMG}" >/dev/null
mmd -i "${ESP_IMG}" ::/EFI ::/EFI/BOOT
mcopy -i "${ESP_IMG}" "${ISO_DIR}/EFI/BOOT/BOOTX64.EFI" ::/EFI/BOOT/BOOTX64.EFI
# --- Build ISO ----------------------------------------------------------------
info "Building ISO..."
xorriso -as mkisofs \
-o "${ISO_OUTPUT}" \
-iso-level 3 \
-full-iso9660-filenames \
-joliet \
-rational-rock \
-volid "${ISO_LABEL}" \
-eltorito-alt-boot \
-e "$(basename "${ESP_IMG}")" \
-no-emul-boot \
-isohybrid-gpt-basdat \
-append_partition 2 0xef "${ESP_IMG}" \
"${ISO_DIR}"
# --- Done ---------------------------------------------------------------------
echo ""
ok "═══════════════════════════════════════════════"
ok " ISO built: ${ISO_OUTPUT}"
ok " Size: $(du -sh "${ISO_OUTPUT}" | cut -f1)"
ok ""
ok " Test with:"
ok " qemu-system-x86_64 -enable-kvm -m 4G \\"
ok " -bios ${OVMF_PATH:-/usr/share/edk2/x64/OVMF.fd} \\"
ok " -cdrom ${ISO_OUTPUT} -boot d"
ok "═══════════════════════════════════════════════"
# Cleanup
rm -rf "${BUILD_DIR}"

105
src/iso/build-iso-darkforge.sh Executable file
View File

@@ -0,0 +1,105 @@
#!/bin/bash
# ============================================================================
# DarkForge Linux — ISO Builder (DarkForge Host)
# ============================================================================
# Builds a bootable DarkForge live ISO from a running DarkForge system.
# Use this to create installer media for reinstalls or sharing.
#
# Requirements (DarkForge):
# dpack install squashfs-tools xorriso mtools
#
# Usage:
# sudo bash src/iso/build-iso-darkforge.sh
#
# Output:
# darkforge-live.iso
# ============================================================================
set -euo pipefail
if [ "$(id -u)" -ne 0 ]; then
echo "ERROR: Must be run as root."
exit 1
fi
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
BUILD_DIR="/tmp/darkforge-iso-build"
ROOTFS="${BUILD_DIR}/rootfs"
ISO_DIR="${BUILD_DIR}/iso"
ISO_OUTPUT="${PROJECT_ROOT}/darkforge-live.iso"
ISO_LABEL="DARKFORGE"
info() { echo ">>> $1"; }
ok() { echo ">>> $1"; }
die() { echo "!!! $1"; exit 1; }
for tool in mksquashfs xorriso mkfs.fat mcopy; do
command -v "$tool" >/dev/null 2>&1 || die "Missing: $tool"
done
rm -rf "${BUILD_DIR}"
mkdir -p "${ROOTFS}" "${ISO_DIR}"/{EFI/BOOT,LiveOS}
# --- Snapshot the running system into the live root --------------------------
info "Snapshotting running system..."
# Copy the entire installed system (excluding virtual fs and temp)
rsync -aAX --info=progress2 \
--exclude='/dev/*' \
--exclude='/proc/*' \
--exclude='/sys/*' \
--exclude='/tmp/*' \
--exclude='/run/*' \
--exclude='/mnt/*' \
--exclude='/media/*' \
--exclude='/lost+found' \
--exclude='/var/tmp/*' \
--exclude='/var/cache/dpack/sources/*' \
--exclude='/home/*/.*cache*' \
/ "${ROOTFS}/"
# Override inittab for live mode
cat > "${ROOTFS}/etc/inittab" << 'EOF'
id:3:initdefault:
si::sysinit:/etc/rc.d/rc.sysinit
l3:3:wait:/etc/rc.d/rc.multi
1:2345:respawn:/sbin/agetty --autologin root --noclear 38400 tty1 linux
2:2345:respawn:/sbin/agetty 38400 tty2 linux
ca::ctrlaltdel:/sbin/shutdown -r now
EOF
# Include installer
cp -a "${PROJECT_ROOT}/src/install/"* "${ROOTFS}/install/" 2>/dev/null || true
# Include package repos
mkdir -p "${ROOTFS}/var/lib/dpack/repos"
cp -a "${PROJECT_ROOT}/src/repos/"* "${ROOTFS}/var/lib/dpack/repos/" 2>/dev/null || true
# Copy kernel
cp /boot/vmlinuz "${ISO_DIR}/EFI/BOOT/BOOTX64.EFI" 2>/dev/null || \
cp /boot/vmlinuz-*-darkforge "${ISO_DIR}/EFI/BOOT/BOOTX64.EFI" 2>/dev/null || \
die "No kernel found in /boot/"
# --- Compress and build ISO ---------------------------------------------------
info "Creating squashfs..."
mksquashfs "${ROOTFS}" "${ISO_DIR}/LiveOS/rootfs.img" \
-comp zstd -Xcompression-level 19 -b 1M \
-noappend -wildcards -e 'proc/*' 'sys/*' 'dev/*' 'run/*' 'tmp/*'
info "Creating EFI boot image..."
ESP_IMG="${BUILD_DIR}/efiboot.img"
dd if=/dev/zero of="${ESP_IMG}" bs=1K count=8192 2>/dev/null
mkfs.fat -F 12 "${ESP_IMG}" >/dev/null
mmd -i "${ESP_IMG}" ::/EFI ::/EFI/BOOT
mcopy -i "${ESP_IMG}" "${ISO_DIR}/EFI/BOOT/BOOTX64.EFI" ::/EFI/BOOT/BOOTX64.EFI
info "Building ISO..."
xorriso -as mkisofs \
-o "${ISO_OUTPUT}" -iso-level 3 -full-iso9660-filenames -joliet -rational-rock \
-volid "${ISO_LABEL}" -eltorito-alt-boot \
-e "$(basename "${ESP_IMG}")" -no-emul-boot -isohybrid-gpt-basdat \
-append_partition 2 0xef "${ESP_IMG}" "${ISO_DIR}"
ok "ISO built: ${ISO_OUTPUT} ($(du -sh "${ISO_OUTPUT}" | cut -f1))"
rm -rf "${BUILD_DIR}"