diff --git a/bin/omarchy-setup-security-fingerprint b/bin/omarchy-setup-security-fingerprint index 383aa376..e46aaed9 100755 --- a/bin/omarchy-setup-security-fingerprint +++ b/bin/omarchy-setup-security-fingerprint @@ -74,18 +74,15 @@ if ! omarchy-hw-fingerprint; then exit 1 fi -# Install required packages -echo "Installing required packages..." - -# libfprint-git provides+conflicts libfprint; pacman -S --noconfirm -# defaults the conflict prompt to N and aborts. Pre-remove it (deps-only, -# so an installed fprintd stays put) so stock libfprint installs cleanly. -if pacman -Q libfprint-git &>/dev/null; then - sudo pacman -Rdd --noconfirm libfprint-git +# libfprint-git tracks upstream ahead of the Arch release, so a new reader only +# needs a pin bump in omarchy-pkgs. It conflicts with stock libfprint, and +# --noconfirm answers that prompt with N; --ask 4 accepts the replacement in +# one transaction, so a failed install leaves the existing driver in place. +if omarchy-pkg-missing libfprint-git fprintd usbutils; then + echo "Installing required packages..." + sudo pacman -S --needed --noconfirm --ask 4 libfprint-git fprintd usbutils fi -omarchy-pkg-add libfprint fprintd usbutils - # Enroll first fingerprint echo -e "\e[32m\nLet's setup your right index finger as the first fingerprint.\e[0m" echo -e "Keep moving the finger around on sensor until the process completes.\n" diff --git a/migrations/1785090473.sh b/migrations/1785090473.sh index 1e64b6ee..ca75cf2c 100644 --- a/migrations/1785090473.sh +++ b/migrations/1785090473.sh @@ -1,17 +1,8 @@ -echo "Switch fingerprint support back to stock libfprint" +echo "Repair fingerprint support left without a libfprint" -# libfprint-git existed to carry the focaltech_moc driver and the FocalTech -# FT9349 device ID (2808:a97a) before any release shipped them. libfprint -# 1.94.100 has both, so fingerprint setups go back to the stock Arch package. - -# The remove/install pair below isn't one transaction: if the install failed -# on a previous run, libfprint-git is already gone but fprintd is left with -# no libfprint — the elif finishes the job on rerun. -if pacman -Q libfprint-git &>/dev/null; then - # Deps-only removal keeps fprintd installed while its libfprint - # dependency is swapped out underneath it. - sudo pacman -Rdd --noconfirm libfprint-git - omarchy-pkg-add libfprint -elif pacman -Q fprintd &>/dev/null && ! pacman -Q libfprint &>/dev/null; then - omarchy-pkg-add libfprint +# An earlier version of this migration swapped libfprint-git for stock +# libfprint in two steps. A run that failed between them left fprintd with +# no library; finish with the driver the fingerprint setup installs now. +if omarchy-pkg-present fprintd && omarchy-pkg-missing libfprint && omarchy-pkg-missing libfprint-git; then + omarchy-pkg-add libfprint-git fi diff --git a/test/shell.d/fingerprint-driver-migration-test.sh b/test/shell.d/fingerprint-driver-migration-test.sh new file mode 100755 index 00000000..6b3f523c --- /dev/null +++ b/test/shell.d/fingerprint-driver-migration-test.sh @@ -0,0 +1,60 @@ +#!/bin/bash +# +# The fingerprint driver migration only repairs a machine an earlier version of +# it left with fprintd and no libfprint; any installed driver is left alone. +# The real package helpers run over a stubbed pacman. + +set -euo pipefail + +source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" + +migration="$ROOT/migrations/1785090473.sh" +scratch=$(mktemp -d) +trap 'rm -rf "$scratch"' EXIT +mkdir -p "$scratch/bin" +export CALL_LOG="$scratch/calls" +export PATH="$scratch/bin:$ROOT/bin:$PATH" + +cat > "$scratch/bin/sudo" <<'STUB' +#!/bin/bash +exec "$@" +STUB +# INSTALLED lists the installed package names, one per line; an install adds +# its packages to INSTALLED_LOG so omarchy-pkg-add's follow-up query sees them. +cat > "$scratch/bin/pacman" <<'STUB' +#!/bin/bash +case "$1" in + -Q) grep -qx "$2" <<< "${INSTALLED:-}" || grep -qx "$2" "$INSTALLED_LOG" ;; + -S) + printf 'pacman %s\n' "$*" >> "$CALL_LOG" + for arg in "$@"; do + [[ $arg == -* ]] || printf '%s\n' "$arg" >> "$INSTALLED_LOG" + done + ;; + *) printf 'pacman %s\n' "$*" >> "$CALL_LOG" ;; +esac +STUB +chmod +x "$scratch/bin/"* +export INSTALLED_LOG="$scratch/installed" + +run_migration() { + : > "$CALL_LOG" + : > "$INSTALLED_LOG" + bash -euo pipefail "$migration" > /dev/null +} + +INSTALLED='fprintd' run_migration +grep -qx 'pacman -S --noconfirm --needed libfprint-git' "$CALL_LOG" || fail "fprintd without a library gets libfprint-git" +pass "fprintd without a library gets libfprint-git" + +INSTALLED=$'libfprint-git\nfprintd' run_migration +[[ ! -s $CALL_LOG ]] || fail "an installed libfprint-git is left alone" "$(<"$CALL_LOG")" +pass "an installed libfprint-git is left alone" + +INSTALLED=$'libfprint\nfprintd' run_migration +[[ ! -s $CALL_LOG ]] || fail "an installed stock libfprint is left alone" "$(<"$CALL_LOG")" +pass "an installed stock libfprint is left alone" + +INSTALLED='' run_migration +[[ ! -s $CALL_LOG ]] || fail "a machine without fprintd is left alone" "$(<"$CALL_LOG")" +pass "a machine without fprintd is left alone" diff --git a/test/shell.d/fingerprint-package-test.sh b/test/shell.d/fingerprint-package-test.sh new file mode 100755 index 00000000..70329982 --- /dev/null +++ b/test/shell.d/fingerprint-package-test.sh @@ -0,0 +1,93 @@ +#!/bin/bash +# +# The fingerprint setup installs libfprint-git in place of stock libfprint. The +# two conflict, so the swap has to happen inside one --ask 4 transaction, and a +# rerun with everything installed must not touch pacman at all. The real +# omarchy-pkg-missing runs; pacman and the privileged calls are stubbed. + +set -euo pipefail + +source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" + +scratch=$(mktemp -d) +trap 'rm -rf "$scratch"' EXIT +mkdir -p "$scratch/bin" +export CALL_LOG="$scratch/calls" +export PATH="$scratch/bin:$ROOT/bin:$PATH" + +cat > "$scratch/bin/omarchy-hw-fingerprint" <<'STUB' +#!/bin/bash +exit "${HARDWARE_STATUS:-0}" +STUB +cat > "$scratch/bin/sudo" <<'STUB' +#!/bin/bash +case "$1" in + pacman | fprintd-enroll) exec "$@" ;; + *) echo "Unexpected privileged call: $*" >> "$CALL_LOG"; exit 99 ;; +esac +STUB +# INSTALLED lists the installed package names, one per line. +cat > "$scratch/bin/pacman" <<'STUB' +#!/bin/bash +case "$1" in + -Q) grep -qx "$2" <<< "${INSTALLED:-}" ;; + -S) + printf 'pacman %s\n' "$*" >> "$CALL_LOG" + exit "${INSTALL_STATUS:-0}" + ;; + *) printf 'pacman %s\n' "$*" >> "$CALL_LOG"; exit 99 ;; +esac +STUB +cat > "$scratch/bin/fprintd-enroll" <<'STUB' +#!/bin/bash +# Stop before verification/PAM; no host authentication files may be changed. +echo enroll >> "$CALL_LOG" +exit 1 +STUB +cat > "$scratch/bin/fprintd-verify" <<'STUB' +#!/bin/bash +echo verify >> "$CALL_LOG" +exit 1 +STUB +chmod +x "$scratch/bin/"* + +run_setup() { + : > "$CALL_LOG" + if "$ROOT/bin/omarchy-setup-security-fingerprint" > "$scratch/output" 2>&1; then + fail "setup stops on the simulated enrollment or installation failure" + fi + if grep -q 'Unexpected privileged call' "$CALL_LOG"; then + fail "setup does not change PAM after failed enrollment" + fi +} + +assert_installs() { + grep -qx 'pacman -S --needed --noconfirm --ask 4 libfprint-git fprintd usbutils' "$CALL_LOG" || fail "$1" + (( $(grep -c '^pacman ' "$CALL_LOG") == 1 )) || fail "$1: one pacman transaction" +} + +run_setup +assert_installs "a fresh machine installs libfprint-git, fprintd and usbutils" +grep -qx enroll "$CALL_LOG" || fail "installation is followed by enrollment" +pass "a fresh machine installs libfprint-git and reaches enrollment" + +INSTALLED=$'libfprint\nfprintd\nusbutils' run_setup +assert_installs "installed stock libfprint is replaced in the same transaction" +pass "installed stock libfprint is replaced without a removal step" + +INSTALLED=$'libfprint-git\nfprintd\nusbutils' run_setup +if grep -q '^pacman' "$CALL_LOG"; then + fail "a rerun with everything installed does not touch pacman" +fi +grep -qx enroll "$CALL_LOG" || fail "a rerun with everything installed reaches enrollment" +pass "a rerun with everything installed goes straight to enrollment" + +INSTALL_STATUS=1 run_setup +if grep -qx enroll "$CALL_LOG"; then + fail "a failed package transaction prevents enrollment" +fi +pass "a failed installation stops before enrollment" + +HARDWARE_STATUS=1 run_setup +[[ ! -s $CALL_LOG ]] || fail "missing hardware stops before package operations" +pass "missing hardware performs no package operations"