Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bdff11f0d0 | ||
|
|
730ae083c4 | ||
|
|
d6cd2d30a6 | ||
|
|
be618cc73b | ||
|
|
a6a442b9a0 | ||
|
|
e5f2b466ef | ||
|
|
a12bfea7a8 | ||
|
|
8b3dcc68b2 | ||
|
|
22eb81ebf5 | ||
|
|
8858ac2544 | ||
|
|
6b2f8a6ac7 |
No files matched your search
@@ -711,17 +711,17 @@ drive_provision_owner() {
|
|||||||
log "Driving the deferred-provisioning first-boot setup"
|
log "Driving the deferred-provisioning first-boot setup"
|
||||||
|
|
||||||
# Deferred provisioning defers the keyboard step to first boot; it comes before the user form.
|
# Deferred provisioning defers the keyboard step to first boot; it comes before the user form.
|
||||||
# Pick English (UK) (one row above the preselected English (US)) so the
|
# Pick English (UK) (one row below the preselected English (US), which leads
|
||||||
# verifier can prove the deferred step applied a non-default layout — UK
|
# the list) so the verifier can prove the deferred step applied a non-default
|
||||||
# leaves every letter identical to US, so the typed password (sent as raw
|
# layout — UK leaves every letter identical to US, so the typed password (sent
|
||||||
# keycodes) still matches the literal one the harness pipes to sudo.
|
# as raw keycodes) still matches the literal one the harness pipes to sudo.
|
||||||
# A greeter (animated logo + tagline) opens first boot; Return begins.
|
# A greeter (animated logo + tagline) opens first boot; Return begins.
|
||||||
wait_for_screen "Opinionated" 600
|
wait_for_screen "Opinionated" 600
|
||||||
capture_console "success-provision-00-greeter"
|
capture_console "success-provision-00-greeter"
|
||||||
press ret
|
press ret
|
||||||
|
|
||||||
wait_for_screen "keyboard layout" 120
|
wait_for_screen "keyboard layout" 120
|
||||||
press up
|
press down
|
||||||
capture_console "success-provision-01-keyboard-layout-uk"
|
capture_console "success-provision-01-keyboard-layout-uk"
|
||||||
press ret
|
press ret
|
||||||
|
|
||||||
|
|||||||
+27
-1
@@ -117,7 +117,7 @@ cp "/tmp/$NODE_FILENAME" "$build_cache_dir/airootfs/opt/packages/"
|
|||||||
# The selected omarchy-settings package is needed here so its post_install hook
|
# The selected omarchy-settings package is needed here so its post_install hook
|
||||||
# drops Omarchy's plymouthd.conf into /etc/plymouth before mkarchiso builds the
|
# drops Omarchy's plymouthd.conf into /etc/plymouth before mkarchiso builds the
|
||||||
# live initramfs.
|
# live initramfs.
|
||||||
arch_packages=(linux-t2 git gum jq openssl plymouth python-terminaltexteffects tzupdate omarchy-keyring "$OMARCHY_SETTINGS_PACKAGE" lvm2 cryptsetup parted)
|
arch_packages=(linux-t2 git gum jq openssl plymouth ttfx tzupdate omarchy-keyring "$OMARCHY_SETTINGS_PACKAGE" lvm2 cryptsetup parted)
|
||||||
printf '%s\n' "${arch_packages[@]}" >> "$build_cache_dir/packages.x86_64"
|
printf '%s\n' "${arch_packages[@]}" >> "$build_cache_dir/packages.x86_64"
|
||||||
|
|
||||||
# The live ISO boots linux-t2 (see airootfs/etc/mkinitcpio.d/linux-t2.preset), so
|
# The live ISO boots linux-t2 (see airootfs/etc/mkinitcpio.d/linux-t2.preset), so
|
||||||
@@ -139,6 +139,7 @@ sed -i -E '/^(linux|broadcom-wl)$/d' "$build_cache_dir/packages.x86_64"
|
|||||||
# pulls the published omarchy* from the network mirror like any other package.
|
# pulls the published omarchy* from the network mirror like any other package.
|
||||||
if [[ -d /omarchy-source ]]; then
|
if [[ -d /omarchy-source ]]; then
|
||||||
base_pkg_lists=(/omarchy-source/install/omarchy-base.packages /omarchy-source/install/omarchy-other.packages)
|
base_pkg_lists=(/omarchy-source/install/omarchy-base.packages /omarchy-source/install/omarchy-other.packages)
|
||||||
|
setup_form=/omarchy-source/install/provisioning/setup-form.sh
|
||||||
else
|
else
|
||||||
# Pull the same package lists out of the freshly-downloaded Omarchy runtime
|
# Pull the same package lists out of the freshly-downloaded Omarchy runtime
|
||||||
# package so we don't need a local checkout in the non-local-source path.
|
# package so we don't need a local checkout in the non-local-source path.
|
||||||
@@ -154,12 +155,37 @@ else
|
|||||||
mkdir -p /tmp/omarchy-pkglists
|
mkdir -p /tmp/omarchy-pkglists
|
||||||
bsdtar -xf "$omarchy_pkg" -C /tmp/omarchy-pkglists usr/share/omarchy/install/omarchy-base.packages usr/share/omarchy/install/omarchy-other.packages
|
bsdtar -xf "$omarchy_pkg" -C /tmp/omarchy-pkglists usr/share/omarchy/install/omarchy-base.packages usr/share/omarchy/install/omarchy-other.packages
|
||||||
base_pkg_lists=(/tmp/omarchy-pkglists/usr/share/omarchy/install/omarchy-base.packages /tmp/omarchy-pkglists/usr/share/omarchy/install/omarchy-other.packages)
|
base_pkg_lists=(/tmp/omarchy-pkglists/usr/share/omarchy/install/omarchy-base.packages /tmp/omarchy-pkglists/usr/share/omarchy/install/omarchy-other.packages)
|
||||||
|
# Extracted on its own, tolerating a miss: bsdtar exits non-zero for a member
|
||||||
|
# it can't find, so asking for this alongside the package lists would abort the
|
||||||
|
# build here (set -e) with a bare "Not found in archive" instead of the
|
||||||
|
# actionable error below.
|
||||||
|
bsdtar -xf "$omarchy_pkg" -C /tmp/omarchy-pkglists usr/share/omarchy/install/provisioning/setup-form.sh 2>/dev/null || true
|
||||||
|
setup_form=/tmp/omarchy-pkglists/usr/share/omarchy/install/provisioning/setup-form.sh
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mkdir -p "$build_cache_dir/airootfs/usr/share/omarchy-iso"
|
mkdir -p "$build_cache_dir/airootfs/usr/share/omarchy-iso"
|
||||||
cp "${base_pkg_lists[0]}" "$build_cache_dir/airootfs/usr/share/omarchy-iso/omarchy-base.packages"
|
cp "${base_pkg_lists[0]}" "$build_cache_dir/airootfs/usr/share/omarchy-iso/omarchy-base.packages"
|
||||||
cp "${base_pkg_lists[1]}" "$build_cache_dir/airootfs/usr/share/omarchy-iso/omarchy-other.packages"
|
cp "${base_pkg_lists[1]}" "$build_cache_dir/airootfs/usr/share/omarchy-iso/omarchy-other.packages"
|
||||||
|
|
||||||
|
# The configurator's setup form comes from the runtime this ISO bundles, so the
|
||||||
|
# installer and the first-boot setup that finishes a deferred install can never
|
||||||
|
# disagree. A runtime predating the split ships no such file, which would leave
|
||||||
|
# the configurator with no prompts at all.
|
||||||
|
if [[ ! -f $setup_form ]]; then
|
||||||
|
if [[ -d /omarchy-source ]]; then
|
||||||
|
echo "ERROR: the --local-source checkout ships no install/provisioning/setup-form.sh" >&2
|
||||||
|
remedy="Update the checkout to a revision carrying the shared setup form."
|
||||||
|
else
|
||||||
|
echo "ERROR: $OMARCHY_RUNTIME_PACKAGE does not ship install/provisioning/setup-form.sh" >&2
|
||||||
|
remedy="Publish a runtime carrying the shared setup form, or build with --local-source against a checkout that has it."
|
||||||
|
fi
|
||||||
|
echo " The configurator sources its prompts from that file, so this ISO" >&2
|
||||||
|
echo " would boot into an installer with no questions to ask." >&2
|
||||||
|
echo " $remedy" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
cp "$setup_form" "$build_cache_dir/airootfs/usr/share/omarchy-iso/setup-form.sh"
|
||||||
|
|
||||||
# Collect every package we want available in the offline mirror.
|
# Collect every package we want available in the offline mirror.
|
||||||
declare -a all_packages
|
declare -a all_packages
|
||||||
mapfile -t all_packages < <(
|
mapfile -t all_packages < <(
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# linux-t2 ships for every machine, and on a pre-T2 Mac its applesmc oopses
|
||||||
|
# registering the keyboard-backlight LED, wedging udev. Nothing here needs the SMC.
|
||||||
|
blacklist applesmc
|
||||||
+107
-159
@@ -9,6 +9,21 @@ OMARCHY_SETTINGS_PACKAGE="${OMARCHY_SETTINGS_PACKAGE:-omarchy-settings}"
|
|||||||
|
|
||||||
LOGO_PATH="$OMARCHY_PATH/logo.txt"
|
LOGO_PATH="$OMARCHY_PATH/logo.txt"
|
||||||
|
|
||||||
|
# The setup form — keyboard, account, hostname, and timezone questions, plus the
|
||||||
|
# rules their answers are checked against — shared verbatim with the first-boot
|
||||||
|
# owner setup that finishes a deferred install. build-iso.sh vendors it out of
|
||||||
|
# the runtime package this ISO bundles, so the two can never offer different
|
||||||
|
# layouts or accept different usernames. On the ISO that vendored copy is what
|
||||||
|
# loads; a dry run outside the ISO falls back to the omarchy checkout
|
||||||
|
# bin/omarchy-iso-configurator already points OMARCHY_PATH at.
|
||||||
|
SETUP_FORM="${SETUP_FORM:-/usr/share/omarchy-iso/setup-form.sh}"
|
||||||
|
[[ -r $SETUP_FORM ]] || SETUP_FORM="$OMARCHY_PATH/install/provisioning/setup-form.sh"
|
||||||
|
if [[ ! -r $SETUP_FORM ]]; then
|
||||||
|
echo "ERROR: shared setup form not found at $SETUP_FORM" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
source "$SETUP_FORM"
|
||||||
|
|
||||||
LOGO_WIDTH=$(awk '{ if (length > max) max = length } END { print max+0 }' "$LOGO_PATH")
|
LOGO_WIDTH=$(awk '{ if (length > max) max = length } END { print max+0 }' "$LOGO_PATH")
|
||||||
|
|
||||||
export GUM_CONFIRM_PROMPT_FOREGROUND="6"
|
export GUM_CONFIRM_PROMPT_FOREGROUND="6"
|
||||||
@@ -106,36 +121,42 @@ greeter() {
|
|||||||
|
|
||||||
# ColorShift the logo: a green base (indexed color 2) with a cyan accent (6)
|
# ColorShift the logo: a green base (indexed color 2) with a cyan accent (6)
|
||||||
# drifting through, settling on green. Indexed ANSI colors, not hex — the
|
# drifting through, settling on green. Indexed ANSI colors, not hex — the
|
||||||
# framebuffer console can't render tte's truecolor faithfully (it crushes the
|
# framebuffer console can't render truecolor faithfully (it crushes the
|
||||||
# palette to a muddy lavender), but the 16 indexed colors map to the Tokyo
|
# palette to a muddy lavender), but the 16 indexed colors map to the Tokyo
|
||||||
# Night palette and render true. One long-running invocation (many cycles) so
|
# Night palette and render true. --xterm-colors is what keeps them indexed:
|
||||||
# it never restarts — a restart is what flashed. The effect reads from
|
# ttfx resolves even indexed stops to truecolor otherwise, which would put the
|
||||||
# /dev/null so it never swallows the Return; --reuse-canvas paints upward from
|
# settle color out of step with the `gum style --foreground 2` logo underneath.
|
||||||
# the saved cursor, anchored one row below the logo.
|
# One long-running invocation (many cycles) so it never restarts — a restart is
|
||||||
if command -v tte >/dev/null 2>&1; then
|
# what flashed. The effect reads from /dev/null so it never swallows the
|
||||||
printf '\033[%d;1H\0337' "$((logo_row + logo_h))"
|
# Return; --reuse-canvas paints upward from the saved cursor, anchored one row
|
||||||
# Run tte directly (not inside a `while` subshell) so $anim is tte's own
|
# below the logo.
|
||||||
# PID: killing a wrapping subshell would orphan tte, which then keeps
|
printf '\033[%d;1H\0337' "$((logo_row + logo_h))"
|
||||||
# painting the logo over the keyboard step. --cycles is high enough that it
|
# Run ttfx directly (not inside a `while` subshell) so $anim is ttfx's own
|
||||||
# never ends on its own before Return.
|
# PID: killing a wrapping subshell would orphan ttfx, which then keeps
|
||||||
tte -i "$LOGO_PATH" \
|
# painting the logo over the keyboard step. --cycles is high enough that it
|
||||||
--canvas-width "$cols" \
|
# never ends on its own before Return.
|
||||||
--anchor-text c \
|
# Two columns narrower than the console: ttfx centers its text two columns
|
||||||
--frame-rate 60 \
|
# right of plain centering, so a full-width canvas would sit the animated
|
||||||
--reuse-canvas \
|
# logo a column off the gum-drawn one underneath it.
|
||||||
colorshift \
|
ttfx -i "$LOGO_PATH" \
|
||||||
--gradient-stops 2 10 6 10 \
|
--canvas-width "$((cols > 2 ? cols - 2 : cols))" \
|
||||||
--gradient-frames 3 \
|
--anchor-text c \
|
||||||
--cycles 1000 \
|
--frame-rate 60 \
|
||||||
--final-gradient-stops 2 \
|
--reuse-canvas \
|
||||||
</dev/null >/dev/tty 2>/dev/null &
|
--xterm-colors \
|
||||||
anim=$!
|
colorshift \
|
||||||
fi
|
--gradient-stops 2 10 6 10 \
|
||||||
|
--gradient-frames 3 \
|
||||||
|
--cycles 1000 \
|
||||||
|
--final-gradient-stops 2 \
|
||||||
|
</dev/null >/dev/tty 2>/dev/null &
|
||||||
|
anim=$!
|
||||||
|
|
||||||
IFS= read -r _ </dev/tty || true
|
IFS= read -r _ </dev/tty || true
|
||||||
# Tolerate tte's kill signal in `wait` so a future `set -e` here can't abort.
|
# Tolerate ttfx's kill signal in `wait` so a future `set -e` here can't abort.
|
||||||
[[ -n ${anim:-} ]] && { kill "$anim" 2>/dev/null; wait "$anim" 2>/dev/null || true; }
|
kill "$anim" 2>/dev/null
|
||||||
# tte leaves the tty in raw/no-echo mode when killed; restore it or the gum
|
wait "$anim" 2>/dev/null || true
|
||||||
|
# ttfx leaves the tty in raw/no-echo mode when killed; restore it or the gum
|
||||||
# prompts in the keyboard step that follows silently die.
|
# prompts in the keyboard step that follows silently die.
|
||||||
stty sane </dev/tty 2>/dev/null || true
|
stty sane </dev/tty 2>/dev/null || true
|
||||||
# Reset the screen so the leftover animation frame doesn't linger under the
|
# Reset the screen so the leftover animation frame doesn't linger under the
|
||||||
@@ -172,81 +193,33 @@ notice() {
|
|||||||
# STEP 1: KEYBOARD LAYOUT
|
# STEP 1: KEYBOARD LAYOUT
|
||||||
|
|
||||||
keyboard_form() {
|
keyboard_form() {
|
||||||
# The Ctrl+C hint sits directly under the header (no blank line between),
|
# Ctrl+C is the hidden entry into deferred provisioning, offered on every
|
||||||
# matching the disk-overwrite screen's unencrypted offer. Ctrl+C on the first
|
# visit: the user step unwinds back here and nothing is on disk yet. Callers
|
||||||
# screen is the hidden entry into deferred provisioning; $1 is "true" only for that first
|
# re-check defer_provisioning after this returns.
|
||||||
# invocation, so the review-loop re-edit can't flip a user install into it.
|
local status
|
||||||
local allow_defer_provisioning="${1:-false}"
|
|
||||||
clear_logo
|
while true; do
|
||||||
echo
|
clear_logo
|
||||||
say "Let's setup your machine..."
|
echo
|
||||||
if $allow_defer_provisioning; then
|
say "Let's setup your machine..."
|
||||||
say --foreground 8 "Press Ctrl+C to prepare this machine for another owner."
|
say --foreground 8 "Press Ctrl+C to prepare this machine for another owner."
|
||||||
fi
|
echo
|
||||||
echo
|
|
||||||
|
|
||||||
keyboards=$'Azerbaijani|azerty
|
omarchy_prompt_keyboard && status=0 || status=$?
|
||||||
Belarusian|by
|
((status == 0)) && break
|
||||||
Belgian|be-latin1
|
|
||||||
Bulgarian|bg-cp1251
|
|
||||||
Croatian|croat
|
|
||||||
Czech|cz
|
|
||||||
Danish|dk-latin1
|
|
||||||
Dutch|nl
|
|
||||||
English (UK)|uk
|
|
||||||
English (US)|us
|
|
||||||
English (US, Dvorak)|dvorak
|
|
||||||
English (US, Colemak)|colemak
|
|
||||||
Estonian|et
|
|
||||||
Finnish|fi
|
|
||||||
French|fr
|
|
||||||
French (Canada)|cf
|
|
||||||
French (Switzerland)|fr_CH
|
|
||||||
Georgian|ge
|
|
||||||
German|de
|
|
||||||
German (Switzerland)|de_CH-latin1
|
|
||||||
Greek|gr
|
|
||||||
Hebrew|il
|
|
||||||
Hungarian|hu
|
|
||||||
Icelandic|is-latin1
|
|
||||||
Irish|ie
|
|
||||||
Italian|it
|
|
||||||
Japanese|jp106
|
|
||||||
Kazakh|kazakh
|
|
||||||
Kyrgyz|kyrgyz
|
|
||||||
Lao|la-latin1
|
|
||||||
Latvian|lv
|
|
||||||
Lithuanian|lt
|
|
||||||
Macedonian|mk-utf
|
|
||||||
Norwegian|no-latin1
|
|
||||||
Polish|pl
|
|
||||||
Portuguese|pt-latin1
|
|
||||||
Portuguese (Brazil)|br-abnt2
|
|
||||||
Romanian|ro
|
|
||||||
Russian|ru
|
|
||||||
Serbian|sr-latin
|
|
||||||
Slovak|sk-qwertz
|
|
||||||
Slovenian|slovene
|
|
||||||
Spanish|es
|
|
||||||
Spanish (Latin American)|la-latin1
|
|
||||||
Swedish|sv-latin1
|
|
||||||
Tajik|tj_alt-UTF8
|
|
||||||
Turkish|trq
|
|
||||||
Ukrainian|ua'
|
|
||||||
choice=$(printf '%s\n' "$keyboards" | cut -d'|' -f1 | gum choose --height 10 --selected "English (US)" --header "Select keyboard layout")
|
|
||||||
local status=$?
|
|
||||||
|
|
||||||
if $allow_defer_provisioning && (( status == 130 )); then
|
# Esc means "back", and nothing precedes the first screen, so re-ask.
|
||||||
if confirm_prepare_for_another_owner; then
|
((status == OMARCHY_FORM_BACK)) && continue
|
||||||
defer_provisioning=true
|
|
||||||
return 0
|
if ((status == OMARCHY_FORM_SIGNAL)); then
|
||||||
|
if confirm_prepare_for_another_owner; then
|
||||||
|
defer_provisioning=true
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
continue # declined: back to the keyboard picker
|
||||||
fi
|
fi
|
||||||
keyboard_form true # declined: back to the keyboard picker
|
abort
|
||||||
return
|
done
|
||||||
fi
|
|
||||||
(( status == 0 )) || abort
|
|
||||||
|
|
||||||
keyboard=$(printf '%s\n' "$keyboards" | awk -F'|' -v c="$choice" '$1==c{print $2; exit}')
|
|
||||||
|
|
||||||
# Only attempt to load keyboard layout if we're on a real console
|
# Only attempt to load keyboard layout if we're on a real console
|
||||||
# loadkeys only works on Linux virtual consoles (tty), not in terminal emulators
|
# loadkeys only works on Linux virtual consoles (tty), not in terminal emulators
|
||||||
@@ -275,66 +248,37 @@ confirm_prepare_for_another_owner() {
|
|||||||
user_form() {
|
user_form() {
|
||||||
step "Let's setup your user account..."
|
step "Let's setup your user account..."
|
||||||
|
|
||||||
while true; do
|
# Each prompt reports 0 (set), OMARCHY_FORM_BACK (Esc), or OMARCHY_FORM_SIGNAL
|
||||||
username=$(gum input --placeholder "Alphanumeric without spaces (like dhh)" --prompt.foreground="#845DF9" --prompt "Username> ") || abort
|
# (Ctrl+C). Both non-zero cases unwind to user_step, which decides what they
|
||||||
|
# mean here; the form itself never aborts.
|
||||||
|
omarchy_prompt_username || return $?
|
||||||
|
omarchy_prompt_password || return $?
|
||||||
|
|
||||||
if [[ "$username" =~ ^[a-z_][a-z0-9_-]*[$]?$ ]]; then
|
# Hash the password with SHA-512 crypt ($6$), which is what openssl passwd offers
|
||||||
if [[ "$username" =~ ^(root|bin|daemon|mail|ftp|http|nobody|dbus|systemd-coredump|systemd-network|systemd-oom|systemd-journal-remote|systemd-resolve|systemd-timesync|tss|uuidd|alpm|git|avahi|cups|lp|_talkd|polkitd|rtkit|qemu|brltty|gluster|rpc|libvirt-qemu|pcscd|nvidia-persistenced|sddm)$ ]] ; then
|
|
||||||
notice "Username is reserved for system" 1
|
|
||||||
else
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
notice "Username must be alphanumeric with no spaces" 1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
while true; do
|
|
||||||
password=$(gum input --placeholder "Used for user + root, and disk encryption when enabled" --prompt.foreground="#845DF9" --password --prompt "Password> ") || abort
|
|
||||||
password_confirmation=$(gum input --placeholder "Must match the password you just typed" --prompt.foreground="#845DF9" --password --prompt "Confirm> ") || abort
|
|
||||||
|
|
||||||
if [[ -n "$password" && "$password" == "$password_confirmation" ]]; then
|
|
||||||
break
|
|
||||||
elif [[ -z "$password" ]]; then
|
|
||||||
notice "Your password can't be blank!" 1
|
|
||||||
else
|
|
||||||
notice "Passwords didn't match!" 1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Hash the password using yescrypt
|
|
||||||
password_hash=$(printf '%s' "$password" | openssl passwd -6 -stdin)
|
password_hash=$(printf '%s' "$password" | openssl passwd -6 -stdin)
|
||||||
|
|
||||||
full_name=$(gum input --placeholder "Used for git authentication (hit return to skip)" --prompt.foreground="#845DF9" --prompt "Full name> ")
|
omarchy_prompt_identity || return $?
|
||||||
email_address=$(gum input --placeholder "Used for git authentication (hit return to skip)" --prompt.foreground="#845DF9" --prompt "Email address> ")
|
omarchy_prompt_hostname || return $?
|
||||||
|
omarchy_prompt_timezone || return $?
|
||||||
while true; do
|
|
||||||
hostname=$(gum input --placeholder "Letters, digits, and dashes (or return for 'omarchy')" --prompt.foreground="#845DF9" --prompt "Hostname> ")
|
|
||||||
|
|
||||||
if [[ "$hostname" =~ ^[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?$ ]]; then
|
|
||||||
break
|
|
||||||
elif [[ -z $hostname ]]; then
|
|
||||||
hostname="omarchy"
|
|
||||||
break
|
|
||||||
else
|
|
||||||
notice "Hostname must be 1-63 letters, digits, or dashes, and cannot start or end with a dash" 1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Pick timezone
|
|
||||||
geo_guessed_timezone=$(tzupdate -p 2>/dev/null)
|
|
||||||
|
|
||||||
if [[ -n $geo_guessed_timezone ]]; then
|
|
||||||
timezone=$(timedatectl list-timezones | gum choose --height 10 --selected "$geo_guessed_timezone" --header "Timezone") || abort
|
|
||||||
else
|
|
||||||
timezone=$(timedatectl list-timezones | gum filter --height 10 --header "Timezone") || abort
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Returns 0 on confirmed account details, or as soon as the keyboard screen
|
||||||
|
# arms deferred provisioning.
|
||||||
user_step() {
|
user_step() {
|
||||||
user_form
|
local status
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
|
user_form && status=0 || status=$?
|
||||||
|
if ((status != 0)); then
|
||||||
|
# Esc unwinds to the start of the form. Ctrl+C has no side channel in the
|
||||||
|
# user step (only the keyboard and disk screens give it a meaning), so it
|
||||||
|
# keeps its long-standing behaviour of ending the install.
|
||||||
|
((status == OMARCHY_FORM_BACK)) || abort
|
||||||
|
keyboard_form
|
||||||
|
$defer_provisioning && return 0
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
# Add manual padding since gum table -p doesn't respect padding
|
# Add manual padding since gum table -p doesn't respect padding
|
||||||
echo -e "Field,Value
|
echo -e "Field,Value
|
||||||
Username,$username
|
Username,$username
|
||||||
@@ -351,7 +295,7 @@ Keyboard,$keyboard" |
|
|||||||
break
|
break
|
||||||
else
|
else
|
||||||
keyboard_form
|
keyboard_form
|
||||||
user_form
|
$defer_provisioning && return 0
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
@@ -569,8 +513,9 @@ run_partition_decide() {
|
|||||||
bl_part=$(detect_bitlocker || true)
|
bl_part=$(detect_bitlocker || true)
|
||||||
if [[ -n "$bl_part" ]]; then
|
if [[ -n "$bl_part" ]]; then
|
||||||
say --foreground 1 "BitLocker signature detected on $bl_part."
|
say --foreground 1 "BitLocker signature detected on $bl_part."
|
||||||
say "Suspend or disable BitLocker in Windows before installing alongside it."
|
say "Turn BitLocker off in Windows and wait for the drive to finish decrypting, then try again."
|
||||||
abort "Aborted: BitLocker is active on this disk."
|
say "Suspending BitLocker is not enough — the drive stays encrypted."
|
||||||
|
abort "Aborted: BitLocker is enabled on this disk."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Omarchy always creates its own dedicated ESP in free space and never
|
# Omarchy always creates its own dedicated ESP in free space and never
|
||||||
@@ -993,7 +938,7 @@ confirm_disk_overwrite() {
|
|||||||
# Decide what and how to install: sets install_target (full_disk|free_space),
|
# Decide what and how to install: sets install_target (full_disk|free_space),
|
||||||
# install_target and encrypt_installation. Nothing is written to disk yet —
|
# install_target and encrypt_installation. Nothing is written to disk yet —
|
||||||
# the user step runs between this and the execution below. (deferred provisioning never
|
# the user step runs between this and the execution below. (deferred provisioning never
|
||||||
# reaches here; it's armed by Ctrl+C on the first screen and goes straight to
|
# reaches here; it's armed by Ctrl+C on the keyboard screen and goes straight to
|
||||||
# disk selection.)
|
# disk selection.)
|
||||||
select_installation() {
|
select_installation() {
|
||||||
local full_disk_only
|
local full_disk_only
|
||||||
@@ -1047,7 +992,14 @@ wait_for_stable_terminal
|
|||||||
|
|
||||||
greeter
|
greeter
|
||||||
|
|
||||||
keyboard_form true
|
keyboard_form
|
||||||
|
|
||||||
|
# Normal install: keyboard (loaded above, so the LUKS passphrase is typed under
|
||||||
|
# the right layout) → user → disk → install mode. The user step can unwind to
|
||||||
|
# the keyboard screen and arm deferral there, hence the second check.
|
||||||
|
if ! $defer_provisioning; then
|
||||||
|
user_step
|
||||||
|
fi
|
||||||
|
|
||||||
if $defer_provisioning; then
|
if $defer_provisioning; then
|
||||||
# Deferred provisioning: no user setup, and the keyboard is deferred to the machine's first
|
# Deferred provisioning: no user setup, and the keyboard is deferred to the machine's first
|
||||||
@@ -1073,10 +1025,6 @@ if $defer_provisioning; then
|
|||||||
done
|
done
|
||||||
install_target="full_disk"
|
install_target="full_disk"
|
||||||
else
|
else
|
||||||
# Normal install: keyboard (done above) → user → disk → install mode. The
|
|
||||||
# keyboard chosen above is already loaded, so the LUKS passphrase entered in
|
|
||||||
# the user step is typed under the right layout.
|
|
||||||
user_step
|
|
||||||
disk_form
|
disk_form
|
||||||
select_installation
|
select_installation
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -123,7 +123,16 @@ repeat() {
|
|||||||
|
|
||||||
logo_width() {
|
logo_width() {
|
||||||
if [[ -f $LOGO_PATH ]]; then
|
if [[ -f $LOGO_PATH ]]; then
|
||||||
awk '{ if (length > max) max = length } END { print max+0 }' "$LOGO_PATH"
|
# The logo is drawn in box-drawing glyphs, three bytes each in UTF-8, so
|
||||||
|
# awk's `length` returns 197 rather than 81 unless the locale happens to be
|
||||||
|
# UTF-8 — and a width wider than the console makes left_padding clamp to
|
||||||
|
# zero, which puts the logo flush left. Count bytes deliberately (LC_ALL=C)
|
||||||
|
# and drop the continuation bytes instead, so the answer is columns in every
|
||||||
|
# locale rather than only in the ones that have a charset.
|
||||||
|
LC_ALL=C awk '
|
||||||
|
{ gsub(/[\200-\277]/, ""); if (length > max) max = length }
|
||||||
|
END { print max+0 }
|
||||||
|
' "$LOGO_PATH"
|
||||||
else
|
else
|
||||||
printf '81\n'
|
printf '81\n'
|
||||||
fi
|
fi
|
||||||
@@ -142,8 +151,18 @@ LOGO_HEIGHT="$(logo_height)"
|
|||||||
(( LOGO_WIDTH > 0 )) || LOGO_WIDTH=81
|
(( LOGO_WIDTH > 0 )) || LOGO_WIDTH=81
|
||||||
(( LOGO_HEIGHT > 0 )) || LOGO_HEIGHT=1
|
(( LOGO_HEIGHT > 0 )) || LOGO_HEIGHT=1
|
||||||
CONTENT_WIDTH="$LOGO_WIDTH"
|
CONTENT_WIDTH="$LOGO_WIDTH"
|
||||||
|
# Rows the progress screen occupies: logo, blank, title, blank, bar, blank, tip.
|
||||||
|
PROGRESS_HEIGHT=$((LOGO_HEIGHT + 6))
|
||||||
|
TOP_ROW=1
|
||||||
DYNAMIC_ROW=$((LOGO_HEIGHT + 3))
|
DYNAMIC_ROW=$((LOGO_HEIGHT + 3))
|
||||||
|
LAST_SIZE=""
|
||||||
LAST_PM=10
|
LAST_PM=10
|
||||||
|
# The finish screen mirrors the greeter's frame — logo, blank, one line, gap,
|
||||||
|
# one button — so the last thing the installer shows matches the first. It runs
|
||||||
|
# one row taller: `gum confirm` always paints prompt/blank/button and clears all
|
||||||
|
# three, so the button cannot sit closer than two rows under a title we drew.
|
||||||
|
FINISH_HEIGHT=$((LOGO_HEIGHT + 5))
|
||||||
|
FINISH_TOP_ROW=2
|
||||||
|
|
||||||
# ── Install progress ─────────────────────────────────────────────────────────
|
# ── Install progress ─────────────────────────────────────────────────────────
|
||||||
#
|
#
|
||||||
@@ -389,15 +408,40 @@ current_tip() {
|
|||||||
printf '%s' "${tips[$(( (NOW - DASH_T0) / 8 % ${#tips[@]} ))]}"
|
printf '%s' "${tips[$(( (NOW - DASH_T0) / 8 % ${#tips[@]} ))]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Vertically center a block of the given height, echoing the row it starts on.
|
||||||
|
# Both screens here center the way the configurator's greeter centers its
|
||||||
|
# welcome, so the install opens, runs, and closes on the same composition
|
||||||
|
# rather than stacking each frame from row one.
|
||||||
|
centered_top_row() {
|
||||||
|
local height="$1" rows top
|
||||||
|
rows="$(term_size)"
|
||||||
|
rows="${rows%% *}"
|
||||||
|
[[ $rows =~ ^[0-9]+$ ]] || rows=24
|
||||||
|
top=$(( (rows - height) / 2 ))
|
||||||
|
(( top < 0 )) && top=0
|
||||||
|
printf '%d' "$(( top + 1 ))"
|
||||||
|
}
|
||||||
|
|
||||||
|
measure_progress_layout() {
|
||||||
|
LAST_SIZE="$(term_size)"
|
||||||
|
TOP_ROW="$(centered_top_row "$PROGRESS_HEIGHT")"
|
||||||
|
DYNAMIC_ROW=$(( TOP_ROW + LOGO_HEIGHT + 1 ))
|
||||||
|
}
|
||||||
|
|
||||||
render_static() {
|
render_static() {
|
||||||
printf '%s' "$CLEAR"
|
measure_progress_layout
|
||||||
blank_line
|
printf '%s%s%d;1H' "$CLEAR" "$CSI" "$TOP_ROW"
|
||||||
render_logo
|
render_logo
|
||||||
blank_line
|
blank_line
|
||||||
}
|
}
|
||||||
|
|
||||||
render_dynamic() {
|
render_dynamic() {
|
||||||
local pm
|
local pm
|
||||||
|
# The console can still resize under us (the framebuffer settles late in VMs).
|
||||||
|
# Horizontal padding is recomputed every frame anyway, but the logo is drawn
|
||||||
|
# once, so redraw the static half at the new center rather than leave the
|
||||||
|
# centered layout stranded against a stale geometry.
|
||||||
|
[[ "$(term_size)" == "$LAST_SIZE" ]] || render_static
|
||||||
set_now
|
set_now
|
||||||
install_progress
|
install_progress
|
||||||
pm=$PROGRESS_PM
|
pm=$PROGRESS_PM
|
||||||
@@ -452,30 +496,45 @@ render_finish() {
|
|||||||
local duration effect_canvas_width
|
local duration effect_canvas_width
|
||||||
duration="$(install_duration || true)"
|
duration="$(install_duration || true)"
|
||||||
duration="${duration:-Complete}"
|
duration="${duration:-Complete}"
|
||||||
|
# ttfx centers its text two columns right of plain centering, so hand it a
|
||||||
|
# canvas two columns narrower and the animated logo lands exactly on the
|
||||||
|
# static one left_padding already drew. Otherwise the logo jumps a column
|
||||||
|
# sideways when the effect starts and again when it ends.
|
||||||
effect_canvas_width="$(term_cols)"
|
effect_canvas_width="$(term_cols)"
|
||||||
(( effect_canvas_width > 1 )) && effect_canvas_width=$((effect_canvas_width - 1))
|
(( effect_canvas_width > 2 )) && effect_canvas_width=$((effect_canvas_width - 2))
|
||||||
|
|
||||||
|
# Set before anything that can fail, so reboot_prompt still lands on the
|
||||||
|
# button row even if the logo or the effect below never renders.
|
||||||
|
FINISH_TOP_ROW="$(centered_top_row "$FINISH_HEIGHT")"
|
||||||
|
|
||||||
{
|
{
|
||||||
printf '%s%s' "$SHOW_CURSOR" "$CLEAR"
|
printf '%s%s%s%d;1H' "$SHOW_CURSOR" "$CLEAR" "$CSI" "$FINISH_TOP_ROW"
|
||||||
blank_line
|
|
||||||
render_logo
|
render_logo
|
||||||
blank_line
|
blank_line
|
||||||
center "Installed Omarchy in ${duration}" "$CONTENT_WIDTH"
|
center "Installed Omarchy in ${duration}" "$CONTENT_WIDTH"
|
||||||
blank_line
|
blank_line
|
||||||
} >"$TTY_PATH"
|
} >"$TTY_PATH"
|
||||||
|
|
||||||
if command -v tte >/dev/null 2>&1 && command -v timeout >/dev/null 2>&1 && [[ -f $LOGO_PATH ]]; then
|
if [[ -f $LOGO_PATH ]]; then
|
||||||
# TTE reuse mode restores a saved cursor before moving up over its input.
|
# Reuse mode restores a saved cursor before moving up over its input. Save
|
||||||
# Save this anchor so the effect redraws the same logo rows shown above.
|
# this anchor so the effect redraws the same logo rows shown above.
|
||||||
printf '%s%d;1H\0337' "$CSI" "$((LOGO_HEIGHT + 2))" >"$TTY_PATH"
|
printf '%s%d;1H\0337' "$CSI" "$((FINISH_TOP_ROW + LOGO_HEIGHT))" >"$TTY_PATH"
|
||||||
timeout 8s tte -i "$LOGO_PATH" \
|
# Laseretch runs ~4s at 260fps, so the 8s cap is a stuck-process guard rather
|
||||||
|
# than a trim. --xterm-colors keeps the whole effect in the 256-index palette
|
||||||
|
# the way the greeter does, since the console reduces indexed codes well and
|
||||||
|
# truecolor ones badly, and it settles on indexed 2 — the green gum already
|
||||||
|
# drew the logo in above, so the send-off ends on the frame it started from
|
||||||
|
# instead of recoloring it. The laser and sparks keep their own hot palette.
|
||||||
|
timeout 8s ttfx -i "$LOGO_PATH" \
|
||||||
--canvas-width "$effect_canvas_width" \
|
--canvas-width "$effect_canvas_width" \
|
||||||
--anchor-text c \
|
--anchor-text c \
|
||||||
--frame-rate 260 \
|
--frame-rate 260 \
|
||||||
--reuse-canvas \
|
--reuse-canvas \
|
||||||
|
--xterm-colors \
|
||||||
laseretch \
|
laseretch \
|
||||||
>"$TTY_PATH" 2>/dev/null || true
|
--final-gradient-stops 2 \
|
||||||
printf '%s%d;1H' "$CSI" "$((LOGO_HEIGHT + 8))" >"$TTY_PATH"
|
>"$TTY_PATH" 2>/dev/null || dashboard_log "logo effect skipped (ttfx exited $?)"
|
||||||
|
printf '%s%d;1H' "$CSI" "$((FINISH_TOP_ROW + LOGO_HEIGHT + 5))" >"$TTY_PATH"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -486,8 +545,10 @@ reboot_prompt() {
|
|||||||
# applies, so setting both stops on the finish screen without a keyboard.
|
# applies, so setting both stops on the finish screen without a keyboard.
|
||||||
interactive || return 0
|
interactive || return 0
|
||||||
|
|
||||||
# Position the cursor one blank line below the title so the button sits right there.
|
# gum paints its prompt row at the cursor and the button two rows under it, so
|
||||||
printf '%s%d;1H%s' "$CSI" "$((LOGO_HEIGHT + 5))" "$CLEAR_LINE" >"$TTY_PATH"
|
# anchor on the row right below the title to put the button as close to it as
|
||||||
|
# gum allows. FINISH_HEIGHT budgets for the same three rows.
|
||||||
|
printf '%s%d;1H%s' "$CSI" "$((FINISH_TOP_ROW + LOGO_HEIGHT + 2))" "$CLEAR_LINE" >"$TTY_PATH"
|
||||||
|
|
||||||
prompt_pad=$(( ( $(term_cols) - 18 ) / 2 ))
|
prompt_pad=$(( ( $(term_cols) - 18 ) / 2 ))
|
||||||
(( prompt_pad < 0 )) && prompt_pad=0
|
(( prompt_pad < 0 )) && prompt_pad=0
|
||||||
|
|||||||
@@ -18,8 +18,9 @@ from pathlib import Path
|
|||||||
def configure_keyboard(target: Path, language: str) -> bool:
|
def configure_keyboard(target: Path, language: str) -> bool:
|
||||||
"""Write the console keymap into a mounted target without booting it.
|
"""Write the console keymap into a mounted target without booting it.
|
||||||
|
|
||||||
Returns False for layouts localectl doesn't know (the configurator offers
|
Returns False for layouts localectl doesn't know, matching archinstall:
|
||||||
two, ba and khmer), matching archinstall: warn and keep the default.
|
warn and keep the default. Every layout the configurator offers is known;
|
||||||
|
the guard is for the kb_layout an autoinstall drive can name freely.
|
||||||
"""
|
"""
|
||||||
if not language.strip():
|
if not language.strip():
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ Phase ordering (full-disk and protected/pre-mounted):
|
|||||||
base install, early Omarchy packages, Limine setup,
|
base install, early Omarchy packages, Limine setup,
|
||||||
useradd, runtime Omarchy packages, fstab
|
useradd, runtime Omarchy packages, fstab
|
||||||
configure_hibernation → root-owned swap/resume drop-ins
|
configure_hibernation → root-owned swap/resume drop-ins
|
||||||
run_system_finalizer → arch-chroot root omarchy-setup-system, including Snapper
|
run_system_finalizer → arch-chroot root omarchy-apply-system, including Snapper
|
||||||
finalize_limine_boot → final Limine config/UKI build after hardware drop-ins
|
finalize_limine_boot → final Limine config/UKI build after hardware drop-ins
|
||||||
run_chroot_finalizer → arch-chroot -u user omarchy-provision-user
|
run_chroot_finalizer → arch-chroot -u user omarchy-provision-user
|
||||||
configure_login → sddm state + encrypted-install autologin
|
configure_login → sddm state + encrypted-install autologin
|
||||||
@@ -598,7 +598,7 @@ DEFERRED_BOOT_HOOKS = (
|
|||||||
# limine-entry-tool's 90-mkinitcpio-install.hook triggers on usr/lib/firmware/*,
|
# limine-entry-tool's 90-mkinitcpio-install.hook triggers on usr/lib/firmware/*,
|
||||||
# usr/src/*/dkms.conf and usr/lib/modules/*/pkgbase, and anything but a
|
# usr/src/*/dkms.conf and usr/lib/modules/*/pkgbase, and anything but a
|
||||||
# usr/lib/modules path makes it rebuild the initramfs and UKI for EVERY
|
# usr/lib/modules path makes it rebuild the initramfs and UKI for EVERY
|
||||||
# installed kernel. omarchy-setup-system's hardware scripts routinely install
|
# installed kernel. omarchy-apply-system's hardware scripts routinely install
|
||||||
# such packages (sof-firmware on Intel audio, nvidia-open-dkms, linux-ptl on
|
# such packages (sof-firmware on Intel audio, nvidia-open-dkms, linux-ptl on
|
||||||
# Panther Lake, linux-t2 on Macs), so the phase can pay for several full UKI
|
# Panther Lake, linux-t2 on Macs), so the phase can pay for several full UKI
|
||||||
# builds. finalize_limine_boot runs limine-update right after, which pipes
|
# builds. finalize_limine_boot runs limine-update right after, which pipes
|
||||||
@@ -1005,7 +1005,7 @@ def _debug_run(ctx: InstallContext, cmd: list[str]) -> None:
|
|||||||
# 1. point the target at the offline pacman.conf
|
# 1. point the target at the offline pacman.conf
|
||||||
# 2. bind-mount the offline mirror + /opt/packages into /mnt for target pacman
|
# 2. bind-mount the offline mirror + /opt/packages into /mnt for target pacman
|
||||||
# and bundled language runtimes
|
# and bundled language runtimes
|
||||||
# 3. arch-chroot as root → omarchy-setup-system --first-install
|
# 3. arch-chroot as root → omarchy-apply-system --first-install
|
||||||
# 4. arch-chroot as user → omarchy-provision-user --first-install
|
# 4. arch-chroot as user → omarchy-provision-user --first-install
|
||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -1137,9 +1137,9 @@ def _run_target_setup_command(ctx: InstallContext, cmd: list[str], *, user: str
|
|||||||
|
|
||||||
def run_system_finalizer(ctx: InstallContext) -> None:
|
def run_system_finalizer(ctx: InstallContext) -> None:
|
||||||
if ctx.defer_provisioning:
|
if ctx.defer_provisioning:
|
||||||
cmd = ["/usr/bin/omarchy-setup-system", "--defer-provisioning", "--first-install"]
|
cmd = ["/usr/bin/omarchy-apply-system", "--defer-provisioning", "--first-install"]
|
||||||
else:
|
else:
|
||||||
cmd = ["/usr/bin/omarchy-setup-system", "--install-user", ctx.username, "--first-install"]
|
cmd = ["/usr/bin/omarchy-apply-system", "--install-user", ctx.username, "--first-install"]
|
||||||
|
|
||||||
_mask_mkinitcpio_pacman_hooks(ctx, ctx.target, TARGET_DEFERRED_BOOT_HOOKS)
|
_mask_mkinitcpio_pacman_hooks(ctx, ctx.target, TARGET_DEFERRED_BOOT_HOOKS)
|
||||||
try:
|
try:
|
||||||
|
|||||||
+30
-14
@@ -14,14 +14,30 @@ KEYBOARD = importlib.util.module_from_spec(SPEC)
|
|||||||
assert SPEC.loader
|
assert SPEC.loader
|
||||||
SPEC.loader.exec_module(KEYBOARD)
|
SPEC.loader.exec_module(KEYBOARD)
|
||||||
|
|
||||||
CONFIGURATOR = (ROOT / "configs/airootfs/root/configurator").read_text()
|
# The layout list lives in the Omarchy runtime now, shared verbatim with the
|
||||||
KEYBOARD_BLOCK = re.search(r"keyboards=\$'(.*?)'\n choice=", CONFIGURATOR, re.DOTALL)
|
# first-boot owner setup; build-iso.sh vendors it onto the ISO. Read it from
|
||||||
assert KEYBOARD_BLOCK
|
# wherever this checkout can see a runtime, and skip the coverage test rather
|
||||||
SUPPORTED_KEYMAPS = [
|
# than fail when none is around (a bare CI checkout of just this repo).
|
||||||
line.split("|", 1)[1]
|
SETUP_FORM_CANDIDATES = (
|
||||||
for line in KEYBOARD_BLOCK.group(1).splitlines()
|
Path("/omarchy-source/install/provisioning/setup-form.sh"),
|
||||||
]
|
ROOT.parent / "omarchy/install/provisioning/setup-form.sh",
|
||||||
UNSUPPORTED_KEYMAPS = {"ba", "khmer"}
|
Path("/usr/share/omarchy/install/provisioning/setup-form.sh"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def supported_keymaps():
|
||||||
|
for candidate in SETUP_FORM_CANDIDATES:
|
||||||
|
if not candidate.is_file():
|
||||||
|
continue
|
||||||
|
block = re.search(
|
||||||
|
r"OMARCHY_KEYBOARD_LAYOUTS=\$'(.*?)'\n", candidate.read_text(), re.DOTALL
|
||||||
|
)
|
||||||
|
assert block, f"no layout list in {candidate}"
|
||||||
|
return [line.split("|", 1)[1] for line in block.group(1).splitlines()]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
SUPPORTED_KEYMAPS = supported_keymaps()
|
||||||
|
|
||||||
|
|
||||||
class KeyboardConfigurationTest(unittest.TestCase):
|
class KeyboardConfigurationTest(unittest.TestCase):
|
||||||
@@ -45,15 +61,15 @@ class KeyboardConfigurationTest(unittest.TestCase):
|
|||||||
self.assertEqual(lines.count("FONT=default8x16"), 1)
|
self.assertEqual(lines.count("FONT=default8x16"), 1)
|
||||||
|
|
||||||
def test_all_configurator_keymaps_are_known_to_localectl(self):
|
def test_all_configurator_keymaps_are_known_to_localectl(self):
|
||||||
|
if SUPPORTED_KEYMAPS is None:
|
||||||
|
self.skipTest("no Omarchy runtime checkout to read the layout list from")
|
||||||
for keymap in SUPPORTED_KEYMAPS:
|
for keymap in SUPPORTED_KEYMAPS:
|
||||||
with self.subTest(keymap=keymap), tempfile.TemporaryDirectory() as directory:
|
with self.subTest(keymap=keymap), tempfile.TemporaryDirectory() as directory:
|
||||||
target = self.target(directory)
|
target = self.target(directory)
|
||||||
configured = KEYBOARD.configure_keyboard(target, keymap)
|
self.assertTrue(KEYBOARD.configure_keyboard(target, keymap))
|
||||||
self.assertEqual(configured, keymap not in UNSUPPORTED_KEYMAPS)
|
lines = (target / "etc/vconsole.conf").read_text().splitlines()
|
||||||
if configured:
|
self.assertIn(f"KEYMAP={keymap}", lines)
|
||||||
lines = (target / "etc/vconsole.conf").read_text().splitlines()
|
self.assertEqual(lines.count("FONT=default8x16"), 1)
|
||||||
self.assertIn(f"KEYMAP={keymap}", lines)
|
|
||||||
self.assertEqual(lines.count("FONT=default8x16"), 1)
|
|
||||||
|
|
||||||
def test_unknown_and_empty_keymaps_match_archinstall_behavior(self):
|
def test_unknown_and_empty_keymaps_match_archinstall_behavior(self):
|
||||||
for keymap, expected in (("definitely-not-a-keymap", False), ("", True)):
|
for keymap, expected in (("definitely-not-a-keymap", False), ("", True)):
|
||||||
|
|||||||
Reference in new issue
Block a user