diff --git a/assets/installer/colors.gum b/assets/installer/colors.gum index 58b3630..17e6cf6 100644 --- a/assets/installer/colors.gum +++ b/assets/installer/colors.gum @@ -28,7 +28,7 @@ export GUM_CHOOSE_HEADER_FOREGROUND="$VEILOR_FG" export GUM_CHOOSE_ITEM_FOREGROUND="$VEILOR_FG" export GUM_CHOOSE_SELECTED_FOREGROUND="$VEILOR_FG" export GUM_CHOOSE_SELECTED_BACKGROUND="$VEILOR_DIM" -export GUM_CHOOSE_CURSOR="› " +export GUM_CHOOSE_CURSOR="❯ " # ── gum input ────────────────────────────────────────── # Single-line text entry (hostname). @@ -36,7 +36,7 @@ export GUM_INPUT_PROMPT_FOREGROUND="$VEILOR_DIM" export GUM_INPUT_CURSOR_FOREGROUND="$VEILOR_FG" export GUM_INPUT_PLACEHOLDER_FOREGROUND="$VEILOR_MUTE" export GUM_INPUT_HEADER_FOREGROUND="$VEILOR_FG" -export GUM_INPUT_PROMPT="› " +export GUM_INPUT_PROMPT="❯ " # ── gum write (multi-line) ───────────────────────────── # Reserved for any longer-form prompts; not used in v0.5.1 yet. diff --git a/overlay/etc/os-release.d/veilor b/overlay/etc/os-release.d/veilor index 6946c2f..cfd5dc2 100644 --- a/overlay/etc/os-release.d/veilor +++ b/overlay/etc/os-release.d/veilor @@ -2,8 +2,8 @@ NAME="veilor-os" PRETTY_NAME="veilor-os 0.1" ID=veilor ID_LIKE=fedora -VERSION="0.1" -VERSION_ID="0.1" +VERSION="0.5.8" +VERSION_ID="0.5.8" HOME_URL="https://github.com/veilor-org/veilor-os" DOCUMENTATION_URL="https://github.com/veilor-org/veilor-os/tree/main/docs" BUG_REPORT_URL="https://github.com/veilor-org/veilor-os/issues" diff --git a/overlay/usr/local/bin/veilor-installer b/overlay/usr/local/bin/veilor-installer index 6a0d9aa..5e56dbc 100644 --- a/overlay/usr/local/bin/veilor-installer +++ b/overlay/usr/local/bin/veilor-installer @@ -54,23 +54,34 @@ fi banner() { clear + # Read version + date for version line. /etc/os-release has VERSION_ID. + local ver="" + [[ -r /etc/os-release ]] && ver=$(. /etc/os-release; echo "${VERSION_ID:-0.0}") + local d + d=$(date +%Y-%m-%d) + local vline="veilor-os ${ver} · ${d} · live" + if [[ -r $BANNER_FILE ]]; then if [[ $TUI == gum ]]; then - # gum style draws a rounded border + padding around the banner. - gum style --border rounded --margin "1" --padding "1 2" \ + # gum style: rounded border, banner + blank line + version line. + gum style --border rounded --margin "0 2" --padding "1 3" \ --border-foreground "${VEILOR_DIM:-240}" \ --foreground "${VEILOR_FG:-15}" \ - "$(cat "$BANNER_FILE")" + "$(cat "$BANNER_FILE")" \ + "" \ + "$vline" else cat "$BANNER_FILE" echo + echo " $vline" + echo fi else # Fallback ASCII if banner.txt missing (older overlay). - cat << 'EOF' + cat << EOF veilor-os installer - hardened. branded. yours. + $vline EOF fi @@ -180,10 +191,14 @@ prompt_error() { } main_menu() { - prompt_choose "Welcome" \ + # Empty header — banner already provides context. `──────` line splits + # primary actions (top) from session controls (bottom). Cursor `❯` set + # via GUM_CHOOSE_CURSOR in colors.gum. + prompt_choose "" \ "Install" \ - "live - (KDE)" \ - "live - shell" \ + "live · KDE" \ + "live · shell" \ + "──────" \ "Reboot" \ "Power off" } @@ -202,7 +217,7 @@ collect_answers() { prompt_error "No installable disks found." return 1 fi - disk=$(prompt_choose_pairs "Select install disk (will be ERASED)" "${disks_pairs[@]}") || return 1 + disk=$(prompt_choose_pairs "[1/4] Select install disk · WILL BE ERASED" "${disks_pairs[@]}") || return 1 # ── Hostname ── # Hardcoded for branded consistency. Post-install: `hostnamectl set-hostname`. @@ -231,27 +246,48 @@ collect_answers() { } # ── LUKS passphrase ── - luks_pw=$(prompt_password "LUKS passphrase (full-disk encryption)") || return 1 + luks_pw=$(prompt_password "[2/4] Encryption · LUKS2 passphrase (min 8)") || return 1 validate_pw "$luks_pw" "passphrase" || return 1 # ── Admin password ── - admin_pw=$(prompt_password "Admin user password (login after install)") || return 1 + admin_pw=$(prompt_password "[3/4] Admin user · password for 'admin'") || return 1 validate_pw "$admin_pw" "password" || return 1 # ── Locale ── - locale=$(prompt_choose "Choose locale" \ + locale=$(prompt_choose "[4/4] Locale" \ "en_GB.UTF-8" \ "en_US.UTF-8") || return 1 # ── Confirmation ── - prompt_confirm "About to install veilor-os: + # Render summary box + danger lines via gum style, then gum confirm. + # Whiptail fallback: simple yesno. + if [[ $TUI == gum ]]; then + clear + gum style --border rounded --margin "1 2" --padding "1 3" \ + --border-foreground "${VEILOR_DIM:-240}" \ + --foreground "${VEILOR_FG:-15}" \ + "Confirm install" \ + "" \ + " Disk $disk $(gum style --foreground 1 'WILL BE ERASED')" \ + " Locale $locale" \ + " LUKS ✓ set" \ + " Admin ✓ set" \ + "" \ + "$(gum style --foreground 3 'This action is irreversible.')" + gum confirm --affirmative "Yes, install" --negative "Cancel" "Proceed?" || return 1 + else + whiptail --title "Confirm install" --yesno \ +"About to install veilor-os: - Disk: $disk (will be ERASED) + Disk: $disk (WILL BE ERASED) Locale: $locale LUKS: set Admin pw: set -Proceed?" || return 1 +This action is irreversible. + +Proceed?" 16 60 || return 1 + fi # Export to caller via globals SEL_DISK=$disk @@ -524,36 +560,59 @@ KSEOF } run_install() { - prompt_message "Installing veilor-os to $SEL_DISK ... -This will take 10-30 minutes. -Logs: /var/log/veilor-installer.log + /tmp/anaconda.log" - sleep 1 - # Hand off to anaconda. --kickstart runs unattended. - # LANG / LC_ALL must be set — anaconda's keyboard.activate_keyboard() - # raises AnacondaError: 'LANG' if missing. tty1 inherits no locale by - # default. Use the locale the user picked (or fall back to en_GB). + # Anaconda env setup (see comments below). export LANG="${SEL_LOCALE:-en_GB.UTF-8}" export LC_ALL="$LANG" - # XDG_RUNTIME_DIR must exist — anaconda's display.setup_display() - # tries os.getenv("XDG_RUNTIME_DIR") + constants.WAYLAND_SOCKET_NAME - # and crashes with TypeError on None. We're running unattended so - # no graphical display is needed, but the env var still has to exist. + # LANG / LC_ALL: anaconda's keyboard.activate_keyboard() raises + # AnacondaError: 'LANG' if missing. tty1 inherits no locale by default. + # XDG_RUNTIME_DIR: anaconda's display.setup_display() probes + # os.getenv("XDG_RUNTIME_DIR") + Wayland socket and crashes with + # TypeError on None. We're running unattended so no display needed, + # but env var still has to exist. export XDG_RUNTIME_DIR="/run/user/$(id -u)" mkdir -p "$XDG_RUNTIME_DIR" chmod 0700 "$XDG_RUNTIME_DIR" - # --cmdline: fully unattended text-only mode. No Wayland setup, no - # graphical/text TUI. Reads kickstart, executes, reboots. Right mode - # for our gum-driven flow where ks is fully self-contained. - if anaconda --cmdline --kickstart=/run/install/veilor-generated.ks; then - prompt_message "Install complete. System will reboot. -Remove the install media after shutdown." - sleep 3 - systemctl reboot - else - prompt_error "Anaconda exited non-zero. + + if [[ $TUI == gum ]]; then + clear + # gum spin runs anaconda as subprocess. --show-output forwards + # stdout/stderr to /tmp/anaconda-cmdline.log so we can debug. + # --title shows live-updating header. --spinner dot is minimal. + local rc=0 + gum spin --spinner dot \ + --title "Installing veilor-os to $SEL_DISK · this takes 10-30 min · logs on tty2" \ + --show-output \ + -- bash -c 'anaconda --cmdline --kickstart=/run/install/veilor-generated.ks 2>&1 | tee /tmp/anaconda-cmdline.log' || rc=$? + if [[ $rc -eq 0 ]]; then + gum style --foreground 2 --border rounded --margin "1 2" --padding "1 3" \ + "✓ Install complete" \ + "" \ + "System will reboot in 5 seconds." \ + "Remove the install media." + sleep 5 + systemctl reboot + else + prompt_error "Anaconda exited non-zero (status $rc). Logs at /tmp/anaconda.log + /var/log/veilor-installer.log. Press OK to drop to shell." - return 1 + return 1 + fi + else + prompt_message "Installing veilor-os to $SEL_DISK ... +This will take 10-30 minutes. +Logs: /var/log/veilor-installer.log + /tmp/anaconda.log" + sleep 1 + if anaconda --cmdline --kickstart=/run/install/veilor-generated.ks; then + prompt_message "Install complete. System will reboot. +Remove the install media after shutdown." + sleep 3 + systemctl reboot + else + prompt_error "Anaconda exited non-zero. +Logs at /tmp/anaconda.log + /var/log/veilor-installer.log. +Press OK to drop to shell." + return 1 + fi fi } @@ -595,10 +654,12 @@ while true; do run_install || continue fi ;; - "live - (KDE)") launch_desktop ;; - "live - shell") drop_to_shell ;; + "live · KDE") launch_desktop ;; + "live · shell") drop_to_shell ;; + "──────") banner ;; # separator clicked: redraw, no-op "Reboot") systemctl reboot ;; "Power off") systemctl poweroff ;; - *) drop_to_shell ;; + "") banner ;; # ESC/cancel from menu: redraw + *) banner ;; esac done