diff --git a/install/hardware/fix-yt6801-ethernet-adapter.sh b/install/hardware/fix-yt6801-ethernet-adapter.sh index 8dac5db0..e048cc79 100644 --- a/install/hardware/fix-yt6801-ethernet-adapter.sh +++ b/install/hardware/fix-yt6801-ethernet-adapter.sh @@ -1,4 +1,9 @@ # Use the upstream driver for the Motorcomm YT6801 adapter used by the Slimbook Executive. -if [[ -n $(lspci -Dn -d 1f0a:6801) ]]; then +if ! yt6801_devices=$(lspci -Dn -d 1f0a:6801); then + echo "Unable to discover YT6801 adapters." >&2 + return 1 +fi + +if [[ -n $yt6801_devices ]]; then omarchy-pkg-drop yt6801-dkms fi diff --git a/migrations/1788279117.sh b/migrations/1788279117.sh index a154e269..35f3571a 100644 --- a/migrations/1788279117.sh +++ b/migrations/1788279117.sh @@ -1,43 +1,86 @@ echo "Replace the YT6801 vendor DKMS driver with the upstream kernel driver" -mapfile -t yt6801_devices < <(lspci -Dn -d 1f0a:6801 | awk '{ print $1 }') - -if (( ${#yt6801_devices[@]} == 0 )); then - omarchy-pkg-drop yt6801-dkms - exit 0 -fi - -for device in "${yt6801_devices[@]}"; do - if [[ ! $device =~ ^[[:xdigit:]]{4}:[[:xdigit:]]{2}:[[:xdigit:]]{2}\.[[:xdigit:]]$ ]]; then - echo "Invalid YT6801 PCI address: $device" >&2 - exit 1 - fi -done - -if ! modinfo -F alias dwmac-motorcomm | grep -Fqx 'pci:v00001F0Ad00006801sv*sd*bc*sc*i*'; then - echo "The running kernel does not provide YT6801 support in dwmac-motorcomm." >&2 - echo "Reboot into the latest Omarchy kernel and rerun omarchy-migrate." >&2 +if ! yt6801_pci=$(lspci -Dn -d 1f0a:6801); then + echo "Unable to discover YT6801 adapters; rerun omarchy-migrate." >&2 exit 1 fi -# Load and validate the replacement before removing the installed fallback. -sudo modprobe dwmac-motorcomm -omarchy-pkg-drop yt6801-dkms +yt6801_devices=() +while read -r device _; do + [[ -n $device ]] || continue + if [[ ! $device =~ ^[[:xdigit:]]{4}:[[:xdigit:]]{2}:[[:xdigit:]]{2}\.[0-7]$ ]]; then + echo "Invalid YT6801 PCI address: $device" >&2 + exit 1 + fi + yt6801_devices+=("$device") +done <<< "$yt6801_pci" -if lsmod | awk '$1 == "yt6801" { found = 1 } END { exit !found }'; then - sudo modprobe -r yt6801 +yt6801_uses_upstream() { + local driver + driver=$(readlink -f "/sys/bus/pci/devices/$1/driver") && + [[ $driver == "/sys/bus/pci/drivers/dwmac-motorcomm" ]] +} + +yt6801_check_binding() { + local device + for device in "${yt6801_devices[@]}"; do + if ! yt6801_uses_upstream "$device"; then + echo "YT6801 device $device did not bind to dwmac-motorcomm." >&2 + echo "Reboot into the latest Omarchy kernel and rerun omarchy-migrate." >&2 + exit 1 + fi + done +} + +# Check discovery before changing hardware or packages. A failed query must not +# turn into an empty list and mark the migration complete. +installed_packages=$(pacman -Qq) +loaded_modules=$(lsmod) + +yt6801_pending=() +for device in "${yt6801_devices[@]}"; do + if ! yt6801_uses_upstream "$device"; then + yt6801_pending+=("$device") + fi +done + +if (( ${#yt6801_pending[@]} > 0 )); then + if ! aliases=$(modinfo -F alias dwmac-motorcomm) || + ! grep -Fxq 'pci:v00001F0Ad00006801sv*sd*bc*sc*i*' <<< "$aliases"; then + echo "The running kernel does not provide YT6801 support in dwmac-motorcomm." >&2 + echo "Reboot into the latest Omarchy kernel and rerun omarchy-migrate." >&2 + exit 1 + fi + + # Validate the replacement before unloading the installed fallback. Already + # migrated machines need no module load or privilege prompt for later users. + sudo modprobe dwmac-motorcomm fi -for device in "${yt6801_devices[@]}"; do - if ! lspci -Dks "$device" | grep -Eq 'Kernel driver in use: dwmac[-_]motorcomm'; then +if grep -Eq '^yt6801[[:space:]]' <<< "$loaded_modules"; then + # rmmod also works on retries after DKMS has deleted the module files and + # lookup index. Never force removal of a module that is still busy. + sudo rmmod yt6801 +fi + +for device in "${yt6801_pending[@]}"; do + if ! yt6801_uses_upstream "$device"; then printf '%s\n' "$device" | sudo tee /sys/bus/pci/drivers_probe >/dev/null fi done +yt6801_check_binding -for device in "${yt6801_devices[@]}"; do - if ! lspci -Dks "$device" | grep -Eq 'Kernel driver in use: dwmac[-_]motorcomm'; then - echo "YT6801 device $device did not bind to dwmac-motorcomm." >&2 - echo "Reboot and rerun omarchy-migrate." >&2 - exit 1 - fi -done +# Keep the package available until the live cutover succeeds. Package removal +# invokes DKMS/depmod hooks, so unloading must happen before this transaction. +if grep -Fxq 'yt6801-dkms' <<< "$installed_packages"; then + omarchy-pkg-drop yt6801-dkms +fi + +installed_packages=$(pacman -Qq) +loaded_modules=$(lsmod) +if grep -Fxq 'yt6801-dkms' <<< "$installed_packages" || + grep -Eq '^yt6801[[:space:]]' <<< "$loaded_modules"; then + echo "The YT6801 vendor package or module is still present; rerun omarchy-migrate." >&2 + exit 1 +fi +yt6801_check_binding diff --git a/test/shell.d/yt6801-driver-test.sh b/test/shell.d/yt6801-driver-test.sh index 857a9680..db8b12c1 100644 --- a/test/shell.d/yt6801-driver-test.sh +++ b/test/shell.d/yt6801-driver-test.sh @@ -1,96 +1,372 @@ #!/bin/bash -source "$(dirname "$0")/base-test.sh" +set -euo pipefail + +source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" test_tmp=$(mktemp -d) trap 'rm -rf "$test_tmp"' EXIT stub_bin="$test_tmp/bin" -calls="$test_tmp/calls" mkdir -p "$stub_bin" +export TEST_STATE="$test_tmp/state" +export TEST_CALLS="$test_tmp/calls" +export TEST_FAULT="" +export TEST_DEVICE="0000:01:00.0" +export TEST_DEVICE_2="0000:02:00.0" +export PATH="$stub_bin:$PATH" -cat >"$stub_bin/lspci" <<'SH' +# Model kernel state separately from package files: DKMS removal deletes the +# latter and refreshes depmod, but cannot unload the module already in memory. +cat >"$stub_bin/stub" <<'SH' #!/bin/bash -if [[ $* == "-Dn -d 1f0a:6801" ]]; then - printf '%s\n' '0000:01:00.0 0200: 1f0a:6801' -elif [[ $* == "-Dks 0000:01:00.0" ]]; then - if [[ -e $TEST_BOUND_STATE ]]; then - printf '%s\n' 'Kernel driver in use: dwmac-motorcomm' - else - printf '%s\n' 'Kernel modules: dwmac-motorcomm' +set -euo pipefail +command=${0##*/} +printf '%s %s\n' "$command" "$*" >>"$TEST_CALLS" + +case "$command" in + lspci) + [[ $* == "-Dn -d 1f0a:6801" ]] || exit 90 + [[ $TEST_FAULT != "pci-fail" ]] || exit 1 + cat "$TEST_STATE/devices" + [[ $TEST_FAULT != "pci-partial" ]] || exit 1 + ;; + pacman) + if [[ $* == "-Qq" ]]; then + query_count=$(<"$TEST_STATE/queries") + query_count=$((query_count + 1)) + printf '%s\n' "$query_count" >"$TEST_STATE/queries" + printf '%s\n' linux + if [[ -e $TEST_STATE/package ]]; then + printf '%s\n' yt6801-dkms + fi + [[ $TEST_FAULT != "query-fail" ]] || exit 1 + if [[ $TEST_FAULT == "helper-query-fail" ]] && (( query_count == 2 )); then + exit 1 + fi + if [[ $TEST_FAULT == "final-query-fail" ]] && [[ ! -e $TEST_STATE/package ]]; then + exit 1 + fi + elif [[ $* == "-Rns --noconfirm yt6801-dkms" ]]; then + [[ $TEST_FAULT != "remove-fail" ]] || exit 1 + if [[ $TEST_FAULT != "remove-noop" ]]; then + rm -f "$TEST_STATE/package" "$TEST_STATE/module-files" + fi + if [[ $TEST_FAULT == "remove-reload" ]]; then + touch "$TEST_STATE/vendor-loaded" + elif [[ $TEST_FAULT == "remove-unbind" ]]; then + rm -f "$TEST_STATE/bindings/$TEST_DEVICE" + fi + [[ $TEST_FAULT != "remove-interrupt" ]] || exit 1 + else + exit 90 + fi + ;; + lsmod) + [[ $TEST_FAULT != "modules-fail" ]] || exit 1 + if [[ $TEST_FAULT == "final-modules-fail" && ! -e $TEST_STATE/package ]]; then + exit 1 + fi + printf '%s\n' 'Module Size Used by' + if [[ -e $TEST_STATE/vendor-loaded ]]; then + printf '%s\n' 'yt6801 65536 0' + fi + ;; + readlink) + [[ $# == 2 && $1 == "-f" && $2 == /sys/bus/pci/devices/*/driver ]] || exit 90 + device=${2%/driver} + device=${device##*/} + [[ $TEST_FAULT != "binding-read-fail" ]] || exit 1 + if [[ -e $TEST_STATE/bindings/$device ]]; then + printf '/sys/bus/pci/drivers/%s\n' "$(<"$TEST_STATE/bindings/$device")" + else + printf '%s\n' "$2" + fi + ;; + modinfo) + [[ $* == "-F alias dwmac-motorcomm" ]] || exit 90 + [[ $TEST_FAULT != "alias-fail" ]] || exit 1 + if [[ $TEST_FAULT == "alias-wrong" ]]; then + printf '%s\n' 'pci:v00001F0Ad00006802sv*sd*bc*sc*i*' + else + printf '%s\n' 'pci:v00001F0Ad00006801sv*sd*bc*sc*i*' + fi + ;; + modprobe) + # Real modprobe cannot resolve a module through its deleted lookup entry. + if [[ $* == "-r yt6801" ]]; then + [[ -e $TEST_STATE/module-files ]] || exit 1 + exit 90 + fi + [[ $* == "dwmac-motorcomm" ]] || exit 90 + [[ $TEST_FAULT != "load-fail" ]] || exit 1 + if [[ $TEST_FAULT != "load-noop" ]]; then + touch "$TEST_STATE/upstream-loaded" + fi + [[ $TEST_FAULT != "load-interrupt" ]] || exit 1 + ;; + rmmod) + [[ $* == "yt6801" ]] || exit 90 + [[ $TEST_FAULT != "unload-fail" ]] || exit 1 + [[ -e $TEST_STATE/vendor-loaded ]] || exit 1 + if [[ $TEST_FAULT != "unload-noop" ]]; then + rm "$TEST_STATE/vendor-loaded" + for binding in "$TEST_STATE/bindings/"*; do + if [[ -f $binding && $(<"$binding") == "yt6801" ]]; then + rm "$binding" + fi + done + fi + [[ $TEST_FAULT != "unload-interrupt" ]] || exit 1 + ;; + tee) + [[ $* == "/sys/bus/pci/drivers_probe" ]] || exit 90 + read -r device + [[ $device == "$TEST_DEVICE" || $device == "$TEST_DEVICE_2" ]] || exit 90 + [[ $TEST_FAULT != "probe-fail" ]] || exit 1 + if [[ $TEST_FAULT != "probe-noop" && -e $TEST_STATE/upstream-loaded && ! -e $TEST_STATE/bindings/$device ]]; then + if [[ $TEST_FAULT != "second-probe-noop" || $device != "$TEST_DEVICE_2" ]]; then + printf '%s\n' dwmac-motorcomm >"$TEST_STATE/bindings/$device" + fi + fi + [[ $TEST_FAULT != "probe-interrupt" ]] || exit 1 + ;; + sudo) + [[ $TEST_FAULT != "sudo-fail" ]] || exit 1 + case "$*" in + 'modprobe dwmac-motorcomm'|'rmmod yt6801'|'tee /sys/bus/pci/drivers_probe'|'pacman -Rns --noconfirm yt6801-dkms') "$@" ;; + *) exit 90 ;; + esac + ;; + omarchy-notification-dismiss) ;; + *) exit 90 ;; +esac +SH +chmod +x "$stub_bin/stub" +for command in lspci pacman lsmod readlink modinfo modprobe rmmod tee sudo omarchy-notification-dismiss; do + ln -s stub "$stub_bin/$command" +done +ln -s "$ROOT/bin/omarchy-pkg-drop" "$stub_bin/omarchy-pkg-drop" + +reset_state() { + rm -rf "$TEST_STATE" + mkdir -p "$TEST_STATE/bindings" + : >"$TEST_CALLS" + TEST_FAULT="" + printf '%s\n' 0 >"$TEST_STATE/queries" + printf '%s 0200: 1f0a:6801\n' "$TEST_DEVICE" >"$TEST_STATE/devices" + printf '%s\n' yt6801 >"$TEST_STATE/bindings/$TEST_DEVICE" + touch "$TEST_STATE/package" "$TEST_STATE/module-files" "$TEST_STATE/vendor-loaded" +} + +run_migration() { + bash -euo pipefail "$ROOT/migrations/1788279117.sh" >"$test_tmp/output" 2>&1 +} + +assert_complete() { + [[ ! -e $TEST_STATE/package ]] || fail "completed migration left the vendor package installed" + [[ ! -e $TEST_STATE/vendor-loaded ]] || fail "completed migration left the vendor module loaded" + local device rest + while read -r device rest; do + [[ -n $device ]] || continue + [[ -f $TEST_STATE/bindings/$device && $(<"$TEST_STATE/bindings/$device") == "dwmac-motorcomm" ]] || + fail "completed migration left an adapter without the upstream driver" "$device" + done <"$TEST_STATE/devices" +} + +assert_no_privileges() { + if grep -q '^sudo ' "$TEST_CALLS"; then + fail "$1" "$(<"$TEST_CALLS")" fi +} + +reset_state +run_migration +assert_complete +# Enforce the safe ordering across the modeled DKMS package transaction. +[[ $(grep -E '^(modprobe|rmmod|tee|pacman -Rns) ' "$TEST_CALLS") == "$(printf '%s\n' \ + 'modprobe dwmac-motorcomm' 'rmmod yt6801' 'tee /sys/bus/pci/drivers_probe' 'pacman -Rns --noconfirm yt6801-dkms')" ]] || + fail "the replacement must bind before DKMS package removal" +pass "migration cuts over before removing the vendor package and module files" + +: >"$TEST_CALLS" +TEST_FAULT=sudo-fail +run_migration +assert_complete +assert_no_privileges "a later user's completed migration must need no privileges" +if grep -Eq '^(modinfo|modprobe|rmmod|tee) ' "$TEST_CALLS"; then + fail "a completed migration must not need module metadata or reprobe" fi -SH +pass "completed machines need no privileges, module metadata, or reprobe" -cat >"$stub_bin/modinfo" <<'SH' -#!/bin/bash -printf '%s\n' 'pci:v00001F0Ad00006801sv*sd*bc*sc*i*' -SH +reset_state +printf '%s 0200: 1f0a:6801\n' "$TEST_DEVICE_2" >>"$TEST_STATE/devices" +printf '%s\n' yt6801 >"$TEST_STATE/bindings/$TEST_DEVICE_2" +run_migration +assert_complete +pass "every target adapter is rebound and verified" -cat >"$stub_bin/lsmod" <<'SH' -#!/bin/bash -[[ ${TEST_VENDOR_LOADED:-1} == 1 ]] && printf '%s\n' 'yt6801 65536 0' -SH - -for command in modprobe omarchy-pkg-drop tee; do - cat >"$stub_bin/$command" <<'SH' -#!/bin/bash -printf '%s\t%s\n' "${0##*/}" "$*" >>"$TEST_CALLS" -if [[ ${0##*/} == "tee" ]]; then - cat >/dev/null - touch "$TEST_BOUND_STATE" -fi -SH +for fault in pci-fail pci-partial query-fail modules-fail alias-fail alias-wrong; do + reset_state + TEST_FAULT=$fault + if run_migration; then + fail "migration must fail on $fault" + fi + assert_no_privileges "$fault must be detected before changing hardware or packages" + [[ -e $TEST_STATE/package && -e $TEST_STATE/vendor-loaded ]] || fail "$fault lost the installed fallback" + pass "$fault fails before cutover" done -cat >"$stub_bin/sudo" <<'SH' -#!/bin/bash -printf 'sudo\t%s\n' "$*" >>"$TEST_CALLS" -"$@" -SH - -chmod +x "$stub_bin"/* -export TEST_CALLS="$calls" -export TEST_BOUND_STATE="$test_tmp/upstream-bound" - -PATH="$stub_bin:$PATH" bash "$ROOT/install/hardware/fix-yt6801-ethernet-adapter.sh" -grep -Fqx $'omarchy-pkg-drop\tyt6801-dkms' "$calls" || - fail "hardware setup did not remove the vendor DKMS package" -if grep -Fq $'modprobe\t' "$calls"; then - fail "hardware setup tried to load a module against the installer kernel" +reset_state +printf '%s\n' 'invalid-address 0200: 1f0a:6801' >"$TEST_STATE/devices" +if run_migration; then + fail "malformed PCI enumeration must fail the migration" fi +assert_no_privileges "invalid PCI data must be rejected before privileged work" +pass "invalid PCI addresses cannot reach the reprobe command" -: >"$calls" -TEST_VENDOR_LOADED=1 PATH="$stub_bin:$PATH" bash -euo pipefail "$ROOT/migrations/1788279117.sh" -grep -Fqx $'sudo\tmodprobe dwmac-motorcomm' "$calls" || - fail "migration did not validate-load the upstream driver" -grep -Fqx $'omarchy-pkg-drop\tyt6801-dkms' "$calls" || - fail "migration did not remove the vendor DKMS package" -grep -Fqx $'sudo\tmodprobe -r yt6801' "$calls" || - fail "migration did not unload the vulnerable driver" -grep -Fqx $'sudo\ttee /sys/bus/pci/drivers_probe' "$calls" || - fail "migration did not reprobe the YT6801 device" +for fault in sudo-fail load-fail load-noop unload-fail unload-noop probe-fail probe-noop binding-read-fail helper-query-fail remove-fail; do + reset_state + TEST_FAULT=$fault + if run_migration; then + fail "migration must fail on $fault" + fi + [[ -e $TEST_STATE/package ]] || fail "$fault discarded the package before cutover succeeded" + TEST_FAULT="" + run_migration + assert_complete + pass "$fault preserves the package and permits a successful retry" +done -# A failed first cutover can leave the vendor module unloaded and the device -# unbound. Rerunning must still reprobe instead of repeating the same failure. -: >"$calls" -rm -f "$TEST_BOUND_STATE" -TEST_VENDOR_LOADED=0 PATH="$stub_bin:$PATH" bash -euo pipefail "$ROOT/migrations/1788279117.sh" -if grep -Fqx $'sudo\tmodprobe -r yt6801' "$calls"; then - fail "retry tried to unload a vendor module that was already absent" +for fault in remove-noop remove-reload remove-unbind final-query-fail final-modules-fail; do + reset_state + TEST_FAULT=$fault + if run_migration; then + fail "postconditions must detect $fault" + fi + TEST_FAULT="" + run_migration + assert_complete + pass "postconditions detect $fault and a retry completes" +done + +for fault in load-interrupt unload-interrupt probe-interrupt remove-interrupt; do + reset_state + TEST_FAULT=$fault + if run_migration; then + fail "interrupted $fault must fail the migration" + fi + TEST_FAULT="" + run_migration + assert_complete + pass "retry completes after $fault" +done + +reset_state +rm "$TEST_STATE/package" "$TEST_STATE/module-files" +run_migration +assert_complete +pass "retry unloads the vendor module even after an earlier DKMS removal deleted its files" + +reset_state +rm "$TEST_STATE/vendor-loaded" "$TEST_STATE/bindings/$TEST_DEVICE" +run_migration +assert_complete +pass "an initially unbound adapter is reprobed without unloading an absent vendor module" + +reset_state +rm "$TEST_STATE/vendor-loaded" +printf '%s\n' dwmac-motorcomm >"$TEST_STATE/bindings/$TEST_DEVICE" +TEST_FAULT=alias-fail +run_migration +assert_complete +pass "an already-bound adapter only needs package cleanup even if module metadata is unavailable" + +reset_state +printf '%s 0200: 1f0a:6801\n' "$TEST_DEVICE_2" >>"$TEST_STATE/devices" +printf '%s\n' yt6801 >"$TEST_STATE/bindings/$TEST_DEVICE_2" +TEST_FAULT=second-probe-noop +if run_migration; then + fail "one working adapter must not hide another adapter's failed cutover" fi -grep -Fqx $'sudo\ttee /sys/bus/pci/drivers_probe' "$calls" || - fail "retry did not reprobe an unbound YT6801 device" -[[ -e $TEST_BOUND_STATE ]] || fail "retry did not restore the upstream binding" +[[ -e $TEST_STATE/package ]] || fail "partial cutover removed the vendor package" +TEST_FAULT="" +run_migration +assert_complete +pass "partial multi-adapter cutover remains pending and can be retried" -: >"$calls" -TEST_VENDOR_LOADED=0 PATH="$stub_bin:$PATH" bash -euo pipefail "$ROOT/migrations/1788279117.sh" -if grep -Fq $'sudo\ttee /sys/bus/pci/drivers_probe' "$calls"; then - fail "settled migration unnecessarily reprobed an already-bound device" +for driver in dwmac-motorcomm-extra dwmac_motorcomm vfio-pci; do + reset_state + rm "$TEST_STATE/vendor-loaded" + printf '%s\n' "$driver" >"$TEST_STATE/bindings/$TEST_DEVICE" + if run_migration; then + fail "unexpected driver $driver must not count as upstream binding" + fi + [[ -e $TEST_STATE/package && $(<"$TEST_STATE/bindings/$TEST_DEVICE") == "$driver" ]] || + fail "failed cutover must preserve an unrelated binding and the package" + pass "exact sysfs binding rejects $driver without forcibly unbinding it" +done + +reset_state +: >"$TEST_STATE/devices" +rm -f "$TEST_STATE/bindings/$TEST_DEVICE" +run_migration +assert_complete +pass "without target hardware, the migration still unloads and removes the vendor driver" + +: >"$TEST_CALLS" +TEST_FAULT=sudo-fail +run_migration +assert_no_privileges "an unaffected machine must need no privileges" +pass "an unaffected machine is a successful no-op" + +# Setup leaves are sourced, and must not inspect/load the live ISO kernel. +reset_state +bash -eE -c 'source "$1"' bash "$ROOT/install/hardware/fix-yt6801-ethernet-adapter.sh" +[[ ! -e $TEST_STATE/package ]] || fail "hardware setup did not remove the vendor package" +if grep -Eq '^(modinfo|modprobe|rmmod|tee) ' "$TEST_CALLS"; then + fail "hardware setup must not manipulate the installer kernel" fi +pass "fresh setup removes the package independently of the ISO kernel" + +for fault in pci-fail pci-partial query-fail remove-fail; do + reset_state + TEST_FAULT=$fault + if bash -eE -c 'source "$1"' bash "$ROOT/install/hardware/fix-yt6801-ethernet-adapter.sh" >"$test_tmp/output" 2>&1; then + fail "hardware setup must fail on $fault" + fi + [[ -e $TEST_STATE/package ]] || fail "failed setup unexpectedly removed the package" + pass "fresh setup propagates $fault" +done + +reset_state +: >"$TEST_STATE/devices" +bash -eE -c 'source "$1"' bash "$ROOT/install/hardware/fix-yt6801-ethernet-adapter.sh" +assert_no_privileges "setup without target hardware must need no privileges" +pass "fresh setup skips machines without target hardware" + +# Exercise the real runner's completion markers and queue, without using the +# current user's state directory or running any other repository migrations. +reset_state +mkdir -p "$test_tmp/omarchy/migrations" +ln -s "$ROOT/migrations/1788279117.sh" "$test_tmp/omarchy/migrations/1788279117.sh" +printf '%s\n' 'touch "$TEST_STATE/later-migration"' >"$test_tmp/omarchy/migrations/1788279118.sh" +export OMARCHY_PATH="$test_tmp/omarchy" +export OMARCHY_MIGRATION_STATE="$test_tmp/migration-state" +TEST_FAULT=probe-noop +if "$ROOT/bin/omarchy-migrate" >"$test_tmp/output" 2>&1; then + fail "migration runner must fail when cutover is incomplete" +fi +[[ ! -e $OMARCHY_MIGRATION_STATE/1788279117.sh && ! -e $TEST_STATE/later-migration ]] || + fail "failed cutover must not write a completion marker or run later migrations" +TEST_FAULT="" +"$ROOT/bin/omarchy-migrate" >"$test_tmp/output" 2>&1 +assert_complete +[[ -f $OMARCHY_MIGRATION_STATE/1788279117.sh && -f $TEST_STATE/later-migration ]] || + fail "successful retry must mark completion and continue the queue" +pass "the real migration runner keeps failures pending and completes successful retries" if grep -Fxq 'yt6801-dkms' "$ROOT/install/omarchy-other.packages"; then fail "default package list still installs yt6801-dkms" fi - -pass "YT6801 installs and retry-safe migrations use the upstream kernel driver" +pass "the default package list no longer installs the vendor driver"