diff --git a/bin/omarchy-provision-first-run b/bin/omarchy-provision-first-run index 11c2eb88165..7b307f0fcee 100755 --- a/bin/omarchy-provision-first-run +++ b/bin/omarchy-provision-first-run @@ -43,8 +43,6 @@ if omarchy-done check "$FIRST_RUN_DONE" && (( force == 0 )); then exit 0 fi -omarchy-provision-user "${finalize_user_args[@]}" || true - mkdir -p "$state_dir" FIRST_RUN_LOG="$state_dir/first-run.log" @@ -54,6 +52,18 @@ log_first_run() { printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" >>"$FIRST_RUN_LOG" } +# A failed finalization keeps first-run retryable like any other step. Dropping +# the status here used to let the later steps mark first-run complete over a +# user who was never finalized. +log_first_run "Starting: finalize user" +if omarchy-provision-user "${finalize_user_args[@]}"; then + log_first_run "Completed: finalize user" +else + finalize_user_status=$? + first_run_failed=1 + log_first_run "Failed: finalize user (exit code: $finalize_user_status)" +fi + run_first_run_step() { local name="$1" shift diff --git a/install/hardware/apple/fix-spi-keyboard.sh b/install/hardware/apple/fix-spi-keyboard.sh index 330081b7631..86722d4941d 100644 --- a/install/hardware/apple/fix-spi-keyboard.sh +++ b/install/hardware/apple/fix-spi-keyboard.sh @@ -1,5 +1,6 @@ -# Detect MacBook models that need SPI keyboard modules -product_name="$(cat /sys/class/dmi/id/product_name 2>/dev/null)" +# Detect MacBook models that need SPI keyboard modules. +# Apple Silicon has no DMI; under set -e an unguarded read aborts the hardware pass. +product_name="$(cat /sys/class/dmi/id/product_name 2>/dev/null || true)" if [[ $product_name =~ MacBook[89],1|MacBook1[02],1|MacBookPro13,[123]|MacBookPro14,[123] ]]; then echo "Detected MacBook with SPI keyboard" diff --git a/install/hardware/fix-surface-keyboard.sh b/install/hardware/fix-surface-keyboard.sh index 8f71c9bed4f..a79ba028b28 100644 --- a/install/hardware/fix-surface-keyboard.sh +++ b/install/hardware/fix-surface-keyboard.sh @@ -2,7 +2,7 @@ # Module list derived from Chris McLeod's manual install instructions # https://chrismcleod.dev/blog/installing-arch-linux-with-secure-boot-on-a-microsoft-surface-laptop-studio/ if omarchy-hw-surface; then - product_name="$(cat /sys/class/dmi/id/product_name 2>/dev/null)" + product_name="$(cat /sys/class/dmi/id/product_name 2>/dev/null || true)" echo "Detected Surface Device" # Modules already exist in the rootfs for the default kernel. diff --git a/install/hardware/intel/lpmd.sh b/install/hardware/intel/lpmd.sh index ad8cf37462d..c5de59c7583 100644 --- a/install/hardware/intel/lpmd.sh +++ b/install/hardware/intel/lpmd.sh @@ -3,7 +3,7 @@ # Meteor Lake (170/172), Lunar Lake (189), Panther Lake (204) if omarchy-hw-intel && omarchy-battery-present; then - cpu_model=$(grep -m1 "^model\s*:" /proc/cpuinfo 2>/dev/null | cut -d: -f2 | tr -d ' ') + cpu_model=$(grep -m1 "^model\s*:" /proc/cpuinfo 2>/dev/null | cut -d: -f2 | tr -d ' ' || true) if [[ "$cpu_model" =~ ^(151|154|170|172|183|186|189|191|204)$ ]]; then omarchy-pkg-add intel-lpmd sudo systemctl enable intel_lpmd.service diff --git a/install/hardware/intel/thermald.sh b/install/hardware/intel/thermald.sh index 3ccd4340f47..06850ec2c35 100644 --- a/install/hardware/intel/thermald.sh +++ b/install/hardware/intel/thermald.sh @@ -3,7 +3,7 @@ if omarchy-hw-intel; then # Check if Sandy Bridge or newer (model >= 42). Sandy Bridge: model 42 (mobile), 45 (desktop) - cpu_model=$(grep -m1 "^model\s*:" /proc/cpuinfo 2>/dev/null | cut -d: -f2 | tr -d ' ') + cpu_model=$(grep -m1 "^model\s*:" /proc/cpuinfo 2>/dev/null | cut -d: -f2 | tr -d ' ' || true) cpu_model=${cpu_model:-0} if ((cpu_model >= 42)) && omarchy-battery-present; then omarchy-pkg-add thermald diff --git a/test/shell.d/first-run-test.sh b/test/shell.d/first-run-test.sh index 0fd593757de..494f096ae75 100644 --- a/test/shell.d/first-run-test.sh +++ b/test/shell.d/first-run-test.sh @@ -33,3 +33,49 @@ if grep -F 'skip-first-run-update-notification' "$ROOT/install/user/first-run/wi fi pass "first-run uses one lifecycle completion marker" + +# A failed finalization must keep first-run retryable. The old driver discarded +# that status with "|| true" and then marked first-run complete when its later +# steps happened to succeed, so a transient finalization failure permanently +# skipped the user's required setup. +retry_tmp=$(mktemp -d) +trap 'rm -rf "$test_tmp" "$retry_tmp"' EXIT +retry_bin="$retry_tmp/bin" +retry_root="$retry_tmp/omarchy" +mkdir -p "$retry_bin" "$retry_root/install/user/first-run" "$retry_tmp/home" + +cat >"$retry_bin/omarchy-done" <<'SH' +#!/bin/bash +case "$1:$2" in + check:first-run-user) exit 1 ;; + mark:first-run-user) touch "$OMARCHY_TEST_FIRST_RUN_MARKER" ;; +esac +SH +cat >"$retry_bin/omarchy-provision-user" <<'SH' +#!/bin/bash +touch "$OMARCHY_TEST_FINALIZE_CALLED" +exit 42 +SH +for helper in omarchy-hook-install omarchy-notification-wait; do + printf '#!/bin/bash\nexit 0\n' >"$retry_bin/$helper" +done +chmod +x "$retry_bin"/* + +for leaf in welcome.sh timezone.sh wifi.sh enable-user-units.sh gnome-theme.sh \ + gtk-primary-paste.sh audio-tuning.sh; do + printf 'exit 0\n' >"$retry_root/install/user/first-run/$leaf" +done + +retry_marker="$retry_tmp/first-run-marker" +retry_finalize="$retry_tmp/finalize-called" +HOME="$retry_tmp/home" PATH="$retry_bin:$PATH" OMARCHY_PATH="$retry_root" \ + OMARCHY_TEST_FIRST_RUN_MARKER="$retry_marker" \ + OMARCHY_TEST_FINALIZE_CALLED="$retry_finalize" \ + bash "$ROOT/bin/omarchy-provision-first-run" >"$retry_tmp/output" + +[[ -e $retry_finalize ]] || fail "first-run attempts user finalization" +[[ ! -e $retry_marker ]] || fail "failed user finalization keeps first-run retryable" +grep -F 'Failed: finalize user (exit code: 42)' \ + "$retry_tmp/home/.local/state/omarchy/first-run.log" >/dev/null || + fail "first-run records a finalization failure" +pass "failed user finalization keeps first-run retryable" diff --git a/test/shell.d/install-leaf-guards-test.sh b/test/shell.d/install-leaf-guards-test.sh new file mode 100755 index 00000000000..02ec5230042 --- /dev/null +++ b/test/shell.d/install-leaf-guards-test.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +set -euo pipefail + +source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" + +# run_logged sources every leaf under bash -eE, where an assignment from a +# failing command substitution aborts the leaf, and the abort takes every later +# stage down with it. Hardware probes read sysfs and procfs paths that simply +# do not exist on other machines (Apple Silicon has no DMI), so each probe has +# to tolerate a missing file. Two accepted forms: +# product_name="$(cat /sys/class/dmi/id/product_name 2>/dev/null || true)" +# if product_name=$(cat /sys/class/dmi/id/product_name 2>/dev/null); then +# bash's $(