fix(installer): implement robust step-based navigation and fix multi-line sed error
- Implement a step-based state machine in main loop to support 'Back' navigation via Esc. - Refactor all prompts to use safe exit-code capture (rc -eq 130/1) and handle 'not submitted' output. - Add input flushing after Esc events to prevent cascading backtrack signals. - Add short-circuit checks to every wizard stage for reliable skip-forward behavior. - Fix sed error when generating multi-disk configurations by escaping newlines in additional_disks. - Add explicit 'Set a hostname' message to the hostname prompt. - Convert unsafe short-circuit lists to safe if statements to prevent set -e crashes.
This commit is contained in:
@@ -115,9 +115,28 @@ load_state() {
|
|||||||
|
|
||||||
# Helper to run commands via nix run
|
# Helper to run commands via nix run
|
||||||
nrun() {
|
nrun() {
|
||||||
local pkg="$1"
|
local cmd="$1"
|
||||||
shift
|
shift
|
||||||
nix run --extra-experimental-features "nix-command flakes" "nixpkgs#$pkg" -- "$@"
|
if command -v "$cmd" >/dev/null 2>&1; then
|
||||||
|
"$cmd" "$@"
|
||||||
|
else
|
||||||
|
nix run --extra-experimental-features "nix-command flakes" "nixpkgs#$cmd" -- "$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
clear_step_state() {
|
||||||
|
case "$1" in
|
||||||
|
select_disk) TARGET_DRIVE="" ;;
|
||||||
|
get_luks_passphrase) LUKS_PASSWORD="" ;;
|
||||||
|
configure_user) USERNAME=""; HOSTNAME=""; USER_PASSWORD="" ;;
|
||||||
|
select_keymap_locale) KEYMAP_LAYOUT=""; KEYMAP_VARIANT=""; LOCALE="" ;;
|
||||||
|
select_timezone) TIMEZONE="" ;;
|
||||||
|
select_hardware) HARDWARE_MODULES=""; NOMARCHY_HW_OPTS="" ;;
|
||||||
|
confirm_form_factor) FORM_FACTOR="" ;;
|
||||||
|
configure_impermanence) ENABLE_IMPERMANENCE="" ;;
|
||||||
|
select_profiles) SELECTED_PROFILES="" ;;
|
||||||
|
select_nomarchy_rev) NOMARCHY_REV="" ;;
|
||||||
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
header() {
|
header() {
|
||||||
@@ -201,7 +220,7 @@ check_environment() {
|
|||||||
while ! ping -c 1 -W 2 1.1.1.1 &>/dev/null; do
|
while ! ping -c 1 -W 2 1.1.1.1 &>/dev/null; do
|
||||||
error "No internet connection"
|
error "No internet connection"
|
||||||
local choice
|
local choice
|
||||||
choice=$(gum choose "Open Network Manager (nmtui)" "Retry" "Exit")
|
choice=$(gum choose "Open Network Manager (nmtui)" "Retry" "Exit") || exit 1
|
||||||
case "$choice" in
|
case "$choice" in
|
||||||
*nmtui*) nmtui ;;
|
*nmtui*) nmtui ;;
|
||||||
*Exit*) exit 1 ;;
|
*Exit*) exit 1 ;;
|
||||||
@@ -218,8 +237,8 @@ select_disk() {
|
|||||||
section "Disk Selection"
|
section "Disk Selection"
|
||||||
|
|
||||||
if [[ -n "$TARGET_DRIVE" ]]; then
|
if [[ -n "$TARGET_DRIVE" ]]; then
|
||||||
success "Resumed: $TARGET_DRIVE"
|
success "Selected: $TARGET_DRIVE"
|
||||||
return
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Build a richer drive table than the bare `NAME SIZE` lsblk default.
|
# Build a richer drive table than the bare `NAME SIZE` lsblk default.
|
||||||
@@ -230,7 +249,7 @@ select_disk() {
|
|||||||
| grep -vE '^(/dev/(loop|ram|zram|sr))')
|
| grep -vE '^(/dev/(loop|ram|zram|sr))')
|
||||||
|
|
||||||
while IFS= read -r line; do
|
while IFS= read -r line; do
|
||||||
[[ -z "$line" ]] && continue
|
if [[ -z "$line" ]]; then continue; fi
|
||||||
# NAME and SIZE are reliably whitespace-free; ROTA/TRAN are short
|
# NAME and SIZE are reliably whitespace-free; ROTA/TRAN are short
|
||||||
# tokens; VENDOR/MODEL/SERIAL can carry internal spaces. Pull the
|
# tokens; VENDOR/MODEL/SERIAL can carry internal spaces. Pull the
|
||||||
# first four fields off the front, treat the rest as the
|
# first four fields off the front, treat the rest as the
|
||||||
@@ -254,9 +273,9 @@ select_disk() {
|
|||||||
vendor=$(lsblk -d -n -o VENDOR "$dev" 2>/dev/null | sed -E 's/^[[:space:]]+//;s/[[:space:]]+$//')
|
vendor=$(lsblk -d -n -o VENDOR "$dev" 2>/dev/null | sed -E 's/^[[:space:]]+//;s/[[:space:]]+$//')
|
||||||
model=$(lsblk -d -n -o MODEL "$dev" 2>/dev/null | sed -E 's/^[[:space:]]+//;s/[[:space:]]+$//')
|
model=$(lsblk -d -n -o MODEL "$dev" 2>/dev/null | sed -E 's/^[[:space:]]+//;s/[[:space:]]+$//')
|
||||||
serial=$(lsblk -d -n -o SERIAL "$dev" 2>/dev/null | sed -E 's/^[[:space:]]+//;s/[[:space:]]+$//')
|
serial=$(lsblk -d -n -o SERIAL "$dev" 2>/dev/null | sed -E 's/^[[:space:]]+//;s/[[:space:]]+$//')
|
||||||
[[ -z "$vendor" ]] && vendor="--"
|
if [[ -z "$vendor" ]]; then vendor="--"; fi
|
||||||
[[ -z "$model" ]] && model="--"
|
if [[ -z "$model" ]]; then model="--"; fi
|
||||||
[[ -z "$serial" ]] && serial="--"
|
if [[ -z "$serial" ]]; then serial="--"; fi
|
||||||
|
|
||||||
# Tab-separated for column -t -s, then collapse internal whitespace
|
# Tab-separated for column -t -s, then collapse internal whitespace
|
||||||
# in MODEL so multi-space brand strings don't break alignment.
|
# in MODEL so multi-space brand strings don't break alignment.
|
||||||
@@ -281,20 +300,25 @@ select_disk() {
|
|||||||
# gum choose gets the same aligned rows so the picker reads like the table.
|
# gum choose gets the same aligned rows so the picker reads like the table.
|
||||||
local picker
|
local picker
|
||||||
picker=$(printf '%s' "$rows" | column -t -s $'\t')
|
picker=$(printf '%s' "$rows" | column -t -s $'\t')
|
||||||
local choice
|
local choice rc=0
|
||||||
choice=$(printf '%s\n' "$picker" | gum choose --no-limit --header "Select target drive(s) - Use Space to select multiple for BTRFS RAID/Single")
|
choice=$(printf '%s\n' "$picker" | nrun gum choose --no-limit --header "Select target drive(s) - Use Space to select multiple for BTRFS RAID/Single") || rc=$?
|
||||||
|
if [[ $rc -eq 130 || $rc -eq 1 || "$choice" == "not submitted" ]]; then return 130; fi
|
||||||
|
if [[ $rc -ne 0 ]]; then exit $rc; fi
|
||||||
TARGET_DRIVE=$(awk '{print $1}' <<<"$choice" | xargs)
|
TARGET_DRIVE=$(awk '{print $1}' <<<"$choice" | xargs)
|
||||||
|
|
||||||
if [[ -z "$TARGET_DRIVE" ]]; then
|
if [[ -z "$TARGET_DRIVE" ]]; then
|
||||||
error "No drive selected"
|
error "No drive selected"
|
||||||
exit 1
|
return 130
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$DRY_RUN" != "true" ]]; then
|
if [[ "$DRY_RUN" != "true" ]]; then
|
||||||
echo ""
|
echo ""
|
||||||
gum style --foreground 9 --bold "⚠ WARNING: All data on $TARGET_DRIVE will be DESTROYED!"
|
nrun gum style --foreground 9 --bold "⚠ WARNING: All data on $TARGET_DRIVE will be DESTROYED!"
|
||||||
echo ""
|
echo ""
|
||||||
if ! gum confirm "Are you sure you want to use $TARGET_DRIVE?"; then
|
rc=0
|
||||||
|
nrun gum confirm "Are you sure you want to use $TARGET_DRIVE?" || rc=$?
|
||||||
|
if [[ $rc -ne 0 ]]; then
|
||||||
|
if [[ $rc -eq 130 || $rc -eq 1 ]]; then return 130; fi
|
||||||
error "Aborted"
|
error "Aborted"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -318,7 +342,7 @@ get_luks_passphrase() {
|
|||||||
# Already set this session (review-edit loop iterated). Don't re-prompt;
|
# Already set this session (review-edit loop iterated). Don't re-prompt;
|
||||||
# password isn't persisted across runs but is held in memory until
|
# password isn't persisted across runs but is held in memory until
|
||||||
# execute_installation unsets it.
|
# execute_installation unsets it.
|
||||||
[[ -n "${LUKS_PASSWORD:-}" ]] && return
|
if [[ -n "${LUKS_PASSWORD:-}" ]]; then return; fi
|
||||||
|
|
||||||
section "Disk Encryption"
|
section "Disk Encryption"
|
||||||
|
|
||||||
@@ -326,12 +350,19 @@ get_luks_passphrase() {
|
|||||||
info "Enter a strong passphrase (you'll need this at every boot)."
|
info "Enter a strong passphrase (you'll need this at every boot)."
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
local pass1 pass2
|
local pass1 pass2 rc=0
|
||||||
while true; do
|
while true; do
|
||||||
pass1=$(gum input --password --placeholder "Enter LUKS passphrase")
|
rc=0
|
||||||
[[ -z "$pass1" ]] && continue
|
pass1=$(nrun gum input --password --placeholder "Enter LUKS passphrase") || rc=$?
|
||||||
|
if [[ $rc -eq 130 || $rc -eq 1 || "$pass1" == "not submitted" ]]; then return 130; fi
|
||||||
|
if [[ $rc -ne 0 ]]; then exit $rc; fi
|
||||||
|
|
||||||
pass2=$(gum input --password --placeholder "Confirm passphrase")
|
if [[ -z "$pass1" ]]; then continue; fi
|
||||||
|
|
||||||
|
rc=0
|
||||||
|
pass2=$(nrun gum input --password --placeholder "Confirm passphrase") || rc=$?
|
||||||
|
if [[ $rc -eq 130 || $rc -eq 1 || "$pass2" == "not submitted" ]]; then return 130; fi
|
||||||
|
if [[ $rc -ne 0 ]]; then exit $rc; fi
|
||||||
|
|
||||||
if [[ "$pass1" == "$pass2" ]]; then
|
if [[ "$pass1" == "$pass2" ]]; then
|
||||||
LUKS_PASSWORD="$pass1"
|
LUKS_PASSWORD="$pass1"
|
||||||
@@ -351,9 +382,24 @@ get_luks_passphrase() {
|
|||||||
configure_user() {
|
configure_user() {
|
||||||
section "User Configuration"
|
section "User Configuration"
|
||||||
|
|
||||||
|
if [[ -n "$USERNAME" && -n "$HOSTNAME" ]]; then
|
||||||
|
# Password check skipped in dry run or if already set
|
||||||
|
if [[ "$DRY_RUN" == "true" ]] || [[ -n "$USER_PASSWORD" ]]; then
|
||||||
|
success "User $USERNAME @ $HOSTNAME configured"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
local rc=0
|
||||||
if [[ -z "$USERNAME" ]]; then
|
if [[ -z "$USERNAME" ]]; then
|
||||||
USERNAME=$(nrun gum input --placeholder "Enter username (lowercase, no spaces)")
|
rc=0
|
||||||
|
USERNAME=$(nrun gum input --placeholder "Enter username (lowercase, no spaces)") || rc=$?
|
||||||
|
if [[ $rc -eq 130 || $rc -eq 1 || "$USERNAME" == "not submitted" ]]; then return 130; fi
|
||||||
|
if [[ $rc -ne 0 ]]; then exit $rc; fi
|
||||||
|
|
||||||
if [[ -z "$USERNAME" ]] || [[ ! "$USERNAME" =~ ^[a-z][a-z0-9_-]*$ ]]; then
|
if [[ -z "$USERNAME" ]] || [[ ! "$USERNAME" =~ ^[a-z][a-z0-9_-]*$ ]]; then
|
||||||
|
# If they just hit Enter/Esc and we got here, they might want to go back
|
||||||
|
if [[ -z "$USERNAME" ]]; then return 130; fi
|
||||||
error "Invalid username"
|
error "Invalid username"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -361,8 +407,14 @@ configure_user() {
|
|||||||
success "Username: $USERNAME"
|
success "Username: $USERNAME"
|
||||||
|
|
||||||
if [[ -z "$HOSTNAME" ]]; then
|
if [[ -z "$HOSTNAME" ]]; then
|
||||||
HOSTNAME=$(nrun gum input --value "nomarchy" --placeholder "Hostname for this machine")
|
info "Set a hostname for this machine"
|
||||||
|
rc=0
|
||||||
|
HOSTNAME=$(nrun gum input --value "nomarchy" --placeholder "Hostname for this machine") || rc=$?
|
||||||
|
if [[ $rc -eq 130 || $rc -eq 1 || "$HOSTNAME" == "not submitted" ]]; then return 130; fi
|
||||||
|
if [[ $rc -ne 0 ]]; then exit $rc; fi
|
||||||
|
|
||||||
if [[ -z "$HOSTNAME" ]] || [[ ! "$HOSTNAME" =~ ^[a-z0-9][a-z0-9-]*$ ]]; then
|
if [[ -z "$HOSTNAME" ]] || [[ ! "$HOSTNAME" =~ ^[a-z0-9][a-z0-9-]*$ ]]; then
|
||||||
|
if [[ -z "$HOSTNAME" ]]; then return 130; fi
|
||||||
error "Invalid hostname (use lowercase letters, digits, and hyphens only)"
|
error "Invalid hostname (use lowercase letters, digits, and hyphens only)"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -380,10 +432,17 @@ configure_user() {
|
|||||||
info "Set a password for your user account"
|
info "Set a password for your user account"
|
||||||
local pass1 pass2
|
local pass1 pass2
|
||||||
while true; do
|
while true; do
|
||||||
pass1=$(nrun gum input --password --placeholder "Enter user password")
|
rc=0
|
||||||
[[ -z "$pass1" ]] && continue
|
pass1=$(nrun gum input --password --placeholder "Enter user password") || rc=$?
|
||||||
|
if [[ $rc -eq 130 || $rc -eq 1 || "$pass1" == "not submitted" ]]; then return 130; fi
|
||||||
|
if [[ $rc -ne 0 ]]; then exit $rc; fi
|
||||||
|
|
||||||
pass2=$(nrun gum input --password --placeholder "Confirm user password")
|
if [[ -z "$pass1" ]]; then continue; fi
|
||||||
|
|
||||||
|
rc=0
|
||||||
|
pass2=$(nrun gum input --password --placeholder "Confirm user password") || rc=$?
|
||||||
|
if [[ $rc -eq 130 || $rc -eq 1 || "$pass2" == "not submitted" ]]; then return 130; fi
|
||||||
|
if [[ $rc -ne 0 ]]; then exit $rc; fi
|
||||||
|
|
||||||
if [[ "$pass1" == "$pass2" ]]; then
|
if [[ "$pass1" == "$pass2" ]]; then
|
||||||
USER_PASSWORD="$pass1"
|
USER_PASSWORD="$pass1"
|
||||||
@@ -407,17 +466,28 @@ configure_user() {
|
|||||||
select_keymap_locale() {
|
select_keymap_locale() {
|
||||||
section "Keyboard & Language"
|
section "Keyboard & Language"
|
||||||
|
|
||||||
|
if [[ -n "$KEYMAP_LAYOUT" && -n "$LOCALE" ]]; then
|
||||||
|
success "Keymap ($KEYMAP_LAYOUT) and Locale ($LOCALE) set"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local choice rc=0
|
||||||
if [[ -z "$KEYMAP_LAYOUT" ]]; then
|
if [[ -z "$KEYMAP_LAYOUT" ]]; then
|
||||||
local choice
|
rc=0
|
||||||
choice=$(printf '%s\n' "${COMMON_KEYMAPS[@]}" "Other…" \
|
choice=$(printf '%s\n' "${COMMON_KEYMAPS[@]}" "Other…" \
|
||||||
| nrun gum choose --header "Keyboard layout")
|
| nrun gum choose --header "Keyboard layout") || rc=$?
|
||||||
|
if [[ $rc -eq 130 || $rc -eq 1 || "$choice" == "not submitted" ]]; then return 130; fi
|
||||||
|
if [[ $rc -ne 0 ]]; then exit $rc; fi
|
||||||
if [[ "$choice" == "Other…" ]]; then
|
if [[ "$choice" == "Other…" ]]; then
|
||||||
|
rc=0
|
||||||
KEYMAP_LAYOUT=$(localectl list-x11-keymap-layouts 2>/dev/null \
|
KEYMAP_LAYOUT=$(localectl list-x11-keymap-layouts 2>/dev/null \
|
||||||
| nrun gum filter --placeholder "Search keyboard layout…")
|
| nrun gum filter --placeholder "Search keyboard layout…") || rc=$?
|
||||||
|
if [[ $rc -eq 130 || $rc -eq 1 || "$KEYMAP_LAYOUT" == "not submitted" ]]; then return 130; fi
|
||||||
|
if [[ $rc -ne 0 ]]; then exit $rc; fi
|
||||||
else
|
else
|
||||||
KEYMAP_LAYOUT="$choice"
|
KEYMAP_LAYOUT="$choice"
|
||||||
fi
|
fi
|
||||||
[[ -z "$KEYMAP_LAYOUT" ]] && KEYMAP_LAYOUT="us"
|
if [[ -z "$KEYMAP_LAYOUT" ]]; then KEYMAP_LAYOUT="us"; fi
|
||||||
fi
|
fi
|
||||||
success "Keyboard layout: $KEYMAP_LAYOUT"
|
success "Keyboard layout: $KEYMAP_LAYOUT"
|
||||||
|
|
||||||
@@ -427,12 +497,20 @@ select_keymap_locale() {
|
|||||||
variants=$(localectl list-x11-keymap-variants "$KEYMAP_LAYOUT" 2>/dev/null || true)
|
variants=$(localectl list-x11-keymap-variants "$KEYMAP_LAYOUT" 2>/dev/null || true)
|
||||||
if [[ -n "$variants" ]]; then
|
if [[ -n "$variants" ]]; then
|
||||||
local v
|
local v
|
||||||
|
rc=0
|
||||||
v=$(printf '(none)\n%s\n' "$variants" \
|
v=$(printf '(none)\n%s\n' "$variants" \
|
||||||
| nrun gum filter --placeholder "Variant (optional)" --value "(none)")
|
| nrun gum filter --placeholder "Variant (optional)" --value "(none)") || rc=$?
|
||||||
[[ "$v" == "(none)" || -z "$v" ]] || KEYMAP_VARIANT="$v"
|
if [[ $rc -eq 130 || $rc -eq 1 || "$v" == "not submitted" ]]; then return 130; fi
|
||||||
|
if [[ $rc -ne 0 ]]; then exit $rc; fi
|
||||||
|
if [[ -n "$v" && "$v" != "(none)" ]]; then KEYMAP_VARIANT="$v"; fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
[[ -n "$KEYMAP_VARIANT" ]] && success "Variant: $KEYMAP_VARIANT" || success "Variant: (default)"
|
|
||||||
|
if [[ -n "$KEYMAP_VARIANT" ]]; then
|
||||||
|
success "Variant: $KEYMAP_VARIANT"
|
||||||
|
else
|
||||||
|
success "Variant: (default)"
|
||||||
|
fi
|
||||||
|
|
||||||
# Apply to the live session, best-effort.
|
# Apply to the live session, best-effort.
|
||||||
loadkeys "$KEYMAP_LAYOUT" 2>/dev/null || true
|
loadkeys "$KEYMAP_LAYOUT" 2>/dev/null || true
|
||||||
@@ -442,16 +520,22 @@ select_keymap_locale() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -z "$LOCALE" ]]; then
|
if [[ -z "$LOCALE" ]]; then
|
||||||
local choice
|
local choice rc=0
|
||||||
|
rc=0
|
||||||
choice=$(printf '%s\n' "${COMMON_LOCALES[@]}" "Other…" \
|
choice=$(printf '%s\n' "${COMMON_LOCALES[@]}" "Other…" \
|
||||||
| nrun gum choose --header "Language / locale")
|
| nrun gum choose --header "Language / locale") || rc=$?
|
||||||
|
if [[ $rc -eq 130 || $rc -eq 1 || "$choice" == "not submitted" ]]; then return 130; fi
|
||||||
|
if [[ $rc -ne 0 ]]; then exit $rc; fi
|
||||||
if [[ "$choice" == "Other…" ]]; then
|
if [[ "$choice" == "Other…" ]]; then
|
||||||
|
rc=0
|
||||||
LOCALE=$(localectl list-locales 2>/dev/null \
|
LOCALE=$(localectl list-locales 2>/dev/null \
|
||||||
| nrun gum filter --placeholder "Search locale…")
|
| nrun gum filter --placeholder "Search locale…") || rc=$?
|
||||||
|
if [[ $rc -eq 130 || $rc -eq 1 || "$LOCALE" == "not submitted" ]]; then return 130; fi
|
||||||
|
if [[ $rc -ne 0 ]]; then exit $rc; fi
|
||||||
else
|
else
|
||||||
LOCALE="$choice"
|
LOCALE="$choice"
|
||||||
fi
|
fi
|
||||||
[[ -z "$LOCALE" ]] && LOCALE="en_US.UTF-8"
|
if [[ -z "$LOCALE" ]]; then LOCALE="en_US.UTF-8"; fi
|
||||||
fi
|
fi
|
||||||
success "Locale: $LOCALE"
|
success "Locale: $LOCALE"
|
||||||
|
|
||||||
@@ -465,16 +549,18 @@ select_keymap_locale() {
|
|||||||
select_timezone() {
|
select_timezone() {
|
||||||
section "Timezone"
|
section "Timezone"
|
||||||
|
|
||||||
if [[ -n "$TIMEZONE" ]] && [[ "$TIMEZONE" != "UTC" ]]; then
|
if [[ -n "$TIMEZONE" ]]; then
|
||||||
success "Resumed: $TIMEZONE"
|
success "Timezone set to $TIMEZONE"
|
||||||
return
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local timezones
|
local timezones rc=0
|
||||||
timezones=$(timedatectl list-timezones 2>/dev/null || echo "UTC")
|
timezones=$(timedatectl list-timezones 2>/dev/null || echo "UTC")
|
||||||
TIMEZONE=$(echo "$timezones" | gum filter --placeholder "Search timezone...")
|
rc=0
|
||||||
|
TIMEZONE=$(echo "$timezones" | gum filter --placeholder "Search timezone...") || rc=$?
|
||||||
[[ -z "$TIMEZONE" ]] && TIMEZONE="UTC"
|
if [[ $rc -eq 130 || $rc -eq 1 || "$TIMEZONE" == "not submitted" ]]; then return 130; fi
|
||||||
|
if [[ $rc -ne 0 ]]; then exit $rc; fi
|
||||||
|
if [[ -z "$TIMEZONE" ]]; then TIMEZONE="UTC"; fi
|
||||||
success "Timezone: $TIMEZONE"
|
success "Timezone: $TIMEZONE"
|
||||||
save_state
|
save_state
|
||||||
}
|
}
|
||||||
@@ -487,8 +573,8 @@ select_hardware() {
|
|||||||
section "Hardware Configuration"
|
section "Hardware Configuration"
|
||||||
|
|
||||||
if [[ -n "$HARDWARE_MODULES" ]]; then
|
if [[ -n "$HARDWARE_MODULES" ]]; then
|
||||||
success "Resumed hardware modules"
|
success "Hardware configured"
|
||||||
return
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local dmi_vendor dmi_product detect_output
|
local dmi_vendor dmi_product detect_output
|
||||||
@@ -514,24 +600,39 @@ select_hardware() {
|
|||||||
done <<< "$detect_output"
|
done <<< "$detect_output"
|
||||||
|
|
||||||
# Let the user accept, extend, or replace the detection.
|
# Let the user accept, extend, or replace the detection.
|
||||||
local choice
|
local choice rc=0
|
||||||
choice=$(nrun gum choose --header "Hardware configuration" \
|
while true; do
|
||||||
"Accept detected modules" \
|
rc=0
|
||||||
"Add an extra nixos-hardware module" \
|
choice=$(nrun gum choose --header "Hardware configuration" \
|
||||||
"Pick from the manual list (override)")
|
"Accept detected modules" \
|
||||||
|
"Add an extra nixos-hardware module" \
|
||||||
|
"Pick from the manual list (override)") || rc=$?
|
||||||
|
if [[ $rc -eq 130 || $rc -eq 1 || "$choice" == "not submitted" ]]; then return 130; fi
|
||||||
|
if [[ $rc -ne 0 ]]; then exit $rc; fi
|
||||||
|
|
||||||
case "$choice" in
|
case "$choice" in
|
||||||
"Add an extra nixos-hardware module")
|
"Add an extra nixos-hardware module")
|
||||||
local extra
|
local extra extra_rc=0
|
||||||
extra=$(nrun gum input --placeholder "e.g. asus-zephyrus-ga401 (no 'nixos-hardware.' prefix)")
|
extra=$(nrun gum input --placeholder "e.g. asus-zephyrus-ga401 (no 'nixos-hardware.' prefix)") || extra_rc=$?
|
||||||
[[ -n "$extra" ]] && modules+=("$extra")
|
if [[ $extra_rc -eq 130 || $extra_rc -eq 1 || "$extra" == "not submitted" ]]; then continue; fi
|
||||||
;;
|
if [[ $extra_rc -ne 0 ]]; then exit $extra_rc; fi
|
||||||
"Pick from the manual list (override)")
|
if [[ -n "$extra" ]]; then modules+=("$extra"); fi
|
||||||
modules=()
|
break
|
||||||
hw_opts=()
|
;;
|
||||||
_select_hardware_manual modules hw_opts
|
"Pick from the manual list (override)")
|
||||||
;;
|
modules=()
|
||||||
esac
|
hw_opts=()
|
||||||
|
local manual_rc=0
|
||||||
|
_select_hardware_manual modules hw_opts || manual_rc=$?
|
||||||
|
if [[ $manual_rc -eq 130 ]]; then continue; fi
|
||||||
|
if [[ $manual_rc -ne 0 ]]; then exit $manual_rc; fi
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
"Accept detected modules")
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
# De-duplicate while preserving order.
|
# De-duplicate while preserving order.
|
||||||
local seen="" uniq_mods=() m
|
local seen="" uniq_mods=() m
|
||||||
@@ -571,66 +672,76 @@ _select_hardware_manual() {
|
|||||||
local -n _mods_ref="$1"
|
local -n _mods_ref="$1"
|
||||||
local -n _opts_ref="$2"
|
local -n _opts_ref="$2"
|
||||||
|
|
||||||
local vendor
|
local vendor rc=0
|
||||||
|
rc=0
|
||||||
vendor=$(nrun gum choose --header "Pick vendor" \
|
vendor=$(nrun gum choose --header "Pick vendor" \
|
||||||
"Framework" "Dell" "Lenovo" "Apple (T2 Mac)" "Microsoft Surface" "ASUS" "System76" "Other...")
|
"Framework" "Dell" "Lenovo" "Apple (T2 Mac)" "Microsoft Surface" "ASUS" "System76" "Other...") || rc=$?
|
||||||
|
if [[ $rc -eq 130 || $rc -eq 1 || "$vendor" == "not submitted" ]]; then return 130; fi
|
||||||
|
if [[ $rc -ne 0 ]]; then exit $rc; fi
|
||||||
case "$vendor" in
|
case "$vendor" in
|
||||||
Framework)
|
Framework)
|
||||||
local model
|
local model model_rc=0
|
||||||
model=$(nrun gum choose \
|
model=$(nrun gum choose \
|
||||||
"framework-16-7040-amd" \
|
"framework-16-7040-amd" \
|
||||||
"framework-13-amd-ai-300-series" \
|
"framework-13-amd-ai-300-series" \
|
||||||
"framework-13-7040-amd" \
|
"framework-13-7040-amd" \
|
||||||
"framework-13-13th-gen-intel" \
|
"framework-13-13th-gen-intel" \
|
||||||
"framework-13-12th-gen-intel" \
|
"framework-13-12th-gen-intel" \
|
||||||
"framework-13-11th-gen-intel")
|
"framework-13-11th-gen-intel") || model_rc=$?
|
||||||
|
if [[ $model_rc -eq 130 || $model_rc -eq 1 || "$model" == "not submitted" ]]; then return 130; fi
|
||||||
|
if [[ $model_rc -ne 0 ]]; then exit $model_rc; fi
|
||||||
_mods_ref+=("$model")
|
_mods_ref+=("$model")
|
||||||
_opts_ref+=("isFramework=true")
|
_opts_ref+=("isFramework=true")
|
||||||
;;
|
;;
|
||||||
Dell)
|
Dell)
|
||||||
local model
|
local model model_rc=0
|
||||||
model=$(nrun gum choose \
|
model=$(nrun gum choose \
|
||||||
"dell-xps-15-9500" "dell-xps-15-9510" "dell-xps-15-9520" \
|
"dell-xps-15-9500" "dell-xps-15-9510" "dell-xps-15-9520" \
|
||||||
"dell-xps-13-9310" "dell-xps-13-9370" "dell-xps-13-9380" \
|
"dell-xps-13-9310" "dell-xps-13-9370" "dell-xps-13-9380" \
|
||||||
"dell-precision-5530" "dell-latitude-7480")
|
"dell-precision-5530" "dell-latitude-7480") || model_rc=$?
|
||||||
|
if [[ $model_rc -eq 130 || $model_rc -eq 1 || "$model" == "not submitted" ]]; then return 130; fi
|
||||||
|
if [[ $model_rc -ne 0 ]]; then exit $model_rc; fi
|
||||||
_mods_ref+=("$model")
|
_mods_ref+=("$model")
|
||||||
[[ "$model" == *xps* ]] && _opts_ref+=("isXPS=true")
|
if [[ "$model" == *xps* ]]; then _opts_ref+=("isXPS=true"); fi
|
||||||
;;
|
;;
|
||||||
Lenovo)
|
Lenovo)
|
||||||
local model
|
local model model_rc=0
|
||||||
model=$(nrun gum choose \
|
model=$(nrun gum choose \
|
||||||
"lenovo-thinkpad-x1-carbon-gen11" "lenovo-thinkpad-x1-carbon-gen10" \
|
"lenovo-thinkpad-x1-carbon-gen11" "lenovo-thinkpad-x1-carbon-gen10" \
|
||||||
"lenovo-thinkpad-x1-carbon-gen9" "lenovo-thinkpad-x1-extreme" \
|
"lenovo-thinkpad-x1-carbon-gen9" "lenovo-thinkpad-x1-extreme" \
|
||||||
"lenovo-thinkpad-t14-amd-gen3" "lenovo-thinkpad-t14-amd-gen2" \
|
"lenovo-thinkpad-t14-amd-gen3" "lenovo-thinkpad-t14-amd-gen2" \
|
||||||
"lenovo-thinkpad-t480" "lenovo-thinkpad-l13")
|
"lenovo-thinkpad-t480" "lenovo-thinkpad-l13") || model_rc=$?
|
||||||
|
if [[ $model_rc -eq 130 || $model_rc -eq 1 || "$model" == "not submitted" ]]; then return 130; fi
|
||||||
|
if [[ $model_rc -ne 0 ]]; then exit $model_rc; fi
|
||||||
_mods_ref+=("$model")
|
_mods_ref+=("$model")
|
||||||
;;
|
;;
|
||||||
"Apple (T2 Mac)")
|
|
||||||
_mods_ref+=("apple-t2")
|
|
||||||
_opts_ref+=("isT2Mac=true")
|
|
||||||
;;
|
|
||||||
"Microsoft Surface")
|
"Microsoft Surface")
|
||||||
local model
|
local model model_rc=0
|
||||||
model=$(nrun gum choose \
|
model=$(nrun gum choose \
|
||||||
"microsoft-surface-pro-9" "microsoft-surface-pro-8" "microsoft-surface-pro-7" \
|
"microsoft-surface-pro-9" "microsoft-surface-pro-8" "microsoft-surface-pro-7" \
|
||||||
"microsoft-surface-laptop-5" "microsoft-surface-laptop-4" "microsoft-surface-laptop-3")
|
"microsoft-surface-laptop-5" "microsoft-surface-laptop-4" "microsoft-surface-laptop-3") || model_rc=$?
|
||||||
|
if [[ $model_rc -eq 130 || $model_rc -eq 1 || "$model" == "not submitted" ]]; then return 130; fi
|
||||||
|
if [[ $model_rc -ne 0 ]]; then exit $model_rc; fi
|
||||||
_mods_ref+=("$model")
|
_mods_ref+=("$model")
|
||||||
;;
|
;;
|
||||||
ASUS)
|
ASUS)
|
||||||
local model
|
local model model_rc=0
|
||||||
model=$(nrun gum choose \
|
model=$(nrun gum choose \
|
||||||
"asus-zephyrus-ga403" "asus-zephyrus-ga402" "asus-zephyrus-ga401" \
|
"asus-zephyrus-ga403" "asus-zephyrus-ga402" "asus-zephyrus-ga401" \
|
||||||
"asus-zephyrus-ga503" "asus-rog-strix-g513" "asus-zenbook-ux")
|
"asus-zephyrus-ga503" "asus-rog-strix-g513" "asus-zenbook-ux") || model_rc=$?
|
||||||
|
if [[ $model_rc -eq 130 || $model_rc -eq 1 || "$model" == "not submitted" ]]; then return 130; fi
|
||||||
|
if [[ $model_rc -ne 0 ]]; then exit $model_rc; fi
|
||||||
_mods_ref+=("$model")
|
_mods_ref+=("$model")
|
||||||
;;
|
;;
|
||||||
System76)
|
System76)
|
||||||
_mods_ref+=("system76")
|
_mods_ref+=("system76")
|
||||||
;;
|
;;
|
||||||
"Other...")
|
"Other...")
|
||||||
local custom
|
local custom custom_rc=0
|
||||||
custom=$(nrun gum input --placeholder "e.g. asus-zephyrus-ga401 (no 'nixos-hardware.' prefix)")
|
custom=$(nrun gum input --placeholder "e.g. asus-zephyrus-ga401 (no 'nixos-hardware.' prefix)") || custom_rc=$?
|
||||||
[[ -n "$custom" ]] && _mods_ref+=("$custom")
|
if [[ $custom_rc -eq 130 || $custom_rc -eq 1 || "$custom" == "not submitted" ]]; then return 130; fi
|
||||||
|
if [[ $custom_rc -ne 0 ]]; then exit $custom_rc; fi
|
||||||
|
if [[ -n "$custom" ]]; then _mods_ref+=("$custom"); fi
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
@@ -656,9 +767,12 @@ confirm_form_factor() {
|
|||||||
fi
|
fi
|
||||||
info "Auto-detected: $default"
|
info "Auto-detected: $default"
|
||||||
|
|
||||||
if nrun gum confirm "Treat this machine as a $default?"; then
|
rc=0
|
||||||
|
nrun gum confirm "Treat this machine as a $default?" || rc=$?
|
||||||
|
if [[ $rc -eq 0 ]]; then
|
||||||
FORM_FACTOR="$default"
|
FORM_FACTOR="$default"
|
||||||
else
|
else
|
||||||
|
if [[ $rc -eq 130 || $rc -eq 1 ]]; then return 130; fi
|
||||||
FORM_FACTOR=$([[ "$default" == "laptop" ]] && echo desktop || echo laptop)
|
FORM_FACTOR=$([[ "$default" == "laptop" ]] && echo desktop || echo laptop)
|
||||||
fi
|
fi
|
||||||
success "Form factor: $FORM_FACTOR"
|
success "Form factor: $FORM_FACTOR"
|
||||||
@@ -672,9 +786,13 @@ confirm_form_factor() {
|
|||||||
configure_impermanence() {
|
configure_impermanence() {
|
||||||
section "Impermanence (Optional)"
|
section "Impermanence (Optional)"
|
||||||
|
|
||||||
if [[ "$RESUME" == "true" ]] && [[ "$ENABLE_IMPERMANENCE" != "" ]]; then
|
if [[ -n "$ENABLE_IMPERMANENCE" ]]; then
|
||||||
success "Resumed: impermanence = $ENABLE_IMPERMANENCE"
|
if [[ "$ENABLE_IMPERMANENCE" == "true" ]]; then
|
||||||
return
|
success "Impermanence enabled"
|
||||||
|
else
|
||||||
|
info "Impermanence disabled"
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
info "Impermanence erases your root filesystem on every boot."
|
info "Impermanence erases your root filesystem on every boot."
|
||||||
@@ -682,10 +800,13 @@ configure_impermanence() {
|
|||||||
info "This provides a clean, reproducible system."
|
info "This provides a clean, reproducible system."
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
if gum confirm "Enable Impermanence?"; then
|
rc=0
|
||||||
|
gum confirm "Enable Impermanence?" || rc=$?
|
||||||
|
if [[ $rc -eq 0 ]]; then
|
||||||
ENABLE_IMPERMANENCE="true"
|
ENABLE_IMPERMANENCE="true"
|
||||||
success "Impermanence enabled"
|
success "Impermanence enabled"
|
||||||
else
|
else
|
||||||
|
if [[ $rc -eq 130 || $rc -eq 1 ]]; then return 130; fi
|
||||||
info "Impermanence disabled (traditional persistent root)"
|
info "Impermanence disabled (traditional persistent root)"
|
||||||
fi
|
fi
|
||||||
save_state
|
save_state
|
||||||
@@ -699,21 +820,23 @@ select_profiles() {
|
|||||||
section "Software Profiles"
|
section "Software Profiles"
|
||||||
|
|
||||||
if [[ -n "$SELECTED_PROFILES" ]]; then
|
if [[ -n "$SELECTED_PROFILES" ]]; then
|
||||||
success "Resumed selected profiles"
|
success "Software profiles selected"
|
||||||
return
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
info "Pick optional software profiles to include in your configuration."
|
info "Pick optional software profiles to include in your configuration."
|
||||||
info "Multiple selection allowed (Space to select, Enter to confirm)."
|
info "Multiple selection allowed (Space to select, Enter to confirm)."
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
rc=0
|
||||||
SELECTED_PROFILES=$(nrun gum choose --no-limit --header "Select Profiles" \
|
SELECTED_PROFILES=$(nrun gum choose --no-limit --header "Select Profiles" \
|
||||||
"Dev (VSCode, Git, Lazygit, Zed, Docker)" \
|
"Dev (VSCode, Git, Lazygit, Zed, Docker)" \
|
||||||
"Gaming (Steam, Gamemode, Lutris, Heroic)" \
|
"Gaming (Steam, Gamemode, Lutris, Heroic)" \
|
||||||
"Office (LibreOffice, Thunderbird, Obsidian, Zotero)" \
|
"Office (LibreOffice, Thunderbird, Obsidian, Zotero)" \
|
||||||
"Media (VLC, OBS Studio, GIMP, Inkscape, Spotify)" \
|
"Media (VLC, OBS Studio, GIMP, Inkscape, Spotify)" \
|
||||||
"CLI Utils (ripgrep, fd, bat, eza, zoxide, fzf)")
|
"CLI Utils (ripgrep, fd, bat, eza, zoxide, fzf)") || rc=$?
|
||||||
|
if [[ $rc -eq 130 || $rc -eq 1 || "$SELECTED_PROFILES" == "not submitted" || "$action" == "not submitted" || "$choices" == "not submitted" ]]; then return 130; fi
|
||||||
|
if [[ $rc -ne 0 ]]; then exit $rc; fi
|
||||||
if [[ -z "$SELECTED_PROFILES" ]]; then
|
if [[ -z "$SELECTED_PROFILES" ]]; then
|
||||||
info "No profiles selected (minimal install)"
|
info "No profiles selected (minimal install)"
|
||||||
else
|
else
|
||||||
@@ -752,11 +875,14 @@ review_configuration() {
|
|||||||
nrun gum style --foreground 9 "This will DESTROY all data on $TARGET_DRIVE"
|
nrun gum style --foreground 9 "This will DESTROY all data on $TARGET_DRIVE"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
local action
|
local action rc=0
|
||||||
|
rc=0
|
||||||
action=$(nrun gum choose --header "Choose:" \
|
action=$(nrun gum choose --header "Choose:" \
|
||||||
"Continue with installation" \
|
"Continue with installation" \
|
||||||
"Edit a field" \
|
"Edit a field" \
|
||||||
"Abort")
|
"Abort") || rc=$?
|
||||||
|
if [[ $rc -eq 130 || $rc -eq 1 || "$action" == "not submitted" ]]; then return 130; fi
|
||||||
|
if [[ $rc -ne 0 ]]; then exit $rc; fi
|
||||||
case "$action" in
|
case "$action" in
|
||||||
"Continue with installation") return 0 ;;
|
"Continue with installation") return 0 ;;
|
||||||
"Edit a field") edit_fields; return 2 ;;
|
"Edit a field") edit_fields; return 2 ;;
|
||||||
@@ -773,7 +899,8 @@ review_configuration() {
|
|||||||
edit_fields() {
|
edit_fields() {
|
||||||
section "Edit Fields"
|
section "Edit Fields"
|
||||||
|
|
||||||
local choices
|
local choices rc=0
|
||||||
|
rc=0
|
||||||
choices=$(nrun gum choose --no-limit --header "Pick fields to re-enter (space to select, enter to confirm):" \
|
choices=$(nrun gum choose --no-limit --header "Pick fields to re-enter (space to select, enter to confirm):" \
|
||||||
"Drive ($TARGET_DRIVE)" \
|
"Drive ($TARGET_DRIVE)" \
|
||||||
"User and host ($USERNAME @ $HOSTNAME)" \
|
"User and host ($USERNAME @ $HOSTNAME)" \
|
||||||
@@ -783,9 +910,10 @@ edit_fields() {
|
|||||||
"Form factor ($FORM_FACTOR)" \
|
"Form factor ($FORM_FACTOR)" \
|
||||||
"Impermanence ($ENABLE_IMPERMANENCE)" \
|
"Impermanence ($ENABLE_IMPERMANENCE)" \
|
||||||
"Profiles (${SELECTED_PROFILES:-none})" \
|
"Profiles (${SELECTED_PROFILES:-none})" \
|
||||||
"Nomarchy rev (${NOMARCHY_REV:-main})")
|
"Nomarchy rev (${NOMARCHY_REV:-main})") || rc=$?
|
||||||
|
if [[ $rc -eq 130 || $rc -eq 1 || "$choices" == "not submitted" ]]; then return 130; fi
|
||||||
[[ -z "$choices" ]] && return
|
if [[ $rc -ne 0 ]]; then exit $rc; fi
|
||||||
|
if [[ -z "$choices" ]]; then return 0; fi
|
||||||
|
|
||||||
while IFS= read -r f; do
|
while IFS= read -r f; do
|
||||||
case "$f" in
|
case "$f" in
|
||||||
@@ -906,7 +1034,10 @@ execute_installation() {
|
|||||||
"
|
"
|
||||||
done
|
done
|
||||||
|
|
||||||
sed "s|@MAIN_DRIVE@|${main_drive}|g; s|@BTRFS_DEVICES@|${btrfs_devs}|g; s|@ADDITIONAL_DISKS@|${additional_disks}|g" "$disko_file" > "$tmp_disko"
|
# Escape newlines for sed
|
||||||
|
local escaped_disks
|
||||||
|
escaped_disks=$(printf '%s\n' "$additional_disks" | sed ':a;N;$!ba;s/\n/\\n/g')
|
||||||
|
sed "s|@MAIN_DRIVE@|${main_drive}|g; s|@BTRFS_DEVICES@|${btrfs_devs}|g; s|@ADDITIONAL_DISKS@|${escaped_disks}|g" "$disko_file" > "$tmp_disko"
|
||||||
else
|
else
|
||||||
disko_file="$NOMARCHY_REPO/installer/disko-golden.nix"
|
disko_file="$NOMARCHY_REPO/installer/disko-golden.nix"
|
||||||
sed "s|@TARGET_DRIVE@|${TARGET_DRIVE}|g" "$disko_file" > "$tmp_disko"
|
sed "s|@TARGET_DRIVE@|${TARGET_DRIVE}|g" "$disko_file" > "$tmp_disko"
|
||||||
@@ -1071,13 +1202,15 @@ EOF
|
|||||||
|
|
||||||
generate_flake_config() {
|
generate_flake_config() {
|
||||||
local impermanence_opt=""
|
local impermanence_opt=""
|
||||||
[[ "$ENABLE_IMPERMANENCE" == "true" ]] && impermanence_opt="nomarchy.system.impermanence.enable = true;"
|
if [[ "$ENABLE_IMPERMANENCE" == "true" ]]; then
|
||||||
|
impermanence_opt="nomarchy.system.impermanence.enable = true;"
|
||||||
|
fi
|
||||||
|
|
||||||
local PROFILE_SYSTEM_OPTS=""
|
local PROFILE_SYSTEM_OPTS=""
|
||||||
local PROFILE_HOME_PACKAGES=""
|
local PROFILE_HOME_PACKAGES=""
|
||||||
|
|
||||||
while IFS= read -r profile; do
|
while IFS= read -r profile; do
|
||||||
[[ -z "$profile" ]] && continue
|
if [[ -z "$profile" ]]; then continue; fi
|
||||||
case "$profile" in
|
case "$profile" in
|
||||||
"Dev "*)
|
"Dev "*)
|
||||||
PROFILE_SYSTEM_OPTS+=$'\n # Dev profile\n nomarchy.system.virtualization.docker.enable = true;'
|
PROFILE_SYSTEM_OPTS+=$'\n # Dev profile\n nomarchy.system.virtualization.docker.enable = true;'
|
||||||
@@ -1431,11 +1564,7 @@ main() {
|
|||||||
|
|
||||||
check_environment
|
check_environment
|
||||||
|
|
||||||
# Loop: prompts (each skips when its var is set) → review → Continue|Edit.
|
local steps=(
|
||||||
# On Edit, edit_fields() clears the chosen vars and the next iteration
|
|
||||||
# re-prompts only those. Continue breaks. --resume lands here with vars
|
|
||||||
# already loaded, so the first pass goes straight to review.
|
|
||||||
while true; do
|
|
||||||
select_disk
|
select_disk
|
||||||
get_luks_passphrase
|
get_luks_passphrase
|
||||||
configure_user
|
configure_user
|
||||||
@@ -1445,7 +1574,65 @@ main() {
|
|||||||
confirm_form_factor
|
confirm_form_factor
|
||||||
configure_impermanence
|
configure_impermanence
|
||||||
select_profiles
|
select_profiles
|
||||||
if review_configuration; then break; fi
|
)
|
||||||
|
|
||||||
|
local current_step=0
|
||||||
|
while true; do
|
||||||
|
if (( current_step < 0 )); then
|
||||||
|
error "Installation aborted by user."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( current_step < ${#steps[@]} )); then
|
||||||
|
local step_func="${steps[current_step]}"
|
||||||
|
|
||||||
|
# Run the step. Each step returns 130 if the user presses Esc.
|
||||||
|
local rc=0
|
||||||
|
"$step_func" || rc=$?
|
||||||
|
|
||||||
|
if (( rc != 0 )); then
|
||||||
|
if (( rc == 130 )); then
|
||||||
|
# Clear the current step so it re-prompts if we return to it
|
||||||
|
clear_step_state "$step_func"
|
||||||
|
|
||||||
|
# Flush terminal input buffer to prevent one Esc from cascading
|
||||||
|
# through multiple consecutive prompts.
|
||||||
|
sleep 0.1
|
||||||
|
read -t 0.1 -n 10000 discard 2>/dev/null || true
|
||||||
|
|
||||||
|
# Go back one step
|
||||||
|
current_step=$(( current_step - 1 ))
|
||||||
|
|
||||||
|
# If we aren't at the very beginning, clear the TARGET
|
||||||
|
# step's variables too, so the user is forced to re-enter.
|
||||||
|
if (( current_step >= 0 )); then
|
||||||
|
clear_step_state "${steps[current_step]}"
|
||||||
|
fi
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
exit "$rc"
|
||||||
|
fi
|
||||||
|
current_step=$(( current_step + 1 ))
|
||||||
|
else
|
||||||
|
# All steps done, review the configuration.
|
||||||
|
local review_rc=0
|
||||||
|
review_configuration || review_rc=$?
|
||||||
|
|
||||||
|
if [[ "$review_rc" == "0" ]]; then
|
||||||
|
break
|
||||||
|
elif [[ "$review_rc" == "2" ]]; then
|
||||||
|
# User chose "Edit a field". Restart from step 0; functions
|
||||||
|
# with still-set variables will return 0 and skip ahead.
|
||||||
|
current_step=0
|
||||||
|
elif [[ "$review_rc" == "130" ]]; then
|
||||||
|
# Go back to the last step.
|
||||||
|
current_step=$(( ${#steps[@]} - 1 ))
|
||||||
|
clear_step_state "${steps[current_step]}"
|
||||||
|
continue
|
||||||
|
else
|
||||||
|
exit "$review_rc"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
execute_installation
|
execute_installation
|
||||||
|
|||||||
Reference in New Issue
Block a user