Files
void-installer/iso/build-niri-live-iso.sh
2026-04-25 13:23:49 +02:00

552 lines
21 KiB
Bash
Executable File

#!/bin/bash
# Build a niri/Wayland LIVE desktop ISO (mainline-niri profile).
#
# Boots directly into a niri session with noctalia-shell as user 'live'
# (no password). agetty autologin on tty1 → .bash_profile → dbus-run-session niri.
# All themes, wallpapers, and the
# void-installer are pre-baked into the squashfs.
#
# Requires (host): bash, git, curl, docker, and Bibata-Modern-Ice cursor
# installed at /usr/share/icons/Bibata-Modern-Ice.
#
# Usage:
# iso/build-niri-live-iso.sh
# OUTPUT_ISO=/path/to/output.iso iso/build-niri-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/niri-live-includes"
MKLIVE_DIR="$CACHE_DIR/void-mklive-niri" # separate clone — avoids race with Cinnamon parallel build
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 niri profile settings (KERNEL_PKG, GTK_THEME, CURSOR_THEME, etc.)
# shellcheck disable=SC1091
source "$PROJECT_DIR/config/profiles/mainline-niri/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 (shared with Cinnamon build)
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 (shared cache)
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 (separate dir from Cinnamon build)
echo ">>> staging niri live includes overlay at $INCLUDE_DIR"
# The nix store (staged by Docker/root) uses 444/555 permissions — chmod first.
chmod -R u+rwX "$INCLUDE_DIR" 2>/dev/null || true
rm -rf "$INCLUDE_DIR"
mkdir -p "$INCLUDE_DIR"
install -d -m 0755 "$INCLUDE_DIR/etc"
# ── 3a) greetd config (fallback TUI on tty2) + agetty autologin on tty1 ─
# greetd's initial_session is not used: its PAM session setup fails silently
# in the live environment (pam_elogind ENOSYS, missing D-Bus session bus).
# Instead we autologin via agetty on tty1 and launch niri from .bash_profile.
install -d -m 0755 "$INCLUDE_DIR/etc/greetd"
cat > "$INCLUDE_DIR/etc/greetd/config.toml" <<'EOF'
[terminal]
vt = 2
[default_session]
command = "tuigreet --time --greeting 'Void Linux Live (niri)' --cmd 'niri --session'"
user = "_greeter"
EOF
# agetty-tty1 autologin: override conf so agetty-tty1 (mklive's default sv)
# automatically logs in the live user on tty1 without racing with a custom sv.
install -d -m 0755 "$INCLUDE_DIR/etc/sv/agetty-tty1"
cat > "$INCLUDE_DIR/etc/sv/agetty-tty1/conf" <<EOF
if [ -x /sbin/agetty -o -x /bin/agetty ]; then
if [ "\${tty}" = "tty1" ]; then
GETTY_ARGS="--noclear --autologin ${LIVE_USER}"
fi
fi
BAUD_RATE=38400
TERM_NAME=linux
EOF
# ── 3b) noctalia XBPS repo ──────────────────────────────────────────────
install -d -m 0755 "$INCLUDE_DIR/etc/xbps.d"
cat > "$INCLUDE_DIR/etc/xbps.d/10-noctalia.conf" <<'EOF'
repository=https://universalrepo.r1xelelo.workers.dev/void
EOF
# ── 3c) live-setup.sh (runs from runit/2 before any service starts) ────
install -d -m 0755 "$INCLUDE_DIR/etc/runit"
cat > "$INCLUDE_DIR/etc/runit/live-setup.sh" <<'SV_EOF'
#!/bin/sh
# Niri live session setup. Runs from /etc/runit/2 before services start.
LIVE_USER="${USERNAME:-live}"
[ -f /etc/default/live.conf ] && . /etc/default/live.conf
LIVE_USER="${LIVE_USER:-live}"
# Extra groups (dracut only adds audio,video,wheel)
for g in plugdev input network video audio _seatd; do
groupadd -f "$g" 2>/dev/null || true
usermod -aG "$g" "$LIVE_USER" 2>/dev/null || true
done
install -d -m 0755 /etc/sudoers.d
printf '%s ALL=(ALL) NOPASSWD: ALL\n' "$LIVE_USER" > /etc/sudoers.d/live
chmod 0440 /etc/sudoers.d/live
# nsswitch: remove mdns (library absent on Void; causes DNS lookup hangs)
if [ -f /etc/nsswitch.conf ]; then
sed -i '/^hosts:/s/mdns[^ ]* *//g' /etc/nsswitch.conf
fi
# Fallback DNS (QEMU's 10.0.2.3 is unreliable; NM will overwrite once DHCP settles)
if ! grep -q '^nameserver' /etc/resolv.conf 2>/dev/null || \
grep -q '^nameserver 10\.0\.2\.3' /etc/resolv.conf 2>/dev/null; then
printf 'nameserver 8.8.8.8\nnameserver 1.1.1.1\n' > /etc/resolv.conf
fi
# Ensure XDG_RUNTIME_DIR exists for the live user (greetd + elogind handle
# this normally, but set it here as a safety net).
XDG_RUN="/run/user/$(id -u "$LIVE_USER" 2>/dev/null || echo 1000)"
install -d -m 0700 "$XDG_RUN" 2>/dev/null || true
chown "$LIVE_USER" "$XDG_RUN" 2>/dev/null || true
# Start elogind here, once, before runsvdir brings up greetd.
# Runit does NOT supervise it — this avoids cgroup-race restart spam.
# We wait until dbus is available (dbus service starts first in runsvdir),
# then start elogind as a background daemon.
# NOTE: live-setup.sh runs BEFORE runsvdir, so dbus isn't up yet here.
# We start elogind from a one-shot at-startup hook instead — see runit/2.
echo "niri live-setup: done (user=$LIVE_USER)"
SV_EOF
chmod 0755 "$INCLUDE_DIR/etc/runit/live-setup.sh"
# runit/2: standard Void runit stage 2 — runs live-setup then hands off to runsvdir
cat > "$INCLUDE_DIR/etc/runit/2" <<'SV_EOF'
#!/bin/sh
PATH=/usr/bin:/usr/sbin
# Live session setup: groups, sudo, DNS
[ -x /etc/runit/live-setup.sh ] && /etc/runit/live-setup.sh
# Select runlevel from cmdline (default: default)
runlevel=default
for arg in $(cat /proc/cmdline); do
if [ -d /etc/runit/runsvdir/"$arg" ]; then
runlevel="$arg"
fi
done
[ -x /etc/rc.local ] && /etc/rc.local
runsvchdir "${runlevel}"
mkdir -p /run/runit/runsvdir
ln -sf /etc/runit/runsvdir/current /run/runit/runsvdir/current
exec env - PATH=$PATH \
runsvdir -P /run/runit/runsvdir/current \
'log: ...........................................................................................................................................................................................................................................................................................................................................................................................................'
SV_EOF
chmod 0755 "$INCLUDE_DIR/etc/runit/2"
# Enable services for the niri live session
install -d -m 0755 "$INCLUDE_DIR/etc/runit/runsvdir/default"
# Custom elogind sv: uses correct binary path and waits for dbus socket before
# starting (prevents "elogind is already running" spam from rapid runit restarts).
install -d -m 0755 "$INCLUDE_DIR/etc/sv/elogind"
cat > "$INCLUDE_DIR/etc/sv/elogind/run" <<'EOF'
#!/bin/sh
exec 2>&1
# Wait for dbus socket — poll every second, up to 30s
i=0
while [ $i -lt 30 ] && [ ! -S /run/dbus/system_bus_socket ]; do
sleep 1; i=$((i+1))
done
exec /usr/libexec/elogind/elogind.wrapper
EOF
chmod 0755 "$INCLUDE_DIR/etc/sv/elogind/run"
# finish: rate-limit restarts to avoid "already running" spam
cat > "$INCLUDE_DIR/etc/sv/elogind/finish" <<'EOF'
#!/bin/sh
sleep 3
EOF
chmod 0755 "$INCLUDE_DIR/etc/sv/elogind/finish"
for svc in dbus elogind NetworkManager sshd; do
ln -sf "/etc/sv/$svc" "$INCLUDE_DIR/etc/runit/runsvdir/default/$svc"
done
# ── 3d) Wayland environment ─────────────────────────────────────────────
install -d -m 0755 "$INCLUDE_DIR/etc/profile.d"
cat > "$INCLUDE_DIR/etc/profile.d/wayland.sh" <<'EOF'
# Wayland defaults (mainline-niri live session)
export QT_QPA_PLATFORM="wayland;xcb"
export GDK_BACKEND="wayland,x11"
export MOZ_ENABLE_WAYLAND=1
export _JAVA_AWT_WM_NONREPARENTING=1
export XDG_CURRENT_DESKTOP=niri
export XDG_SESSION_TYPE=wayland
# Use elogind's logind backend — works correctly on Void/runit via audit session IDs
export LIBSEAT_BACKEND=logind
EOF
chmod 0644 "$INCLUDE_DIR/etc/profile.d/wayland.sh"
# Nix profile.d: adds ~/.nix-profile/bin to PATH for interactive shells
cat > "$INCLUDE_DIR/etc/profile.d/nix-prebaked.sh" <<'EOF'
# Pre-baked nix profile — expose nix package binaries
if [[ -d "${HOME:-}/.nix-profile/bin" ]]; then
case ":$PATH:" in
*":$HOME/.nix-profile/bin:"*) ;;
*) export PATH="$HOME/.nix-profile/bin:$PATH" ;;
esac
fi
EOF
chmod 0644 "$INCLUDE_DIR/etc/profile.d/nix-prebaked.sh"
# Nix daemon config (trusted live user so nix commands work without root)
install -d -m 0755 "$INCLUDE_DIR/etc/nix"
cat > "$INCLUDE_DIR/etc/nix/nix.conf" <<EOF
experimental-features = nix-command flakes
sandbox = false
auto-optimise-store = true
trusted-users = root ${LIVE_USER}
max-jobs = 2
http-connections = 10
EOF
# ── 3e) niri config.kdl in /etc/skel ───────────────────────────────────
# dracut's adduser.sh copies skel → /home/live, so the live user gets a
# ready niri config without any first-boot setup step.
# Pre-bake SSH authorized_keys from host so passwordless SSH just works in QEMU tests.
if _HOST_PUBKEY=$(ssh-add -L 2>/dev/null | head -1) && [ -n "$_HOST_PUBKEY" ]; then
install -d -m 0700 "$INCLUDE_DIR/etc/skel/.ssh"
echo "$_HOST_PUBKEY" > "$INCLUDE_DIR/etc/skel/.ssh/authorized_keys"
chmod 0600 "$INCLUDE_DIR/etc/skel/.ssh/authorized_keys"
fi
KEYMAP_XKB_LAYOUT="${KEYMAP%%-*}" # ch-fr_nodeadkeys → ch
KEYMAP_XKB_VARIANT="${KEYMAP#*-}" # ch-fr_nodeadkeys → fr_nodeadkeys
KEYMAP_XKB_VARIANT="${KEYMAP_XKB_VARIANT//_nodeadkeys/}" # strip trailing _nodeadkeys
install -d -m 0755 "$INCLUDE_DIR/etc/skel/.config/niri"
cat > "$INCLUDE_DIR/etc/skel/.config/niri/config.kdl" <<EOF
// niri config — generated by void-installer (mainline-niri live session).
input {
keyboard {
xkb {
layout "${KEYMAP_XKB_LAYOUT}"
variant "${KEYMAP_XKB_VARIANT}"
}
}
touchpad {
tap
natural-scroll
dwt
}
mouse {
accel-speed 0.0
}
}
layout {
gaps 12
center-focused-column "never"
preset-column-widths {
proportion 0.33333
proportion 0.5
proportion 0.66667
}
default-column-width { proportion 0.5; }
focus-ring {
width 2
active-color "#fabd2f"
inactive-color "#3c3836"
}
border { off; }
}
prefer-no-csd
cursor {
xcursor-theme "${CURSOR_THEME:-Bibata-Modern-Ice}"
xcursor-size 24
}
// Audio / screen session services — started by niri as the live user
spawn-at-startup "pipewire"
spawn-at-startup "pipewire-pulse"
spawn-at-startup "wireplumber"
// Background, notifications, network, bluetooth, auth
spawn-at-startup "swaybg" "-i" "/usr/share/backgrounds/void-installer/pxfuel.jpg" "-m" "fill"
spawn-at-startup "mako"
spawn-at-startup "nm-applet" "--indicator"
spawn-at-startup "blueman-applet"
spawn-at-startup "/usr/libexec/polkit-gnome-authentication-agent-1"
// noctalia-shell (Quickshell-based Wayland shell)
spawn-at-startup "quickshell" "-c" "noctalia-shell"
binds {
Mod+T { spawn "alacritty"; }
Mod+D { spawn "fuzzel"; }
Mod+Q { close-window; }
Mod+Shift+E { quit; }
Print { screenshot; }
Mod+H { focus-column-left; }
Mod+L { focus-column-right; }
Mod+J { focus-window-down; }
Mod+K { focus-window-up; }
Mod+Shift+H { move-column-left; }
Mod+Shift+L { move-column-right; }
Mod+1 { focus-workspace 1; }
Mod+2 { focus-workspace 2; }
Mod+3 { focus-workspace 3; }
Mod+4 { focus-workspace 4; }
Mod+Shift+1 { move-column-to-workspace 1; }
Mod+Shift+2 { move-column-to-workspace 2; }
XF86AudioRaiseVolume { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "5%+"; }
XF86AudioLowerVolume { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "5%-"; }
XF86AudioMute { spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SINK@" "toggle"; }
XF86MonBrightnessUp { spawn "brightnessctl" "set" "+5%"; }
XF86MonBrightnessDown { spawn "brightnessctl" "set" "5%-"; }
}
EOF
# ── 3f) Themes / icons / wallpapers overlay ─────────────────────────────
echo ">>> staging niri customizations overlay"
OVERLAY="$INCLUDE_DIR/etc/installer-overlay"
install -d -m 0755 "$OVERLAY" "$OVERLAY/wallpapers" \
"$OVERLAY/themes" "$OVERLAY/icons"
# 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" 2>/dev/null | wc -l) file(s)"
# Gruvbox GTK theme (for GTK apps running under niri)
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
# Copy wallpapers and assets into usr/share (rootfs overlay)
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
# ── 3g) GTK settings for Wayland apps ───────────────────────────────────
# Write GTK2/3/4 settings to skel so the live user picks them up.
install -d -m 0755 "$INCLUDE_DIR/etc/skel/.config/gtk-3.0"
install -d -m 0755 "$INCLUDE_DIR/etc/skel/.config/gtk-4.0"
cat > "$INCLUDE_DIR/etc/skel/.config/gtk-3.0/settings.ini" <<EOF
[Settings]
gtk-theme-name=${GTK_THEME}
gtk-icon-theme-name=${ICON_THEME}
gtk-cursor-theme-name=${CURSOR_THEME:-Bibata-Modern-Ice}
gtk-cursor-theme-size=24
gtk-font-name=Noto Sans 11
EOF
cp "$INCLUDE_DIR/etc/skel/.config/gtk-3.0/settings.ini" \
"$INCLUDE_DIR/etc/skel/.config/gtk-4.0/settings.ini"
install -d -m 0755 "$INCLUDE_DIR/etc/skel"
cat > "$INCLUDE_DIR/etc/skel/.gtkrc-2.0" <<EOF
gtk-theme-name="${GTK_THEME}"
gtk-icon-theme-name="${ICON_THEME}"
gtk-cursor-theme-name="${CURSOR_THEME:-Bibata-Modern-Ice}"
gtk-cursor-theme-size=24
gtk-font-name="Noto Sans 11"
EOF
# XDG_DATA_DIRS so Wayland apps pick up installed theme/icon .desktop files
cat > "$INCLUDE_DIR/etc/environment" <<'ENVEOF'
XDG_DATA_DIRS=/usr/local/share:/usr/share
QT_QPA_PLATFORM=wayland;xcb
GDK_BACKEND=wayland,x11
MOZ_ENABLE_WAYLAND=1
LIBSEAT_BACKEND=logind
ENVEOF
# ── 3h) .bash_profile: source .bashrc + launch niri on tty1 ───────────
# agetty autologin on tty1 runs a login shell; .bash_profile execs niri
# via dbus-run-session so it gets a D-Bus session bus.
cat > "$INCLUDE_DIR/etc/skel/.bash_profile" <<'EOF'
[[ -f ~/.bashrc ]] && . ~/.bashrc
if [[ "$(tty)" == /dev/tty1 ]] && [[ -z "$WAYLAND_DISPLAY" ]] && [[ -z "$NIRI_SOCKET" ]]; then
exec dbus-run-session -- niri --session
fi
EOF
# ── 3i) Void Linux installer baked in ───────────────────────────────────
install -d -m 0755 "$INCLUDE_DIR/usr/local/lib/void-installer/lib"
install -d -m 0755 "$INCLUDE_DIR/usr/local/share/installer"
install -m 0755 "$PROJECT_DIR/installer/install.sh" \
"$INCLUDE_DIR/usr/local/lib/void-installer/install.sh"
for f in "$PROJECT_DIR/installer/lib/"*.sh; do
install -m 0755 "$f" \
"$INCLUDE_DIR/usr/local/lib/void-installer/lib/$(basename "$f")"
done
install -m 0644 "$PROJECT_DIR/config/install.conf" \
"$INCLUDE_DIR/usr/local/share/installer/install.conf"
install -m 0644 "$PROJECT_DIR/config/profiles/mainline-niri/packages.list" \
"$INCLUDE_DIR/usr/local/share/installer/packages.list"
if [[ -d "$PROJECT_DIR/config/profiles" ]]; then
cp -r "$PROJECT_DIR/config/profiles" \
"$INCLUDE_DIR/usr/local/share/installer/profiles"
fi
install -d -m 0755 "$INCLUDE_DIR/usr/local/bin"
cat > "$INCLUDE_DIR/usr/local/bin/void-install" <<'WRAPEOF'
#!/bin/sh
exec /usr/local/lib/void-installer/install.sh "$@"
WRAPEOF
chmod 0755 "$INCLUDE_DIR/usr/local/bin/void-install"
_SECRETS_SRC="${SECRETS_ENV:-$PROJECT_DIR/secrets.env}"
if [[ -r "$_SECRETS_SRC" ]]; then
install -d -m 0755 "$INCLUDE_DIR/etc"
install -m 0600 "$_SECRETS_SRC" "$INCLUDE_DIR/etc/installer-secrets.env"
echo " baked installer secrets from $_SECRETS_SRC"
else
echo " WARNING: no secrets.env found — installer will prompt at runtime"
fi
install -d -m 0755 "$INCLUDE_DIR/usr/share/applications"
cat > "$INCLUDE_DIR/usr/share/applications/void-installer.desktop" <<'DESKEOF'
[Desktop Entry]
Version=1.0
Type=Application
Name=Install Void Linux
Comment=Install Void Linux to this machine
Exec=alacritty --title "Void Linux Installer" -e sudo /usr/local/bin/void-install
Icon=system-software-install
Terminal=false
Categories=System;
StartupNotify=true
DESKEOF
# 4) build Docker image (reuse the same image as Cinnamon build)
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/mainline-niri/packages.live-desktop.list" \
| tr '\n' ' ')
TS="$(date -u +%Y%m%d)"
OUT_ISO="${OUTPUT_ISO:-$OUT_DIR/void-live-niri-${TS}.iso}"
BOOT_CMDLINE="${BOOT_CMDLINE:-live.user=${LIVE_USER} console=tty0 console=ttyS0,115200}"
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:-us}" \
-e LOCALE="${LOCALE:-en_US.UTF-8}" \
-e ISO_PKGS="$ISO_PKGS" \
-e ISO_TITLE="Void Live (niri / noctalia-shell)" \
-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" \
bash /work/iso/_inner-build-niri-live.sh
echo
echo ">>> Niri live ISO built: $OUT_ISO"
sha256sum "$OUT_ISO" | tee "${OUT_ISO}.sha256" || true