338 lines
13 KiB
Bash
Executable File
338 lines
13 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"
|
|
rm -rf "$INCLUDE_DIR"
|
|
mkdir -p "$INCLUDE_DIR"
|
|
|
|
# ── 3a) live user account ───────────────────────────────────────────────
|
|
# Create user 'live' with no password in the live image.
|
|
# mklive populates /etc/passwd at install time — we drop a post-install hook
|
|
# via the live init that calls useradd. We use an install.conf.d override
|
|
# that mklive will source, plus an rc.local-style runit oneshot.
|
|
install -d -m 0755 "$INCLUDE_DIR/etc"
|
|
cat > "$INCLUDE_DIR/etc/passwd-live" <<EOF
|
|
# placeholder replaced at build time by _inner-build-live.sh
|
|
EOF
|
|
|
|
# rc.local equivalent: runit oneshot that creates the live user on first boot.
|
|
install -d -m 0755 "$INCLUDE_DIR/etc/runit/runsvdir/default"
|
|
install -d -m 0755 "$INCLUDE_DIR/etc/sv/live-user-setup"
|
|
cat > "$INCLUDE_DIR/etc/sv/live-user-setup/run" <<'SV_EOF'
|
|
#!/bin/sh
|
|
# Create the 'live' user (no password) and set up home directory.
|
|
# Runs once; removes itself from default runsvdir on completion.
|
|
set -e
|
|
if ! id live >/dev/null 2>&1; then
|
|
useradd -m -s /bin/bash -G wheel,audio,video,plugdev,input,network,docker live
|
|
passwd -d live
|
|
install -d -m 0700 /home/live/.ssh
|
|
# Apply skel dotfiles
|
|
if [ -d /etc/skel ]; then
|
|
cp -a /etc/skel/. /home/live/ 2>/dev/null || true
|
|
fi
|
|
chown -R live:live /home/live
|
|
fi
|
|
# Enable sudoers entry for live
|
|
if ! grep -q '^live ' /etc/sudoers 2>/dev/null; then
|
|
echo 'live ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers
|
|
fi
|
|
# Remove self from runsvdir so it doesn't restart
|
|
rm -f /etc/runit/runsvdir/default/live-user-setup
|
|
sv stop live-user-setup 2>/dev/null || true
|
|
SV_EOF
|
|
chmod 0755 "$INCLUDE_DIR/etc/sv/live-user-setup/run"
|
|
ln -sf /etc/sv/live-user-setup "$INCLUDE_DIR/etc/runit/runsvdir/default/live-user-setup"
|
|
|
|
# ── 3b) LightDM autologin ───────────────────────────────────────────────
|
|
install -d -m 0755 "$INCLUDE_DIR/etc/lightdm"
|
|
cat > "$INCLUDE_DIR/etc/lightdm/lightdm.conf" <<EOF
|
|
[Seat:*]
|
|
autologin-user=${LIVE_USER}
|
|
autologin-user-timeout=0
|
|
user-session=cinnamon
|
|
greeter-session=lightdm-gtk-greeter
|
|
EOF
|
|
|
|
install -d -m 0755 "$INCLUDE_DIR/etc/runit/runsvdir/default"
|
|
# Enable NetworkManager, lightdm, pipewire and dbus for the live session.
|
|
for svc in dbus NetworkManager lightdm; 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', 'ch+fr')]
|
|
|
|
[org/cinnamon/desktop/keybindings/custom-keybindings/custom0]
|
|
name='Open Terminal'
|
|
command='${DEFAULT_TERMINAL:-alacritty}'
|
|
binding=['<Primary><Alt>t']
|
|
EOF
|
|
|
|
# ── 3e) Copy themes/icons/wallpapers directly into usr/share ────────────
|
|
# (dconf compile happens inside the Docker container — see _inner-build-live.sh)
|
|
install -d -m 0755 "$INCLUDE_DIR/usr/share/backgrounds/void-installer"
|
|
cp -a "$OVERLAY/wallpapers"/. "$INCLUDE_DIR/usr/share/backgrounds/void-installer/" 2>/dev/null || true
|
|
|
|
install -d -m 0755 "$INCLUDE_DIR/usr/share/themes"
|
|
[[ -d "$OVERLAY/themes" ]] && cp -a "$OVERLAY/themes"/. "$INCLUDE_DIR/usr/share/themes/" 2>/dev/null || true
|
|
|
|
install -d -m 0755 "$INCLUDE_DIR/usr/share/icons"
|
|
[[ -d "$OVERLAY/icons" ]] && cp -a "$OVERLAY/icons"/. "$INCLUDE_DIR/usr/share/icons/" 2>/dev/null || true
|
|
|
|
# ── 3f) first-login autostart + profile.d for the live user ─────────────
|
|
install -d -m 0755 "$INCLUDE_DIR/etc/xdg/autostart"
|
|
if [[ -r "$OVERLAY/first-login.sh" ]]; then
|
|
install -d -m 0755 "$INCLUDE_DIR/usr/local/libexec"
|
|
install -m 0755 "$OVERLAY/first-login.sh" "$INCLUDE_DIR/usr/local/libexec/first-login.sh"
|
|
cat > "$INCLUDE_DIR/etc/xdg/autostart/void-live-first-login.desktop" <<EOF
|
|
[Desktop Entry]
|
|
Type=Application
|
|
Name=Void Live First-Login Setup
|
|
Exec=/usr/local/libexec/first-login.sh
|
|
NoDisplay=true
|
|
X-GNOME-Autostart-enabled=true
|
|
OnlyShowIn=X-Cinnamon;
|
|
EOF
|
|
fi
|
|
|
|
install -d -m 0755 "$INCLUDE_DIR/etc/profile.d"
|
|
cat > "$INCLUDE_DIR/etc/profile.d/local-bin.sh" <<'EOF'
|
|
case ":$PATH:" in
|
|
*":$HOME/.local/bin:"*) ;;
|
|
*) export PATH="$HOME/.local/bin:$PATH" ;;
|
|
esac
|
|
EOF
|
|
|
|
# ── 3g) Live-session skel: pre-wire .bash_profile for first-login ────────
|
|
install -d -m 0755 "$INCLUDE_DIR/etc/skel"
|
|
cat > "$INCLUDE_DIR/etc/skel/.bash_profile" <<'EOF'
|
|
# Source .bashrc for interactive login shells.
|
|
[[ -f ~/.bashrc ]] && . ~/.bashrc
|
|
|
|
# Auto-run user environment setup on first interactive login.
|
|
if [[ -z "$_FIRST_LOGIN_RAN" && -x /usr/local/libexec/first-login.sh \
|
|
&& ! -f "$HOME/.first-login-done" ]]; then
|
|
export _FIRST_LOGIN_RAN=1
|
|
/usr/local/libexec/first-login.sh 2>&1 | tee -a "$HOME/.first-login.log"
|
|
fi
|
|
EOF
|
|
|
|
# 4) build Docker image
|
|
echo ">>> building docker image $DOCKER_IMAGE"
|
|
if "$DOCKER" buildx version >/dev/null 2>&1; then
|
|
"$DOCKER" build -t "$DOCKER_IMAGE" "$PROJECT_DIR/iso"
|
|
else
|
|
DOCKER_BUILDKIT=0 "$DOCKER" build -t "$DOCKER_IMAGE" "$PROJECT_DIR/iso"
|
|
fi
|
|
|
|
# 5) packages + output filename
|
|
ISO_PKGS=$(grep -vE '^\s*(#|$)' \
|
|
"$PROJECT_DIR/config/profiles/stable-cinnamon/packages.live-desktop.list" \
|
|
| tr '\n' ' ')
|
|
TS="$(date -u +%Y%m%d)"
|
|
OUT_ISO="${OUTPUT_ISO:-$OUT_DIR/void-live-stable-${TS}.iso}"
|
|
|
|
echo ">>> running mklive.sh inside docker — output: $OUT_ISO"
|
|
"$DOCKER" run --rm --privileged \
|
|
-v "$PROJECT_DIR:/work:rw" \
|
|
-v "$CACHE_DIR:/cache:rw" \
|
|
-e ARCH="${ARCH:-x86_64}" \
|
|
-e REPO_URL="${REPO_URL:-https://repo-default.voidlinux.org/current}" \
|
|
-e KEYMAP="${KEYMAP:-ch-fr_nodeadkeys}" \
|
|
-e LOCALE="${LOCALE:-en_US.UTF-8}" \
|
|
-e ISO_PKGS="$ISO_PKGS" \
|
|
-e ISO_TITLE="Void Live (${GTK_THEME} / Cinnamon)" \
|
|
-e OUT_ISO_REL="${OUT_ISO#$PROJECT_DIR/}" \
|
|
-e BOOT_CMDLINE="${BOOT_CMDLINE:-}" \
|
|
-e INCLUDE_DIR_REL="${INCLUDE_DIR#$PROJECT_DIR/}" \
|
|
-e HOST_UID="$(id -u)" \
|
|
-e HOST_GID="$(id -g)" \
|
|
"$DOCKER_IMAGE" \
|
|
bash /work/iso/_inner-build-live.sh
|
|
|
|
echo
|
|
echo ">>> Live ISO built: $OUT_ISO"
|
|
sha256sum "$OUT_ISO" | tee "${OUT_ISO}.sha256"
|