692 lines
28 KiB
Bash
Executable File
692 lines
28 KiB
Bash
Executable File
#!/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"
|
|
# 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"
|
|
|
|
# ── 3a) live user account ───────────────────────────────────────────────
|
|
# The vmklive dracut hook (adduser.sh) creates the live user in initramfs
|
|
# using the live.user= kernel cmdline parameter (added to BOOT_CMDLINE below).
|
|
# We add extra groups and sudo via /etc/runit/2 override (after pivot_root).
|
|
install -d -m 0755 "$INCLUDE_DIR/etc"
|
|
|
|
# GPU detection + extra group setup — runs from our runit/2 override below.
|
|
install -d -m 0755 "$INCLUDE_DIR/etc/runit"
|
|
cat > "$INCLUDE_DIR/etc/runit/live-setup.sh" <<'SV_EOF'
|
|
#!/bin/sh
|
|
# Runs from /etc/runit/2 before any service starts.
|
|
# 1. Ensures live user has all needed groups.
|
|
# 2. Generates /etc/X11/xorg.conf.d/20-gpu.conf based on detected hardware.
|
|
|
|
LIVE_USER="${USERNAME:-live}"
|
|
[ -f /etc/default/live.conf ] && . /etc/default/live.conf
|
|
LIVE_USER="${USERNAME:-live}"
|
|
|
|
# Extra groups (dracut only adds audio,video,wheel)
|
|
for g in plugdev input network docker; 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
|
|
|
|
# ── Nix daemon-mode setup ───────────────────────────────────────────────
|
|
# Void's xbps nix package ships nix-daemon + a runit service at /etc/sv/nix-daemon
|
|
# and puts the socket at /var/nix/daemon-socket/socket.
|
|
# We configure the daemon to trust the live user so 'nix profile install'
|
|
# works without root and without owning /nix/store.
|
|
if [ -x /usr/bin/nix ]; then
|
|
install -d -m 0755 /etc/nix
|
|
cat > /etc/nix/nix.conf <<NIXCONF
|
|
experimental-features = nix-command flakes
|
|
sandbox = false
|
|
auto-optimise-store = true
|
|
trusted-users = root $LIVE_USER
|
|
max-jobs = 2
|
|
http-connections = 10
|
|
NIXCONF
|
|
# Ensure nix.sh profile.d sets PATH for installed nix packages.
|
|
# The Void nix package ships /etc/profile.d/nix.sh; we just supplement it.
|
|
cat > /etc/profile.d/nix-live.sh <<'NIXSH'
|
|
# Add nix user-profile bin to PATH
|
|
if [ -d "$HOME/.nix-profile/bin" ]; then
|
|
export PATH="$HOME/.nix-profile/bin:$PATH"
|
|
fi
|
|
NIXSH
|
|
chmod 0644 /etc/profile.d/nix-live.sh
|
|
echo "live-setup: nix daemon mode configured (trusted-users = $LIVE_USER)"
|
|
fi
|
|
|
|
# ── nsswitch: remove mdns (library absent on Void; no Avahi daemon) ──────
|
|
# 'mdns' in nsswitch.conf without libnss_mdns causes hangs on every DNS
|
|
# lookup because glibc spins waiting for Avahi's socket. Remove it so
|
|
# standard DNS resolution is used directly.
|
|
if [ -f /etc/nsswitch.conf ]; then
|
|
sed -i '/^hosts:/s/mdns[^ ]* *//g' /etc/nsswitch.conf
|
|
echo "live-setup: removed mdns from nsswitch.conf (hosts line)"
|
|
fi
|
|
|
|
# ── DNS: ensure a working nameserver is configured ───────────────────────
|
|
# NetworkManager will overwrite resolv.conf once DHCP completes.
|
|
# If the DHCP-provided nameserver is broken (e.g. QEMU's 10.0.2.3), add
|
|
# 8.8.8.8 as a fallback. We write it now so first-login.sh can reach the
|
|
# internet immediately after login without waiting for NM to settle.
|
|
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
|
|
echo "live-setup: set fallback DNS (8.8.8.8, 1.1.1.1)"
|
|
fi
|
|
|
|
# ── D-Bus session socket for the live user's login session ───────────────
|
|
# dbus-launch is called by lightdm-session / Xsession automatically.
|
|
# Ensure dbus system bus is accessible (needed by nix and Cinnamon).
|
|
# The dbus runit service is enabled in runsvdir/default; no extra setup needed.
|
|
|
|
# ── GPU / Xorg driver detection ──────────────────────────────────────────
|
|
mkdir -p /etc/X11/xorg.conf.d
|
|
|
|
# Detect GPU via PCI
|
|
HAS_NVIDIA=0
|
|
HAS_INTEL=0
|
|
HAS_AMD=0
|
|
IS_VIRT=0
|
|
|
|
if command -v lspci >/dev/null 2>&1; then
|
|
lspci_out=$(lspci 2>/dev/null)
|
|
echo "$lspci_out" | grep -qi 'NVIDIA' && HAS_NVIDIA=1
|
|
echo "$lspci_out" | grep -qiE 'Intel.*VGA|VGA.*Intel|Intel.*Integrated' && HAS_INTEL=1
|
|
echo "$lspci_out" | grep -qiE 'AMD.*VGA|VGA.*AMD|ATI.*VGA|Radeon' && HAS_AMD=1
|
|
echo "$lspci_out" | grep -qiE 'virtio|VMware|QEMU|VirtualBox|bochs' && IS_VIRT=1
|
|
fi
|
|
|
|
if [ $IS_VIRT -eq 1 ]; then
|
|
# Virtual machine: force modesetting, disable accel, use software GL
|
|
cat > /etc/X11/xorg.conf.d/20-gpu.conf <<'EOF'
|
|
Section "Device"
|
|
Identifier "GPU"
|
|
Driver "modesetting"
|
|
Option "AccelMethod" "none"
|
|
EndSection
|
|
EOF
|
|
# Xsession sources /etc/profile which sources /etc/profile.d/ — so this
|
|
# env var reaches the cinnamon session (LightDM's session-env is not
|
|
# supported in the Void-packaged lightdm 1.32).
|
|
echo 'export LIBGL_ALWAYS_SOFTWARE=1' > /etc/profile.d/live-env.sh
|
|
chmod 0644 /etc/profile.d/live-env.sh
|
|
echo "live-setup: VIRT detected — modesetting (no accel), software GL"
|
|
elif [ $HAS_NVIDIA -eq 1 ] && [ -e /usr/lib/xorg/modules/drivers/nvidia_drv.so ]; then
|
|
# NVIDIA PRIME (Intel iGPU primary + NVIDIA render offload)
|
|
# Real hardware — ensure software GL is NOT forced
|
|
rm -f /etc/profile.d/live-env.sh
|
|
INTEL_BUSID=$(lspci | grep -iE 'VGA.*Intel|Intel.*VGA' | head -1 | \
|
|
awk '{print $1}' | awk -F'[.:]' '{printf "PCI:%d:%d:%d", strtonum("0x"$1), strtonum("0x"$2), strtonum("0x"$3)}')
|
|
NVIDIA_BUSID=$(lspci | grep -i NVIDIA | head -1 | \
|
|
awk '{print $1}' | awk -F'[.:]' '{printf "PCI:%d:%d:%d", strtonum("0x"$1), strtonum("0x"$2), strtonum("0x"$3)}')
|
|
cat > /etc/X11/xorg.conf.d/20-gpu.conf <<EOF
|
|
Section "ServerLayout"
|
|
Identifier "layout"
|
|
Option "AllowNVIDIAGPUScreens"
|
|
EndSection
|
|
Section "Device"
|
|
Identifier "Intel"
|
|
Driver "modesetting"
|
|
BusID "${INTEL_BUSID:-PCI:0:2:0}"
|
|
EndSection
|
|
Section "Device"
|
|
Identifier "NVIDIA"
|
|
Driver "nvidia"
|
|
BusID "${NVIDIA_BUSID:-PCI:1:0:0}"
|
|
Option "AllowEmptyInitialConfiguration"
|
|
EndSection
|
|
EOF
|
|
echo "live-setup: NVIDIA PRIME detected (Intel=${INTEL_BUSID} NVIDIA=${NVIDIA_BUSID})"
|
|
elif [ $HAS_NVIDIA -eq 1 ]; then
|
|
# NVIDIA present but no proprietary driver — use modesetting
|
|
rm -f /etc/profile.d/live-env.sh
|
|
cat > /etc/X11/xorg.conf.d/20-gpu.conf <<'EOF'
|
|
Section "Device"
|
|
Identifier "GPU"
|
|
Driver "modesetting"
|
|
EndSection
|
|
EOF
|
|
echo "live-setup: NVIDIA detected but no proprietary driver — modesetting"
|
|
else
|
|
# Intel / AMD / generic — modesetting
|
|
rm -f /etc/profile.d/live-env.sh
|
|
cat > /etc/X11/xorg.conf.d/20-gpu.conf <<'EOF'
|
|
Section "Device"
|
|
Identifier "GPU"
|
|
Driver "modesetting"
|
|
EndSection
|
|
EOF
|
|
echo "live-setup: Generic/Intel/AMD — modesetting"
|
|
fi
|
|
SV_EOF
|
|
chmod 0755 "$INCLUDE_DIR/etc/runit/live-setup.sh"
|
|
|
|
# Override /etc/runit/2 — mirrors real runit-void stage 2 but runs live-setup.sh first.
|
|
# Real runit-void/2: runsvchdir default → runsvdir /run/runit/runsvdir/current
|
|
cat > "$INCLUDE_DIR/etc/runit/2" <<'SV_EOF'
|
|
#!/bin/sh
|
|
PATH=/usr/bin:/usr/sbin
|
|
|
|
# Live session setup: extra groups, sudo, GPU/Xorg detection
|
|
[ -x /etc/runit/live-setup.sh ] && /etc/runit/live-setup.sh
|
|
|
|
# Mirror real runit-void stage 2: 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"
|
|
|
|
install -d -m 0755 "$INCLUDE_DIR/etc/runit/runsvdir/default"
|
|
|
|
# ── 3b) LightDM autologin ───────────────────────────────────────────────
|
|
install -d -m 0755 "$INCLUDE_DIR/etc/lightdm"
|
|
# .session file: read by the vmklive dracut hook (display-manager-autologin.sh)
|
|
# to configure LightDM autologin session.
|
|
echo 'cinnamon' > "$INCLUDE_DIR/etc/lightdm/.session"
|
|
# lightdm.conf: autologin lines MUST be commented so the dracut
|
|
# display-manager-autologin.sh hook can sed-uncomment them.
|
|
cat > "$INCLUDE_DIR/etc/lightdm/lightdm.conf" <<'EOF'
|
|
[Seat:*]
|
|
#autologin-user=
|
|
#autologin-user-timeout=0
|
|
#autologin-session=
|
|
#user-session=
|
|
session-wrapper=/etc/lightdm/Xsession
|
|
greeter-session=lightdm-gtk-greeter
|
|
EOF
|
|
# NOTE: session-wrapper=/etc/lightdm/Xsession is required on Void Linux —
|
|
# LightDM defaults to 'lightdm-session' which is NOT provided by the Void
|
|
# lightdm package. The /etc/lightdm/Xsession wrapper is installed by the
|
|
# lightdm package and sources /etc/profile (and therefore /etc/profile.d/)
|
|
# so LIBGL_ALWAYS_SOFTWARE=1 placed there by live-setup.sh is propagated to
|
|
# the cinnamon session.
|
|
|
|
install -d -m 0755 "$INCLUDE_DIR/etc/runit/runsvdir/default"
|
|
# Enable services for the live session.
|
|
for svc in dbus NetworkManager lightdm nix-daemon; do
|
|
ln -sf "/etc/sv/$svc" "$INCLUDE_DIR/etc/runit/runsvdir/default/$svc" 2>/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" <<EOF
|
|
[org/cinnamon/desktop/interface]
|
|
gtk-theme='${GTK_THEME}'
|
|
icon-theme='${ICON_THEME}'
|
|
cursor-theme='${CURSOR_THEME}'
|
|
|
|
[org/cinnamon/desktop/wm/preferences]
|
|
theme='${GTK_THEME}'
|
|
|
|
[org/cinnamon/theme]
|
|
name='${GTK_THEME}'
|
|
|
|
[org/cinnamon/desktop/background]
|
|
picture-uri='file:///usr/share/backgrounds/void-installer/${WALLPAPER_FILE}'
|
|
picture-options='zoom'
|
|
|
|
[org/gnome/desktop/background]
|
|
picture-uri='file:///usr/share/backgrounds/void-installer/${WALLPAPER_FILE}'
|
|
picture-options='zoom'
|
|
|
|
[org/gnome/desktop/input-sources]
|
|
sources=[('xkb', '${KEYMAP//-/+}')]
|
|
|
|
[org/gnome/desktop/interface]
|
|
color-scheme='prefer-dark'
|
|
|
|
[org/cinnamon/desktop/default-applications/terminal]
|
|
exec='alacritty'
|
|
exec-arg='-e'
|
|
|
|
[org/gnome/desktop/default-applications/terminal]
|
|
exec='alacritty'
|
|
exec-arg='-e'
|
|
|
|
[org/cinnamon/desktop/keybindings/custom-keybindings/custom0]
|
|
name='Open Terminal'
|
|
command='${DEFAULT_TERMINAL:-alacritty}'
|
|
binding=['<Primary><Alt>t']
|
|
EOF
|
|
|
|
# dconf lock file — prevents the live user's session from overriding the
|
|
# keyboard layout with an empty list on first start.
|
|
install -d -m 0755 "$INCLUDE_DIR/etc/dconf/db/local.d/locks"
|
|
cat > "$INCLUDE_DIR/etc/dconf/db/local.d/locks/keyboard" <<'EOF'
|
|
/org/gnome/desktop/input-sources/sources
|
|
/org/cinnamon/desktop/default-applications/terminal/exec
|
|
/org/cinnamon/desktop/default-applications/terminal/exec-arg
|
|
/org/gnome/desktop/default-applications/terminal/exec
|
|
/org/gnome/desktop/default-applications/terminal/exec-arg
|
|
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
|
|
|
|
# ── 3e-bis) Nemo actions ─────────────────────────────────────────────────
|
|
install -d -m 0755 "$INCLUDE_DIR/usr/share/nemo/actions"
|
|
install -d -m 0755 "$INCLUDE_DIR/usr/local/bin"
|
|
|
|
# Wrapper script: finds 'code' in the live user's nix profile.
|
|
# Used by the Nemo action so it doesn't need to know the nix profile path.
|
|
cat > "$INCLUDE_DIR/usr/local/bin/code-open" <<'EOF'
|
|
#!/bin/sh
|
|
# Open VS Code for the given file/folder arguments.
|
|
# Looks in the live user's nix profile first, then falls back to PATH.
|
|
for d in "/home/live/.nix-profile/bin" /nix/var/nix/profiles/per-user/live/profile/bin \
|
|
/usr/local/bin /usr/bin; do
|
|
if [ -x "$d/code" ]; then
|
|
exec "$d/code" "$@"
|
|
fi
|
|
done
|
|
exec code "$@"
|
|
EOF
|
|
chmod 0755 "$INCLUDE_DIR/usr/local/bin/code-open"
|
|
|
|
# Nemo action: Open in VS Code
|
|
cat > "$INCLUDE_DIR/usr/share/nemo/actions/open-in-vscode.nemo_action" <<'EOF'
|
|
[Nemo Action]
|
|
|
|
Name=Open in VS _Code
|
|
|
|
Comment=Open in Visual Studio Code
|
|
|
|
Exec=/usr/local/bin/code-open %F
|
|
|
|
Icon-Name=code
|
|
|
|
Selection=Any
|
|
|
|
Extensions=any;
|
|
|
|
Dependencies=/usr/local/bin/code-open;
|
|
EOF
|
|
|
|
# ── 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 -m 0755 "$OVERLAY/first-login.sh" "$INCLUDE_DIR/usr/local/libexec/first-login.sh"
|
|
fi
|
|
|
|
# apply-settings.sh: runs once at first Cinnamon login, applies theme/wallpaper/
|
|
# terminal via gsettings (writes to user dconf db — always works).
|
|
cat > "$INCLUDE_DIR/usr/local/libexec/apply-live-settings.sh" <<EOF
|
|
#!/bin/bash
|
|
# Baked live-session settings. Runs once at first Cinnamon login.
|
|
DONE="\$HOME/.void-live-settings-done"
|
|
[[ -f "\$DONE" ]] && exit 0
|
|
|
|
# Wait for D-Bus session bus (LightDM autologin starts it, be safe)
|
|
for _i in \$(seq 15); do
|
|
[[ -n "\${DBUS_SESSION_BUS_ADDRESS:-}" ]] && break
|
|
eval "\$(dbus-launch --sh-syntax 2>/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 — set explicitly via gsettings (belt-and-suspenders alongside
|
|
# the user dconf db pre-baked in /etc/skel at build time).
|
|
gsettings set org.gnome.desktop.input-sources sources "[('xkb', '${KEYMAP//-/+}')]"
|
|
|
|
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 Apply Settings
|
|
Exec=/usr/local/libexec/apply-live-settings.sh
|
|
NoDisplay=true
|
|
X-GNOME-Autostart-enabled=true
|
|
OnlyShowIn=X-Cinnamon;
|
|
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
|
|
# keep the script and lib/ together under /usr/local/lib/void-installer/.
|
|
# SHARE_DIR (/usr/local/share/installer) holds config, packages, profiles.
|
|
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
|
|
# Config and packages — read by install.sh via SHARE_DIR
|
|
install -m 0644 "$PROJECT_DIR/config/install.conf" "$INCLUDE_DIR/usr/local/share/installer/install.conf"
|
|
install -m 0644 "$PROJECT_DIR/config/profiles/stable-cinnamon/packages.list" "$INCLUDE_DIR/usr/local/share/installer/packages.list"
|
|
# Profiles directory (profile.conf + per-profile packages.list)
|
|
if [[ -d "$PROJECT_DIR/config/profiles" ]]; then
|
|
cp -r "$PROJECT_DIR/config/profiles" "$INCLUDE_DIR/usr/local/share/installer/profiles"
|
|
fi
|
|
# Thin wrapper so the user can type `void-install` from anywhere
|
|
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: read from secrets.env at build time, baked as /etc/installer-secrets.env
|
|
# (the file is .gitignored; if absent the installer prompts interactively)
|
|
_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 for passwords at runtime"
|
|
fi
|
|
# Desktop launcher — opens alacritty running void-install as root (live user
|
|
# has passwordless sudo configured by mklive).
|
|
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
|
|
|
|
install -d -m 0755 "$INCLUDE_DIR/etc/profile.d"
|
|
cat >> "$INCLUDE_DIR/etc/profile.d/nix-prebaked.sh" <<'EOF'
|
|
case ":$PATH:" in
|
|
*":$HOME/.local/bin:"*) ;;
|
|
*) export PATH="$HOME/.local/bin:$PATH" ;;
|
|
esac
|
|
EOF
|
|
|
|
# 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
|
|
} > "$INCLUDE_DIR/usr/local/libexec/nix-packages.list"
|
|
cat > "$INCLUDE_DIR/etc/profile.d/nix-packages-path.sh" <<'EOF'
|
|
export NIX_PACKAGES_FILE=/usr/local/libexec/nix-packages.list
|
|
EOF
|
|
|
|
# ── 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
|
|
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}"
|
|
# live.user=live → vmklive dracut hook creates user 'live' (default would be 'anon')
|
|
# console=ttyS0 → serial output for QEMU/real hardware debugging
|
|
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:-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 NIX_PACKAGES_PREBAKE="${NIX_USER_PACKAGES[*]}" \
|
|
-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" || true
|