606 lines
17 KiB
Bash
Executable File
606 lines
17 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Foreground Omarchy ISO install UI supervisor.
|
|
#
|
|
# This process is the only owner of the visible install UI. It launches the
|
|
# actual installer as a non-interactive child, sends child output to the support
|
|
# log, renders progress while the child runs, then renders the final installed
|
|
# time + reboot prompt itself. There is deliberately no background dashboard.
|
|
set -euo pipefail
|
|
|
|
usage() {
|
|
echo "Usage: $0 <log-file> <state-file> -- <installer command...>" >&2
|
|
exit 2
|
|
}
|
|
|
|
(( $# >= 4 )) || usage
|
|
LOG_FILE="$1"
|
|
STATE_FILE="$2"
|
|
shift 2
|
|
[[ ${1:-} == "--" ]] || usage
|
|
shift
|
|
(( $# > 0 )) || usage
|
|
INSTALL_CMD=("$@")
|
|
|
|
TTY_PATH="${OMARCHY_DASHBOARD_TTY:-/dev/tty}"
|
|
LOGO_PATH="${OMARCHY_PATH:-/usr/share/omarchy}/logo.txt"
|
|
CSI=$'\033['
|
|
RESET="${CSI}0m"
|
|
DIM="${CSI}2m"
|
|
HIDE_CURSOR="${CSI}?25l"
|
|
SHOW_CURSOR="${CSI}?25h"
|
|
CLEAR="${CSI}2J${CSI}H"
|
|
CLEAR_LINE="${CSI}2K"
|
|
CLEAR_TO_END="${CSI}J"
|
|
GREEN="${CSI}32m"
|
|
RED="${CSI}31m"
|
|
DARK="${CSI}90m"
|
|
|
|
# The dashboard owns the final reboot prompt now, so keep the gum confirm
|
|
# styling that used to come from install/helpers/presentation.sh.
|
|
export GUM_CONFIRM_PROMPT_FOREGROUND="${GUM_CONFIRM_PROMPT_FOREGROUND:-6}"
|
|
export GUM_CONFIRM_SELECTED_FOREGROUND="${GUM_CONFIRM_SELECTED_FOREGROUND:-0}"
|
|
export GUM_CONFIRM_SELECTED_BACKGROUND="${GUM_CONFIRM_SELECTED_BACKGROUND:-2}"
|
|
export GUM_CONFIRM_UNSELECTED_FOREGROUND="${GUM_CONFIRM_UNSELECTED_FOREGROUND:-7}"
|
|
export GUM_CONFIRM_UNSELECTED_BACKGROUND="${GUM_CONFIRM_UNSELECTED_BACKGROUND:-0}"
|
|
|
|
child_pid=""
|
|
child_pgid=""
|
|
|
|
whimsy=(
|
|
"making Linux feel like home"
|
|
"installing the good defaults"
|
|
"putting the system in order"
|
|
"setting up a civilized desktop"
|
|
"clearing the path to Hyprland"
|
|
"preparing the command line sanctuary"
|
|
"teaching pacman the house rules"
|
|
"laying down the Tokyo Night carpet"
|
|
"arranging the sharp tools safely"
|
|
"keeping the boot path clean"
|
|
"giving Limine the keys"
|
|
"making snapshots worth having"
|
|
"saving future you a few hours"
|
|
"choosing boring where boring wins"
|
|
"leaving room for your dotfiles"
|
|
"tightening the last few screws"
|
|
)
|
|
|
|
cleanup() {
|
|
printf '%s%s' "$RESET" "$SHOW_CURSOR" >"$TTY_PATH" 2>/dev/null || true
|
|
}
|
|
|
|
dashboard_log() {
|
|
printf '[dashboard] %s\n' "$*" >>"$LOG_FILE" 2>/dev/null || true
|
|
}
|
|
|
|
stop_child() {
|
|
[[ -n ${child_pid:-} ]] || return 0
|
|
if kill -0 "$child_pid" 2>/dev/null; then
|
|
if [[ -n ${child_pgid:-} ]]; then
|
|
kill -- -"$child_pgid" 2>/dev/null || true
|
|
fi
|
|
kill "$child_pid" 2>/dev/null || true
|
|
for _ in {1..20}; do
|
|
kill -0 "$child_pid" 2>/dev/null || break
|
|
sleep 0.05
|
|
done
|
|
if kill -0 "$child_pid" 2>/dev/null; then
|
|
[[ -n ${child_pgid:-} ]] && kill -9 -- -"$child_pgid" 2>/dev/null || true
|
|
kill -9 "$child_pid" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
wait "$child_pid" 2>/dev/null || true
|
|
}
|
|
|
|
interrupt() {
|
|
local code="$1"
|
|
stop_child
|
|
cleanup
|
|
exit "$code"
|
|
}
|
|
|
|
trap cleanup EXIT
|
|
trap 'interrupt 130' INT
|
|
trap 'interrupt 143' TERM
|
|
|
|
term_size() { stty size 2>/dev/null <"$TTY_PATH" || printf '24 80\n'; }
|
|
term_cols() { local size; size="$(term_size)"; printf '%s\n' "${size#* }"; }
|
|
|
|
repeat() {
|
|
local char="$1" count="$2" out="" i
|
|
for ((i=0; i<count; i++)); do out+="$char"; done
|
|
printf '%s' "$out"
|
|
}
|
|
|
|
logo_width() {
|
|
if [[ -f $LOGO_PATH ]]; then
|
|
awk '{ if (length > max) max = length } END { print max+0 }' "$LOGO_PATH"
|
|
else
|
|
printf '81\n'
|
|
fi
|
|
}
|
|
|
|
logo_height() {
|
|
if [[ -f $LOGO_PATH ]]; then
|
|
wc -l <"$LOGO_PATH"
|
|
else
|
|
printf '1\n'
|
|
fi
|
|
}
|
|
|
|
LOGO_WIDTH="$(logo_width)"
|
|
LOGO_HEIGHT="$(logo_height)"
|
|
(( LOGO_WIDTH > 0 )) || LOGO_WIDTH=81
|
|
(( LOGO_HEIGHT > 0 )) || LOGO_HEIGHT=1
|
|
CONTENT_WIDTH="$LOGO_WIDTH"
|
|
DYNAMIC_ROW=$((LOGO_HEIGHT + 3))
|
|
LAST_PCT=1
|
|
|
|
left_padding() {
|
|
local width="${1:-$CONTENT_WIDTH}" cols pad
|
|
cols="$(term_cols)"
|
|
pad=$(( (cols - width) / 2 ))
|
|
(( pad < 0 )) && pad=0
|
|
printf '%*s' "$pad" ''
|
|
}
|
|
|
|
visible_len() {
|
|
local text="$1"
|
|
text="$(printf '%b' "$text" | sed -E $'s/\x1b\\[[0-9;?]*[A-Za-z]//g')"
|
|
printf '%s' "${#text}"
|
|
}
|
|
|
|
blank_line() { printf '\r%s\n' "$CLEAR_LINE"; }
|
|
|
|
line_at() {
|
|
local width="${1:-$CONTENT_WIDTH}" indent="${2:-0}"
|
|
shift 2
|
|
printf '\r%s%s%*s' "$CLEAR_LINE" "$(left_padding "$width")" "$indent" ''
|
|
printf '%b' "$*"
|
|
}
|
|
|
|
center() {
|
|
local text="$1" width="${2:-$CONTENT_WIDTH}" len inner_pad
|
|
len="$(visible_len "$text")"
|
|
inner_pad=$(( (width - len) / 2 ))
|
|
(( inner_pad < 0 )) && inner_pad=0
|
|
printf '\r%s%s%*s%b%s\n' "$CLEAR_LINE" "$(left_padding "$width")" "$inner_pad" '' "$text" "$RESET"
|
|
}
|
|
|
|
render_logo() {
|
|
local pad
|
|
pad="$(left_padding "$LOGO_WIDTH")"
|
|
if [[ -f $LOGO_PATH ]]; then
|
|
while IFS= read -r line; do
|
|
printf '\r%s%s%b%s%b\n' "$CLEAR_LINE" "$pad" "$GREEN" "$line" "$RESET"
|
|
done <"$LOGO_PATH"
|
|
else
|
|
center "${GREEN}Omarchy${RESET}" "$CONTENT_WIDTH"
|
|
fi
|
|
}
|
|
|
|
progress_bar() {
|
|
local pct="$1" width="${2:-34}" filled empty
|
|
(( pct < 0 )) && pct=0
|
|
(( pct > 100 )) && pct=100
|
|
filled=$(( pct * width / 100 ))
|
|
empty=$(( width - filled ))
|
|
printf '%s%s%s%s%s %3d%%' "$GREEN" "$(repeat █ "$filled")" "$DARK" "$(repeat ░ "$empty")" "$RESET" "$pct"
|
|
}
|
|
|
|
install_progress() {
|
|
[[ -f $STATE_FILE ]] || { printf '1'; return; }
|
|
python - "$STATE_FILE" <<'PY' 2>/dev/null || printf '1'
|
|
import json, sys, time
|
|
path = sys.argv[1]
|
|
try:
|
|
state = json.load(open(path))
|
|
except Exception:
|
|
print(1, end='')
|
|
raise SystemExit
|
|
phase = state.get('current_phase') or 'Starting installation'
|
|
if state.get('finished_at') or phase == 'Installation complete':
|
|
print(100, end='')
|
|
raise SystemExit
|
|
weights = [
|
|
('Starting installation', 1, 2, 2),
|
|
('Preparing live environment', 2, 6, 5),
|
|
('Verifying protected mounts', 6, 8, 3),
|
|
('Installing Arch + Omarchy', 8, 86, 95),
|
|
('Installing base system', 8, 84, 85),
|
|
('Configuring protected boot', 84, 86, 8),
|
|
('Configuring hibernation', 86, 88, 5),
|
|
('Configuring system', 88, 93, 10),
|
|
('Finalizing Limine boot', 93, 96, 8),
|
|
('Finalizing user', 96, 98, 10),
|
|
('Finalizing in chroot', 88, 98, 10),
|
|
('Configuring login', 98, 99, 3),
|
|
('Configuring DNS resolver', 99, 100, 2),
|
|
('Validating protected boot setup', 99, 100, 5),
|
|
('Validating boot setup', 99, 100, 5),
|
|
]
|
|
start, end, expected = 1, 99, 90
|
|
for name, s, e, seconds in weights:
|
|
if phase == name:
|
|
start, end, expected = s, e, seconds
|
|
break
|
|
phase_started = state.get('phase_started_at') or state.get('started_at') or time.time()
|
|
elapsed = max(0, time.time() - float(phase_started))
|
|
span = max(0, end - start)
|
|
active = min(max(span - 1, 0), int((elapsed / max(expected, 1)) * span))
|
|
print(min(100, start + active), end='')
|
|
PY
|
|
}
|
|
|
|
current_whimsy() {
|
|
local frame="$1" pct="$2"
|
|
printf '%s' "${whimsy[$(( (pct / 8 + frame / 8) % ${#whimsy[@]} ))]}"
|
|
}
|
|
|
|
render_static() {
|
|
printf '%s' "$CLEAR"
|
|
blank_line
|
|
|
|
if command -v tte >/dev/null 2>&1 && command -v timeout >/dev/null 2>&1 && [[ -f $LOGO_PATH ]]; then
|
|
if timeout 8s tte -i "$LOGO_PATH" --canvas-width 0 --anchor-text c --frame-rate 920 laseretch >"$TTY_PATH" 2>/dev/null; then
|
|
blank_line
|
|
return
|
|
fi
|
|
printf '%s' "$CLEAR"
|
|
blank_line
|
|
fi
|
|
|
|
render_logo
|
|
blank_line
|
|
}
|
|
|
|
render_dynamic() {
|
|
local frame="$1" pct spin
|
|
pct="$(install_progress)"
|
|
[[ $pct =~ ^[0-9]+$ ]] || pct=$LAST_PCT
|
|
(( pct > 100 )) && pct=100
|
|
(( pct < LAST_PCT )) && pct=$LAST_PCT
|
|
LAST_PCT=$pct
|
|
spin=("◐" "◓" "◑" "◒")
|
|
|
|
printf '%s%d;1H' "$CSI" "$DYNAMIC_ROW"
|
|
center "Installing Omarchy" "$CONTENT_WIDTH"
|
|
blank_line
|
|
line_at "$CONTENT_WIDTH" $(( (CONTENT_WIDTH - 40) / 2 )) ""
|
|
progress_bar "$pct" 34
|
|
printf '\n'
|
|
blank_line
|
|
center "${GREEN}${spin[$((frame % 4))]} $(current_whimsy "$frame" "$pct") ${RESET}" "$CONTENT_WIDTH"
|
|
blank_line
|
|
center "${DIM}Logs are being saved for support${RESET}" "$CONTENT_WIDTH"
|
|
printf '%s' "$CLEAR_TO_END"
|
|
}
|
|
|
|
install_duration() {
|
|
local path line duration
|
|
|
|
if [[ -f $STATE_FILE ]]; then
|
|
duration="$(python - "$STATE_FILE" <<'PY' 2>/dev/null || true
|
|
import json, sys
|
|
try:
|
|
state = json.load(open(sys.argv[1]))
|
|
started = float(state.get("started_at") or 0)
|
|
finished = float(state.get("finished_at") or 0)
|
|
except Exception:
|
|
raise SystemExit(0)
|
|
if started <= 0 or finished <= 0 or finished < started:
|
|
raise SystemExit(0)
|
|
seconds = int(round(finished - started))
|
|
hours, remainder = divmod(seconds, 3600)
|
|
minutes, seconds = divmod(remainder, 60)
|
|
if hours:
|
|
print(f"{hours}h {minutes}m {seconds}s", end="")
|
|
else:
|
|
print(f"{minutes}m {seconds}s", end="")
|
|
PY
|
|
)"
|
|
[[ -n $duration ]] && { printf '%s' "$duration"; return 0; }
|
|
fi
|
|
|
|
for path in /mnt/var/log/omarchy-install.log "$LOG_FILE"; do
|
|
[[ -f $path ]] || continue
|
|
for prefix in Total Omarchy; do
|
|
line="$(grep "^$prefix:" "$path" 2>/dev/null | tail -n 1 || true)"
|
|
[[ -n $line ]] || continue
|
|
duration="${line#*:}"
|
|
duration="${duration#${duration%%[![:space:]]*}}"
|
|
[[ -n $duration ]] && { printf '%s' "$duration"; return 0; }
|
|
done
|
|
done
|
|
return 0
|
|
}
|
|
|
|
render_finish() {
|
|
local duration message cols
|
|
cols="$(term_cols)"
|
|
printf '%s%s' "$SHOW_CURSOR" "$CLEAR" >"$TTY_PATH"
|
|
if command -v tte >/dev/null 2>&1 && command -v timeout >/dev/null 2>&1 && [[ -f $LOGO_PATH ]]; then
|
|
timeout 8s tte -i "$LOGO_PATH" --canvas-width 0 --anchor-text c --frame-rate 920 laseretch >"$TTY_PATH" 2>/dev/null || true
|
|
else
|
|
render_logo >"$TTY_PATH"
|
|
fi
|
|
duration="$(install_duration || true)"
|
|
message="Finished installing"
|
|
[[ -n $duration ]] && message="Installed in $duration"
|
|
{
|
|
blank_line
|
|
center "$message" "$cols"
|
|
blank_line
|
|
} >"$TTY_PATH"
|
|
}
|
|
|
|
reboot_prompt() {
|
|
local prompt_pad
|
|
prompt_pad=$(( ( $(term_cols) - 18 ) / 2 ))
|
|
(( prompt_pad < 0 )) && prompt_pad=0
|
|
gum confirm \
|
|
--padding "0 0 0 $prompt_pad" \
|
|
--show-help=false \
|
|
--default \
|
|
--affirmative "Reboot Now" \
|
|
--negative "" \
|
|
"" <"$TTY_PATH" >"$TTY_PATH" 2>&1
|
|
}
|
|
|
|
truncate_for_width() {
|
|
local text="$1" width="$2"
|
|
if (( ${#text} > width )); then
|
|
printf '%s…' "${text:0:$((width - 1))}"
|
|
else
|
|
printf '%s' "$text"
|
|
fi
|
|
}
|
|
|
|
render_failure_log_tail() {
|
|
local rows lines width line tail_log label
|
|
rows=$(term_size)
|
|
rows=${rows%% *}
|
|
lines=$((rows - LOGO_HEIGHT - 18))
|
|
(( lines < 6 )) && lines=6
|
|
(( lines > 14 )) && lines=14
|
|
width=$((CONTENT_WIDTH - 4))
|
|
(( width < 40 )) && width=40
|
|
|
|
tail_log="${OMARCHY_FAILURE_TAIL_LOG:-}"
|
|
if [[ -z $tail_log ]]; then
|
|
if [[ -f /mnt/var/log/omarchy-install.log ]]; then
|
|
tail_log=/mnt/var/log/omarchy-install.log
|
|
else
|
|
tail_log="$LOG_FILE"
|
|
fi
|
|
fi
|
|
[[ -f $tail_log ]] || return 0
|
|
|
|
label="Last log lines:"
|
|
[[ $tail_log == /mnt/* ]] && label="Last target log lines:"
|
|
line_at "$CONTENT_WIDTH" 2 "${DIM}${label}${RESET}"
|
|
printf '\n'
|
|
tail -n "$lines" "$tail_log" 2>/dev/null | while IFS= read -r line; do
|
|
line_at "$CONTENT_WIDTH" 2 "$(truncate_for_width "$line" "$width")"
|
|
printf '\n'
|
|
done
|
|
}
|
|
|
|
find_log_uploader() {
|
|
local candidate
|
|
for candidate in omarchy-upload-log /usr/local/bin/omarchy-upload-log /usr/bin/omarchy-upload-log /mnt/usr/bin/omarchy-upload-log; do
|
|
if [[ $candidate == */* ]]; then
|
|
[[ -x $candidate ]] && { printf '%s' "$candidate"; return 0; }
|
|
elif command -v "$candidate" >/dev/null 2>&1; then
|
|
command -v "$candidate"
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
prompt_enter() {
|
|
printf '\nPress Enter to continue…' >"$TTY_PATH"
|
|
IFS= read -r _ <"$TTY_PATH" || true
|
|
}
|
|
|
|
upload_failure_log() {
|
|
local uploader
|
|
printf '%s%s' "$SHOW_CURSOR" "$CLEAR" >"$TTY_PATH" 2>/dev/null || true
|
|
if uploader=$(find_log_uploader); then
|
|
"$uploader" install >"$TTY_PATH" 2>&1 || true
|
|
else
|
|
{
|
|
echo "No log uploader is available in this environment."
|
|
echo "Install log: $LOG_FILE"
|
|
[[ -f /mnt/var/log/omarchy-install.log ]] && echo "Target log: /mnt/var/log/omarchy-install.log"
|
|
} >"$TTY_PATH"
|
|
fi
|
|
prompt_enter
|
|
}
|
|
|
|
view_failure_log() {
|
|
printf '%s%s' "$SHOW_CURSOR" "$CLEAR" >"$TTY_PATH" 2>/dev/null || true
|
|
if command -v less >/dev/null 2>&1 && [[ -f $LOG_FILE ]]; then
|
|
less "$LOG_FILE" <"$TTY_PATH" >"$TTY_PATH" 2>&1 || true
|
|
else
|
|
tail -n 200 "$LOG_FILE" >"$TTY_PATH" 2>&1 || true
|
|
prompt_enter
|
|
fi
|
|
}
|
|
|
|
render_failure() {
|
|
local status="$1" state_summary="${2:-}" line
|
|
printf '%s%s' "$SHOW_CURSOR" "$CLEAR"
|
|
blank_line
|
|
render_logo
|
|
blank_line
|
|
center "${RED}Omarchy installation stopped${RESET}" "$CONTENT_WIDTH"
|
|
center "Installer exited with status $status" "$CONTENT_WIDTH"
|
|
|
|
if [[ -n $state_summary ]]; then
|
|
blank_line
|
|
while IFS= read -r line; do
|
|
[[ -n $line ]] || continue
|
|
center "${DIM}$(truncate_for_width "$line" "$CONTENT_WIDTH")${RESET}" "$CONTENT_WIDTH"
|
|
done <<<"$state_summary"
|
|
fi
|
|
|
|
blank_line
|
|
render_failure_log_tail
|
|
blank_line
|
|
center "${DIM}Get help at https://omarchy.org/discord${RESET}" "$CONTENT_WIDTH"
|
|
blank_line
|
|
}
|
|
|
|
failure_menu() {
|
|
local choice uploader
|
|
|
|
[[ ${OMARCHY_UI_FAILURE_ACTION:-} == "exit" ]] && return 0
|
|
|
|
while true; do
|
|
uploader=""
|
|
uploader=$(find_log_uploader || true)
|
|
if [[ -n $uploader ]]; then
|
|
choice=$(gum choose \
|
|
--height 6 \
|
|
--header "What would you like to do?" \
|
|
"Upload log for support" \
|
|
"View full log" \
|
|
"Drop to shell" \
|
|
"Reboot" \
|
|
"Power off" \
|
|
<"$TTY_PATH" 2>"$TTY_PATH") || choice="Drop to shell"
|
|
else
|
|
choice=$(gum choose \
|
|
--height 5 \
|
|
--header "What would you like to do?" \
|
|
"View full log" \
|
|
"Drop to shell" \
|
|
"Reboot" \
|
|
"Power off" \
|
|
<"$TTY_PATH" 2>"$TTY_PATH") || choice="Drop to shell"
|
|
fi
|
|
|
|
case "$choice" in
|
|
"Upload log for support")
|
|
upload_failure_log
|
|
render_failure "${failure_status:-1}" "${failure_summary:-}" >"$TTY_PATH" 2>/dev/null || true
|
|
;;
|
|
"View full log")
|
|
view_failure_log
|
|
render_failure "${failure_status:-1}" "${failure_summary:-}" >"$TTY_PATH" 2>/dev/null || true
|
|
;;
|
|
"Reboot")
|
|
reboot 2>/dev/null || systemctl reboot 2>/dev/null || true
|
|
;;
|
|
"Power off")
|
|
poweroff 2>/dev/null || systemctl poweroff 2>/dev/null || true
|
|
;;
|
|
"Drop to shell"|"")
|
|
return 0
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
sanitize_child_output() {
|
|
# Archinstall is only a library subsystem in Omarchy's installer. Its own
|
|
# post-install success/reboot banner can be buffered until process exit and
|
|
# appear after an Omarchy failure, which makes the unified log contradictory.
|
|
# Keep that third-party banner out of Omarchy's support log; the raw
|
|
# archinstall log is still uploaded separately by omarchy-upload-log.
|
|
sed -u \
|
|
-e $'s/\x1b\\[[0-9;?]*[A-Za-z]//g' \
|
|
-e '/^Activating systemd-timesyncd for time synchronization using Arch Linux and ntp.org NTP servers$/d' \
|
|
-e '/^Enabling service systemd-timesyncd$/d' \
|
|
-e '/^Setting password for root$/d' \
|
|
-e '/^Updating \/mnt\/etc\/fstab$/d' \
|
|
-e '/^Syncing the system\.\.\.$/d' \
|
|
-e '/^Installation completed without any errors\.$/d' \
|
|
-e '/^Log files temporarily available at \/var\/log\/archinstall\.$/d' \
|
|
-e '/^You may reboot when ready\.$/d'
|
|
}
|
|
|
|
launch_child() {
|
|
mkdir -p "$(dirname "$LOG_FILE")"
|
|
# Child is non-interactive. All visible UI belongs to this supervisor.
|
|
PYTHONUNBUFFERED=1 setsid "${INSTALL_CMD[@]}" </dev/null > >(sanitize_child_output >>"$LOG_FILE") 2>&1 &
|
|
child_pid=$!
|
|
child_pgid=$child_pid
|
|
}
|
|
|
|
[[ -e $TTY_PATH ]] || exit 2
|
|
printf '%s' "$HIDE_CURSOR" >"$TTY_PATH"
|
|
render_static >"$TTY_PATH"
|
|
launch_child
|
|
|
|
frame=0
|
|
while jobs -pr | grep -qx "$child_pid"; do
|
|
render_dynamic "$frame" >"$TTY_PATH" 2>/dev/null || true
|
|
frame=$((frame + 1))
|
|
sleep 0.5
|
|
done
|
|
|
|
set +e
|
|
wait "$child_pid"
|
|
status=$?
|
|
set -e
|
|
child_pid=""
|
|
child_pgid=""
|
|
|
|
dashboard_log "installer child exited with status $status"
|
|
|
|
if (( status != 0 )); then
|
|
failure_status="$status"
|
|
failure_summary=""
|
|
if [[ -f $STATE_FILE ]]; then
|
|
failure_summary="$(python - "$STATE_FILE" <<'PY' 2>/dev/null || true
|
|
import json, sys
|
|
try:
|
|
state = json.load(open(sys.argv[1]))
|
|
except Exception:
|
|
raise SystemExit(0)
|
|
phase = state.get("current_phase")
|
|
if phase:
|
|
print(f"last installer phase: {phase}")
|
|
for item in reversed(state.get("phases", [])):
|
|
if item.get("status") == "failed":
|
|
print(f"failed phase: {item.get('name')}: {item.get('error')}")
|
|
break
|
|
PY
|
|
)"
|
|
if [[ -n $failure_summary ]]; then
|
|
while IFS= read -r line; do
|
|
[[ -n $line ]] && dashboard_log "$line"
|
|
done <<<"$failure_summary"
|
|
fi
|
|
fi
|
|
render_failure "$failure_status" "$failure_summary" >"$TTY_PATH" 2>/dev/null || true
|
|
failure_menu
|
|
exit "$failure_status"
|
|
fi
|
|
|
|
set +e
|
|
render_finish
|
|
finish_render_status=$?
|
|
set -e
|
|
if (( finish_render_status != 0 )); then
|
|
dashboard_log "finish screen render failed with status $finish_render_status; using fallback"
|
|
{
|
|
printf '%s%s' "$SHOW_CURSOR" "$CLEAR"
|
|
printf '\nFinished installing\n\n'
|
|
} >"$TTY_PATH" 2>/dev/null || true
|
|
fi
|
|
|
|
if [[ ${OMARCHY_UI_AUTO_REBOOT:-} == "no" ]]; then
|
|
exit 0
|
|
fi
|
|
|
|
if reboot_prompt; then
|
|
printf '%s' "$CLEAR" >"$TTY_PATH" 2>/dev/null || true
|
|
reboot 2>/dev/null || systemctl reboot 2>/dev/null || true
|
|
fi
|
|
|
|
exit 0
|