The JSON wrapper was the one piece of autoinstall input that wasn't an
existing format: the other files are the configurator's own output, and
the keys end up in sshd's authorized_keys anyway. So take that file
directly -- one key per line, comments and key options work as they do
everywhere else -- and drop the parsing in between. The only thing JSON
bought was parse-error detection, and the phase still fails loudly when
the file carries no usable keys.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The loader now settles udev before probing, so a fast boot cannot miss
the drive and silently drop a headless install into the wizard. All the
files come over in a single cp, and a failed copy removes anything it
already copied and falls back to the wizard: the wizard cleans up only
what it writes itself, so a half-copied ssh.json would otherwise leak
into the interactive install that follows. The command takes an optional
path prefix so tests can run it against a sandbox instead of the live
system.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Look for a drive labeled cidata at startup. If it carries the
configurator's own output files, copy them into /root and skip the
wizard -- everything downstream then runs the ordinary path against
ordinary inputs. No drive, no change.
The required pair matches what the orchestrator treats as mandatory, so
a drive with less than that falls back to the wizard rather than failing
the boot.
Claude-Session: https://claude.ai/code/session_01Sy3HeoBqb13ahLuaiP2UkM
The username regex accepted names that already exist on the target, so
useradd failed inside arch_install_system — after the disk had been
partitioned and formatted, which is the worst point to discover it.
From #59 by @wolfgangw, applied to quattro; it was opened against main.
Co-Authored-By: Wolfgang Woehl <tito@online.de>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The install reads ~3GB out of the offline mirror, and pacman consumes it at
only ~33MB/s (3.14GB of packages across the 95s the package phase is calibrated
at), so on any medium slower than that the install is read-bound. The ISO medium
is idle for the minutes the wizard takes, and the target disk written to later
is a different device, so reading ahead costs nothing that is otherwise in use.
Warms largest-first under a MemAvailable/2 budget. Deliberately no dependency
resolution: asking pacman exactly which files the install will read would copy
the package-set knowledge in phases_impl.py — the early list, the runtime
target, the kernel from the wizard's JSON — into a shell script that would drift
silently. The install reads roughly 69% of the mirror, so warming blindly is
close enough for a cache hint.
The page cache this fills is clean and reclaimable, so the failure mode under
memory pressure is wasted bandwidth rather than OOM. The budget stops small
machines evicting what was just warmed. The job ends when that budget is spent
rather than when the wizard exits, because the partitioning and format window
afterwards is also free medium time.
OMARCHY_NO_PREFETCH=1 turns it off so a single ISO can measure both arms against
the Installing Arch + Omarchy entry in /var/log/omarchy-install-timing.json.
Not yet measured on real USB media, and expected to be worth nothing in the VM
harness, where the ISO is backed by the host's page cache.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Remeasure the terminal at every screen redraw instead of once at boot:
the framebuffer can settle after the configurator starts (VM windows
especially), which left stale padding for the whole flow. Pad the
confirm-screen warnings with say so every line shares the same left
edge. Boot the test VM with SDL instead of GTK, whose GL display path
crops the guest framebuffer on HiDPI Wayland desktops.
Verified in a live VM by driving the whole flow over QMP and reading
each screen back from /dev/vcs1: all screens center on the real console
width.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Three echo-through-awk substitutions to split one line into three
variables is what read was made for.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The free-space and full-disk paths each carried verbatim copies of the
T2 Mac kernel detection, the credentials JSON heredoc, the identity/
encryption answer files, the dry-run print-and-cleanup block, and the
udev settle loops. Pull those into detect_kernel, write_user_files,
print_dry_run_files, and wait_for_device, which also removes a subtle
rm vs rm -f drift between the two dry-run cleanups.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
We decided Omarchy always creates its own dedicated ESP and never adopts
a Windows one, so shared_esp was declared empty and never assigned —
every conditional on it was guaranteed to take the dedicated-ESP branch.
Collapse the whole dual-path structure into straight-line code and keep
detect_windows_esp only for the "leaving it untouched" message.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
build-iso.sh bakes tzupdate into the live environment, so the runtime
pacman fallback was dead there — and in a dry run on a dev machine it
would install a package as a side effect. tzupdate -p already fails
gracefully into the manual timezone filter when the tool is missing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Drop the dead LOGO_PATH override, the redundant package-targets sourcing,
the unused TERM_HEIGHT and OMARCHY_NVIM_PACKAGE, the non-gum exports, and
the logo fallback branches. Trust that logo.txt is where OMARCHY_PATH says.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
archinstall 4.3 maps the deprecated top-level 'bootloader': 'Limine'
to BootloaderConfiguration(bootloader=Limine, removable=True) (per
lib/args.py:185-190 and lib/models/bootloader.py:56-69). Our new
_install_limine_omarchy mirrors archinstall's removable handling: it
would copy BOOTX64.EFI to EFI/BOOT (fallback path) and SKIP
efibootmgr, neither of which is what we want.
Switch to the explicit bootloader_config form with removable=false
so we take the EFI/limine path + register the 'Limine' NVRAM entry
as intended.
Restore the env + redirect setup that the previously-working ISO
installer used (matching /home/ryan/Downloads/ISO scripts/.automated_script.sh):
export COLUMNS=$(tput cols)
export LINES=$(tput lines)
exec > >(tee >(sed CSI-strip >> log) 2>/dev/null) 2>/dev/tty
export CLICOLOR_FORCE=1
export FORCE_COLOR=1
The cutover commit (064b5e2) silently dropped these. Subsequent
attempts to fix the TTY/gum rendering by reshaping the redirect were
all symptom-chasing — the proven shape was correct. The key insight:
- gum draws its TUI on stderr, not stdout. 2>/dev/tty keeps that
rendering on the real terminal.
- stdout-as-pipe (via tee) is *desired*: it lets gum detect a
non-TTY stdout and skip cursor-position probes (CSI 6n). Without
that detection, the DSR replies leak as visible ^[[13;1R bytes.
- CLICOLOR_FORCE/FORCE_COLOR force gum to still emit colour despite
stdout being a pipe.
Also drops the configurator's _drain_tty workaround — unnecessary
once gum stops sending DSR queries in the first place.
bash's `read -t 0` only TESTS whether input is available; it does not
consume the bytes. So the drain loop `while read -t 0 ...; do :; done`
spun forever the moment any bytes were pending (e.g., the DSR reply
sitting in the tty input buffer after gum style sent CSI 6n).
That hung the configurator at the first step() title screen — blank
prompt, blinking cursor, no keyboard form.
Use `read -t 0.05 -N 9999` once, which consumes up to 9999 bytes with
a 50ms timeout. Always returns after 50ms whether there's data or not.
Single call replaces the while loop.
gum's TUI startup queries the terminal (e.g., cursor position via
CSI 6n) and doesn't always read the reply. The reply bytes
("^[[13;1R") then surface visibly next to the next step title.
A one-time drain at script start (in .automated_script.sh) catches
the boot-time replies but not the gum-per-call ones, since the
artifact came back at every new step().
Add a small _drain_tty helper and call it from step() after the
title gum-style invocation so any pending DSR replies are consumed
before the next prompt runs.
Fix the ^[[13;1R cursor-position-report artifacts visible next to the
first configurator step. archiso's boot init leaves DSR responses in the
kernel tty input buffer; the first gum invocation reads them as stray
input and echoes them as visible escape sequences.
- Drain stdin (with a short timeout) from /dev/tty before configurator
runs so any pending replies are consumed.
- Drop the explicit </dev/tty stdin redirect — the parent script's
stdin is already tty1 (inherited from archiso autostart), and the
explicit redirect opened a new fd with subtly different termios.
All install-phase logic now lives in the Python orchestrator and the
omarchy-iso-* bash helpers. .automated_script.sh becomes a thin shim that:
- sources install helpers (env for configurator)
- sets the Tokyo Night palette on the live VT
- tees stripped stdout/stderr to /var/log/omarchy-install.log
- runs the configurator wizard
- exec's omarchy-iso-install with absolute config paths
Absolute paths are required because the wrapper cd's into
/usr/share/omarchy-iso before invoking the orchestrator.
Removed install_base_system, install_omarchy, install_arch, install_disk,
cleanup_install_disk, configure_login_for_unencrypted_install, and
chroot_run_as_user — all replaced by Python phases or bash helpers.
The live ISO runs as root with XDG_RUNTIME_DIR=/run/user/0; arch-chroot
-u ryan switches the UID but doesn't reset the env, so tools running as
ryan tried to write to root's runtime dir (dconf failed to commit,
omarchy-theme-set's flock target hit Permission denied). Drop the var
so tools fall back to no-runtime-dir behaviour, which works inside the
chroot.
Two motivations:
1. Plymouth: the omarchy plymouth theme + /etc/plymouth/plymouthd.conf
ship from omarchy-settings (theme at /usr/share/plymouth/themes/omarchy,
plymouthd.conf via the package's post_install etc-overrides hook).
Without omarchy-settings on the live ISO, mkarchiso builds the initrd
with plymouth pointing at the default theme instead of Omarchy's. Now
it picks up the Omarchy theme on boot like the installed system does.
2. Drops the .pkg.tar.zst extraction hack in .automated_script.sh:
OMARCHY_PATH=/usr/share/omarchy is the real package install path, so
use_omarchy_helpers no longer needs to bsdtar pieces out of the
offline mirror into /tmp/omarchy-iso.
archinstall creates the user from user_credentials.json BEFORE installing
the 'additional packages' list, so when omarchy-settings populates
/etc/skel/.config/... later in the pacstrap, the user's home has already
been created from an empty skel and missed the seed (user boots into a
terminal with no Hyprland configs).
Copy /etc/skel into /home/$OMARCHY_USER after archinstall finishes (by
which point omarchy-settings has populated skel) and chown to the user.
cp -rT keeps it idempotent: existing files get overwritten, dirs merge.
omarchy's hard depends are now just the bricking set, not the full default
install. After archinstall pacstraps the meta packages (which populates
/etc/skel before user creation), install the rest of omarchy-base.packages
in the chroot before running omarchy-install.