aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-04-27 12:45:48 -0500
committerCraig Jennings <c@cjennings.net>2026-04-27 12:45:48 -0500
commit8c69aaaff13da3b7d1d24ed34975e8c5b30409e6 (patch)
treeb296d568c124d1d8ca83275e8c197c1c0d53bca0
parent822075bf99cda84782ef04419855f6c289a6fc13 (diff)
downloadarchangel-8c69aaaff13da3b7d1d24ed34975e8c5b30409e6.tar.gz
archangel-8c69aaaff13da3b7d1d24ed34975e8c5b30409e6.zip
refactor: consolidate installer defaults and FILESYSTEM validation into config.sh
The installer had three sites touching FILESYSTEM: a top-level default in the monolith, a re-default block in gather_input, and a runtime validation block also in gather_input. The same scattering existed for LOCALE, KEYMAP, ENABLE_SSH, and NO_ENCRYPT. A future contributor changing one site wouldn't have known the other two existed. Move all five defaults into the lib/config.sh declarations so config.sh is the single source of truth. Add validate_filesystem() in lib/config.sh and call it from main() between check_config and gather_input, so a typo in a config file's FILESYSTEM= fails fast before any install action runs. The behavior change is stricter. An empty FILESYSTEM in a config file used to be silently defaulted to zfs, now it errors. Interactive mode is unaffected. select_filesystem still controls the value and already errored on cancellation. Bats: 140 → 142. Five tests added in test_config.bats for the defaults pinning and validate_filesystem coverage. Three removed from test_archangel.bats for behavior that moved out of gather_input. Lint clean.
-rwxr-xr-xinstaller/archangel40
-rw-r--r--installer/lib/config.sh23
-rw-r--r--testing-strategy.org2
-rw-r--r--tests/unit/test_archangel.bats45
-rw-r--r--tests/unit/test_config.bats49
5 files changed, 84 insertions, 75 deletions
diff --git a/installer/archangel b/installer/archangel
index e04b8d9..12f1cd0 100755
--- a/installer/archangel
+++ b/installer/archangel
@@ -34,32 +34,20 @@ source "$SCRIPT_DIR/lib/raid.sh"
#############################
# Configuration
#############################
-
-# Filesystem selection (zfs or btrfs)
-FILESYSTEM="zfs" # Default to ZFS, can be changed interactively or via config
-
-# These will be set interactively
-HOSTNAME=""
-TIMEZONE=""
-LOCALE="en_US.UTF-8"
-KEYMAP="us"
-ROOT_PASSWORD=""
-ZFS_PASSPHRASE=""
-WIFI_SSID=""
-WIFI_PASSWORD=""
+#
+# User-facing config variables (FILESYSTEM, LOCALE, KEYMAP, ENABLE_SSH,
+# NO_ENCRYPT, HOSTNAME, TIMEZONE, SELECTED_DISKS, RAID_LEVEL, WIFI_*,
+# ROOT_PASSWORD, ZFS_PASSPHRASE, LUKS_PASSPHRASE) are declared in
+# lib/config.sh with their defaults. The monolith trusts those values.
# ZFS Configuration
POOL_NAME="zroot"
COMPRESSION="zstd"
ASHIFT="12" # 4K sectors (use 13 for 8K)
-# Multi-disk RAID support
-SELECTED_DISKS=() # Array of selected disk paths (/dev/sda, /dev/sdb, ...)
-ROOT_PARTS=() # Array of root partition paths (populated by partition_disks)
-EFI_PARTS=() # Array of EFI partition paths (populated by partition_disks)
-RAID_LEVEL="" # "", "mirror", "raidz1", "raidz2", "raidz3"
-ENABLE_SSH="yes" # Enable SSH with root login (default yes for headless)
-NO_ENCRYPT="no" # Skip ZFS encryption (for testing only)
+# Partition arrays populated by partition_disks
+ROOT_PARTS=()
+EFI_PARTS=()
# Logging — initialized by init_logging() at install time only. Sourcing
# this script (e.g. from a bats test) loads the function definitions
@@ -117,12 +105,6 @@ gather_input() {
if [[ -z "$ROOT_PASSWORD" ]]; then error "Config missing required: ROOT_PASSWORD"; fi
if [[ ${#SELECTED_DISKS[@]} -eq 0 ]]; then error "Config missing required: DISKS"; fi
- # Set defaults for optional values
- [[ -z "$FILESYSTEM" ]] && FILESYSTEM="zfs" || true
- [[ -z "$LOCALE" ]] && LOCALE="en_US.UTF-8" || true
- [[ -z "$KEYMAP" ]] && KEYMAP="us" || true
- [[ -z "$ENABLE_SSH" ]] && ENABLE_SSH="yes" || true
-
# ZFS-specific validation
if [[ "$FILESYSTEM" == "zfs" ]]; then
if [[ "$NO_ENCRYPT" != "yes" && -z "$ZFS_PASSPHRASE" ]]; then
@@ -137,11 +119,6 @@ gather_input() {
fi
fi
- # Validate filesystem choice
- if [[ "$FILESYSTEM" != "zfs" && "$FILESYSTEM" != "btrfs" ]]; then
- error "Invalid FILESYSTEM: $FILESYSTEM (must be 'zfs' or 'btrfs')"
- fi
-
# Determine RAID level if not specified
if [[ -z "$RAID_LEVEL" && ${#SELECTED_DISKS[@]} -gt 1 ]]; then
RAID_LEVEL="mirror"
@@ -1384,6 +1361,7 @@ main() {
parse_args "$@"
preflight_checks
check_config
+ validate_filesystem
gather_input
filesystem_preflight
diff --git a/installer/lib/config.sh b/installer/lib/config.sh
index fefc838..65703d0 100644
--- a/installer/lib/config.sh
+++ b/installer/lib/config.sh
@@ -9,12 +9,18 @@
CONFIG_FILE=""
UNATTENDED=false
-# These get populated by config file or interactive prompts
-FILESYSTEM="" # "zfs" or "btrfs"
+# These get populated by config file or interactive prompts.
+# Optional fields carry their default value here so config.sh is the
+# single source of truth — gather_input trusts what's loaded.
+FILESYSTEM="zfs" # "zfs" or "btrfs"
+LOCALE="en_US.UTF-8"
+KEYMAP="us"
+ENABLE_SSH="yes" # SSH with root login (default yes for headless)
+NO_ENCRYPT="no" # Skip filesystem encryption (testing only)
+
+# Required fields — installer errors out if any are still empty at install time.
HOSTNAME=""
TIMEZONE=""
-LOCALE=""
-KEYMAP=""
SELECTED_DISKS=()
RAID_LEVEL=""
WIFI_SSID=""
@@ -134,3 +140,12 @@ validate_config() {
fi
info "Config validation passed"
}
+
+# Catches a typo in FILESYSTEM= from a config file before the install
+# starts. Called from main() after check_config so a bad value never
+# reaches gather_input or filesystem_preflight.
+validate_filesystem() {
+ if [[ "$FILESYSTEM" != "zfs" && "$FILESYSTEM" != "btrfs" ]]; then
+ error "Invalid FILESYSTEM: $FILESYSTEM (must be 'zfs' or 'btrfs')"
+ fi
+}
diff --git a/testing-strategy.org b/testing-strategy.org
index cfc3953..b008cc1 100644
--- a/testing-strategy.org
+++ b/testing-strategy.org
@@ -62,7 +62,7 @@ Current coverage lives in =tests/unit/=:
| File | What it covers |
|------+----------------|
| =test_common.bats= | =command_exists=, =require_command=, =info=/=warn=/=error=, =enable_color=, =log=, =prompt_password=, =pacstrap_packages=, =install_dropin= |
-| =test_config.bats= | =parse_args=, =load_config=, =validate_config=, =check_config= |
+| =test_config.bats= | =parse_args=, =load_config=, =validate_config=, =validate_filesystem=, =check_config=, default values pinned in config.sh |
| =test_raid.bats= | =raid_valid_levels_for_count=, =raid_is_valid=, =raid_usable_bytes=, =raid_fault_tolerance= |
| =test_disk.bats= | =get_efi_partition=, =get_root_partition=, =partition_disks= (orchestration shape) |
| =test_btrfs.bats= | =get_luks_devices= |
diff --git a/tests/unit/test_archangel.bats b/tests/unit/test_archangel.bats
index ccabec7..749f786 100644
--- a/tests/unit/test_archangel.bats
+++ b/tests/unit/test_archangel.bats
@@ -70,33 +70,9 @@ setup() {
#############################
# Optional-field defaults
#############################
-
-@test "gather_input unattended defaults FILESYSTEM to zfs when empty" {
- HOSTNAME=h
- TIMEZONE=UTC
- ROOT_PASSWORD=x
- SELECTED_DISKS=(/dev/sda)
- FILESYSTEM=""
- NO_ENCRYPT=yes
- gather_input >/dev/null
- [ "$FILESYSTEM" = "zfs" ]
-}
-
-@test "gather_input unattended defaults LOCALE, KEYMAP, ENABLE_SSH when empty" {
- HOSTNAME=h
- TIMEZONE=UTC
- ROOT_PASSWORD=x
- SELECTED_DISKS=(/dev/sda)
- FILESYSTEM=zfs
- NO_ENCRYPT=yes
- LOCALE=""
- KEYMAP=""
- ENABLE_SSH=""
- gather_input >/dev/null
- [ "$LOCALE" = "en_US.UTF-8" ]
- [ "$KEYMAP" = "us" ]
- [ "$ENABLE_SSH" = "yes" ]
-}
+# Default values themselves are pinned in test_config.bats (config.sh
+# is the single source of truth). The remaining test here covers the
+# adjacent guarantee: gather_input doesn't clobber values the user set.
@test "gather_input unattended preserves explicit non-default values" {
HOSTNAME=h
@@ -160,18 +136,9 @@ setup() {
#############################
# Filesystem validity
#############################
-
-@test "gather_input unattended errors when FILESYSTEM is neither zfs nor btrfs" {
- HOSTNAME=h
- TIMEZONE=UTC
- ROOT_PASSWORD=x
- SELECTED_DISKS=(/dev/sda)
- FILESYSTEM=ext4
- NO_ENCRYPT=yes
- run gather_input
- [ "$status" -eq 1 ]
- [[ "$output" == *"Invalid FILESYSTEM"* ]]
-}
+# Validation moved to validate_filesystem in lib/config.sh — covered
+# by test_config.bats. main() calls it between check_config and
+# gather_input so a bad FILESYSTEM= never reaches install time.
#############################
# RAID-level defaulting
diff --git a/tests/unit/test_config.bats b/tests/unit/test_config.bats
index c0c4e42..46f0236 100644
--- a/tests/unit/test_config.bats
+++ b/tests/unit/test_config.bats
@@ -156,3 +156,52 @@ EOF
[ "$CONFIG_FILE" = "/tmp/foo.conf" ]
[ -n "$RED" ]
}
+
+#############################
+# Default values sourced from config.sh
+#############################
+# config.sh is the single source of truth for installer defaults. The
+# monolith no longer re-applies them in gather_input. These tests pin
+# the values so a regression that drops a default surfaces here, not
+# halfway through an unattended install.
+
+@test "config.sh sets defaults for FILESYSTEM, LOCALE, KEYMAP, ENABLE_SSH, NO_ENCRYPT" {
+ [ "$FILESYSTEM" = "zfs" ]
+ [ "$LOCALE" = "en_US.UTF-8" ]
+ [ "$KEYMAP" = "us" ]
+ [ "$ENABLE_SSH" = "yes" ]
+ [ "$NO_ENCRYPT" = "no" ]
+}
+
+#############################
+# validate_filesystem
+#############################
+# Called from main() between check_config and gather_input. Catches a
+# typo in FILESYSTEM= from a config file before the install starts.
+
+@test "validate_filesystem accepts zfs" {
+ FILESYSTEM=zfs
+ run validate_filesystem
+ [ "$status" -eq 0 ]
+}
+
+@test "validate_filesystem accepts btrfs" {
+ FILESYSTEM=btrfs
+ run validate_filesystem
+ [ "$status" -eq 0 ]
+}
+
+@test "validate_filesystem rejects an unknown filesystem" {
+ FILESYSTEM=ext4
+ run validate_filesystem
+ [ "$status" -eq 1 ]
+ [[ "$output" == *"Invalid FILESYSTEM"* ]]
+ [[ "$output" == *"ext4"* ]]
+}
+
+@test "validate_filesystem rejects an empty FILESYSTEM" {
+ FILESYSTEM=""
+ run validate_filesystem
+ [ "$status" -eq 1 ]
+ [[ "$output" == *"Invalid FILESYSTEM"* ]]
+}