Compare commits

..
Author SHA1 Message Date
David Heinemeier HanssonandClaude Opus 5 7c5b97aa30 Tell BitLocker users to decrypt, not suspend
The free-space install detects BitLocker by the -FVE-FS- signature at
offset 3, and suspending BitLocker leaves that signature in place: the
volume stays encrypted, it just gains a clear-key protector. So the old
message sent users to do something that could never let the install
proceed. Aborting on a suspended volume is deliberate, so only the
wording needed fixing.

Fixes #99

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 04:46:48 -07:00
5 changed files with 48 additions and 118 deletions

No files matched your search

+1 -1
View File
@@ -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 ttfx tzupdate omarchy-keyring "$OMARCHY_SETTINGS_PACKAGE" lvm2 cryptsetup parted) arch_packages=(linux-t2 git gum jq openssl plymouth python-terminaltexteffects 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
@@ -1,3 +0,0 @@
# 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
+27 -33
View File
@@ -121,42 +121,36 @@ 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 truecolor faithfully (it crushes the # framebuffer console can't render tte's 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. --xterm-colors is what keeps them indexed: # Night palette and render true. One long-running invocation (many cycles) so
# ttfx resolves even indexed stops to truecolor otherwise, which would put the # it never restarts — a restart is what flashed. The effect reads from
# settle color out of step with the `gum style --foreground 2` logo underneath. # /dev/null so it never swallows the Return; --reuse-canvas paints upward from
# One long-running invocation (many cycles) so it never restarts — a restart is # the saved cursor, anchored one row below the logo.
# what flashed. The effect reads from /dev/null so it never swallows the if command -v tte >/dev/null 2>&1; then
# Return; --reuse-canvas paints upward from the saved cursor, anchored one row printf '\033[%d;1H\0337' "$((logo_row + logo_h))"
# below the logo. # Run tte directly (not inside a `while` subshell) so $anim is tte's own
printf '\033[%d;1H\0337' "$((logo_row + logo_h))" # PID: killing a wrapping subshell would orphan tte, which then keeps
# Run ttfx directly (not inside a `while` subshell) so $anim is ttfx's own # painting the logo over the keyboard step. --cycles is high enough that it
# PID: killing a wrapping subshell would orphan ttfx, which then keeps # never ends on its own before Return.
# painting the logo over the keyboard step. --cycles is high enough that it tte -i "$LOGO_PATH" \
# never ends on its own before Return. --canvas-width "$cols" \
# Two columns narrower than the console: ttfx centers its text two columns --anchor-text c \
# right of plain centering, so a full-width canvas would sit the animated --frame-rate 60 \
# logo a column off the gum-drawn one underneath it. --reuse-canvas \
ttfx -i "$LOGO_PATH" \ colorshift \
--canvas-width "$((cols > 2 ? cols - 2 : cols))" \ --gradient-stops 2 10 6 10 \
--anchor-text c \ --gradient-frames 3 \
--frame-rate 60 \ --cycles 1000 \
--reuse-canvas \ --final-gradient-stops 2 \
--xterm-colors \ </dev/null >/dev/tty 2>/dev/null &
colorshift \ anim=$!
--gradient-stops 2 10 6 10 \ fi
--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 ttfx's kill signal in `wait` so a future `set -e` here can't abort. # Tolerate tte's kill signal in `wait` so a future `set -e` here can't abort.
kill "$anim" 2>/dev/null [[ -n ${anim:-} ]] && { kill "$anim" 2>/dev/null; wait "$anim" 2>/dev/null || true; }
wait "$anim" 2>/dev/null || true # tte leaves the tty in raw/no-echo mode when killed; restore it or the gum
# 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
@@ -123,16 +123,7 @@ repeat() {
logo_width() { logo_width() {
if [[ -f $LOGO_PATH ]]; then if [[ -f $LOGO_PATH ]]; then
# The logo is drawn in box-drawing glyphs, three bytes each in UTF-8, so awk '{ if (length > max) max = length } END { print max+0 }' "$LOGO_PATH"
# 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
@@ -151,18 +142,8 @@ 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 ─────────────────────────────────────────────────────────
# #
@@ -408,40 +389,15 @@ 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() {
measure_progress_layout printf '%s' "$CLEAR"
printf '%s%s%d;1H' "$CLEAR" "$CSI" "$TOP_ROW" blank_line
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
@@ -496,45 +452,30 @@ 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 > 2 )) && effect_canvas_width=$((effect_canvas_width - 2)) (( effect_canvas_width > 1 )) && effect_canvas_width=$((effect_canvas_width - 1))
# 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%s%d;1H' "$SHOW_CURSOR" "$CLEAR" "$CSI" "$FINISH_TOP_ROW" printf '%s%s' "$SHOW_CURSOR" "$CLEAR"
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 [[ -f $LOGO_PATH ]]; then if command -v tte >/dev/null 2>&1 && command -v timeout >/dev/null 2>&1 && [[ -f $LOGO_PATH ]]; then
# Reuse mode restores a saved cursor before moving up over its input. Save # TTE reuse mode restores a saved cursor before moving up over its input.
# this anchor so the effect redraws the same logo rows shown above. # Save this anchor so the effect redraws the same logo rows shown above.
printf '%s%d;1H\0337' "$CSI" "$((FINISH_TOP_ROW + LOGO_HEIGHT))" >"$TTY_PATH" printf '%s%d;1H\0337' "$CSI" "$((LOGO_HEIGHT + 2))" >"$TTY_PATH"
# Laseretch runs ~4s at 260fps, so the 8s cap is a stuck-process guard rather timeout 8s tte -i "$LOGO_PATH" \
# 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 \
--final-gradient-stops 2 \ >"$TTY_PATH" 2>/dev/null || true
>"$TTY_PATH" 2>/dev/null || dashboard_log "logo effect skipped (ttfx exited $?)" printf '%s%d;1H' "$CSI" "$((LOGO_HEIGHT + 8))" >"$TTY_PATH"
printf '%s%d;1H' "$CSI" "$((FINISH_TOP_ROW + LOGO_HEIGHT + 5))" >"$TTY_PATH"
fi fi
} }
@@ -545,10 +486,8 @@ 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
# gum paints its prompt row at the cursor and the button two rows under it, so # Position the cursor one blank line below the title so the button sits right there.
# anchor on the row right below the title to put the button as close to it as printf '%s%d;1H%s' "$CSI" "$((LOGO_HEIGHT + 5))" "$CLEAR_LINE" >"$TTY_PATH"
# 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
@@ -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-apply-system, including Snapper run_system_finalizer → arch-chroot root omarchy-setup-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-apply-system's hardware scripts routinely install # installed kernel. omarchy-setup-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-apply-system --first-install # 3. arch-chroot as root → omarchy-setup-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-apply-system", "--defer-provisioning", "--first-install"] cmd = ["/usr/bin/omarchy-setup-system", "--defer-provisioning", "--first-install"]
else: else:
cmd = ["/usr/bin/omarchy-apply-system", "--install-user", ctx.username, "--first-install"] cmd = ["/usr/bin/omarchy-setup-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: