From 1ed3189a93f98b50b6ac908c56e31aecfcfedeaf Mon Sep 17 00:00:00 2001 From: mozempk Date: Thu, 23 Apr 2026 14:49:01 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20bake=20everything=20into=20live=20ISO?= =?UTF-8?q?=20=E2=80=94=20no=20first-login=20script?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of downloading at first login, everything is ready at boot: - iso/build-live-iso.sh: * apply-live-settings.sh XDG autostart applies theme/wallpaper/terminal via gsettings at first Cinnamon login (reliable vs dconf binary format) * /etc/environment: XDG_DATA_DIRS includes nix profile so Cinnamon menu shows pre-baked nix apps immediately * /etc/profile.d/nix-prebaked.sh: PATH setup for terminal sessions * first-login.sh kept at /usr/local/libexec but NOT autostarted (manual use for Claude/NVM installs) * NIX_PACKAGES_PREBAKE passed to Docker build - iso/_inner-build-live.sh: * Pre-bake nix packages inside Docker before mklive.sh; copy /nix store into squashfs overlay; set /etc/skel/.nix-profile → store profile path * Cached at /cache/nix-prebake (keyed by package list md5) - iso/Dockerfile: add rsync (needed by nix prebake) - packages.live-desktop.list: add vscode + chromium (XBPS, no download) --- .../packages.live-desktop.list | 4 + iso/Dockerfile | 2 +- iso/_inner-build-live.sh | 60 +++++++++++ iso/build-live-iso.sh | 101 ++++++++++++++---- 4 files changed, 144 insertions(+), 23 deletions(-) diff --git a/config/profiles/stable-cinnamon/packages.live-desktop.list b/config/profiles/stable-cinnamon/packages.live-desktop.list index bffc4da..bc2ec55 100644 --- a/config/profiles/stable-cinnamon/packages.live-desktop.list +++ b/config/profiles/stable-cinnamon/packages.live-desktop.list @@ -114,6 +114,10 @@ gtk-engine-murrine dconf dconf-editor +# --- code editors / dev tools --- +vscode +chromium + # --- media / utilities --- vlc flameshot diff --git a/iso/Dockerfile b/iso/Dockerfile index 0665aef..bcc3c3e 100644 --- a/iso/Dockerfile +++ b/iso/Dockerfile @@ -10,7 +10,7 @@ ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y --no-install-recommends \ bash git curl ca-certificates xz-utils tar patch python3 \ mtools xorriso squashfs-tools dosfstools e2fsprogs \ - kmod dconf-cli \ + kmod dconf-cli rsync \ && rm -rf /var/lib/apt/lists/* # xbps-static is downloaded into /cache by the host script and added to PATH diff --git a/iso/_inner-build-live.sh b/iso/_inner-build-live.sh index 8f040b4..bc8a6bc 100755 --- a/iso/_inner-build-live.sh +++ b/iso/_inner-build-live.sh @@ -38,6 +38,66 @@ fi cd "$MKLIVE_DIR" +# ── Pre-bake nix packages ──────────────────────────────────────────────── +# Install the nix user packages inside the Docker container and bake the +# resulting /nix store directly into the squashfs overlay. The live session +# then boots with all apps already present — no network downloads needed. +# +# The store is cached at /cache/nix-prebake; only rebuilt when the package +# list changes (checked via an md5 key file). +if [[ -n "${NIX_PACKAGES_PREBAKE:-}" ]]; then + echo ">>> pre-baking nix packages" + read -r -a _NIX_PKGS <<< "$NIX_PACKAGES_PREBAKE" + + _NIX_CACHE="$CACHE_DIR/nix-prebake" + _CACHE_KEY="$_NIX_CACHE/.done.$(printf '%s\n' "${_NIX_PKGS[@]}" | sort | md5sum | cut -c1-8)" + + mkdir -p "$_NIX_CACHE" + + if [[ -f "$_CACHE_KEY" ]] && [[ -d "$_NIX_CACHE/store" ]] && [[ -f "$_NIX_CACHE/.profile-path" ]]; then + echo " restoring cached nix store ($(du -sh "$_NIX_CACHE/store" 2>/dev/null | cut -f1))" + mkdir -p /nix + rsync -a "$_NIX_CACHE/" /nix/ 2>&1 | tail -1 + else + echo " installing nix (single-user, no-daemon)..." + rm -rf /nix ~/.nix-profile ~/.nix-defexpr ~/.nix-channels + curl -fsSL https://nixos.org/nix/install | sh -s -- --no-daemon --no-channel-add + # shellcheck disable=SC1091 + . /root/.nix-profile/etc/profile.d/nix.sh + + export NIXPKGS_ALLOW_UNFREE=1 + echo " nix profile install: ${_NIX_PKGS[*]}" + nix profile install --impure "${_NIX_PKGS[@]}" 2>&1 + + # Save the profile store path so we can restore from cache next time + readlink -f /root/.nix-profile > "$_NIX_CACHE/.profile-path" + + # Cache the full /nix store + rsync -a /nix/ "$_NIX_CACHE/" 2>&1 | tail -1 + touch "$_CACHE_KEY" + echo " cached nix store: $(du -sh "$_NIX_CACHE/store" 2>/dev/null | cut -f1)" + fi + + # Stage the nix store into the squashfs overlay + echo " staging /nix into overlay ($(du -sh /nix/store 2>/dev/null | cut -f1))" + mkdir -p "$INCLUDE_DIR/nix" + rsync -a /nix/ "$INCLUDE_DIR/nix/" 2>&1 | tail -1 + + # /etc/skel/.nix-profile → the pre-baked store profile path. + # dracut's adduser.sh runs 'useradd -m' which copies skel → /home/live, + # so the live user gets a ready nix profile from the squashfs store. + _STORE_PROFILE=$(cat "$_NIX_CACHE/.profile-path" 2>/dev/null \ + || readlink -f /root/.nix-profile 2>/dev/null || echo "") + if [[ -n "$_STORE_PROFILE" && -d "$_STORE_PROFILE" ]]; then + mkdir -p "$INCLUDE_DIR/etc/skel" + ln -sf "$_STORE_PROFILE" "$INCLUDE_DIR/etc/skel/.nix-profile" + echo " skel/.nix-profile → $_STORE_PROFILE" + else + echo " WARNING: could not determine nix store profile path" + fi +fi +# ── end nix prebake ────────────────────────────────────────────────────── + _cleanup_mklive_builds() { local d sub for d in "$MKLIVE_DIR"/mklive-build.*/; do diff --git a/iso/build-live-iso.sh b/iso/build-live-iso.sh index b871b37..bbb29c3 100755 --- a/iso/build-live-iso.sh +++ b/iso/build-live-iso.sh @@ -485,21 +485,88 @@ Extensions=any; Dependencies=/usr/local/bin/code-open; EOF -# ── 3f) first-login autostart + profile.d for the live user ───────────── +# ── 3f) Live session baked-in configuration ────────────────────────────── +# No first-login script. Everything is baked in at build time. +# The apply-settings script runs once at first Cinnamon login to apply +# gsettings (100% reliable vs dconf system-db binary format variations). +install -d -m 0755 "$INCLUDE_DIR/usr/local/libexec" install -d -m 0755 "$INCLUDE_DIR/etc/xdg/autostart" + +# Keep first-login.sh available for MANUAL use (Claude, NVM installs) but +# do NOT autostart it. User can run it from a terminal if needed. 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/usr/local/libexec/apply-live-settings.sh" </dev/null)" || true + sleep 0.3 +done + +# GTK / Cinnamon DE theme +gsettings set org.cinnamon.desktop.interface gtk-theme '${GTK_THEME}' +gsettings set org.cinnamon.desktop.interface icon-theme '${ICON_THEME}' +gsettings set org.gnome.desktop.interface cursor-theme '${CURSOR_THEME}' +gsettings set org.cinnamon.theme name '${GTK_THEME}' + +# Wallpaper (first .jpg found in the backgrounds dir) +_WP=\$(ls /usr/share/backgrounds/void-installer/*.jpg 2>/dev/null | head -1 || echo '') +[[ -n "\$_WP" ]] && gsettings set org.cinnamon.desktop.background picture-uri "file://\$_WP" +[[ -n "\$_WP" ]] && gsettings set org.gnome.desktop.background picture-uri "file://\$_WP" + +# Default terminal +gsettings set org.cinnamon.desktop.default-applications.terminal exec '${DEFAULT_TERMINAL:-alacritty}' +gsettings set org.cinnamon.desktop.default-applications.terminal exec-arg '-e' +gsettings set org.gnome.desktop.default-applications.terminal exec '${DEFAULT_TERMINAL:-alacritty}' +gsettings set org.gnome.desktop.default-applications.terminal exec-arg '-e' + +# Keyboard layout (Swiss French) +gsettings set org.gnome.desktop.input-sources sources "[('xkb', '${KEYMAP:-ch+fr_nodeadkeys}')]" + +touch "\$DONE" +EOF +chmod 0755 "$INCLUDE_DIR/usr/local/libexec/apply-live-settings.sh" + +cat > "$INCLUDE_DIR/etc/xdg/autostart/void-live-settings.desktop" <<'DESK' [Desktop Entry] Type=Application -Name=Void Live First-Login Setup -Exec=/usr/local/libexec/first-login.sh +Name=Void Live Apply Settings +Exec=/usr/local/libexec/apply-live-settings.sh NoDisplay=true X-GNOME-Autostart-enabled=true OnlyShowIn=X-Cinnamon; -EOF +DESK + +# /etc/environment: XDG_DATA_DIRS includes the pre-baked nix profile so that +# Cinnamon's app menu picks up .desktop files from the nix store without +# requiring any PATH tricks in the graphical session. +cat > "$INCLUDE_DIR/etc/environment" <<'ENVEOF' +XDG_DATA_DIRS=/home/live/.nix-profile/share:/usr/local/share:/usr/share +ENVEOF + +# /etc/profile.d: PATH for interactive terminals (alacritty, etc.) +install -d -m 0755 "$INCLUDE_DIR/etc/profile.d" +cat > "$INCLUDE_DIR/etc/profile.d/nix-prebaked.sh" <<'NIXEOF' +# Pre-baked nix profile — set up PATH for interactive shells. +if [[ -d "${HOME:-}/.nix-profile/bin" ]]; then + case ":$PATH:" in + *":$HOME/.nix-profile/bin:"*) ;; + *) export PATH="$HOME/.nix-profile/bin:$PATH" ;; + esac fi +NIXEOF + + # ── 3f-ins) Void Linux installer in live image ─────────────────────────── # install.sh uses INSTALLER_DIR=$(dirname $0)/lib for its libraries, so we @@ -552,41 +619,30 @@ StartupNotify=true DESKEOF install -d -m 0755 "$INCLUDE_DIR/etc/profile.d" -cat > "$INCLUDE_DIR/etc/profile.d/local-bin.sh" <<'EOF' +cat >> "$INCLUDE_DIR/etc/profile.d/nix-prebaked.sh" <<'EOF' case ":$PATH:" in *":$HOME/.local/bin:"*) ;; *) export PATH="$HOME/.local/bin:$PATH" ;; esac EOF -# ── 3f-bis) Nix user packages list for first-login.sh ─────────────────── -# Write NIX_USER_PACKAGES (from install.conf) as a plain list so -# first-login.sh can read it without sourcing install.conf. +# nix-packages.list: kept so first-login.sh can still be run manually install -d -m 0755 "$INCLUDE_DIR/usr/local/libexec" { - for pkg in "${NIX_USER_PACKAGES[@]}"; do - echo "$pkg" - done + for pkg in "${NIX_USER_PACKAGES[@]}"; do echo "$pkg"; done } > "$INCLUDE_DIR/usr/local/libexec/nix-packages.list" -# Also set the env var so first-login.sh knows where to find it. cat > "$INCLUDE_DIR/etc/profile.d/nix-packages-path.sh" <<'EOF' export NIX_PACKAGES_FILE=/usr/local/libexec/nix-packages.list EOF -# ── 3g) Live-session skel: pre-wire .bash_profile for first-login ──────── +# ── 3g) Skel: .bash_profile sources .bashrc only (no first-login autorun) ── 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 @@ -618,6 +674,7 @@ echo ">>> running mklive.sh inside docker — output: $OUT_ISO" -e OUT_ISO_REL="${OUT_ISO#$PROJECT_DIR/}" \ -e BOOT_CMDLINE="${BOOT_CMDLINE:-}" \ -e INCLUDE_DIR_REL="${INCLUDE_DIR#$PROJECT_DIR/}" \ + -e NIX_PACKAGES_PREBAKE="${NIX_USER_PACKAGES[*]}" \ -e HOST_UID="$(id -u)" \ -e HOST_GID="$(id -g)" \ "$DOCKER_IMAGE" \