#!/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 ─────────────────────────────────────────────── # 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 < /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 < /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" <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) 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" < "$INCLUDE_DIR/etc/profile.d/local-bin.sh" <<'EOF' case ":$PATH:" in *":$HOME/.local/bin:"*) ;; *) export PATH="$HOME/.local/bin:$PATH" ;; esac EOF # ── 3f-bis) Nix user packages list for first-login.sh ─────────────────── # Write NIX_USER_PACKAGES (from install.conf) as a plain list so # first-login.sh can read it without sourcing install.conf. 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" # Also set the env var so first-login.sh knows where to find it. cat > "$INCLUDE_DIR/etc/profile.d/nix-packages-path.sh" <<'EOF' export NIX_PACKAGES_FILE=/usr/local/libexec/nix-packages.list EOF # ── 3g) Live-session skel: pre-wire .bash_profile for first-login ──────── 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}" # 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 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