From 6269f2f87776a31d08611e649a343431769a30b3 Mon Sep 17 00:00:00 2001 From: mozempk Date: Thu, 23 Apr 2026 00:02:21 +0200 Subject: [PATCH] feat: add live desktop ISO build (stable-cinnamon Cinnamon live session) --- Makefile | 8 +- .../packages.live-desktop.list | 146 ++++++++ iso/_inner-build-live.sh | 65 ++++ iso/build-live-iso.sh | 337 ++++++++++++++++++ 4 files changed, 555 insertions(+), 1 deletion(-) create mode 100644 config/profiles/stable-cinnamon/packages.live-desktop.list create mode 100755 iso/_inner-build-live.sh create mode 100755 iso/build-live-iso.sh diff --git a/Makefile b/Makefile index f630a3d..38e7369 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,7 @@ # # Targets: # make iso build the auto-installing ISO (uses docker) +# make live build the full Cinnamon live desktop ISO # make test-disk create a fresh QEMU test disk that mimics XPS 17 layout # make test full automated QEMU smoke test # make test-iso rebuild only the TEST ISO variant @@ -14,7 +15,7 @@ PROJECT_DIR := $(CURDIR) OUT := $(PROJECT_DIR)/out SECRETS := $(PROJECT_DIR)/secrets.env -.PHONY: all iso test test-disk test-iso qemu shellcheck clean distclean check-secrets check-docker +.PHONY: all iso live test test-disk test-iso qemu shellcheck clean distclean check-secrets check-docker all: iso @@ -28,6 +29,9 @@ check-docker: iso: check-secrets check-docker $(PROJECT_DIR)/iso/build-iso.sh +live: check-secrets check-docker + $(PROJECT_DIR)/iso/build-live-iso.sh + test-iso: check-secrets check-docker REBUILD_ISO=1 $(PROJECT_DIR)/tests/run-qemu-test.sh @@ -48,6 +52,8 @@ shellcheck: $(PROJECT_DIR)/installer/lib/*.sh \ $(PROJECT_DIR)/iso/build-iso.sh \ $(PROJECT_DIR)/iso/_inner-build.sh \ + $(PROJECT_DIR)/iso/_inner-build-live.sh \ + $(PROJECT_DIR)/iso/build-live-iso.sh \ $(PROJECT_DIR)/tests/*.sh \ $(PROJECT_DIR)/tests/lib/*.sh diff --git a/config/profiles/stable-cinnamon/packages.live-desktop.list b/config/profiles/stable-cinnamon/packages.live-desktop.list new file mode 100644 index 0000000..8b40766 --- /dev/null +++ b/config/profiles/stable-cinnamon/packages.live-desktop.list @@ -0,0 +1,146 @@ +# Packages included in the LIVE desktop ISO squashfs. +# This is the full stable-cinnamon desktop environment — the live session +# boots directly into Cinnamon so you can test on real hardware. +# (Separate from packages.list which is the target-install list.) + +# --- base / boot --- +base-system +linux +linux-firmware +linux-firmware-network +intel-ucode +dracut + +# --- core userspace --- +sudo +bash +bash-completion +git +curl +wget +vim +nano +htop +tmux +unzip +zip +xz +rsync +pciutils +usbutils +lsof +file +which +man-pages +mdocml +ca-certificates +xtools +gptfdisk +parted +btrfs-progs +dosfstools + +# --- networking --- +NetworkManager +NetworkManager-openvpn +openssh +iwd +wpa_supplicant +chrony + +# --- audio --- +pipewire +wireplumber +alsa-pipewire +pavucontrol +alsa-utils + +# --- graphics / xorg --- +xorg-minimal +xorg-fonts +xorg-input-drivers +xf86-input-libinput +xf86-video-intel +mesa-dri +mesa-vulkan-intel +intel-video-accel +vulkan-loader + +# --- nvidia (PRIME offload) --- +nvidia +nvidia-libs-32bit +nvidia-vaapi-driver + +# --- desktop --- +cinnamon +xdg-user-dirs +xdg-utils +xdg-desktop-portal +xdg-desktop-portal-gtk +gvfs +gvfs-mtp +gvfs-smb +file-roller +gnome-keyring +seahorse +network-manager-applet +blueman +bluez + +# --- display manager --- +lightdm +lightdm-gtk3-greeter + +# --- fonts --- +noto-fonts-ttf +noto-fonts-emoji +noto-fonts-cjk +liberation-fonts-ttf +dejavu-fonts-ttf +font-awesome6 + +# --- terminal --- +alacritty + +# --- gtk theming deps --- +sassc +gnome-themes-extra +gtk-engine-murrine +dconf +dconf-editor + +# --- media / utilities --- +vlc +flameshot + +# --- containers --- +docker +docker-compose + +# --- nix package manager --- +nix + +# --- zram / swap --- +zramen + +# --- power / laptop --- +tlp +tlp-rdw +acpi +acpid +upower +brightnessctl + +# --- printing --- +cups +cups-filters +cups-pk-helper +system-config-printer + +# --- bluetooth --- +bluez-alsa + +# --- live session utils --- +dialog +ncurses +util-linux diff --git a/iso/_inner-build-live.sh b/iso/_inner-build-live.sh new file mode 100755 index 0000000..cd9e5fe --- /dev/null +++ b/iso/_inner-build-live.sh @@ -0,0 +1,65 @@ +#!/bin/bash +# Runs INSIDE the docker container (as root). Invoked by iso/build-live-iso.sh. +# Expects the project bind-mounted at /work and the cache at /cache. +# +# Required env (set by build-live-iso.sh): +# ARCH, REPO_URL, KEYMAP, LOCALE, ISO_PKGS, ISO_TITLE, OUT_ISO_REL, +# INCLUDE_DIR_REL + +set -Eeuo pipefail + +: "${ARCH:?}"; : "${REPO_URL:?}"; : "${KEYMAP:?}"; : "${LOCALE:?}" +: "${ISO_PKGS:?}"; : "${ISO_TITLE:?}"; : "${OUT_ISO_REL:?}" +: "${INCLUDE_DIR_REL:?}" + +CACHE_DIR=/cache +PROJECT_DIR=/work +MKLIVE_DIR="$CACHE_DIR/void-mklive" +INCLUDE_DIR="$PROJECT_DIR/$INCLUDE_DIR_REL" +OUT_ISO="$PROJECT_DIR/$OUT_ISO_REL" + +export PATH="$CACHE_DIR/xbps-static/usr/bin:$PATH" + +[[ -d "$MKLIVE_DIR" ]] || { echo "ERROR: $MKLIVE_DIR missing"; exit 1; } +[[ -d "$INCLUDE_DIR" ]] || { echo "ERROR: $INCLUDE_DIR missing"; exit 1; } +command -v xbps-install.static >/dev/null \ + || { echo "ERROR: xbps-install.static not on PATH"; exit 1; } + +mkdir -p "$(dirname "$OUT_ISO")" + +# Compile dconf system-db inside the include dir so it ships compiled. +if command -v dconf >/dev/null 2>&1 && [[ -d "$INCLUDE_DIR/etc/dconf/db/local.d" ]]; then + DCONF_PROFILE_PATH="$INCLUDE_DIR/etc/dconf/profile" \ + DCONF_SYSTEM_DB_PATH="$INCLUDE_DIR/etc/dconf/db" \ + dconf update "$INCLUDE_DIR/etc/dconf/db" 2>/dev/null || true +fi + +cd "$MKLIVE_DIR" + +_cleanup_mklive_builds() { + local d sub + for d in "$MKLIVE_DIR"/mklive-build.*/; do + [[ -d "$d" ]] || continue + for sub in tmp-rootfs/sys tmp-rootfs/proc tmp-rootfs/dev tmp-rootfs/run \ + image/rootfs/sys image/rootfs/proc image/rootfs/dev image/rootfs/run; do + [[ -d "$d$sub" ]] && umount -R --lazy "$d$sub" 2>/dev/null || true + done + rm -rf "$d" 2>/dev/null || true + done +} +trap _cleanup_mklive_builds EXIT + +./mklive.sh \ + -a "$ARCH" \ + -r "$REPO_URL" \ + -c "$CACHE_DIR/xbps-live-pkgs" \ + -H "$CACHE_DIR/xbps-host-pkgs" \ + -k "$KEYMAP" \ + -l "$LOCALE" \ + -T "$ISO_TITLE" \ + -p "$ISO_PKGS" \ + -I "$INCLUDE_DIR" \ + -C "${BOOT_CMDLINE:-}" \ + -o "$OUT_ISO" + +chown "$(stat -c '%u:%g' "$PROJECT_DIR")" "$OUT_ISO" "${OUT_ISO}".* 2>/dev/null || true diff --git a/iso/build-live-iso.sh b/iso/build-live-iso.sh new file mode 100755 index 0000000..10f4a47 --- /dev/null +++ b/iso/build-live-iso.sh @@ -0,0 +1,337 @@ +#!/bin/bash +# Build a full Cinnamon LIVE desktop ISO for testing on real hardware. +# +# Boots directly into a Cinnamon session as user 'live' (no password). +# All themes, icons, cursor, wallpapers, dotfiles and VS Code config are +# pre-applied from the same overlay used by the installer. +# +# Requires (host): bash, git, curl, docker, and Bibata-Modern-Ice cursor +# installed at /usr/share/icons/Bibata-Modern-Ice. +# +# Usage: +# iso/build-live-iso.sh +# OUTPUT_ISO=/path/to/output.iso iso/build-live-iso.sh + +set -Eeuo pipefail + +PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +CACHE_DIR="${CACHE_DIR:-$PROJECT_DIR/cache}" +OUT_DIR="${OUT_DIR:-$PROJECT_DIR/out}" +BUILD_DIR="${BUILD_DIR:-$PROJECT_DIR/build}" +INCLUDE_DIR="$BUILD_DIR/live-includes" +MKLIVE_DIR="$CACHE_DIR/void-mklive" +MKLIVE_REPO="${MKLIVE_REPO:-https://github.com/void-linux/void-mklive.git}" +MKLIVE_REF="${MKLIVE_REF:-master}" +PATCH_DIR="$PROJECT_DIR/iso/patches" +DOCKER_IMAGE="${DOCKER_IMAGE:-void-installer-builder:latest}" +DOCKER="${DOCKER:-docker}" +LIVE_USER="${LIVE_USER:-live}" + +# shellcheck disable=SC1091 +source "$PROJECT_DIR/config/install.conf" + +# Load profile defaults (GTK_THEME, CURSOR_THEME, ICON_THEME etc.) +# shellcheck disable=SC1091 +source "$PROJECT_DIR/config/profiles/stable-cinnamon/profile.conf" + +command -v "$DOCKER" >/dev/null \ + || { echo "ERROR: '$DOCKER' not in PATH"; exit 1; } +"$DOCKER" info >/dev/null 2>&1 \ + || { echo "ERROR: '$DOCKER' daemon unreachable"; exit 1; } + +mkdir -p "$CACHE_DIR" "$OUT_DIR" "$BUILD_DIR" + +# 1) clone + patch mklive +if [[ ! -d "$MKLIVE_DIR/.git" ]]; then + echo ">>> cloning void-mklive" + git clone --depth=1 --branch "$MKLIVE_REF" "$MKLIVE_REPO" "$MKLIVE_DIR" +fi +if compgen -G "$PATCH_DIR/*.patch" >/dev/null; then + echo ">>> resetting + applying iso/patches/" + ( cd "$MKLIVE_DIR" && git checkout -- . ) + for p in "$PATCH_DIR"/*.patch; do + echo " $(basename "$p")" + ( cd "$MKLIVE_DIR" && patch -p1 --silent < "$p" ) + done +fi + +# 2) xbps-static +XBPS_STATIC_DIR="$CACHE_DIR/xbps-static" +if [[ ! -x "$XBPS_STATIC_DIR/usr/bin/xbps-install.static" ]]; then + echo ">>> downloading xbps-static" + mkdir -p "$XBPS_STATIC_DIR" + curl -fsSL "https://repo-default.voidlinux.org/static/xbps-static-latest.x86_64-musl.tar.xz" \ + | tar xJf - -C "$XBPS_STATIC_DIR" +fi + +# 3) build includes overlay +echo ">>> staging live includes overlay at $INCLUDE_DIR" +rm -rf "$INCLUDE_DIR" +mkdir -p "$INCLUDE_DIR" + +# ── 3a) live user account ─────────────────────────────────────────────── +# Create user 'live' with no password in the live image. +# mklive populates /etc/passwd at install time — we drop a post-install hook +# via the live init that calls useradd. We use an install.conf.d override +# that mklive will source, plus an rc.local-style runit oneshot. +install -d -m 0755 "$INCLUDE_DIR/etc" +cat > "$INCLUDE_DIR/etc/passwd-live" < "$INCLUDE_DIR/etc/sv/live-user-setup/run" <<'SV_EOF' +#!/bin/sh +# Create the 'live' user (no password) and set up home directory. +# Runs once; removes itself from default runsvdir on completion. +set -e +if ! id live >/dev/null 2>&1; then + useradd -m -s /bin/bash -G wheel,audio,video,plugdev,input,network,docker live + passwd -d live + install -d -m 0700 /home/live/.ssh + # Apply skel dotfiles + if [ -d /etc/skel ]; then + cp -a /etc/skel/. /home/live/ 2>/dev/null || true + fi + chown -R live:live /home/live +fi +# Enable sudoers entry for live +if ! grep -q '^live ' /etc/sudoers 2>/dev/null; then + echo 'live ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers +fi +# Remove self from runsvdir so it doesn't restart +rm -f /etc/runit/runsvdir/default/live-user-setup +sv stop live-user-setup 2>/dev/null || true +SV_EOF +chmod 0755 "$INCLUDE_DIR/etc/sv/live-user-setup/run" +ln -sf /etc/sv/live-user-setup "$INCLUDE_DIR/etc/runit/runsvdir/default/live-user-setup" + +# ── 3b) LightDM autologin ─────────────────────────────────────────────── +install -d -m 0755 "$INCLUDE_DIR/etc/lightdm" +cat > "$INCLUDE_DIR/etc/lightdm/lightdm.conf" </dev/null || true +done + +# ── 3c) Themes / icons / wallpapers (same as installer) ───────────────── +echo ">>> staging customizations overlay" +OVERLAY="$INCLUDE_DIR/etc/installer-overlay" +install -d -m 0755 "$OVERLAY" "$OVERLAY/wallpapers" \ + "$OVERLAY/themes" "$OVERLAY/icons" \ + "$OVERLAY/skel" "$OVERLAY/vscode-user" + +# Wallpapers +WP_SRC="${WALLPAPERS_SRC:-$HOME/Scaricati}" +shopt -s nullglob +for f in "$WP_SRC"/pxfuel*.jpg; do + install -m 0644 "$f" "$OVERLAY/wallpapers/$(basename "$f")" +done +shopt -u nullglob +echo " wallpapers: $(ls "$OVERLAY/wallpapers" | wc -l) file(s)" + +# Gruvbox GTK theme +THEME_CACHE="$CACHE_DIR/gruvbox-gtk-theme" +THEME_BUILD="$CACHE_DIR/gruvbox-gtk-built" +if [[ ! -d "$THEME_CACHE/.git" ]]; then + git clone --depth=1 https://github.com/Fausto-Korpsvart/Gruvbox-GTK-Theme.git "$THEME_CACHE" || true +fi +if [[ -x "$THEME_CACHE/themes/install.sh" && ! -d "$THEME_BUILD" ]]; then + echo " building gruvbox themes" + install -d -m 0755 "$THEME_BUILD" + "$DOCKER" run --rm \ + -v "$THEME_CACHE":/src \ + -v "$THEME_BUILD":/out \ + debian:stable-slim sh -c ' + export DEBIAN_FRONTEND=noninteractive + apt-get update -qq >/dev/null + apt-get install -y --no-install-recommends sassc bash >/dev/null + cd /src/themes && bash install.sh -d /out -t default -c dark -s standard + ' || true +fi +if [[ -d "$THEME_BUILD" ]]; then + for d in "$THEME_BUILD"/Gruvbox-Dark*; do + [[ -d "$d" ]] && cp -a "$d" "$OVERLAY/themes/$(basename "$d")" + done + echo " themes: $(ls "$OVERLAY/themes" 2>/dev/null | wc -l) variant(s)" +fi + +# Gruvbox Plus icons +ICON_CACHE="$CACHE_DIR/gruvbox-plus-icons" +if [[ ! -d "$ICON_CACHE/.git" ]]; then + git clone --depth=1 https://github.com/SylEleuth/gruvbox-plus-icon-pack.git "$ICON_CACHE" || true +fi +if [[ -d "$ICON_CACHE/Gruvbox-Plus-Dark" ]]; then + cp -a "$ICON_CACHE/Gruvbox-Plus-Dark" "$OVERLAY/icons/Gruvbox-Plus-Dark" + echo " icons: Gruvbox-Plus-Dark" +fi + +# Bibata cursor +BIBATA_SRC="${BIBATA_SRC:-/usr/share/icons/Bibata-Modern-Ice}" +if [[ -d "$BIBATA_SRC" ]]; then + cp -a "$BIBATA_SRC" "$OVERLAY/icons/Bibata-Modern-Ice" + echo " cursor: Bibata-Modern-Ice" +fi + +# Dotfiles +DOTFILES_SRC="${DOTFILES_SRC:-$HOME}" +for f in .bashrc .bash_aliases .gitconfig; do + [[ -r "$DOTFILES_SRC/$f" ]] && install -m 0644 "$DOTFILES_SRC/$f" "$OVERLAY/skel/$f" +done + +# VS Code user config +VSCODE_SRC="${VSCODE_USER_SRC:-$HOME/.config/Code/User}" +if [[ -d "$VSCODE_SRC" ]]; then + for f in settings.json keybindings.json mcp.json tasks.json; do + [[ -r "$VSCODE_SRC/$f" ]] && install -m 0644 "$VSCODE_SRC/$f" "$OVERLAY/vscode-user/$f" + done + [[ -d "$VSCODE_SRC/snippets" ]] && cp -a "$VSCODE_SRC/snippets" "$OVERLAY/vscode-user/snippets" + [[ -d "$VSCODE_SRC/globalStorage" ]] && cp -a "$VSCODE_SRC/globalStorage" "$OVERLAY/vscode-user/globalStorage" +fi +if command -v code >/dev/null 2>&1; then + code --list-extensions > "$OVERLAY/vscode-extensions.txt" 2>/dev/null || true +fi + +# Claude config +CLAUDE_SRC="${CLAUDE_SRC:-$HOME/.claude}" +[[ -d "$CLAUDE_SRC" ]] && cp -a "$CLAUDE_SRC" "$OVERLAY/claude" +[[ -r "${HOME}/.claude.json" ]] && install -m 0600 "${HOME}/.claude.json" "$OVERLAY/claude.json" + +# first-login.sh +[[ -r "$PROJECT_DIR/installer/first-login.sh" ]] && \ + install -m 0755 "$PROJECT_DIR/installer/first-login.sh" "$OVERLAY/first-login.sh" + +# ── 3d) Pre-apply themes/dconf to the live user's home via /etc/skel ──── +# We pre-write the dconf system-db so it takes effect without a user build step. +install -d -m 0755 "$INCLUDE_DIR/etc/dconf/db/local.d" \ + "$INCLUDE_DIR/etc/dconf/profile" +cat > "$INCLUDE_DIR/etc/dconf/profile/user" <<'EOF' +user-db:user +system-db:local +EOF + +WALLPAPER_FILE=$(ls "$OVERLAY/wallpapers/"*.jpg 2>/dev/null | head -1 | xargs basename 2>/dev/null || echo "pxfuel.jpg") +cat > "$INCLUDE_DIR/etc/dconf/db/local.d/00-cinnamon" <t'] +EOF + +# ── 3e) Copy themes/icons/wallpapers directly into usr/share ──────────── +# (dconf compile happens inside the Docker container — see _inner-build-live.sh) +install -d -m 0755 "$INCLUDE_DIR/usr/share/backgrounds/void-installer" +cp -a "$OVERLAY/wallpapers"/. "$INCLUDE_DIR/usr/share/backgrounds/void-installer/" 2>/dev/null || true + +install -d -m 0755 "$INCLUDE_DIR/usr/share/themes" +[[ -d "$OVERLAY/themes" ]] && cp -a "$OVERLAY/themes"/. "$INCLUDE_DIR/usr/share/themes/" 2>/dev/null || true + +install -d -m 0755 "$INCLUDE_DIR/usr/share/icons" +[[ -d "$OVERLAY/icons" ]] && cp -a "$OVERLAY/icons"/. "$INCLUDE_DIR/usr/share/icons/" 2>/dev/null || true + +# ── 3f) first-login autostart + profile.d for the live user ───────────── +install -d -m 0755 "$INCLUDE_DIR/etc/xdg/autostart" +if [[ -r "$OVERLAY/first-login.sh" ]]; then + install -d -m 0755 "$INCLUDE_DIR/usr/local/libexec" + install -m 0755 "$OVERLAY/first-login.sh" "$INCLUDE_DIR/usr/local/libexec/first-login.sh" + cat > "$INCLUDE_DIR/etc/xdg/autostart/void-live-first-login.desktop" < "$INCLUDE_DIR/etc/profile.d/local-bin.sh" <<'EOF' +case ":$PATH:" in + *":$HOME/.local/bin:"*) ;; + *) export PATH="$HOME/.local/bin:$PATH" ;; +esac +EOF + +# ── 3g) Live-session skel: pre-wire .bash_profile for first-login ──────── +install -d -m 0755 "$INCLUDE_DIR/etc/skel" +cat > "$INCLUDE_DIR/etc/skel/.bash_profile" <<'EOF' +# Source .bashrc for interactive login shells. +[[ -f ~/.bashrc ]] && . ~/.bashrc + +# Auto-run user environment setup on first interactive login. +if [[ -z "$_FIRST_LOGIN_RAN" && -x /usr/local/libexec/first-login.sh \ + && ! -f "$HOME/.first-login-done" ]]; then + export _FIRST_LOGIN_RAN=1 + /usr/local/libexec/first-login.sh 2>&1 | tee -a "$HOME/.first-login.log" +fi +EOF + +# 4) build Docker image +echo ">>> building docker image $DOCKER_IMAGE" +if "$DOCKER" buildx version >/dev/null 2>&1; then + "$DOCKER" build -t "$DOCKER_IMAGE" "$PROJECT_DIR/iso" +else + DOCKER_BUILDKIT=0 "$DOCKER" build -t "$DOCKER_IMAGE" "$PROJECT_DIR/iso" +fi + +# 5) packages + output filename +ISO_PKGS=$(grep -vE '^\s*(#|$)' \ + "$PROJECT_DIR/config/profiles/stable-cinnamon/packages.live-desktop.list" \ + | tr '\n' ' ') +TS="$(date -u +%Y%m%d)" +OUT_ISO="${OUTPUT_ISO:-$OUT_DIR/void-live-stable-${TS}.iso}" + +echo ">>> running mklive.sh inside docker — output: $OUT_ISO" +"$DOCKER" run --rm --privileged \ + -v "$PROJECT_DIR:/work:rw" \ + -v "$CACHE_DIR:/cache:rw" \ + -e ARCH="${ARCH:-x86_64}" \ + -e REPO_URL="${REPO_URL:-https://repo-default.voidlinux.org/current}" \ + -e KEYMAP="${KEYMAP:-ch-fr_nodeadkeys}" \ + -e LOCALE="${LOCALE:-en_US.UTF-8}" \ + -e ISO_PKGS="$ISO_PKGS" \ + -e ISO_TITLE="Void Live (${GTK_THEME} / Cinnamon)" \ + -e OUT_ISO_REL="${OUT_ISO#$PROJECT_DIR/}" \ + -e BOOT_CMDLINE="${BOOT_CMDLINE:-}" \ + -e INCLUDE_DIR_REL="${INCLUDE_DIR#$PROJECT_DIR/}" \ + -e HOST_UID="$(id -u)" \ + -e HOST_GID="$(id -g)" \ + "$DOCKER_IMAGE" \ + bash /work/iso/_inner-build-live.sh + +echo +echo ">>> Live ISO built: $OUT_ISO" +sha256sum "$OUT_ISO" | tee "${OUT_ISO}.sha256"