Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0631c0530a | ||
|
|
ed710a3b41 |
No files matched your search
@@ -2,89 +2,49 @@
|
||||
# Preview the shipped Omarchy installer dashboard without building an ISO.
|
||||
set -euo pipefail
|
||||
|
||||
# The dashboard drives the package phase from the target's pacman database, so
|
||||
# fake one here; without it the preview falls back to its time curve and the bar
|
||||
# barely moves, which reads as a bug.
|
||||
PREVIEW_PACKAGES="${PREVIEW_PACKAGES:-925}"
|
||||
|
||||
write_simulation_state() {
|
||||
local state_file="$1" started_at="$2" phase_started_at="$3" current_phase="$4"
|
||||
local target="$5" index="$6" total="$7"
|
||||
local finished_at="${8:-null}" temporary="${state_file}.tmp"
|
||||
local finished_at="${5:-null}" temporary="${state_file}.tmp"
|
||||
|
||||
jq -n \
|
||||
--argjson started_at "$started_at" \
|
||||
--argjson phase_started_at "$phase_started_at" \
|
||||
--arg current_phase "$current_phase" \
|
||||
--arg target "$target" \
|
||||
--argjson current_index "$index" \
|
||||
--argjson total_phases "$total" \
|
||||
--argjson finished_at "$finished_at" \
|
||||
'{
|
||||
started_at: $started_at,
|
||||
phase_started_at: $phase_started_at,
|
||||
current_phase: $current_phase,
|
||||
target: $target,
|
||||
current_index: $current_index,
|
||||
total_phases: $total_phases
|
||||
current_phase: $current_phase
|
||||
} + if $finished_at == null then {} else {finished_at: $finished_at} end' \
|
||||
>"$temporary"
|
||||
mv -f -- "$temporary" "$state_file"
|
||||
}
|
||||
|
||||
# Fill the fake database over the phase's duration, so the bar is driven by the
|
||||
# same count it uses on a real install.
|
||||
simulate_packages() {
|
||||
local db="$1" duration="$2" total="$3" steps=25 i n
|
||||
for ((i = 1; i <= steps; i++)); do
|
||||
n=$(( total * i / steps ))
|
||||
while (( $(ls -1 "$db" 2>/dev/null | wc -l) < n )); do
|
||||
mkdir -p "$db/preview-$(( $(ls -1 "$db" 2>/dev/null | wc -l) ))-1.0-1"
|
||||
done
|
||||
sleep "$(awk -v d="$duration" -v s="$steps" 'BEGIN { printf "%.3f", d / s }')"
|
||||
done
|
||||
}
|
||||
|
||||
simulate_install() {
|
||||
local state_file="$1"
|
||||
local started_at="$EPOCHREALTIME"
|
||||
local phase phase_started_at finished_at duration i index total
|
||||
local target db
|
||||
target="$(dirname -- "$state_file")/target"
|
||||
db="$target/var/lib/pacman/local"
|
||||
mkdir -p "$db"
|
||||
local phase phase_started_at finished_at duration i
|
||||
local -a phases=(
|
||||
"Starting installation" 0.5
|
||||
"Preparing live environment" 0.8
|
||||
"Preparing install target" 0.3
|
||||
"Installing Arch + Omarchy" 5.0
|
||||
"Configuring hibernation" 0.4
|
||||
"Configuring system" 1.2
|
||||
"Finalizing Limine boot" 1.0
|
||||
"Finalizing user" 1.0
|
||||
"Configuring login" 0.6
|
||||
"Configuring DNS resolver" 0.3
|
||||
"Validating boot setup" 0.8
|
||||
)
|
||||
total=$(( ${#phases[@]} / 2 ))
|
||||
|
||||
for ((i = 0; i < ${#phases[@]}; i += 2)); do
|
||||
phase="${phases[i]}"
|
||||
duration="${phases[i + 1]}"
|
||||
index=$(( i / 2 ))
|
||||
phase_started_at="$EPOCHREALTIME"
|
||||
write_simulation_state "$state_file" "$started_at" "$phase_started_at" \
|
||||
"$phase" "$target" "$index" "$total"
|
||||
if [[ $phase == "Installing Arch + Omarchy" ]]; then
|
||||
simulate_packages "$db" "$duration" "$PREVIEW_PACKAGES"
|
||||
else
|
||||
sleep "$duration"
|
||||
fi
|
||||
write_simulation_state "$state_file" "$started_at" "$phase_started_at" "$phase"
|
||||
sleep "$duration"
|
||||
done
|
||||
|
||||
finished_at="$EPOCHREALTIME"
|
||||
write_simulation_state "$state_file" "$started_at" "$finished_at" \
|
||||
"Installation complete" "$target" "$(( total - 1 ))" "$total" "$finished_at"
|
||||
write_simulation_state "$state_file" "$started_at" "$finished_at" "Installation complete" "$finished_at"
|
||||
sleep 0.3
|
||||
}
|
||||
|
||||
@@ -131,12 +91,6 @@ log_file="$preview_dir/install.log"
|
||||
state_file="$preview_dir/state.json"
|
||||
touch "$log_file"
|
||||
|
||||
# Match what the simulated database will reach, so the preview shows the
|
||||
# package-driven bar rather than the missing-signal fallback.
|
||||
printf '%s\n' "$PREVIEW_PACKAGES" >"$preview_dir/expected-packages"
|
||||
|
||||
OMARCHY_UI_AUTO_REBOOT=no \
|
||||
OMARCHY_EXPECTED_PACKAGES_FILE="$preview_dir/expected-packages" \
|
||||
PREVIEW_PACKAGES="$PREVIEW_PACKAGES" \
|
||||
"$dashboard" "$log_file" "$state_file" -- \
|
||||
"$0" --simulate-install "$state_file"
|
||||
+13
-64
@@ -116,6 +116,19 @@ cp "/tmp/$NODE_FILENAME" "$build_cache_dir/airootfs/opt/packages/"
|
||||
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"
|
||||
|
||||
# The live ISO boots linux-t2 (see airootfs/etc/mkinitcpio.d/linux-t2.preset), so
|
||||
# stock linux is a second kernel nobody boots: ~147MB of ISO, plus its own archiso
|
||||
# initramfs, copied into both the ISO tree and the size-constrained FAT EFI image.
|
||||
#
|
||||
# It cannot just be deleted — releng's broadcom-wl hard-depends on it, and it is
|
||||
# the only releng package that does, so pacman would drag the kernel straight back
|
||||
# in. broadcom-wl is a prebuilt module for stock linux and cannot load on the
|
||||
# kernel we boot, so it has done nothing since we started booting T2 anyway. The
|
||||
# install is entirely offline and the live environment needs no Wi-Fi driver.
|
||||
#
|
||||
# Anchored so linux-t2 and linux-firmware are untouched.
|
||||
sed -i -E '/^(linux|broadcom-wl)$/d' "$build_cache_dir/packages.x86_64"
|
||||
|
||||
# Build the offline mirror: everything pacstrap might want during the target
|
||||
# install. With --local-source, the omarchy* packages we just built are
|
||||
# already in the mirror and we filter them out below. Without it, pacman -Syw
|
||||
@@ -234,70 +247,6 @@ repo-add "$offline_mirror_dir/offline.db.tar.gz" "$offline_mirror_dir/"*.pkg.tar
|
||||
mkdir -p /var/cache/omarchy/mirror
|
||||
ln -sf "$offline_mirror_dir" /var/cache/omarchy/mirror/offline
|
||||
|
||||
# Denominator for the install dashboard's progress bar. Resolving the mirror's
|
||||
# own package lists against the mirror we just indexed, with an empty local db,
|
||||
# is the question pacstrap asks at install time — same resolver, same repo, same
|
||||
# lists — so no hand-kept constant can drift.
|
||||
#
|
||||
# It over-counts by ~1 in 925: archinstall.packages lists both amd-ucode and
|
||||
# intel-ucode because the mirror must contain either. phases.py records expected
|
||||
# and actual in the timing JSON, so growing drift shows up in acceptance runs.
|
||||
# The early-bootstrap set is already inside this closure, so restating it would
|
||||
# only add a second list to drift.
|
||||
resolve_expected_packages() {
|
||||
local resolve_root=/tmp/omarchy-expected-packages
|
||||
local resolved
|
||||
local -a targets
|
||||
|
||||
rm -rf "$resolve_root"
|
||||
mkdir -p "$resolve_root/var/lib/pacman"
|
||||
|
||||
mapfile -t targets < <(
|
||||
{
|
||||
grep -hv '^#\|^$' /builder/archinstall.packages
|
||||
# Read the shipped copy, which is what _runtime_package_list reads at
|
||||
# install time, not the build-time source it came from.
|
||||
grep -hv '^#\|^$' \
|
||||
"$build_cache_dir/airootfs/usr/share/omarchy-iso/omarchy-base.packages"
|
||||
printf '%s\n' "$OMARCHY_RUNTIME_PACKAGE" "$OMARCHY_SETTINGS_PACKAGE" \
|
||||
"$OMARCHY_NVIM_PACKAGE"
|
||||
} | sort -u
|
||||
)
|
||||
|
||||
pacman --config "$build_cache_dir/pacman-offline.conf" \
|
||||
--root "$resolve_root" --dbpath "$resolve_root/var/lib/pacman" \
|
||||
--noconfirm -Sy >/dev/null || return 1
|
||||
|
||||
# Capture before counting: no pipefail here, so a pacman failure inside a
|
||||
# pipeline would become a plausible partial count, which never trips the
|
||||
# dashboard's fallback.
|
||||
resolved="$(pacman --config "$build_cache_dir/pacman-offline.conf" \
|
||||
--root "$resolve_root" --dbpath "$resolve_root/var/lib/pacman" \
|
||||
--noconfirm -S --print --print-format '%n' "${targets[@]}")" || return 1
|
||||
|
||||
printf '%s\n' "$resolved" | sort -u | grep -c .
|
||||
}
|
||||
|
||||
# Worth failing the build over: -S --print only aborts when a target is missing
|
||||
# from the offline repo, which would fail pacstrap the same way. A count that
|
||||
# merely looks wrong is not — the dashboard falls back without the file.
|
||||
if ! expected_packages="$(resolve_expected_packages)"; then
|
||||
echo "ERROR: could not resolve the target package count from the offline mirror." >&2
|
||||
echo " pacman -S --print aborts the whole transaction if any single target" >&2
|
||||
echo " is missing, so this almost certainly means pacstrap would fail the" >&2
|
||||
echo " same way at install time." >&2
|
||||
exit 1
|
||||
fi
|
||||
if (( expected_packages < 600 || expected_packages > 2000 )); then
|
||||
echo "WARNING: resolved target package count $expected_packages is outside the" >&2
|
||||
echo " expected 600-2000 range; shipping no denominator so the install" >&2
|
||||
echo " dashboard falls back to its time-based curve." >&2
|
||||
else
|
||||
printf '%s\n' "$expected_packages" \
|
||||
>"$build_cache_dir/airootfs/usr/share/omarchy-iso/expected-packages"
|
||||
echo "Target install resolves to $expected_packages packages."
|
||||
fi
|
||||
|
||||
# Live ISO uses the same offline pacman.conf.
|
||||
cp "$build_cache_dir/pacman-offline.conf" "$build_cache_dir/airootfs/etc/pacman.conf"
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# mkinitcpio preset file for the 'linux-t2' package on archiso
|
||||
#
|
||||
# The filename is the pkgbase, and that is load-bearing. Kernel packages ship
|
||||
# nothing in /boot — they only install usr/lib/modules/<kver>/vmlinuz — so
|
||||
# mkinitcpio's alpm hook sources /etc/mkinitcpio.d/<pkgbase>.preset and copies
|
||||
# THAT package's kernel to whatever ALL_kver names (is_kernelcopy in
|
||||
# /usr/share/libalpm/scripts/mkinitcpio). Named linux.preset, this pointed the
|
||||
# stock kernel at the T2 filenames, so the ISO booted stock Arch while every
|
||||
# boot entry said linux-t2 and T2 Macs got no keyboard or trackpad.
|
||||
#
|
||||
# releng ships its own linux.preset for the stock kernel; leave it alone.
|
||||
|
||||
PRESETS=('archiso')
|
||||
|
||||
ALL_kver='/boot/vmlinuz-linux-t2'
|
||||
archiso_config='/etc/mkinitcpio.conf.d/archiso.conf'
|
||||
|
||||
archiso_image="/boot/initramfs-linux-t2.img"
|
||||
@@ -1,9 +0,0 @@
|
||||
# mkinitcpio preset file for the 'linux' package on archiso
|
||||
|
||||
PRESETS=('archiso')
|
||||
|
||||
ALL_kver='/boot/vmlinuz-linux-t2'
|
||||
archiso_config='/etc/mkinitcpio.conf.d/archiso.conf'
|
||||
|
||||
archiso_image="/boot/initramfs-linux-t2.img"
|
||||
|
||||
@@ -137,63 +137,7 @@ LOGO_HEIGHT="$(logo_height)"
|
||||
(( LOGO_HEIGHT > 0 )) || LOGO_HEIGHT=1
|
||||
CONTENT_WIDTH="$LOGO_WIDTH"
|
||||
DYNAMIC_ROW=$((LOGO_HEIGHT + 3))
|
||||
LAST_PM=10
|
||||
|
||||
# ── Install progress ─────────────────────────────────────────────────────────
|
||||
#
|
||||
# Position = max(floor, work), both non-decreasing, in per-mille of the bar.
|
||||
#
|
||||
# floor lo + (span-1) * t / (t+tau), on time since this process saw the
|
||||
# phase. Asymptotic, so it approaches the band end without reaching it.
|
||||
# tau is a shape constant, not a duration prediction.
|
||||
# work packages installed / PKG_TOTAL, for the package phase only (~85% of
|
||||
# the install). libalpm makes one directory per package under
|
||||
# <target>/var/lib/pacman/local; the denominator comes from
|
||||
# build-iso.sh, so there is no second package list to drift.
|
||||
#
|
||||
# Per-mille because a whole-percent asymptote over a 2-point band has two
|
||||
# reachable values and stops moving. No give-up predicate: two discontinuous
|
||||
# curves behind a monotonic clamp freeze. With no signal the floor alone still
|
||||
# finishes the band.
|
||||
EXPECTED_PACKAGES_FILE="${OMARCHY_EXPECTED_PACKAGES_FILE:-/usr/share/omarchy-iso/expected-packages}"
|
||||
PKG_TOTAL=0
|
||||
if [[ -r $EXPECTED_PACKAGES_FILE ]]; then
|
||||
read -r PKG_TOTAL _ <"$EXPECTED_PACKAGES_FILE" 2>/dev/null || PKG_TOTAL=0
|
||||
fi
|
||||
[[ $PKG_TOTAL =~ ^[0-9]+$ ]] || PKG_TOTAL=0
|
||||
# A wrong denominator is worse than none: it never triggers the fallback.
|
||||
(( PKG_TOTAL < 300 || PKG_TOTAL > 5000 )) && PKG_TOTAL=0
|
||||
|
||||
PKG_BONUS_MAX_PM=100 # cap on how far the bar may run ahead of the count
|
||||
PKG_TAIL_PM=40 # band held back so a stalled bar has somewhere to go
|
||||
PKG_FLOOR_TAU=900 # with a work signal, time is only a backstop until the
|
||||
# first package, so slow it until it cannot outrun data
|
||||
|
||||
POS=10 # position, per-mille, monotonic
|
||||
PROGRESS_PM=10
|
||||
NOW=0 # monotonic seconds, refreshed once per frame
|
||||
DASH_T0=0
|
||||
PHASE_NAME="" # last phase this process observed
|
||||
PHASE_T0=0 # NOW when it was first observed
|
||||
PHASE_LO=10 # POS on entry, for an unknown phase with no index
|
||||
PKG_DB_DIR=""
|
||||
PKG_COUNT=0
|
||||
PKG_BASE=-1 # package count when the phase started
|
||||
PKG_SEEN=0 # running max of packages since PKG_BASE
|
||||
PKG_LAST_CHANGE=0
|
||||
PKG_BONUS=0
|
||||
|
||||
# Monotonic clock: tzupdate and systemd-timesyncd can step the wall clock
|
||||
# mid-install, which would jump every time-driven band to its ceiling.
|
||||
set_now() {
|
||||
local up rest
|
||||
if read -r up rest </proc/uptime 2>/dev/null && [[ $up == [0-9]* ]]; then
|
||||
NOW=${up%%.*}
|
||||
else
|
||||
NOW=$EPOCHSECONDS
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
LAST_PCT=1
|
||||
|
||||
left_padding() {
|
||||
local width="${1:-$CONTENT_WIDTH}" cols pad
|
||||
@@ -239,146 +183,72 @@ render_logo() {
|
||||
}
|
||||
|
||||
progress_bar() {
|
||||
local pm="$1" width="${2:-34}" filled empty
|
||||
(( pm < 0 )) && pm=0
|
||||
(( pm > 1000 )) && pm=1000
|
||||
filled=$(( pm * width / 1000 ))
|
||||
local pct="$1" width="${2:-34}" filled empty
|
||||
(( pct < 0 )) && pct=0
|
||||
(( pct > 100 )) && pct=100
|
||||
filled=$(( pct * width / 100 ))
|
||||
empty=$(( width - filled ))
|
||||
printf '%s%s%s%s%s' "$WHITE" "$(repeat █ "$filled")" "$DARK" "$(repeat ░ "$empty")" "$RESET"
|
||||
}
|
||||
|
||||
# Packages libalpm has finished installing. local/ holds one directory per
|
||||
# package and nothing else in it is a directory, so */ is exact. One glob, no
|
||||
# forks: this runs twice a second while the disk is saturated.
|
||||
count_packages() {
|
||||
local -a entries
|
||||
PKG_COUNT=0
|
||||
[[ -n $PKG_DB_DIR ]] || return 0
|
||||
entries=("$PKG_DB_DIR"/*/)
|
||||
# Unmatched globs expand to the pattern; nullglob stays off script-wide.
|
||||
if (( ${#entries[@]} == 1 )) && [[ ! -d ${entries[0]} ]]; then
|
||||
return 0
|
||||
fi
|
||||
PKG_COUNT=${#entries[@]}
|
||||
return 0
|
||||
}
|
||||
|
||||
# Sets PROGRESS_PM. Never call in a command substitution: POS, the phase timer
|
||||
# and the package counters must survive between frames. Print nothing — output
|
||||
# here would land on the TTY between the cursor escape and the bar.
|
||||
install_progress() {
|
||||
local phase finished index total target
|
||||
local lo hi tau span t pos work gap n pkg_signal=0
|
||||
local phase finished phase_started elapsed span active pct
|
||||
local start=1 end=99 expected=90
|
||||
local -a state
|
||||
|
||||
PROGRESS_PM=$POS
|
||||
[[ -f $STATE_FILE ]] || return 0
|
||||
[[ -f $STATE_FILE ]] || { printf '1'; return; }
|
||||
|
||||
mapfile -t state < <(jq -r '
|
||||
(.current_phase // "Starting installation"),
|
||||
((.finished_at // 0) | tonumber? // 0 | floor),
|
||||
((.current_index // -1) | tonumber? // -1 | floor),
|
||||
((.total_phases // 0) | tonumber? // 0 | floor),
|
||||
(.target // "/mnt")
|
||||
((.phase_started_at // .started_at // 0) | tonumber? // 0 | floor)
|
||||
' "$STATE_FILE" 2>/dev/null)
|
||||
(( ${#state[@]} == 5 )) || return 0
|
||||
phase="${state[0]}" finished="${state[1]}"
|
||||
index="${state[2]}" total="${state[3]}" target="${state[4]}"
|
||||
[[ -n $target ]] || target="/mnt"
|
||||
(( ${#state[@]} == 3 )) || { printf '1'; return; }
|
||||
phase="${state[0]}" finished="${state[1]}" phase_started="${state[2]}"
|
||||
|
||||
if (( finished > 0 )) || [[ $phase == "Installation complete" ]]; then
|
||||
POS=1000
|
||||
PROGRESS_PM=1000
|
||||
return 0
|
||||
printf '100'
|
||||
return
|
||||
fi
|
||||
|
||||
# Transitions are observed here rather than read from the state file, which
|
||||
# keeps wall clock out of the dashboard entirely.
|
||||
if [[ $phase != "$PHASE_NAME" ]]; then
|
||||
PHASE_NAME="$phase"
|
||||
PHASE_T0=$NOW
|
||||
PHASE_LO=$POS
|
||||
PKG_BASE=-1
|
||||
PKG_SEEN=0
|
||||
PKG_BONUS=0
|
||||
PKG_LAST_CHANGE=$NOW
|
||||
fi
|
||||
|
||||
# Bands in per-mille, plus tau. The tail bands are wide relative to what they
|
||||
# cost on NVMe: run_system_finalizer can DKMS-build a driver and
|
||||
# finalize_limine_boot rebuilds every UKI, so they vary most on real hardware.
|
||||
# Over-allotting costs a step at a boundary; under-allotting costs a stall.
|
||||
# Phase weights: start%, end%, expected seconds in phase.
|
||||
case "$phase" in
|
||||
"Starting installation") lo=10 hi=25 tau=5 ;;
|
||||
"Preparing live environment") lo=25 hi=50 tau=25 ;;
|
||||
"Preparing install target") lo=50 hi=65 tau=20 ;;
|
||||
"Installing Arch + Omarchy") lo=65 hi=700 tau=150 pkg_signal=1 ;;
|
||||
"Configuring hibernation") lo=700 hi=720 tau=10 ;;
|
||||
"Configuring system") lo=720 hi=850 tau=120 ;;
|
||||
"Finalizing Limine boot") lo=850 hi=950 tau=90 ;;
|
||||
"Finalizing user") lo=950 hi=978 tau=30 ;;
|
||||
"Configuring login") lo=978 hi=988 tau=5 ;;
|
||||
"Configuring DNS resolver") lo=988 hi=994 tau=5 ;;
|
||||
"Validating boot setup") lo=994 hi=1000 tau=5 ;;
|
||||
*)
|
||||
# Unknown names used to inherit a 1->99 sweep. Derive an even band from
|
||||
# the published index instead; with no index, a bounded slice, so a
|
||||
# renamed phase degrades to one cell rather than a runaway.
|
||||
if (( total >= 2 && index >= 0 && index < total )); then
|
||||
lo=$(( 10 + 990 * index / total ))
|
||||
hi=$(( 10 + 990 * (index + 1) / total ))
|
||||
else
|
||||
lo=$PHASE_LO
|
||||
hi=$(( PHASE_LO + 30 ))
|
||||
fi
|
||||
tau=60
|
||||
;;
|
||||
"Starting installation") start=1 end=2 expected=2 ;;
|
||||
"Preparing live environment") start=2 end=6 expected=5 ;;
|
||||
"Verifying protected mounts") start=6 end=8 expected=3 ;;
|
||||
"Installing Arch + Omarchy") start=8 end=86 expected=95 ;;
|
||||
"Installing base system") start=8 end=84 expected=85 ;;
|
||||
"Configuring protected boot") start=84 end=86 expected=8 ;;
|
||||
"Configuring hibernation") start=86 end=88 expected=5 ;;
|
||||
"Configuring system") start=88 end=93 expected=10 ;;
|
||||
"Finalizing Limine boot") start=93 end=96 expected=8 ;;
|
||||
"Finalizing user") start=96 end=98 expected=10 ;;
|
||||
"Finalizing in chroot") start=88 end=98 expected=10 ;;
|
||||
"Configuring login") start=98 end=99 expected=3 ;;
|
||||
"Configuring DNS resolver") start=99 end=100 expected=2 ;;
|
||||
"Validating protected boot setup") start=99 end=100 expected=5 ;;
|
||||
"Validating boot setup") start=99 end=100 expected=5 ;;
|
||||
esac
|
||||
|
||||
(( pkg_signal && PKG_TOTAL > 0 )) || pkg_signal=0
|
||||
(( pkg_signal )) && tau=$PKG_FLOOR_TAU
|
||||
|
||||
t=$(( NOW - PHASE_T0 ))
|
||||
(( t < 0 )) && t=0
|
||||
span=$(( hi - lo ))
|
||||
(( span < 2 )) && span=2
|
||||
(( tau < 1 )) && tau=1
|
||||
|
||||
pos=$(( lo + (span - 1) * t / (t + tau) ))
|
||||
|
||||
if (( pkg_signal )); then
|
||||
PKG_DB_DIR="$target/var/lib/pacman/local"
|
||||
count_packages
|
||||
# Baseline on first sight: a target that already holds packages would
|
||||
# otherwise pin the bar at the band ceiling for the whole phase.
|
||||
(( PKG_BASE < 0 )) && PKG_BASE=$PKG_COUNT
|
||||
n=$(( PKG_COUNT - PKG_BASE ))
|
||||
(( n < 0 )) && n=0
|
||||
# Running max: a reinstalled package briefly drops its version-stamped dir.
|
||||
if (( n > PKG_SEEN )); then
|
||||
PKG_SEEN=$n
|
||||
PKG_LAST_CHANGE=$NOW
|
||||
fi
|
||||
gap=$(( NOW - PKG_LAST_CHANGE ))
|
||||
(( gap < 0 )) && gap=0
|
||||
(( gap > PKG_BONUS_MAX_PM )) && gap=$PKG_BONUS_MAX_PM
|
||||
(( gap > PKG_BONUS )) && PKG_BONUS=$gap
|
||||
work=$(( lo + (span - 1 - PKG_TAIL_PM) * PKG_SEEN / PKG_TOTAL + PKG_BONUS ))
|
||||
(( work > pos )) && pos=$work
|
||||
fi
|
||||
|
||||
(( pos > hi - 1 )) && pos=$(( hi - 1 ))
|
||||
(( pos > POS )) && POS=$pos
|
||||
(( POS > 1000 )) && POS=1000
|
||||
PROGRESS_PM=$POS
|
||||
return 0
|
||||
(( phase_started > 0 )) || phase_started=$EPOCHSECONDS
|
||||
elapsed=$(( EPOCHSECONDS - phase_started ))
|
||||
(( elapsed < 0 )) && elapsed=0
|
||||
span=$(( end - start ))
|
||||
(( span < 0 )) && span=0
|
||||
(( expected < 1 )) && expected=1
|
||||
active=$(( elapsed * span / expected ))
|
||||
(( active > span - 1 )) && active=$(( span - 1 ))
|
||||
(( active < 0 )) && active=0
|
||||
pct=$(( start + active ))
|
||||
(( pct > 100 )) && pct=100
|
||||
printf '%s' "$pct"
|
||||
}
|
||||
|
||||
# One tip per 8 seconds, in order. On elapsed time, not a frame counter: a frame
|
||||
# is 0.5s of sleep plus a render, which stretches under pacstrap I/O — on the
|
||||
# machine where the tips, not the bar, are carrying liveness.
|
||||
# One tip per ~8 seconds (frames tick every 0.5s), in order, so each stays
|
||||
# readable and none are skipped when progress jumps.
|
||||
current_tip() {
|
||||
printf '%s' "${tips[$(( (NOW - DASH_T0) / 8 % ${#tips[@]} ))]}"
|
||||
local frame="$1"
|
||||
printf '%s' "${tips[$(( frame / 16 % ${#tips[@]} ))]}"
|
||||
}
|
||||
|
||||
render_static() {
|
||||
@@ -389,24 +259,21 @@ render_static() {
|
||||
}
|
||||
|
||||
render_dynamic() {
|
||||
local pm
|
||||
set_now
|
||||
install_progress
|
||||
pm=$PROGRESS_PM
|
||||
[[ $pm =~ ^[0-9]+$ ]] || pm=$LAST_PM
|
||||
(( pm > 1000 )) && pm=1000
|
||||
# POS is monotone by construction; this is a guard, not the mechanism.
|
||||
(( pm < LAST_PM )) && pm=$LAST_PM
|
||||
LAST_PM=$pm
|
||||
local frame="$1" pct
|
||||
pct="$(install_progress)"
|
||||
[[ $pct =~ ^[0-9]+$ ]] || pct=$LAST_PCT
|
||||
(( pct > 100 )) && pct=100
|
||||
(( pct < LAST_PCT )) && pct=$LAST_PCT
|
||||
LAST_PCT=$pct
|
||||
|
||||
printf '%s%d;1H' "$CSI" "$DYNAMIC_ROW"
|
||||
center "Installing Omarchy" "$CONTENT_WIDTH"
|
||||
blank_line
|
||||
line_at "$CONTENT_WIDTH" $(( (CONTENT_WIDTH - 34) / 2 )) ""
|
||||
progress_bar "$pm" 34
|
||||
progress_bar "$pct" 34
|
||||
printf '\n'
|
||||
blank_line
|
||||
center "${DIM}Tip:${RESET} ${GREEN}$(current_tip)${RESET}" "$CONTENT_WIDTH"
|
||||
center "${DIM}Tip:${RESET} ${GREEN}$(current_tip "$frame")${RESET}" "$CONTENT_WIDTH"
|
||||
printf '%s' "$CLEAR_TO_END"
|
||||
}
|
||||
|
||||
@@ -674,14 +541,12 @@ launch_child() {
|
||||
[[ -e $TTY_PATH ]] || exit 2
|
||||
printf '%s' "$HIDE_CURSOR" >"$TTY_PATH"
|
||||
render_static >"$TTY_PATH"
|
||||
set_now
|
||||
DASH_T0=$NOW
|
||||
PHASE_T0=$NOW
|
||||
PKG_LAST_CHANGE=$NOW
|
||||
launch_child
|
||||
|
||||
frame=0
|
||||
while jobs -pr | grep -qx "$child_pid"; do
|
||||
render_dynamic >"$TTY_PATH" 2>/dev/null || true
|
||||
render_dynamic "$frame" >"$TTY_PATH" 2>/dev/null || true
|
||||
frame=$((frame + 1))
|
||||
sleep 0.5
|
||||
done
|
||||
|
||||
@@ -694,13 +559,6 @@ child_pgid=""
|
||||
|
||||
dashboard_log "installer child exited with status $status"
|
||||
|
||||
if (( status == 0 )); then
|
||||
# finished_at lands microseconds before the child exits, so the 0.5s poll
|
||||
# rarely renders a full bar before the finish screen replaces it.
|
||||
render_dynamic >"$TTY_PATH" 2>/dev/null || true
|
||||
sleep 0.4
|
||||
fi
|
||||
|
||||
if (( status != 0 )); then
|
||||
failure_status="$status"
|
||||
failure_summary=""
|
||||
|
||||
@@ -65,15 +65,11 @@ def main() -> int:
|
||||
info(f"Installing Omarchy for {ctx.username} → {ctx.target}")
|
||||
|
||||
from .phases_impl import (
|
||||
boost_cpu_governor,
|
||||
cleanup_bind_mounts,
|
||||
cleanup_protected_state,
|
||||
cleanup_target_hook_masks,
|
||||
restore_cpu_governors,
|
||||
)
|
||||
|
||||
governors = boost_cpu_governor()
|
||||
|
||||
success = False
|
||||
try:
|
||||
try:
|
||||
@@ -89,7 +85,6 @@ def main() -> int:
|
||||
info("Installation complete.")
|
||||
return 0
|
||||
finally:
|
||||
restore_cpu_governors(governors)
|
||||
cleanup_bind_mounts(ctx)
|
||||
cleanup_target_hook_masks(ctx)
|
||||
if not success:
|
||||
|
||||
@@ -4,7 +4,6 @@ the InstallContext and either return cleanly or raise to abort the install."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
import traceback
|
||||
from collections.abc import Callable
|
||||
@@ -26,9 +25,6 @@ def run(ctx: InstallContext, phases: list[tuple[str, PhaseFn]]) -> None:
|
||||
state_path = ctx.state_dir / "state.json"
|
||||
state = {
|
||||
"started_at": time.time(),
|
||||
# The dashboard counts packages under <target>/var/lib/pacman/local;
|
||||
# publish the path rather than have the UI assume /mnt.
|
||||
"target": str(ctx.target),
|
||||
"total_phases": len(phases),
|
||||
"current_index": 0,
|
||||
"current_phase": "Starting installation",
|
||||
@@ -67,10 +63,6 @@ def run(ctx: InstallContext, phases: list[tuple[str, PhaseFn]]) -> None:
|
||||
state["current_index"] = max(len(phases) - 1, 0)
|
||||
state["current_phase"] = "Installation complete"
|
||||
state["finished_at"] = time.time()
|
||||
# Expected vs actual for the bar's denominator, so drift is visible in
|
||||
# acceptance runs rather than only by watching a bar creep.
|
||||
state["installed_packages"] = _installed_package_count(ctx.target)
|
||||
state["expected_packages"] = _expected_package_count()
|
||||
_write_state(state_path, state)
|
||||
|
||||
timing_path = ctx.target / "var" / "log" / "omarchy-install-timing.json"
|
||||
@@ -78,25 +70,6 @@ def run(ctx: InstallContext, phases: list[tuple[str, PhaseFn]]) -> None:
|
||||
_write_state(timing_path, state)
|
||||
|
||||
|
||||
def _installed_package_count(target: Path) -> int:
|
||||
"""Packages libalpm installed into the target — one directory each under
|
||||
local/, which is what the dashboard counts live."""
|
||||
local_db = target / "var" / "lib" / "pacman" / "local"
|
||||
try:
|
||||
with os.scandir(local_db) as entries:
|
||||
return sum(1 for entry in entries if entry.is_dir())
|
||||
except OSError:
|
||||
return 0
|
||||
|
||||
|
||||
def _expected_package_count() -> int:
|
||||
path = Path("/usr/share/omarchy-iso/expected-packages")
|
||||
try:
|
||||
return int(path.read_text().split()[0])
|
||||
except (OSError, ValueError, IndexError):
|
||||
return 0
|
||||
|
||||
|
||||
def _write_state(path: Path, state: dict) -> None:
|
||||
# Dashboard polls this file while phases update it. Write atomically so the
|
||||
# reader never observes a truncated/partial JSON document and resets UI.
|
||||
|
||||
@@ -1396,40 +1396,6 @@ def _validate_pre_mounted_filesystems(ctx: InstallContext) -> None:
|
||||
raise RuntimeError(f"{crypttab} missing LUKS UUID {storage['luks_uuid']}")
|
||||
|
||||
|
||||
CPU_SYSFS = Path("/sys/devices/system/cpu")
|
||||
|
||||
|
||||
def boost_cpu_governor() -> dict[Path, str]:
|
||||
"""Run the live CPUs flat out for the install.
|
||||
|
||||
Package extraction and the UKI build are both CPU-bound, and archiso boots
|
||||
on whatever governor the kernel defaults to. Writing an unsupported
|
||||
governor just fails, so nothing needs probing first, and hosts without
|
||||
cpufreq (most VMs) have no paths at all. Returns the prior governors.
|
||||
"""
|
||||
saved: dict[Path, str] = {}
|
||||
for path in sorted(CPU_SYSFS.glob("cpu*/cpufreq/scaling_governor")):
|
||||
try:
|
||||
saved[path] = path.read_text().strip()
|
||||
path.write_text("performance\n")
|
||||
except OSError:
|
||||
saved.pop(path, None)
|
||||
|
||||
if saved:
|
||||
info(f"› CPU governor set to performance ({len(saved)} CPUs)")
|
||||
return saved
|
||||
|
||||
|
||||
def restore_cpu_governors(saved: dict[Path, str]) -> None:
|
||||
"""Only matters when an install fails and the user keeps using the live
|
||||
environment — a successful one reboots out of it."""
|
||||
for path, governor in saved.items():
|
||||
try:
|
||||
path.write_text(f"{governor}\n")
|
||||
except OSError:
|
||||
continue
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# cleanup_bind_mounts: invoked from main()'s finally so bind mounts get
|
||||
# unwound on success, failure, or interrupt. Idempotent.
|
||||
|
||||
@@ -76,9 +76,9 @@ aarch64 has no BIOS — only UEFI. Drop the syslinux path entirely on ARM.
|
||||
|
||||
The cleanest cut: keep one `grub.cfg` shared, strip the x86 shell/memtest fragments at build time when `OMARCHY_ARCH=aarch64`. Don't fork the file.
|
||||
|
||||
### 6. mkinitcpio preset — `configs/airootfs/etc/mkinitcpio.d/linux.preset`
|
||||
### 6. mkinitcpio preset — `configs/airootfs/etc/mkinitcpio.d/linux-t2.preset`
|
||||
|
||||
References `vmlinuz-linux-t2` and `initramfs-linux-t2.img` (lines 5, 8). For aarch64, swap to `vmlinuz-linux` / `initramfs-linux.img`. Generate the file at build time from a template, keyed on arch.
|
||||
Names `vmlinuz-linux-t2` and `initramfs-linux-t2.img`. The filename is the pkgbase and the alpm hook keys off it, so aarch64 does not rename this file — it simply does not ship it, and releng's own `linux.preset` covers the stock kernel. Drop `linux-t2` from `arch_packages` on aarch64 and the boot entries point at `vmlinuz-linux` / `initramfs-linux.img`.
|
||||
|
||||
### 7. Configurator — `configs/airootfs/root/configurator`
|
||||
|
||||
@@ -145,7 +145,7 @@ Code:
|
||||
- `configs/efiboot/loader/loader.conf` — per-arch default entry
|
||||
- `configs/efiboot/loader/entries/01-archiso-x86_64-linux.conf` — generate aarch64 sibling at build time
|
||||
- `configs/grub/grub.cfg`, `loopback.cfg` — strip x86-only fragments on aarch64
|
||||
- `configs/airootfs/etc/mkinitcpio.d/linux.preset` — template by arch (drop linux-t2 on aarch64)
|
||||
- `configs/airootfs/etc/mkinitcpio.d/linux-t2.preset` — omit on aarch64 (drop linux-t2 there; releng's linux.preset covers the stock kernel)
|
||||
- `configs/airootfs/root/configurator` — branch archinstall JSON mirror list on `uname -m`; optionally guard lspci T2 probe
|
||||
- `.github/workflows/nightly-build.yml` — matrix x86_64/aarch64
|
||||
|
||||
|
||||
Reference in new issue
Block a user