- Add kernel 7 (linux-mainline) as primary boot entry - Add kernel 6 (linux) as secondary fallback boot entry - Simplified boot menu: single entry per kernel, no failsafe variants - Add snd-intel-dspcfg.dsp_driver=1 + snd_hda_intel.dmic_detect=0 to BOOT_CMDLINE - Fix elogind sv: dual D-Bus + PID check to suppress already-running warnings - Replace cp -rfpPv with rsync -aHX in copy_include_directories (nixpkgs speed fix) - Fix trailing slash bash glob bug in mklive.sh and postsetup-nvidia.sh - Add -v linux-mainline to _inner-build-niri-live.sh for correct primary kernel
886 lines
36 KiB
Bash
Executable File
886 lines
36 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) xbps repo configuration ─────────────────────────────────────────
|
|
# Write all repos into xbps.d so they are available in the live session
|
|
# (not just at build time). The nonfree repo is needed for nvidia et al.
|
|
install -d -m 0755 "$INCLUDE_DIR/etc/xbps.d"
|
|
cat > "$INCLUDE_DIR/etc/xbps.d/00-void-repos.conf" <<EOF
|
|
repository=${REPO_URL:-https://repo-default.voidlinux.org/current}
|
|
repository=${REPO_URL%/current}/current/nonfree
|
|
repository=${REPO_URL%/current}/current/multilib
|
|
repository=${REPO_URL%/current}/current/multilib/nonfree
|
|
EOF
|
|
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 bluetooth; 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
|
|
|
|
# Fix sound device permissions — PCM/control nodes are created root:root during
|
|
# early boot before the audio group is guaranteed to exist. Re-apply the correct
|
|
# ownership now that the group is present, then trigger udev to re-evaluate the
|
|
# sound subsystem so the persistent 70-sound-perms.rules rule is applied too.
|
|
chown root:audio /dev/snd/pcmC* /dev/snd/controlC* /dev/snd/hwC* 2>/dev/null || true
|
|
chmod 660 /dev/snd/pcmC* /dev/snd/controlC* /dev/snd/hwC* 2>/dev/null || true
|
|
udevadm trigger --subsystem-match=sound 2>/dev/null || true
|
|
|
|
# Timezone
|
|
ln -sf /usr/share/zoneinfo/Europe/Zurich /etc/localtime 2>/dev/null || true
|
|
|
|
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
|
|
|
|
# Helper: is elogind's login1 D-Bus name already registered on the system bus?
|
|
_login1_on_dbus() {
|
|
dbus-send --system --print-reply --dest=org.freedesktop.DBus \
|
|
/org/freedesktop/DBus org.freedesktop.DBus.GetNameOwner \
|
|
string:org.freedesktop.login1 >/dev/null 2>&1
|
|
}
|
|
|
|
# Helper: is elogind process alive via its PID file?
|
|
_elogind_alive() {
|
|
for _pf in /run/elogind.pid /run/elogind/elogind.pid; do
|
|
[ -f "$_pf" ] && kill -0 "$(cat "$_pf" 2>/dev/null)" 2>/dev/null && return 0
|
|
done
|
|
return 1
|
|
}
|
|
|
|
# If elogind is already running (process alive or D-Bus name taken), yield
|
|
# permanently so runit does not spam restarts.
|
|
if _elogind_alive || _login1_on_dbus; then
|
|
echo "elogind-sv: already running — yielding to avoid restart spam"
|
|
exec tail -f /dev/null
|
|
fi
|
|
|
|
# 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; if the run was very short (elogind crashed or
|
|
# reported "already running" before our detection), back off longer.
|
|
cat > "$INCLUDE_DIR/etc/sv/elogind/finish" <<'EOF'
|
|
#!/bin/sh
|
|
# $1=exitcode, $2=signal (or -1)
|
|
exitcode="$1"
|
|
# Non-zero exit with no signal = elogind bailed out (e.g. "already running").
|
|
# Sleep longer to avoid log spam; next run will yield via the D-Bus/PID check.
|
|
if [ "$exitcode" != "0" ] && [ "$2" = "-1" ]; then
|
|
sleep 10
|
|
else
|
|
sleep 3
|
|
fi
|
|
EOF
|
|
chmod 0755 "$INCLUDE_DIR/etc/sv/elogind/finish"
|
|
|
|
for svc in dbus elogind NetworkManager bluetoothd 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
|
|
# Force GTK apps (and Electron/VSCode) to use XDG portal file dialogs
|
|
export GTK_USE_PORTAL=1
|
|
export ELECTRON_OZONE_PLATFORM_HINT=auto
|
|
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
|
|
# Expose nix .desktop files and icons to XDG-compliant launchers/shells
|
|
if [[ -d "${HOME:-}/.nix-profile/share" ]]; then
|
|
case ":${XDG_DATA_DIRS:-}:" in
|
|
*":$HOME/.nix-profile/share:"*) ;;
|
|
*) export XDG_DATA_DIRS="$HOME/.nix-profile/share:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}" ;;
|
|
esac
|
|
fi
|
|
export NIXPKGS_ALLOW_UNFREE=1
|
|
# Pre-baked nix is single-user (no daemon) — bypass daemon connection attempt
|
|
export NIX_REMOTE=local
|
|
# Flake commands ignore NIXPKGS_ALLOW_UNFREE unless --impure is passed.
|
|
# Wrap nix so interactive installs work without extra flags.
|
|
nix() { command nix "$@" --impure; }
|
|
export -f nix
|
|
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
|
|
build-users-group =
|
|
sandbox = false
|
|
auto-optimise-store = true
|
|
trusted-users = root ${LIVE_USER}
|
|
max-jobs = 2
|
|
http-connections = 10
|
|
EOF
|
|
|
|
# nixpkgs config: allow unfree packages for all users
|
|
install -d -m 0755 "$INCLUDE_DIR/etc/skel/.config/nixpkgs"
|
|
echo '{ allowUnfree = true; }' > "$INCLUDE_DIR/etc/skel/.config/nixpkgs/config.nix"
|
|
|
|
# git: GUI askpass so prompts work without a controlling terminal
|
|
install -d -m 0755 "$INCLUDE_DIR/usr/local/bin"
|
|
cat > "$INCLUDE_DIR/usr/local/bin/git-askpass" <<'EOF'
|
|
#!/bin/sh
|
|
for cmd in zenity qarma; do
|
|
command -v "$cmd" >/dev/null 2>&1 || continue
|
|
case "$1" in
|
|
*[Uu]sername*) exec "$cmd" --entry --title="Git Credentials" --text="$1" ;;
|
|
*) exec "$cmd" --password --title="Git Credentials" --text="$1" ;;
|
|
esac
|
|
done
|
|
printf '%s' "$1" >&2; read -r answer; printf '%s\n' "$answer"
|
|
EOF
|
|
chmod 0755 "$INCLUDE_DIR/usr/local/bin/git-askpass"
|
|
cat > "$INCLUDE_DIR/etc/gitconfig" <<'EOF'
|
|
[core]
|
|
askPass = /usr/local/bin/git-askpass
|
|
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"
|
|
// Keep WirePlumber running: wait for the PipeWire socket then start it,
|
|
// restart on any crash or pipewire restart (e.g. after package installs).
|
|
spawn-at-startup "sh" "-c" "while true; do while [ ! -S /run/user/\$(id -u)/pipewire-0 ]; do sleep 0.2; done; wireplumber; sleep 1; done"
|
|
|
|
// Background, notifications and auth
|
|
spawn-at-startup "swaybg" "-i" "/usr/share/backgrounds/void-installer/pxfuel.jpg" "-m" "fill"
|
|
spawn-at-startup "mako"
|
|
spawn-at-startup "/usr/libexec/polkit-gnome-authentication-agent-1"
|
|
|
|
// noctalia-shell — wait for org.bluez before launching so the BT module
|
|
// initialises correctly (bluetoothd may not be on D-Bus yet at this point).
|
|
spawn-at-startup "sh" "-c" "i=0; while [ \$i -lt 30 ] && ! dbus-send --system --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.GetNameOwner string:org.bluez >/dev/null 2>&1; do sleep 1; i=\$((i+1)); done; exec quickshell -c noctalia-shell"
|
|
|
|
// First-login setup: installs Claude Code (and NVM) once, then closes
|
|
spawn-at-startup "sh" "-c" "[ -f ~/.first-login-done ] || alacritty -T 'Void Setup' -e /usr/local/libexec/first-login.sh"
|
|
|
|
binds {
|
|
Mod+T { spawn "alacritty"; }
|
|
Mod+D { spawn "sh" "-c" "quickshell msg -c noctalia-shell launcher toggle"; }
|
|
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
|
|
|
|
# first-login.sh — deployed by _deploy_first_login() to the installed system.
|
|
[[ -r "$PROJECT_DIR/installer/first-login.sh" ]] && \
|
|
install -m 0755 "$PROJECT_DIR/installer/first-login.sh" "$OVERLAY/first-login.sh"
|
|
|
|
# Claude Code config + auth tokens from host (deployed to the installed system).
|
|
CLAUDE_SRC="${CLAUDE_SRC:-$HOME/.claude}"
|
|
[[ -d "$CLAUDE_SRC" ]] && { cp -a "$CLAUDE_SRC" "$OVERLAY/claude"; echo " claude: ~/.claude bundled"; }
|
|
[[ -r "${HOME}/.claude.json" ]] && install -m 0600 "${HOME}/.claude.json" "$OVERLAY/claude.json"
|
|
|
|
# VS Code user config + extension list from host.
|
|
VSCODE_SRC="${VSCODE_USER_SRC:-$HOME/.config/Code/User}"
|
|
install -d -m 0755 "$OVERLAY/vscode-user"
|
|
if [[ -d "$VSCODE_SRC" ]]; then
|
|
for f in settings.json keybindings.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"
|
|
command -v code >/dev/null 2>&1 && \
|
|
code --list-extensions > "$OVERLAY/vscode-extensions.txt" 2>/dev/null || true
|
|
echo " vscode-user: staged from $VSCODE_SRC"
|
|
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 + dark theme dconf ────────────────────────────────
|
|
# 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"
|
|
|
|
# dconf system keyfile: ensures GTK dark theme is reported to all apps via
|
|
# xdg-desktop-portal-gtk regardless of whether the user has a dconf DB yet.
|
|
install -d -m 0755 "$INCLUDE_DIR/etc/dconf/db/local.d"
|
|
install -d -m 0755 "$INCLUDE_DIR/etc/dconf/profile"
|
|
cat > "$INCLUDE_DIR/etc/dconf/db/local.d/01-dark-theme" <<'EOF'
|
|
[org/gnome/desktop/interface]
|
|
color-scheme='prefer-dark'
|
|
gtk-theme='${GTK_THEME}'
|
|
icon-theme='${ICON_THEME}'
|
|
cursor-theme='${CURSOR_THEME:-Bibata-Modern-Ice}'
|
|
cursor-size=24
|
|
EOF
|
|
sed -i "s/'\${GTK_THEME}'/'${GTK_THEME}'/; s/'\${ICON_THEME}'/'${ICON_THEME}'/; s/'\${CURSOR_THEME:-Bibata-Modern-Ice}'/'${CURSOR_THEME:-Bibata-Modern-Ice}'/" \
|
|
"$INCLUDE_DIR/etc/dconf/db/local.d/01-dark-theme"
|
|
printf 'user-db:user\nsystem-db:local\n' > "$INCLUDE_DIR/etc/dconf/profile/user"
|
|
|
|
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: include the pre-baked nix profile share dir so launchers
|
|
# and icon themes pick up nix-installed apps on first boot.
|
|
# /etc/environment is loaded by pam_env for ALL session types (tty autologin
|
|
# and greetd), so this is the most reliable place to set this baseline.
|
|
cat > "$INCLUDE_DIR/etc/environment" <<ENVEOF
|
|
XDG_DATA_DIRS=/home/${LIVE_USER}/.nix-profile/share:/usr/local/share:/usr/share
|
|
QT_QPA_PLATFORM=wayland;xcb
|
|
GDK_BACKEND=wayland,x11
|
|
MOZ_ENABLE_WAYLAND=1
|
|
LIBSEAT_BACKEND=logind
|
|
GTK_USE_PORTAL=1
|
|
ELECTRON_OZONE_PLATFORM_HINT=auto
|
|
ENVEOF
|
|
|
|
# ── noctalia default settings ─────────────────────────────────────────────
|
|
# Baseline settings.json placed in skel so the live user (and any installed
|
|
# user whose home is created from skel) starts with:
|
|
# - correct wallpaper directory pointing at the baked-in backgrounds
|
|
# - Gruvbox dark colour scheme
|
|
# - real app icons (iconMode=apps) instead of tabler monochrome icons
|
|
install -d -m 0755 "$INCLUDE_DIR/etc/skel/.config/noctalia"
|
|
cat > "$INCLUDE_DIR/etc/skel/.config/noctalia/settings.json" <<NOCEOF
|
|
{
|
|
"appLauncher": {
|
|
"iconMode": "apps",
|
|
"sortByMostUsed": true,
|
|
"showCategories": true,
|
|
"viewMode": "grid",
|
|
"pinnedApps": [],
|
|
"terminalCommand": "alacritty -e",
|
|
"density": "default",
|
|
"position": "center"
|
|
},
|
|
"colorSchemes": {
|
|
"darkMode": true,
|
|
"predefinedScheme": "Gruvbox",
|
|
"generationMethod": "tonal-spot",
|
|
"manualSunrise": "06:30",
|
|
"manualSunset": "18:30",
|
|
"monitorForColors": ""
|
|
},
|
|
"wallpaper": {
|
|
"enabled": true,
|
|
"directory": "/usr/share/backgrounds/void-installer",
|
|
"fillMode": "crop",
|
|
"fillColor": "#000000",
|
|
"automationEnabled": false,
|
|
"favorites": [],
|
|
"hideWallpaperFilenames": false,
|
|
"linkLightAndDarkWallpapers": true,
|
|
"monitorDirectories": [],
|
|
"enableMultiMonitorDirectories": false
|
|
}
|
|
}
|
|
NOCEOF
|
|
|
|
# ── Default browser: google-chrome (nix) ──────────────────────────────────
|
|
# System-wide MIME defaults so chrome is the default browser from first boot.
|
|
# Written to /etc/xdg/mimeapps.list (system default) and skel.
|
|
install -d -m 0755 "$INCLUDE_DIR/etc/xdg"
|
|
cat > "$INCLUDE_DIR/etc/xdg/mimeapps.list" <<'EOF'
|
|
[Default Applications]
|
|
text/html=google-chrome.desktop
|
|
x-scheme-handler/http=google-chrome.desktop
|
|
x-scheme-handler/https=google-chrome.desktop
|
|
x-scheme-handler/about=google-chrome.desktop
|
|
x-scheme-handler/unknown=google-chrome.desktop
|
|
application/pdf=google-chrome.desktop
|
|
application/xhtml+xml=google-chrome.desktop
|
|
application/xml=google-chrome.desktop
|
|
EOF
|
|
install -d -m 0755 "$INCLUDE_DIR/etc/skel/.config"
|
|
cp "$INCLUDE_DIR/etc/xdg/mimeapps.list" "$INCLUDE_DIR/etc/skel/.config/mimeapps.list"
|
|
|
|
# ── Portal backend configuration for niri ─────────────────────────────────
|
|
# Without this, xdg-desktop-portal doesn't know which backend to use for
|
|
# XDG_CURRENT_DESKTOP=niri, causing file-picker / portal calls to fail.
|
|
install -d -m 0755 "$INCLUDE_DIR/etc/xdg/xdg-desktop-portal"
|
|
cat > "$INCLUDE_DIR/etc/xdg/xdg-desktop-portal/niri-portals.conf" <<'EOF'
|
|
[preferred]
|
|
default=gtk
|
|
org.freedesktop.impl.portal.FileChooser=gtk
|
|
org.freedesktop.impl.portal.AppChooser=gtk
|
|
org.freedesktop.impl.portal.OpenURI=gtk
|
|
org.freedesktop.impl.portal.Print=gtk
|
|
org.freedesktop.impl.portal.Screenshot=gtk
|
|
org.freedesktop.impl.portal.Inhibit=gtk
|
|
org.freedesktop.impl.portal.Notification=gtk
|
|
org.freedesktop.impl.portal.Settings=gtk
|
|
EOF
|
|
|
|
# ── Sound device udev rules ────────────────────────────────────────
|
|
# PCM/control nodes are created root:root at early boot before the audio
|
|
# group is provisioned; this rule ensures correct ownership on every boot.
|
|
install -d -m 0755 "$INCLUDE_DIR/etc/udev/rules.d"
|
|
cat > "$INCLUDE_DIR/etc/udev/rules.d/70-sound-perms.rules" <<'EOF'
|
|
# Allow the audio group to access ALSA PCM and control devices.
|
|
SUBSYSTEM=="sound", GROUP="audio", MODE="0660"
|
|
EOF
|
|
|
|
# ── Timezone ──────────────────────────────────────────────────────────
|
|
# Create the /etc/localtime symlink and rc.conf TIMEZONE setting so the live
|
|
# session starts in the correct timezone without requiring a user step.
|
|
ln -sf /usr/share/zoneinfo/Europe/Zurich "$INCLUDE_DIR/etc/localtime"
|
|
cat > "$INCLUDE_DIR/etc/rc.conf" <<'RCEOF'
|
|
KEYMAP="ch"
|
|
HARDWARECLOCK="UTC"
|
|
TIMEZONE="Europe/Zurich"
|
|
RCEOF
|
|
|
|
# ── NVIDIA PRIME overlay ────────────────────────────────────────────────
|
|
echo ">>> staging NVIDIA PRIME overlay"
|
|
|
|
# Blacklist nouveau — prevents the open-source driver from grabbing the GPU
|
|
# before the proprietary nvidia driver.
|
|
install -d -m 0755 "$INCLUDE_DIR/etc/modprobe.d"
|
|
cat > "$INCLUDE_DIR/etc/modprobe.d/blacklist-nouveau.conf" <<'EOF'
|
|
blacklist nouveau
|
|
options nouveau modeset=0
|
|
EOF
|
|
|
|
# Intel BT on this platform suffers firmware download failures when USB
|
|
# autosuspend is active for the btusb adapter.
|
|
cat > "$INCLUDE_DIR/etc/modprobe.d/btusb-quirks.conf" <<'EOF'
|
|
options btusb enable_autosuspend=0
|
|
EOF
|
|
|
|
# Load nvidia modules early so the DRM device node exists before niri starts.
|
|
install -d -m 0755 "$INCLUDE_DIR/etc/modules-load.d"
|
|
cat > "$INCLUDE_DIR/etc/modules-load.d/nvidia.conf" <<'EOF'
|
|
nvidia
|
|
nvidia_modeset
|
|
nvidia_uvm
|
|
nvidia_drm
|
|
EOF
|
|
|
|
# dracut: include nvidia modules in initramfs; omit nouveau.
|
|
install -d -m 0755 "$INCLUDE_DIR/etc/dracut.conf.d"
|
|
cat > "$INCLUDE_DIR/etc/dracut.conf.d/10-nvidia.conf" <<'EOF'
|
|
add_drivers+=" nvidia nvidia_modeset nvidia_uvm nvidia_drm "
|
|
omit_drivers+=" nouveau "
|
|
EOF
|
|
|
|
# Xorg output-class configs: intel=modesetting (primary), nvidia=PRIME offload.
|
|
# Needed by xwayland-satellite for X11 clients running under niri.
|
|
install -d -m 0755 "$INCLUDE_DIR/etc/X11/xorg.conf.d"
|
|
cat > "$INCLUDE_DIR/etc/X11/xorg.conf.d/10-intel.conf" <<'EOF'
|
|
Section "OutputClass"
|
|
Identifier "intel"
|
|
MatchDriver "i915"
|
|
Driver "modesetting"
|
|
EndSection
|
|
EOF
|
|
cat > "$INCLUDE_DIR/etc/X11/xorg.conf.d/20-nvidia.conf" <<'EOF'
|
|
Section "OutputClass"
|
|
Identifier "nvidia"
|
|
MatchDriver "nvidia-drm"
|
|
Driver "nvidia"
|
|
Option "AllowEmptyInitialConfiguration"
|
|
Option "PrimaryGPU" "no"
|
|
ModulePath "/usr/lib/nvidia/xorg"
|
|
ModulePath "/usr/lib/xorg/modules"
|
|
EndSection
|
|
EOF
|
|
|
|
# prime-run: launch any app on the NVIDIA dGPU via PRIME render-offload.
|
|
cat > "$INCLUDE_DIR/usr/local/bin/prime-run" <<'EOF'
|
|
#!/bin/sh
|
|
# Run a program on the NVIDIA dGPU via PRIME render offload.
|
|
exec env __NV_PRIME_RENDER_OFFLOAD=1 \
|
|
__VK_LAYER_NV_optimus=NVIDIA_only \
|
|
__GLX_VENDOR_LIBRARY_NAME=nvidia \
|
|
"$@"
|
|
EOF
|
|
chmod 0755 "$INCLUDE_DIR/usr/local/bin/prime-run"
|
|
|
|
# ── 3g2) niri-session wrapper ─────────────────────────────────────────────
|
|
# greetd/tuigreet starts niri-session (not niri --session directly) so that
|
|
# /etc/profile is sourced first, ensuring /etc/profile.d/* scripts run and
|
|
# XDG_DATA_DIRS gets ~/.nix-profile/share prepended for the compositor and
|
|
# all apps it spawns (noctalia-shell, fuzzel, etc.).
|
|
install -d -m 0755 "$INCLUDE_DIR/usr/local/bin"
|
|
cat > "$INCLUDE_DIR/usr/local/bin/niri-session" <<'EOF'
|
|
#!/bin/bash
|
|
# niri-session — wrapper started by greetd/tuigreet.
|
|
# Sources /etc/profile so that all /etc/profile.d/* scripts run
|
|
# (nix paths, wayland env, XDG_DATA_DIRS with ~/.nix-profile/share, etc.)
|
|
# before handing off to the compositor.
|
|
[ -f /etc/profile ] && . /etc/profile
|
|
exec niri --session "$@"
|
|
EOF
|
|
chmod 0755 "$INCLUDE_DIR/usr/local/bin/niri-session"
|
|
|
|
# ── 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
|
|
# Wrap niri startup: start GNOME Keyring daemon first (inside the D-Bus session)
|
|
# so Chrome/VSCode/apps can store secrets via org.freedesktop.secrets.
|
|
exec dbus-run-session -- sh -c '
|
|
if command -v gnome-keyring-daemon >/dev/null 2>&1; then
|
|
eval "$(gnome-keyring-daemon --start --components=secrets,pkcs11 2>/dev/null)" || true
|
|
export GNOME_KEYRING_CONTROL GNOME_KEYRING_PID SSH_AUTH_SOCK
|
|
fi
|
|
exec 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"
|
|
|
|
# first-login.sh at /usr/local/libexec: available in the live session and
|
|
# deployed by _deploy_first_login() to the installed system via the overlay.
|
|
install -d -m 0755 "$INCLUDE_DIR/usr/local/libexec"
|
|
install -m 0755 "$PROJECT_DIR/installer/first-login.sh" \
|
|
"$INCLUDE_DIR/usr/local/libexec/first-login.sh"
|
|
|
|
# nix-packages.list: tells first-login.sh which nix packages to install.
|
|
# In the live session these are already prebaked; in the installed system the
|
|
# first-boot-nix runit service handles them, so this is informational only.
|
|
{
|
|
printf '# Nix user packages\n'
|
|
printf '%s\n' "${NIX_USER_PACKAGES[@]}"
|
|
} > "$INCLUDE_DIR/usr/local/libexec/nix-packages.list"
|
|
|
|
_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 nvidia-drm.modeset=1 rd.driver.blacklist=nouveau modprobe.blacklist=nouveau btusb.enable_autosuspend=0 snd-intel-dspcfg.dsp_driver=1 snd_hda_intel.dmic_detect=0}"
|
|
|
|
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
|