|
|
|
@@ -10,7 +10,11 @@ readonly ESP_SIZE="1GiB"
|
|
|
|
|
readonly TARGET="/mnt"
|
|
|
|
|
readonly -a PKGS_EXCLUDE=(archiso mkinitcpio-archiso mkinitcpio-nfs-utils syslinux memtest86+)
|
|
|
|
|
|
|
|
|
|
skip() { local x; for x in "${PKGS_EXCLUDE[@]}"; do [[ "$x" == "$1" ]] && return 0; done; return 1; }
|
|
|
|
|
skip() {
|
|
|
|
|
local x
|
|
|
|
|
for x in "${PKGS_EXCLUDE[@]}"; do [[ "$x" == "$1" ]] && return 0; done
|
|
|
|
|
return 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DISK=""
|
|
|
|
|
KEYMAP=""
|
|
|
|
@@ -35,8 +39,9 @@ SSHD="true"
|
|
|
|
|
SSHD_APPLIED="false"
|
|
|
|
|
TPM_DISCLAIMER=""
|
|
|
|
|
TARGET_DB_SYNCED="false"
|
|
|
|
|
UPDATE_STATUS="not run"
|
|
|
|
|
|
|
|
|
|
log() { echo "[$(date '+%H:%M:%S')] $*" >> "$LOG"; }
|
|
|
|
|
log() { echo "[$(date '+%H:%M:%S')] $*" >>"$LOG"; }
|
|
|
|
|
|
|
|
|
|
cleanup() {
|
|
|
|
|
set +e
|
|
|
|
@@ -60,7 +65,11 @@ trap cleanup EXIT
|
|
|
|
|
page_output() {
|
|
|
|
|
local ctx="$1"
|
|
|
|
|
if [[ -c /dev/tty && -t 1 ]]; then
|
|
|
|
|
{ gum style --foreground 11 --bold "$ctx"; echo ""; cat; } | gum pager || true
|
|
|
|
|
{
|
|
|
|
|
gum style --foreground 11 --bold "$ctx"
|
|
|
|
|
echo ""
|
|
|
|
|
cat
|
|
|
|
|
} | gum pager || true
|
|
|
|
|
else
|
|
|
|
|
gum style --foreground 9 --bold "$ctx" || true
|
|
|
|
|
cat >&2 || true
|
|
|
|
@@ -82,7 +91,7 @@ page_log_since() {
|
|
|
|
|
local mark="$1" total=0
|
|
|
|
|
[[ -f "$LOG" ]] || return 0
|
|
|
|
|
total=$(wc -l <"$LOG") || total=0
|
|
|
|
|
(( mark < total )) || return 0
|
|
|
|
|
((mark < total)) || return 0
|
|
|
|
|
sed -n "$((mark + 1)),\$p" "$LOG" | page_output "$2"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -91,15 +100,21 @@ page_log_since() {
|
|
|
|
|
# partial output) and - on failure - page the lines it added so the error
|
|
|
|
|
# reaches the user, not just the log. Returns the command's exit status.
|
|
|
|
|
# Usage: run_logged [-s SPINNER [-t TITLE]] "what failed" cmd [args...]
|
|
|
|
|
# -s show a gum spinner while cmd runs: dot for linear commands, jump for
|
|
|
|
|
# ones that compile. Frames render on stderr (the terminal), never $LOG.
|
|
|
|
|
# -s show a gum spinner while cmd runs (always dot, for a consistent look).
|
|
|
|
|
# Frames render on stderr (the terminal), never $LOG.
|
|
|
|
|
# -t spinner title; omit when an info line already says what is running.
|
|
|
|
|
run_logged() {
|
|
|
|
|
local ctx="" spinner="" title="" rc=0 mark=0 total=0
|
|
|
|
|
while [[ "${1:-}" == -[st] ]]; do
|
|
|
|
|
case "$1" in
|
|
|
|
|
-s) spinner="$2"; shift 2 ;;
|
|
|
|
|
-t) title="$2"; shift 2 ;;
|
|
|
|
|
-s)
|
|
|
|
|
spinner="$2"
|
|
|
|
|
shift 2
|
|
|
|
|
;;
|
|
|
|
|
-t)
|
|
|
|
|
title="$2"
|
|
|
|
|
shift 2
|
|
|
|
|
;;
|
|
|
|
|
esac
|
|
|
|
|
done
|
|
|
|
|
ctx="$1"
|
|
|
|
@@ -123,16 +138,16 @@ run_logged() {
|
|
|
|
|
if [[ -n "$title" ]]; then
|
|
|
|
|
spin+=(--title "$title")
|
|
|
|
|
fi
|
|
|
|
|
"${spin[@]}" -- sh -c '"$@" 2>&1' sh "$@" \
|
|
|
|
|
| tee -a "$LOG" >/dev/null || rc=$?
|
|
|
|
|
"${spin[@]}" -- sh -c '"$@" 2>&1' sh "$@" |
|
|
|
|
|
tee -a "$LOG" >/dev/null || rc=$?
|
|
|
|
|
else
|
|
|
|
|
"$@" 2>&1 | tee -a "$LOG" >/dev/null || rc=$?
|
|
|
|
|
fi
|
|
|
|
|
if (( rc != 0 )); then
|
|
|
|
|
if ((rc != 0)); then
|
|
|
|
|
if [[ -f "$LOG" ]]; then
|
|
|
|
|
total=$(wc -l <"$LOG") || total=0
|
|
|
|
|
fi
|
|
|
|
|
if (( mark < total )); then
|
|
|
|
|
if ((mark < total)); then
|
|
|
|
|
page_log_since "$mark" "${ctx} (exit ${rc}) - output below, full log: $LOG"
|
|
|
|
|
else
|
|
|
|
|
# Silent failure: nothing to page, but still surface where and why.
|
|
|
|
@@ -153,9 +168,15 @@ on_err() {
|
|
|
|
|
}
|
|
|
|
|
trap 'on_err "$LINENO"' ERR
|
|
|
|
|
|
|
|
|
|
(( EUID == 0 )) || { echo "Run as root" >&2; exit 1; }
|
|
|
|
|
((EUID == 0)) || {
|
|
|
|
|
echo "Run as root" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
die() { gum style --foreground 9 --bold "ERROR: $*"; exit 1; }
|
|
|
|
|
die() {
|
|
|
|
|
gum style --foreground 9 --bold "ERROR: $*"
|
|
|
|
|
exit 1
|
|
|
|
|
}
|
|
|
|
|
info() { gum style --foreground 12 --bold "$*"; }
|
|
|
|
|
ok() { gum style --foreground 10 --bold "$*"; }
|
|
|
|
|
warn() { gum style --foreground 11 --bold "$*"; }
|
|
|
|
@@ -173,7 +194,7 @@ divider() {
|
|
|
|
|
|
|
|
|
|
part() { [[ "$DISK" =~ [0-9]$ ]] && echo "${DISK}p$1" || echo "${DISK}$1"; }
|
|
|
|
|
|
|
|
|
|
valid_username() { [[ "$1" =~ ^[a-z_][a-z0-9_-]*$ ]] && (( ${#1} <= 32 )); }
|
|
|
|
|
valid_username() { [[ "$1" =~ ^[a-z_][a-z0-9_-]*$ ]] && ((${#1} <= 32)); }
|
|
|
|
|
|
|
|
|
|
# Numeric uid:gid of $USERNAME as seen inside $TARGET. Always needed for
|
|
|
|
|
# chowns from the live medium, whose /etc/passwd has no $USERNAME. Both ids
|
|
|
|
@@ -274,8 +295,8 @@ screen_keymap() {
|
|
|
|
|
"Use US keyboard layout (us)?"; then
|
|
|
|
|
KEYMAP="us"
|
|
|
|
|
else
|
|
|
|
|
KEYMAP=$(localectl list-keymaps | gum filter --header "+ Keymap" --placeholder "Search keymaps...") \
|
|
|
|
|
|| die "No keymap selected."
|
|
|
|
|
KEYMAP=$(localectl list-keymaps | gum filter --header "+ Keymap" --placeholder "Search keymaps...") ||
|
|
|
|
|
die "No keymap selected."
|
|
|
|
|
fi
|
|
|
|
|
[[ -z "$KEYMAP" ]] && die "No keymap selected."
|
|
|
|
|
loadkeys "$KEYMAP" 2>/dev/null || true
|
|
|
|
@@ -304,7 +325,7 @@ screen_network() {
|
|
|
|
|
local try=0
|
|
|
|
|
while ! ping -c1 -W3 archlinux.org &>/dev/null; do
|
|
|
|
|
try=$((try + 1))
|
|
|
|
|
if (( try >= 5 )); then
|
|
|
|
|
if ((try >= 5)); then
|
|
|
|
|
gum confirm --affirmative "Retry" --negative "Abort" \
|
|
|
|
|
"Network check failed. Retry?" || die "No network connection."
|
|
|
|
|
try=0
|
|
|
|
@@ -348,12 +369,12 @@ screen_disk() {
|
|
|
|
|
disks+=("${name} ${size} ${model:-unknown model}")
|
|
|
|
|
done < <(lsblk -dpnP -o NAME,SIZE,MODEL,TYPE)
|
|
|
|
|
|
|
|
|
|
(( ${#disks[@]} > 0 )) || die "No disks found."
|
|
|
|
|
((${#disks[@]} > 0)) || die "No disks found."
|
|
|
|
|
|
|
|
|
|
while true; do
|
|
|
|
|
local choice
|
|
|
|
|
choice=$(gum choose --header "+ Target disk (live USB hidden)" "${disks[@]}") \
|
|
|
|
|
|| die "No disk selected."
|
|
|
|
|
choice=$(gum choose --header "+ Target disk (live USB hidden)" "${disks[@]}") ||
|
|
|
|
|
die "No disk selected."
|
|
|
|
|
DISK=$(echo "$choice" | awk '{print $1}')
|
|
|
|
|
|
|
|
|
|
gum confirm --affirmative "Wipe ${DISK}" --negative "Go back" \
|
|
|
|
@@ -419,8 +440,8 @@ screen_filesystem() {
|
|
|
|
|
clear
|
|
|
|
|
header "Filesystem"
|
|
|
|
|
echo ""
|
|
|
|
|
FILESYSTEM=$(gum choose --header "+ Root filesystem" "ext4" "btrfs") \
|
|
|
|
|
|| die "No filesystem selected."
|
|
|
|
|
FILESYSTEM=$(gum choose --header "+ Root filesystem" "ext4" "btrfs") ||
|
|
|
|
|
die "No filesystem selected."
|
|
|
|
|
log "Filesystem: $FILESYSTEM"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -429,8 +450,8 @@ screen_username() {
|
|
|
|
|
header "Create User"
|
|
|
|
|
echo ""
|
|
|
|
|
local input
|
|
|
|
|
input=$(gum input --placeholder "username" --char-limit 32 --header "+ Username") \
|
|
|
|
|
|| die "No username entered."
|
|
|
|
|
input=$(gum input --placeholder "username" --char-limit 32 --header "+ Username") ||
|
|
|
|
|
die "No username entered."
|
|
|
|
|
[[ -z "$input" ]] && die "Username cannot be empty."
|
|
|
|
|
valid_username "$input" || die "Invalid username. Use lowercase letters, digits, _ or -."
|
|
|
|
|
USERNAME="$input"
|
|
|
|
@@ -447,7 +468,7 @@ screen_password() {
|
|
|
|
|
|
|
|
|
|
local pass1 pass2
|
|
|
|
|
pass1=$(gum input --password --header "+ Password") || die "No password entered."
|
|
|
|
|
(( ${#pass1} >= PASS_MIN )) || die "Password must be at least ${PASS_MIN} characters."
|
|
|
|
|
((${#pass1} >= PASS_MIN)) || die "Password must be at least ${PASS_MIN} characters."
|
|
|
|
|
pass2=$(gum input --password --header "+ Confirm password") || die "No confirmation entered."
|
|
|
|
|
[[ "$pass1" == "$pass2" ]] || die "Passwords do not match."
|
|
|
|
|
PASS="$pass1"
|
|
|
|
@@ -458,11 +479,11 @@ screen_hostname() {
|
|
|
|
|
clear
|
|
|
|
|
header "Hostname"
|
|
|
|
|
echo ""
|
|
|
|
|
HOSTNAME=$(gum input --placeholder "archlinux" --char-limit 63 --header "+ Hostname") \
|
|
|
|
|
|| die "No hostname entered."
|
|
|
|
|
HOSTNAME=$(gum input --placeholder "archlinux" --char-limit 63 --header "+ Hostname") ||
|
|
|
|
|
die "No hostname entered."
|
|
|
|
|
[[ -z "$HOSTNAME" ]] && HOSTNAME="archlinux"
|
|
|
|
|
[[ "$HOSTNAME" =~ ^[A-Za-z0-9]([A-Za-z0-9-]{0,62})$ && "$HOSTNAME" != *- ]] \
|
|
|
|
|
|| die "Invalid hostname. Use letters, digits and '-' (max 63, no leading/trailing '-')."
|
|
|
|
|
[[ "$HOSTNAME" =~ ^[A-Za-z0-9]([A-Za-z0-9-]{0,62})$ && "$HOSTNAME" != *- ]] ||
|
|
|
|
|
die "Invalid hostname. Use letters, digits and '-' (max 63, no leading/trailing '-')."
|
|
|
|
|
log "Hostname: $HOSTNAME"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -477,8 +498,8 @@ screen_git() {
|
|
|
|
|
echo ""
|
|
|
|
|
GH_USERNAME=$(gum input --placeholder "octocat (empty to skip)" \
|
|
|
|
|
--header "+ GitHub username for SSH keys (optional)") || die "Git configuration cancelled."
|
|
|
|
|
GIT_NAME=$(gum input --value "$USERNAME" --header "+ Git name (Enter keeps '${USERNAME}')") \
|
|
|
|
|
|| die "Git configuration cancelled."
|
|
|
|
|
GIT_NAME=$(gum input --value "$USERNAME" --header "+ Git name (Enter keeps '${USERNAME}')") ||
|
|
|
|
|
die "Git configuration cancelled."
|
|
|
|
|
GIT_EMAIL=$(gum input --placeholder "user@example.com (empty to skip)" \
|
|
|
|
|
--header "+ Git email (optional)") || die "Git configuration cancelled."
|
|
|
|
|
log "Git: name='${GIT_NAME}' email='${GIT_EMAIL}' github='${GH_USERNAME}'"
|
|
|
|
@@ -488,8 +509,8 @@ screen_timezone() {
|
|
|
|
|
clear
|
|
|
|
|
header "Timezone"
|
|
|
|
|
echo ""
|
|
|
|
|
TIMEZONE=$(timedatectl list-timezones | gum filter --header "+ Timezone" --placeholder "Search...") \
|
|
|
|
|
|| die "No timezone selected."
|
|
|
|
|
TIMEZONE=$(timedatectl list-timezones | gum filter --header "+ Timezone" --placeholder "Search...") ||
|
|
|
|
|
die "No timezone selected."
|
|
|
|
|
log "Timezone: $TIMEZONE"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -500,8 +521,8 @@ screen_locale() {
|
|
|
|
|
local locales
|
|
|
|
|
locales=$(sed -n 's/^#\?\([a-z][a-z]*_[A-Z][A-Z]\.UTF-8\) UTF-8.*/\1/p' /etc/locale.gen | sort -u)
|
|
|
|
|
[[ -z "$locales" ]] && locales="en_US.UTF-8"
|
|
|
|
|
LOCALE=$(echo "$locales" | gum filter --header "+ Locale" --placeholder "Search...") \
|
|
|
|
|
|| die "No locale selected."
|
|
|
|
|
LOCALE=$(echo "$locales" | gum filter --header "+ Locale" --placeholder "Search...") ||
|
|
|
|
|
die "No locale selected."
|
|
|
|
|
log "Locale: $LOCALE"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -669,14 +690,14 @@ install_pacstrap() {
|
|
|
|
|
# "name version". With copytoram the boot medium is unmounted and the
|
|
|
|
|
# file is gone - the live system's own `pacman -Q` is the same data.
|
|
|
|
|
if [[ -f "$pkglist" ]]; then
|
|
|
|
|
mapfile -t src_lines < "$pkglist"
|
|
|
|
|
mapfile -t src_lines <"$pkglist"
|
|
|
|
|
else
|
|
|
|
|
warn "${pkglist} not found (copytoram?) - using the live system's package list."
|
|
|
|
|
log "Pacstrap: ${pkglist} missing, falling back to pacman -Q"
|
|
|
|
|
mapfile -t src_lines < <(pacman -Q 2>>"$LOG" || true)
|
|
|
|
|
fi
|
|
|
|
|
(( ${#src_lines[@]} > 0 )) \
|
|
|
|
|
|| die "No package list: ${pkglist} is missing and pacman -Q returned nothing."
|
|
|
|
|
((${#src_lines[@]} > 0)) ||
|
|
|
|
|
die "No package list: ${pkglist} is missing and pacman -Q returned nothing."
|
|
|
|
|
|
|
|
|
|
# Parse only the first field, then drop names not resolvable in the
|
|
|
|
|
# live session's repos (incl. [local-aur] = /opt/localrepo).
|
|
|
|
@@ -688,7 +709,7 @@ install_pacstrap() {
|
|
|
|
|
while IFS= read -r db_line; do
|
|
|
|
|
[[ -n "$db_line" ]] && available["$db_line"]=1
|
|
|
|
|
done < <(pacman -Slq 2>>"$LOG" || true)
|
|
|
|
|
(( ${#available[@]} > 0 )) && filter_ok=true
|
|
|
|
|
((${#available[@]} > 0)) && filter_ok=true
|
|
|
|
|
else
|
|
|
|
|
warn "pacman -Syy failed (output above) — installing without resolvability filter."
|
|
|
|
|
fi
|
|
|
|
@@ -705,7 +726,7 @@ install_pacstrap() {
|
|
|
|
|
pkgs+=("$pkg")
|
|
|
|
|
done < <(printf '%s\n' "${src_lines[@]}")
|
|
|
|
|
|
|
|
|
|
if (( ${#dropped[@]} > 0 )); then
|
|
|
|
|
if ((${#dropped[@]} > 0)); then
|
|
|
|
|
warn "Skipped ${#dropped[@]} pkglist package(s) not found in configured repos:"
|
|
|
|
|
gum style --foreground 11 "${dropped[@]/#/ - }"
|
|
|
|
|
local d
|
|
|
|
@@ -740,14 +761,14 @@ install_pacstrap() {
|
|
|
|
|
log "Pacstrap: ${#pkgs[@]} packages, ${#dropped[@]} skipped"
|
|
|
|
|
run_logged -s dot -t "Installing ${#pkgs[@]} packages (this may take a while)" \
|
|
|
|
|
"pacstrap failed" \
|
|
|
|
|
pacstrap "$TARGET" "${pkgs[@]}" --noconfirm \
|
|
|
|
|
|| die "pacstrap failed - the target system is incomplete (output above, full log: $LOG)."
|
|
|
|
|
pacstrap "$TARGET" "${pkgs[@]}" --noconfirm ||
|
|
|
|
|
die "pacstrap failed - the target system is incomplete (output above, full log: $LOG)."
|
|
|
|
|
|
|
|
|
|
# mono's post_install runs cert-sync before the post-transaction hook has
|
|
|
|
|
# generated ca-certificates.crt, leaving mono's certificate store empty.
|
|
|
|
|
if [[ -x "${TARGET}/usr/bin/cert-sync" ]]; then
|
|
|
|
|
arch-chroot "$TARGET" cert-sync --quiet /etc/ssl/certs/ca-certificates.crt \
|
|
|
|
|
>> "$LOG" 2>&1 || warn "cert-sync failed - mono apps may reject TLS certificates."
|
|
|
|
|
>>"$LOG" 2>&1 || warn "cert-sync failed - mono apps may reject TLS certificates."
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
ok "Packages installed."
|
|
|
|
@@ -755,7 +776,7 @@ install_pacstrap() {
|
|
|
|
|
|
|
|
|
|
install_fstab() {
|
|
|
|
|
info "Generating fstab..."
|
|
|
|
|
genfstab -U "$TARGET" >> "${TARGET}/etc/fstab"
|
|
|
|
|
genfstab -U "$TARGET" >>"${TARGET}/etc/fstab"
|
|
|
|
|
ok "fstab generated."
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -770,14 +791,14 @@ install_cachyos_repo() {
|
|
|
|
|
if grep -q '^\[core\]' "$conf"; then
|
|
|
|
|
sed -i '0,/^\[core\]/s//[cachyos]\nInclude = \/etc\/pacman.d\/cachyos-mirrorlist\n\n[core]/' "$conf"
|
|
|
|
|
else
|
|
|
|
|
printf '\n[cachyos]\nInclude = /etc/pacman.d/cachyos-mirrorlist\n' >> "$conf"
|
|
|
|
|
printf '\n[cachyos]\nInclude = /etc/pacman.d/cachyos-mirrorlist\n' >>"$conf"
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
# chaotic-aur packages (yay, ...) came from the ISO - without the repo
|
|
|
|
|
# they turn foreign and never update.
|
|
|
|
|
if ! grep -q '^\[chaotic-aur\]' "$conf" 2>/dev/null \
|
|
|
|
|
&& [[ -f "${TARGET}/etc/pacman.d/chaotic-mirrorlist" ]]; then
|
|
|
|
|
printf '\n[chaotic-aur]\nInclude = /etc/pacman.d/chaotic-mirrorlist\n' >> "$conf"
|
|
|
|
|
if ! grep -q '^\[chaotic-aur\]' "$conf" 2>/dev/null &&
|
|
|
|
|
[[ -f "${TARGET}/etc/pacman.d/chaotic-mirrorlist" ]]; then
|
|
|
|
|
printf '\n[chaotic-aur]\nInclude = /etc/pacman.d/chaotic-mirrorlist\n' >>"$conf"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# [local-aur] lives only on the live medium (file:///opt/localrepo). The
|
|
|
|
@@ -800,18 +821,20 @@ install_locale() {
|
|
|
|
|
info "Setting locale..."
|
|
|
|
|
|
|
|
|
|
arch-chroot "$TARGET" sed -i "s/^#${LOCALE}/${LOCALE}/" /etc/locale.gen
|
|
|
|
|
arch-chroot "$TARGET" locale-gen
|
|
|
|
|
run_logged -s dot -t "Generating locales" "locale-gen failed" \
|
|
|
|
|
arch-chroot "$TARGET" locale-gen ||
|
|
|
|
|
die "locale-gen failed (output above, full log: $LOG)."
|
|
|
|
|
|
|
|
|
|
echo "LANG=${LOCALE}" > "${TARGET}/etc/locale.conf"
|
|
|
|
|
echo "KEYMAP=${KEYMAP}" > "${TARGET}/etc/vconsole.conf"
|
|
|
|
|
echo "LANG=${LOCALE}" >"${TARGET}/etc/locale.conf"
|
|
|
|
|
echo "KEYMAP=${KEYMAP}" >"${TARGET}/etc/vconsole.conf"
|
|
|
|
|
|
|
|
|
|
ok "Locale and keymap set."
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
install_hostname() {
|
|
|
|
|
info "Setting hostname..."
|
|
|
|
|
echo "$HOSTNAME" > "${TARGET}/etc/hostname"
|
|
|
|
|
cat > "${TARGET}/etc/hosts" <<EOF
|
|
|
|
|
echo "$HOSTNAME" >"${TARGET}/etc/hostname"
|
|
|
|
|
cat >"${TARGET}/etc/hosts" <<EOF
|
|
|
|
|
127.0.0.1 localhost
|
|
|
|
|
::1 localhost
|
|
|
|
|
127.0.1.1 ${HOSTNAME}.localdomain ${HOSTNAME}
|
|
|
|
@@ -871,7 +894,7 @@ install_user() {
|
|
|
|
|
|
|
|
|
|
arch-chroot "$TARGET" passwd -l root
|
|
|
|
|
|
|
|
|
|
echo '%wheel ALL=(ALL:ALL) ALL' > "${TARGET}/etc/sudoers.d/10-wheel"
|
|
|
|
|
echo '%wheel ALL=(ALL:ALL) ALL' >"${TARGET}/etc/sudoers.d/10-wheel"
|
|
|
|
|
chmod 440 "${TARGET}/etc/sudoers.d/10-wheel"
|
|
|
|
|
|
|
|
|
|
ok "User ${USERNAME} created (wheel group, sudo enabled). Root locked."
|
|
|
|
@@ -888,12 +911,12 @@ install_git_ssh() {
|
|
|
|
|
local git_name="${GIT_NAME:-$USERNAME}"
|
|
|
|
|
run_logged "git config user.name failed" \
|
|
|
|
|
arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
|
|
|
|
git config --global user.name "$git_name" \
|
|
|
|
|
|| warn "git config user.name failed (output above)."
|
|
|
|
|
git config --global user.name "$git_name" ||
|
|
|
|
|
warn "git config user.name failed (output above)."
|
|
|
|
|
run_logged "git config user.email failed" \
|
|
|
|
|
arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
|
|
|
|
git config --global user.email "$GIT_EMAIL" \
|
|
|
|
|
|| warn "git config user.email failed (output above)."
|
|
|
|
|
git config --global user.email "$GIT_EMAIL" ||
|
|
|
|
|
warn "git config user.email failed (output above)."
|
|
|
|
|
ok "Git configured for ${USERNAME} (${git_name} <${GIT_EMAIL}>)."
|
|
|
|
|
log "Git: ${git_name} <${GIT_EMAIL}>"
|
|
|
|
|
else
|
|
|
|
@@ -929,16 +952,17 @@ install_git_ssh() {
|
|
|
|
|
sid_ok="true"
|
|
|
|
|
log "ssh-import-id: already present in target"
|
|
|
|
|
else
|
|
|
|
|
echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" > "${TARGET}/etc/sudoers.d/99-ssh-import-id"
|
|
|
|
|
echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" >"${TARGET}/etc/sudoers.d/99-ssh-import-id"
|
|
|
|
|
chmod 440 "${TARGET}/etc/sudoers.d/99-ssh-import-id"
|
|
|
|
|
run_logged "yay -Ss ssh-import-id found nothing" \
|
|
|
|
|
run_logged -s dot -t "Searching the AUR for ssh-import-id" \
|
|
|
|
|
"yay -Ss ssh-import-id found nothing" \
|
|
|
|
|
arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
|
|
|
|
env HOME="$home_in_target" yay -Ss ssh-import-id \
|
|
|
|
|
|| warn "yay -Ss ssh-import-id found nothing (output above)."
|
|
|
|
|
env HOME="$home_in_target" yay -Ss ssh-import-id ||
|
|
|
|
|
warn "yay -Ss ssh-import-id found nothing (output above)."
|
|
|
|
|
if run_logged -s dot -t "Installing ssh-import-id" \
|
|
|
|
|
"pacman -S python-distro failed" \
|
|
|
|
|
arch-chroot "$TARGET" pacman -S --noconfirm python-distro \
|
|
|
|
|
&& run_logged -s jump -t "Installing ssh-import-id" \
|
|
|
|
|
arch-chroot "$TARGET" pacman -S --noconfirm python-distro &&
|
|
|
|
|
run_logged -s dot -t "Installing ssh-import-id" \
|
|
|
|
|
"yay failed to install ssh-import-id" \
|
|
|
|
|
arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
|
|
|
|
env HOME="$home_in_target" yay -S --noconfirm ssh-import-id; then
|
|
|
|
@@ -951,7 +975,7 @@ install_git_ssh() {
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if [[ "$sid_ok" == "true" ]]; then
|
|
|
|
|
if run_logged "ssh-import-id failed" \
|
|
|
|
|
if run_logged -s dot -t "Importing GitHub SSH keys" "ssh-import-id failed" \
|
|
|
|
|
arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
|
|
|
|
env HOME="$home_in_target" ssh-import-id "gh:${GH_USERNAME}"; then
|
|
|
|
|
ok "SSH keys imported via ssh-import-id."
|
|
|
|
@@ -968,7 +992,7 @@ install_git_ssh() {
|
|
|
|
|
local keys_url="https://github.com/${GH_USERNAME}.keys"
|
|
|
|
|
local keys="" curl_rc=0
|
|
|
|
|
keys=$(curl -fsSL --max-time 30 "$keys_url" 2>>"$LOG") || curl_rc=$?
|
|
|
|
|
if (( curl_rc != 0 )); then
|
|
|
|
|
if ((curl_rc != 0)); then
|
|
|
|
|
warn "Could not fetch ${keys_url} (curl exit ${curl_rc}) - no SSH keys imported."
|
|
|
|
|
# curl's stderr went to $LOG only - put it in front of the user.
|
|
|
|
|
page_log_tail "curl error output (q to quit)" 60 || true
|
|
|
|
@@ -984,8 +1008,8 @@ install_git_ssh() {
|
|
|
|
|
touch "$auth"
|
|
|
|
|
while IFS= read -r line; do
|
|
|
|
|
[[ -z "$line" ]] && continue
|
|
|
|
|
grep -qxF -- "$line" "$auth" 2>/dev/null || printf '%s\n' "$line" >> "$auth"
|
|
|
|
|
done <<< "$keys"
|
|
|
|
|
grep -qxF -- "$line" "$auth" 2>/dev/null || printf '%s\n' "$line" >>"$auth"
|
|
|
|
|
done <<<"$keys"
|
|
|
|
|
ok "SSH keys appended from ${keys_url}."
|
|
|
|
|
log "SSH keys: curl ${keys_url}"
|
|
|
|
|
fi
|
|
|
|
@@ -1024,14 +1048,14 @@ install_end4_dots() {
|
|
|
|
|
cp -a /opt/dots-hyprland "$repo_on_host"
|
|
|
|
|
chown -R "$owner" "$repo_on_host"
|
|
|
|
|
|
|
|
|
|
echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" > "${TARGET}/etc/sudoers.d/99-end4-install"
|
|
|
|
|
echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" >"${TARGET}/etc/sudoers.d/99-end4-install"
|
|
|
|
|
chmod 440 "${TARGET}/etc/sudoers.d/99-end4-install"
|
|
|
|
|
|
|
|
|
|
# Everything appended to $LOG from here belongs to this attempt; used
|
|
|
|
|
# further down to report which steps end-4 had to skip.
|
|
|
|
|
local log_mark=0
|
|
|
|
|
if [[ -f "$LOG" ]]; then
|
|
|
|
|
log_mark=$(wc -l < "$LOG")
|
|
|
|
|
log_mark=$(wc -l <"$LOG")
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# stdin feeds "i" to end-4's x() [R/e:i] failure prompt, which otherwise
|
|
|
|
@@ -1046,11 +1070,11 @@ install_end4_dots() {
|
|
|
|
|
# --skip-alldeps: every dependency was already installed at ISO build time
|
|
|
|
|
# (customize_airootfs.sh runs ./setup install-deps), so this only copies
|
|
|
|
|
# configs, sets up services and builds the python venv from the warm cache.
|
|
|
|
|
# Both attempts run under a jump spinner (this step compiles the venv and
|
|
|
|
|
# Both attempts run under a dot spinner (this step compiles the venv and
|
|
|
|
|
# pip bits): --show-output + the sh stderr-merge keep every line flowing
|
|
|
|
|
# into $LOG, the frames render on the terminal, and stdin still feeds the
|
|
|
|
|
# `yes i` process substitution through to ./setup.
|
|
|
|
|
if ! gum spin --show-output --spinner jump -- \
|
|
|
|
|
if ! gum spin --show-output -s dot -- \
|
|
|
|
|
sh -c '"$@" 2>&1' sh \
|
|
|
|
|
arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
|
|
|
|
env HOME="$home_in_target" bash -c \
|
|
|
|
@@ -1058,7 +1082,7 @@ install_end4_dots() {
|
|
|
|
|
< <(yes i) >>"$LOG"; then
|
|
|
|
|
warn "end-4 setup failed, retrying files only (see $LOG)."
|
|
|
|
|
setups_done=false
|
|
|
|
|
gum spin --show-output --spinner jump -- \
|
|
|
|
|
gum spin --show-output -s dot -- \
|
|
|
|
|
sh -c '"$@" 2>&1' sh \
|
|
|
|
|
arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
|
|
|
|
env HOME="$home_in_target" bash -c \
|
|
|
|
@@ -1083,13 +1107,13 @@ install_end4_dots() {
|
|
|
|
|
local mods_conf="${TARGET}/etc/modules-load.d/i2c-dev.conf"
|
|
|
|
|
if [[ ! -f "$mods_conf" ]]; then
|
|
|
|
|
mkdir -p "$(dirname "$mods_conf")"
|
|
|
|
|
echo i2c-dev > "$mods_conf"
|
|
|
|
|
echo i2c-dev >"$mods_conf"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if [[ -f "${TARGET}/usr/lib/systemd/system/bluetooth.service" ]]; then
|
|
|
|
|
run_logged "Could not enable bluetooth.service" \
|
|
|
|
|
arch-chroot "$TARGET" systemctl enable bluetooth.service \
|
|
|
|
|
|| warn "Could not enable bluetooth.service (output above)."
|
|
|
|
|
arch-chroot "$TARGET" systemctl enable bluetooth.service ||
|
|
|
|
|
warn "Could not enable bluetooth.service (output above)."
|
|
|
|
|
fi
|
|
|
|
|
# `systemctl --user` cannot work in this chroot either: it needs
|
|
|
|
|
# /run/user/UID/bus, which does not exist until the target boots (it only
|
|
|
|
@@ -1101,10 +1125,10 @@ install_end4_dots() {
|
|
|
|
|
# appended to $LOG and paged to the user (as the bluetooth block's
|
|
|
|
|
# run_logged does), never kept in the log alone.
|
|
|
|
|
local yd_rc=0 yd_err=""
|
|
|
|
|
yd_err=$( { mkdir -p "$yd_wants" \
|
|
|
|
|
&& ln -sfn /usr/lib/systemd/user/ydotool.service "$yd_wants/ydotool.service"; } 2>&1 ) \
|
|
|
|
|
|| yd_rc=$?
|
|
|
|
|
if (( yd_rc == 0 )); then
|
|
|
|
|
yd_err=$({ mkdir -p "$yd_wants" &&
|
|
|
|
|
ln -sfn /usr/lib/systemd/user/ydotool.service "$yd_wants/ydotool.service"; } 2>&1) ||
|
|
|
|
|
yd_rc=$?
|
|
|
|
|
if ((yd_rc == 0)); then
|
|
|
|
|
log "ydotool.service enabled for ${USERNAME} (symlinked in user unit dir)."
|
|
|
|
|
else
|
|
|
|
|
if [[ -n "$yd_err" ]]; then
|
|
|
|
@@ -1123,8 +1147,8 @@ install_end4_dots() {
|
|
|
|
|
# throwaway session bus as the target user. gsettings exits 0 even when
|
|
|
|
|
# the commit fails, so read the key back in a second session — only a
|
|
|
|
|
# matching value counts as persisted.
|
|
|
|
|
if ((log_mark > 0)) \
|
|
|
|
|
&& [[ -x "${TARGET}/usr/bin/dbus-run-session" && -x "${TARGET}/usr/bin/gsettings" ]]; then
|
|
|
|
|
if ((log_mark > 0)) &&
|
|
|
|
|
[[ -x "${TARGET}/usr/bin/dbus-run-session" && -x "${TARGET}/usr/bin/gsettings" ]]; then
|
|
|
|
|
local gs_line gs_schema gs_key gs_val gs_got gs_norm gs_raw gs_err gs_rc
|
|
|
|
|
# Snapshot and parse first (the loop below appends to $LOG), keeping
|
|
|
|
|
# log order: a key set twice replays chronologically so the last
|
|
|
|
@@ -1155,6 +1179,8 @@ install_end4_dots() {
|
|
|
|
|
gs_val=${BASH_REMATCH[3]}
|
|
|
|
|
# env -i: root's env is exactly what broke dconf; rebuild it for
|
|
|
|
|
# the target user with only what gsettings/dbus need.
|
|
|
|
|
# GIO_USE_VFS=local: otherwise the session bus activates gvfsd, whose
|
|
|
|
|
# gvfsd-fuse leaves a dead ~/.gvfs mount that breaks chown -R below.
|
|
|
|
|
# gsettings exits 0 even when the commit fails, so both the exit
|
|
|
|
|
# status AND the read-back below decide success; its output is
|
|
|
|
|
# kept in $LOG and paged on failure.
|
|
|
|
@@ -1164,16 +1190,18 @@ install_end4_dots() {
|
|
|
|
|
env -i HOME="$home_in_target" USER="$USERNAME" LOGNAME="$USERNAME" \
|
|
|
|
|
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin \
|
|
|
|
|
XDG_CONFIG_HOME="${home_in_target}/.config" \
|
|
|
|
|
GIO_USE_VFS=local GVFS_DISABLE_FUSE=1 \
|
|
|
|
|
dbus-run-session -- \
|
|
|
|
|
gsettings set "$gs_schema" "$gs_key" "$gs_val" 2>&1) || gs_rc=$?
|
|
|
|
|
if [[ -n "$gs_err" ]]; then
|
|
|
|
|
printf '%s\n' "$gs_err" >>"$LOG"
|
|
|
|
|
fi
|
|
|
|
|
if (( gs_rc == 0 )); then
|
|
|
|
|
if ((gs_rc == 0)); then
|
|
|
|
|
gs_got=$(arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
|
|
|
|
env -i HOME="$home_in_target" USER="$USERNAME" LOGNAME="$USERNAME" \
|
|
|
|
|
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin \
|
|
|
|
|
XDG_CONFIG_HOME="${home_in_target}/.config" \
|
|
|
|
|
GIO_USE_VFS=local GVFS_DISABLE_FUSE=1 \
|
|
|
|
|
dbus-run-session -- \
|
|
|
|
|
gsettings get "$gs_schema" "$gs_key" 2>>"$LOG") || gs_got=""
|
|
|
|
|
# Normalize the GVariant read-back: strings come back quoted
|
|
|
|
@@ -1249,15 +1277,15 @@ install_end4_dots() {
|
|
|
|
|
"$fish_src/" "$fish_dst/"
|
|
|
|
|
if [[ -f "${fish_dst}/config.fish" ]]; then
|
|
|
|
|
sed -i "s/alias q 'qs -c ii'/alias q 'qs -c end4-pC'/" "${fish_dst}/config.fish"
|
|
|
|
|
grep -qF "alias q 'qs -c end4-pC'" "${fish_dst}/config.fish" \
|
|
|
|
|
|| warn "fish 'q' alias still points at the upstream ii config (see $LOG)."
|
|
|
|
|
grep -qF "alias q 'qs -c end4-pC'" "${fish_dst}/config.fish" ||
|
|
|
|
|
warn "fish 'q' alias still points at the upstream ii config (see $LOG)."
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
local configs_ok=true
|
|
|
|
|
[[ -e "${TARGET}${home_in_target}/.config/hypr/hyprland.lua" \
|
|
|
|
|
&& -d "${TARGET}${home_in_target}/.config/quickshell/ii" \
|
|
|
|
|
&& -d "${TARGET}${home_in_target}/.config/quickshell/end4-pC" ]] || configs_ok=false
|
|
|
|
|
[[ -e "${TARGET}${home_in_target}/.config/hypr/hyprland.lua" &&
|
|
|
|
|
-d "${TARGET}${home_in_target}/.config/quickshell/ii" &&
|
|
|
|
|
-d "${TARGET}${home_in_target}/.config/quickshell/end4-pC" ]] || configs_ok=false
|
|
|
|
|
# Files alone are not enough: with --skip-alldeps the dependencies must
|
|
|
|
|
# have come from the ISO. hyprland and quickshell are installed at ISO
|
|
|
|
|
# build time, so their binaries missing here means pacstrap dropped them
|
|
|
|
@@ -1265,26 +1293,15 @@ install_end4_dots() {
|
|
|
|
|
# the image's uv cache at install time, so a missing python means the
|
|
|
|
|
# python step of end-4's setup never got to run.
|
|
|
|
|
if $configs_ok; then
|
|
|
|
|
[[ -x "${TARGET}/usr/bin/hyprctl" && -x "${TARGET}/usr/bin/quickshell" ]] \
|
|
|
|
|
|| configs_ok=false
|
|
|
|
|
[[ -x "${TARGET}/usr/bin/hyprctl" && -x "${TARGET}/usr/bin/quickshell" ]] ||
|
|
|
|
|
configs_ok=false
|
|
|
|
|
|
|
|
|
|
# uv writes bin/python as an absolute symlink into the chroot
|
|
|
|
|
# (/home/.../uv/python/...). Tested verbatim from the live medium
|
|
|
|
|
# that link dangles - /home/$USERNAME exists only under $TARGET -
|
|
|
|
|
# so walk the chain here and re-root absolute hops under $TARGET
|
|
|
|
|
# (relative hops stay inside the venv and need no repointing).
|
|
|
|
|
local venv_probe="${TARGET}${home_in_target}/.local/state/quickshell/.venv/bin/python"
|
|
|
|
|
local venv_link hops=0
|
|
|
|
|
while [[ -L "$venv_probe" && $hops -lt 10 ]]; do
|
|
|
|
|
venv_link=$(readlink "$venv_probe")
|
|
|
|
|
if [[ "$venv_link" == /* ]]; then
|
|
|
|
|
venv_probe="${TARGET}${venv_link}"
|
|
|
|
|
else
|
|
|
|
|
venv_probe="$(dirname "$venv_probe")/$venv_link"
|
|
|
|
|
fi
|
|
|
|
|
hops=$((hops + 1))
|
|
|
|
|
done
|
|
|
|
|
[[ -x "$venv_probe" ]] || configs_ok=false
|
|
|
|
|
# uv's bin/python is a chain of absolute symlinks (the venv link AND
|
|
|
|
|
# uv's cpython-3.12-... dir link) into /home/$USERNAME, which exists
|
|
|
|
|
# only under $TARGET - resolve and run it inside the chroot instead.
|
|
|
|
|
arch-chroot "$TARGET" \
|
|
|
|
|
"${home_in_target}/.local/state/quickshell/.venv/bin/python" -B -c '' \
|
|
|
|
|
>>"$LOG" 2>&1 || configs_ok=false
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if [[ -d /etc/skel/.config/hypr/custom ]]; then
|
|
|
|
@@ -1304,6 +1321,19 @@ install_end4_dots() {
|
|
|
|
|
# fork/fish/hypr restores, and the presets sed -i just rewrote through a
|
|
|
|
|
# temp file) left root-owned files in the home — one pass fixes them all.
|
|
|
|
|
# Numeric ids again: this chown also runs on the medium, not in the chroot.
|
|
|
|
|
# A dead FUSE mount (gvfs ~/.gvfs, kio-fuse, ...) makes chown -R fail with
|
|
|
|
|
# EACCES even as root; unmount everything under the home first. The home
|
|
|
|
|
# is no mount point itself, so filter the full mount table by prefix
|
|
|
|
|
# (findmnt -R would match nothing); sort -r unmounts deepest first. The
|
|
|
|
|
# || true keeps an empty match from tripping the inherited ERR trap,
|
|
|
|
|
# whose message would otherwise be read as a mount path.
|
|
|
|
|
local mnt
|
|
|
|
|
while read -r mnt; do
|
|
|
|
|
umount "$mnt" 2>/dev/null || umount -l "$mnt" 2>/dev/null || true
|
|
|
|
|
log "unmounted stray mount in home: $mnt"
|
|
|
|
|
done < <(findmnt -rno TARGET 2>/dev/null |
|
|
|
|
|
awk -v p="${TARGET}${home_in_target}/" 'index($0, p) == 1' | sort -r || true)
|
|
|
|
|
rmdir "${TARGET}${home_in_target}/.gvfs" 2>/dev/null || true
|
|
|
|
|
chown -R "$owner" "${TARGET}${home_in_target}"
|
|
|
|
|
|
|
|
|
|
if $dots_ok && $configs_ok; then
|
|
|
|
@@ -1322,7 +1352,7 @@ install_end4_dots() {
|
|
|
|
|
# is precisely the failure mode the fallback exists to undo.
|
|
|
|
|
|
|
|
|
|
write_mode_a_hooks() {
|
|
|
|
|
cat > "${TARGET}/etc/mkinitcpio.conf.d/alvin.conf" <<'HOOKS'
|
|
|
|
|
cat >"${TARGET}/etc/mkinitcpio.conf.d/alvin.conf" <<'HOOKS'
|
|
|
|
|
HOOKS=(base systemd autodetect microcode modconf kms keyboard sd-vconsole block sd-encrypt filesystems fsck)
|
|
|
|
|
HOOKS
|
|
|
|
|
}
|
|
|
|
@@ -1340,7 +1370,9 @@ install_mkinitcpio() {
|
|
|
|
|
info "Generating initramfs configuration..."
|
|
|
|
|
|
|
|
|
|
if [[ "$LUKS" != "true" ]]; then
|
|
|
|
|
arch-chroot "$TARGET" mkinitcpio -P
|
|
|
|
|
run_logged -s dot -t "Generating initramfs" "mkinitcpio -P failed" \
|
|
|
|
|
arch-chroot "$TARGET" mkinitcpio -P ||
|
|
|
|
|
die "mkinitcpio -P failed (output above, full log: $LOG)."
|
|
|
|
|
ok "initramfs generated."
|
|
|
|
|
return
|
|
|
|
|
fi
|
|
|
|
@@ -1363,7 +1395,7 @@ install_mkinitcpio() {
|
|
|
|
|
# install_unl0kr: the package must exist first, and grub-mkconfig
|
|
|
|
|
# needs the final image for its initrd line. The two hook families
|
|
|
|
|
# must never be mixed in one HOOKS list.
|
|
|
|
|
cat > "${TARGET}/etc/mkinitcpio.conf.d/alvin.conf" <<'HOOKS'
|
|
|
|
|
cat >"${TARGET}/etc/mkinitcpio.conf.d/alvin.conf" <<'HOOKS'
|
|
|
|
|
HOOKS=(base udev autodetect microcode modconf kms keyboard keymap consolefont block unl0kr encrypt filesystems fsck)
|
|
|
|
|
HOOKS
|
|
|
|
|
log "mkinitcpio: Mode B (unl0kr) HOOKS written; build deferred to install_unl0kr"
|
|
|
|
@@ -1374,7 +1406,9 @@ HOOKS
|
|
|
|
|
# Mode A: systemd initramfs with sd-encrypt (matches the rd.luks.name
|
|
|
|
|
# kernel cmdline written by install_bootloader).
|
|
|
|
|
write_mode_a_hooks
|
|
|
|
|
arch-chroot "$TARGET" mkinitcpio -P
|
|
|
|
|
run_logged -s dot -t "Generating initramfs" "mkinitcpio -P failed" \
|
|
|
|
|
arch-chroot "$TARGET" mkinitcpio -P ||
|
|
|
|
|
die "mkinitcpio -P failed (output above, full log: $LOG)."
|
|
|
|
|
|
|
|
|
|
ok "initramfs generated."
|
|
|
|
|
}
|
|
|
|
@@ -1403,7 +1437,7 @@ install_bootloader() {
|
|
|
|
|
sed -i 's/^GRUB_DISABLE_BOOTNEXT=.*/GRUB_DISABLE_BOOTNEXT=true/' \
|
|
|
|
|
"${TARGET}/etc/default/grub"
|
|
|
|
|
else
|
|
|
|
|
printf '\nGRUB_DISABLE_BOOTNEXT=true\n' >> "${TARGET}/etc/default/grub"
|
|
|
|
|
printf '\nGRUB_DISABLE_BOOTNEXT=true\n' >>"${TARGET}/etc/default/grub"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Two installs, both verified - this is the step whose failure strands a
|
|
|
|
@@ -1465,8 +1499,8 @@ install_bootloader() {
|
|
|
|
|
esp_partnum=${esp_partnum:-1}
|
|
|
|
|
# Match the loader path, not just the label: a stale ALVIN entry left by
|
|
|
|
|
# a previous install on another disk would satisfy a bare label match.
|
|
|
|
|
boot_entry=$(efibootmgr -v 2>/dev/null \
|
|
|
|
|
| grep -iF '\EFI\ALVIN\grubx64.efi' | head -n1) || boot_entry=""
|
|
|
|
|
boot_entry=$(efibootmgr -v 2>/dev/null |
|
|
|
|
|
grep -iF '\EFI\ALVIN\grubx64.efi' | head -n1) || boot_entry=""
|
|
|
|
|
if [[ -z "$boot_entry" ]]; then
|
|
|
|
|
warn "No ALVIN entry in the firmware NVRAM - creating one now."
|
|
|
|
|
if ! run_logged "efibootmgr could not create the NVRAM boot entry" \
|
|
|
|
@@ -1474,8 +1508,8 @@ install_bootloader() {
|
|
|
|
|
-l '\EFI\ALVIN\grubx64.efi'; then
|
|
|
|
|
die "Could not create the firmware NVRAM boot entry (output above). Refusing to finish: the install stops before grub-mkconfig runs, so \\EFI\\BOOT\\BOOTX64.EFI exists but is not bootable yet. Check UEFI setup (NVRAM write access) and re-run."
|
|
|
|
|
fi
|
|
|
|
|
boot_entry=$(efibootmgr -v 2>/dev/null \
|
|
|
|
|
| grep -iF '\EFI\ALVIN\grubx64.efi' | head -n1) || boot_entry=""
|
|
|
|
|
boot_entry=$(efibootmgr -v 2>/dev/null |
|
|
|
|
|
grep -iF '\EFI\ALVIN\grubx64.efi' | head -n1) || boot_entry=""
|
|
|
|
|
if [[ -z "$boot_entry" ]]; then
|
|
|
|
|
die "NVRAM boot entry still missing after efibootmgr -c. Refusing to finish: the install stops before grub-mkconfig runs, so the system is not bootable yet - check UEFI setup and re-run."
|
|
|
|
|
fi
|
|
|
|
@@ -1560,11 +1594,11 @@ install_tpm() {
|
|
|
|
|
# after, so the secret exists only for the duration of this call.
|
|
|
|
|
export PASSWORD="$PASS"
|
|
|
|
|
local cenrc=0
|
|
|
|
|
run_logged "systemd-cryptenroll failed" \
|
|
|
|
|
run_logged -s dot -t "Enrolling the TPM2 unlock key" "systemd-cryptenroll failed" \
|
|
|
|
|
systemd-cryptenroll --tpm2-device=auto \
|
|
|
|
|
--tpm2-pcrs=0+7 "$(part 2)" || cenrc=$?
|
|
|
|
|
unset PASSWORD
|
|
|
|
|
if (( cenrc == 0 )); then
|
|
|
|
|
if ((cenrc == 0)); then
|
|
|
|
|
TPM_STATUS="enrolled"
|
|
|
|
|
# Enrolment alone does nothing at boot: systemd-cryptsetup only uses
|
|
|
|
|
# the TPM2 keyslot when the cmdline asks for it. install_bootloader
|
|
|
|
@@ -1575,8 +1609,8 @@ install_tpm() {
|
|
|
|
|
luks_uuid=$(blkid -s UUID -o value "$(part 2)")
|
|
|
|
|
write_mode_a_cmdline "$luks_uuid" "rd.luks.options=${luks_uuid}=tpm2-device=auto"
|
|
|
|
|
run_logged -s dot "grub-mkconfig failed (rd.luks.options)" \
|
|
|
|
|
arch-chroot "$TARGET" grub-mkconfig -o /boot/grub/grub.cfg \
|
|
|
|
|
|| warn "grub-mkconfig failed after adding rd.luks.options (output above) - TPM2 unlock will not activate at boot."
|
|
|
|
|
arch-chroot "$TARGET" grub-mkconfig -o /boot/grub/grub.cfg ||
|
|
|
|
|
warn "grub-mkconfig failed after adding rd.luks.options (output above) - TPM2 unlock will not activate at boot."
|
|
|
|
|
ok "TPM2 auto-unlock enrolled and activated (rd.luks.options, PCR 0+7)."
|
|
|
|
|
else
|
|
|
|
|
TPM_STATUS="failed"
|
|
|
|
@@ -1621,11 +1655,11 @@ unl0kr_fallback_mode_a() {
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
run_logged -s dot "mkinitcpio -P failed (unl0kr fallback)" \
|
|
|
|
|
arch-chroot "$TARGET" mkinitcpio -P \
|
|
|
|
|
|| warn "mkinitcpio -P failed during unl0kr fallback (output above)."
|
|
|
|
|
arch-chroot "$TARGET" mkinitcpio -P ||
|
|
|
|
|
warn "mkinitcpio -P failed during unl0kr fallback (output above)."
|
|
|
|
|
run_logged -s dot "grub-mkconfig failed (unl0kr fallback)" \
|
|
|
|
|
arch-chroot "$TARGET" grub-mkconfig -o /boot/grub/grub.cfg \
|
|
|
|
|
|| warn "grub-mkconfig failed during unl0kr fallback (output above)."
|
|
|
|
|
arch-chroot "$TARGET" grub-mkconfig -o /boot/grub/grub.cfg ||
|
|
|
|
|
warn "grub-mkconfig failed during unl0kr fallback (output above)."
|
|
|
|
|
warn "The system will ask for the LUKS passphrase at boot - test a reboot before relying on it."
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -1639,15 +1673,15 @@ install_unl0kr() {
|
|
|
|
|
local build_rc=0
|
|
|
|
|
|
|
|
|
|
# yay runs as the new user and needs passwordless sudo for this one call.
|
|
|
|
|
echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" > "${TARGET}/etc/sudoers.d/99-unl0kr-install"
|
|
|
|
|
echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" >"${TARGET}/etc/sudoers.d/99-unl0kr-install"
|
|
|
|
|
chmod 440 "${TARGET}/etc/sudoers.d/99-unl0kr-install"
|
|
|
|
|
run_logged -s jump -t "Building unl0kr from the AUR (this may take a while)" \
|
|
|
|
|
run_logged -s dot -t "Building unl0kr from the AUR (this may take a while)" \
|
|
|
|
|
"yay failed to build/install unl0kr" \
|
|
|
|
|
arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
|
|
|
|
env HOME="$home_in_target" yay -S --noconfirm unl0kr || build_rc=$?
|
|
|
|
|
rm -f "${TARGET}/etc/sudoers.d/99-unl0kr-install"
|
|
|
|
|
|
|
|
|
|
if (( build_rc != 0 )); then
|
|
|
|
|
if ((build_rc != 0)); then
|
|
|
|
|
unl0kr_fallback_mode_a "yay failed to build it (exit ${build_rc})"
|
|
|
|
|
return 0
|
|
|
|
|
fi
|
|
|
|
@@ -1655,8 +1689,8 @@ install_unl0kr() {
|
|
|
|
|
# Record what the AUR actually gave us - advertised and PKGBUILD versions
|
|
|
|
|
# have disagreed before, so trust pacman, not the web page.
|
|
|
|
|
local built_ver
|
|
|
|
|
built_ver=$(arch-chroot "$TARGET" pacman -Qi unl0kr 2>/dev/null \
|
|
|
|
|
| awk -F': ' '/^Version/{print $2}') || built_ver=""
|
|
|
|
|
built_ver=$(arch-chroot "$TARGET" pacman -Qi unl0kr 2>/dev/null |
|
|
|
|
|
awk -F': ' '/^Version/{print $2}') || built_ver=""
|
|
|
|
|
log "unl0kr built version: ${built_ver:-unknown}"
|
|
|
|
|
|
|
|
|
|
# The package must ship busybox initcpio hooks (its prepare() strips the
|
|
|
|
@@ -1703,14 +1737,14 @@ install_unl0kr() {
|
|
|
|
|
break
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
if (( ok_all )); then
|
|
|
|
|
if ((ok_all)); then
|
|
|
|
|
keep+=("$m")
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
log "unl0kr MODULES: ${keep[*]:-none}"
|
|
|
|
|
if ((${#keep[@]} > 0)); then
|
|
|
|
|
printf 'MODULES=(%s)\n' "${keep[*]}" \
|
|
|
|
|
>> "${TARGET}/etc/mkinitcpio.conf.d/alvin.conf"
|
|
|
|
|
>>"${TARGET}/etc/mkinitcpio.conf.d/alvin.conf"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
info "Rebuilding initramfs with unl0kr..."
|
|
|
|
@@ -1734,7 +1768,7 @@ install_unl0kr() {
|
|
|
|
|
unl0kr_fallback_mode_a "lsinitcpio could not read the image"
|
|
|
|
|
return 0
|
|
|
|
|
fi
|
|
|
|
|
if ! grep -qw 'unl0kr' <<< "$analysis"; then
|
|
|
|
|
if ! grep -qw 'unl0kr' <<<"$analysis"; then
|
|
|
|
|
unl0kr_fallback_mode_a "unl0kr hook missing from initramfs"
|
|
|
|
|
return 0
|
|
|
|
|
fi
|
|
|
|
@@ -1746,21 +1780,21 @@ install_unl0kr() {
|
|
|
|
|
# perfectly good unl0kr install on every run.
|
|
|
|
|
local -a expect=() missing=()
|
|
|
|
|
local linux_kver mi m_norm mods_section
|
|
|
|
|
linux_kver=$(arch-chroot "$TARGET" pacman -Ql linux 2>/dev/null \
|
|
|
|
|
| sed -n 's|.* /usr/lib/modules/\([^/]*\)/modules\.builtin.*|\1|p' \
|
|
|
|
|
| head -n1) || linux_kver=""
|
|
|
|
|
linux_kver=$(arch-chroot "$TARGET" pacman -Ql linux 2>/dev/null |
|
|
|
|
|
sed -n 's|.* /usr/lib/modules/\([^/]*\)/modules\.builtin.*|\1|p' |
|
|
|
|
|
head -n1) || linux_kver=""
|
|
|
|
|
for m in "${keep[@]+"${keep[@]}"}"; do
|
|
|
|
|
mi=$(arch-chroot "$TARGET" modinfo -k "$linux_kver" "$m" 2>/dev/null || true)
|
|
|
|
|
if ! grep -q '(builtin)' <<< "$mi"; then
|
|
|
|
|
if ! grep -q '(builtin)' <<<"$mi"; then
|
|
|
|
|
expect+=("$m")
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
log "unl0kr image check: kernel=${linux_kver:-unknown} expect=${expect[*]:-none}"
|
|
|
|
|
mods_section=$(awk '/==> Included modules/{f=1;next} f&&/^==>/{exit} f' <<< "$analysis" \
|
|
|
|
|
| tr -s ' \t' '\n' | tr '_' '-')
|
|
|
|
|
mods_section=$(awk '/==> Included modules/{f=1;next} f&&/^==>/{exit} f' <<<"$analysis" |
|
|
|
|
|
tr -s ' \t' '\n' | tr '_' '-')
|
|
|
|
|
for m in "${expect[@]+"${expect[@]}"}"; do
|
|
|
|
|
m_norm=${m//_/-}
|
|
|
|
|
grep -qxF -- "$m_norm" <<< "$mods_section" || missing+=("$m")
|
|
|
|
|
grep -qxF -- "$m_norm" <<<"$mods_section" || missing+=("$m")
|
|
|
|
|
done
|
|
|
|
|
if ((${#missing[@]} > 0)); then
|
|
|
|
|
unl0kr_fallback_mode_a "modules missing from initramfs: ${missing[*]}"
|
|
|
|
@@ -1790,7 +1824,7 @@ ensure_hyprland_session() {
|
|
|
|
|
return 0
|
|
|
|
|
fi
|
|
|
|
|
mkdir -p "$(dirname "$desktop")"
|
|
|
|
|
cat > "$desktop" <<'DESKTOP'
|
|
|
|
|
cat >"$desktop" <<'DESKTOP'
|
|
|
|
|
[Desktop Entry]
|
|
|
|
|
Name=Hyprland
|
|
|
|
|
Comment=Hyprland wayland session
|
|
|
|
@@ -1807,8 +1841,8 @@ install_none_profile() {
|
|
|
|
|
owner=$(target_owner)
|
|
|
|
|
|
|
|
|
|
local shell_name
|
|
|
|
|
shell_name=$(arch-chroot "$TARGET" getent passwd "$USERNAME" \
|
|
|
|
|
| awk -F: '{print $NF}') || shell_name="/bin/bash"
|
|
|
|
|
shell_name=$(arch-chroot "$TARGET" getent passwd "$USERNAME" |
|
|
|
|
|
awk -F: '{print $NF}') || shell_name="/bin/bash"
|
|
|
|
|
|
|
|
|
|
local profile=""
|
|
|
|
|
case "$shell_name" in
|
|
|
|
@@ -1826,7 +1860,7 @@ install_none_profile() {
|
|
|
|
|
return 0
|
|
|
|
|
fi
|
|
|
|
|
mkdir -p "${fish_cfg}/conf.d"
|
|
|
|
|
cat > "${fish_cfg}/conf.d/alvin-hyprland.fish" <<'FISH'
|
|
|
|
|
cat >"${fish_cfg}/conf.d/alvin-hyprland.fish" <<'FISH'
|
|
|
|
|
# alvin-install: start Hyprland on tty1 (pam_systemd sets XDG_VTNR).
|
|
|
|
|
if status is-login; and test -z "$WAYLAND_DISPLAY"; and test "$XDG_VTNR" = 1
|
|
|
|
|
exec Hyprland
|
|
|
|
@@ -1838,8 +1872,8 @@ FISH
|
|
|
|
|
return 0
|
|
|
|
|
;;
|
|
|
|
|
*)
|
|
|
|
|
if [[ -f "${TARGET}${home_in_target}/.bash_profile" \
|
|
|
|
|
|| ! -f "${TARGET}${home_in_target}/.profile" ]]; then
|
|
|
|
|
if [[ -f "${TARGET}${home_in_target}/.bash_profile" ||
|
|
|
|
|
! -f "${TARGET}${home_in_target}/.profile" ]]; then
|
|
|
|
|
profile="${home_in_target}/.bash_profile"
|
|
|
|
|
else
|
|
|
|
|
profile="${home_in_target}/.profile"
|
|
|
|
@@ -1863,7 +1897,7 @@ FISH
|
|
|
|
|
echo "if [[ -z \"\${WAYLAND_DISPLAY:-}\" && \"\${XDG_VTNR:-}\" == \"1\" ]]; then"
|
|
|
|
|
echo " exec Hyprland"
|
|
|
|
|
echo "fi"
|
|
|
|
|
} >> "${TARGET}${profile}"
|
|
|
|
|
} >>"${TARGET}${profile}"
|
|
|
|
|
|
|
|
|
|
chown "$owner" "${TARGET}${profile}" 2>/dev/null || true
|
|
|
|
|
ok "tty login will start Hyprland via ${profile}."
|
|
|
|
@@ -1879,8 +1913,8 @@ install_login_manager() {
|
|
|
|
|
for u in greetd sddm gdm; do
|
|
|
|
|
if [[ -f "${TARGET}/usr/lib/systemd/system/${u}.service" ]]; then
|
|
|
|
|
run_logged "Could not disable ${u}.service" \
|
|
|
|
|
arch-chroot "$TARGET" systemctl disable "$u.service" \
|
|
|
|
|
|| warn "Could not disable ${u}.service (output above)."
|
|
|
|
|
arch-chroot "$TARGET" systemctl disable "$u.service" ||
|
|
|
|
|
warn "Could not disable ${u}.service (output above)."
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
rm -f "${TARGET}/etc/systemd/system/getty@tty1.service.d/autologin.conf"
|
|
|
|
@@ -1910,11 +1944,11 @@ install_login_manager() {
|
|
|
|
|
# this version does not (ArchWiki: user = "greeter").
|
|
|
|
|
if ! arch-chroot "$TARGET" id greeter >/dev/null 2>&1; then
|
|
|
|
|
run_logged "Could not create the greeter user" \
|
|
|
|
|
arch-chroot "$TARGET" useradd -r -M -s /usr/bin/nologin greeter \
|
|
|
|
|
|| warn "Could not create the greeter user (output above)."
|
|
|
|
|
arch-chroot "$TARGET" useradd -r -M -s /usr/bin/nologin greeter ||
|
|
|
|
|
warn "Could not create the greeter user (output above)."
|
|
|
|
|
fi
|
|
|
|
|
mkdir -p "${TARGET}/etc/greetd"
|
|
|
|
|
cat > "${TARGET}/etc/greetd/config.toml" <<'TOML'
|
|
|
|
|
cat >"${TARGET}/etc/greetd/config.toml" <<'TOML'
|
|
|
|
|
[terminal]
|
|
|
|
|
vt = 1
|
|
|
|
|
|
|
|
|
@@ -2032,7 +2066,7 @@ install_autologin() {
|
|
|
|
|
local dropin="${TARGET}/etc/systemd/system/getty@tty1.service.d/autologin.conf"
|
|
|
|
|
if [[ "$AUTOLOGIN" == "enabled" ]]; then
|
|
|
|
|
mkdir -p "$(dirname "$dropin")"
|
|
|
|
|
cat > "$dropin" <<EOF
|
|
|
|
|
cat >"$dropin" <<EOF
|
|
|
|
|
[Service]
|
|
|
|
|
ExecStart=
|
|
|
|
|
ExecStart=-/usr/bin/agetty --autologin ${USERNAME} --noclear %I \$TERM
|
|
|
|
@@ -2045,7 +2079,7 @@ EOF
|
|
|
|
|
greetd)
|
|
|
|
|
# Whole-file rewrite makes both directions idempotent.
|
|
|
|
|
if [[ "$AUTOLOGIN" == "enabled" ]]; then
|
|
|
|
|
cat > "${TARGET}/etc/greetd/config.toml" <<EOF
|
|
|
|
|
cat >"${TARGET}/etc/greetd/config.toml" <<EOF
|
|
|
|
|
[terminal]
|
|
|
|
|
vt = 1
|
|
|
|
|
|
|
|
|
@@ -2059,7 +2093,7 @@ command = "Hyprland"
|
|
|
|
|
EOF
|
|
|
|
|
log "Autologin: greetd [initial_session] enabled"
|
|
|
|
|
else
|
|
|
|
|
cat > "${TARGET}/etc/greetd/config.toml" <<'TOML'
|
|
|
|
|
cat >"${TARGET}/etc/greetd/config.toml" <<'TOML'
|
|
|
|
|
[terminal]
|
|
|
|
|
vt = 1
|
|
|
|
|
|
|
|
|
@@ -2073,7 +2107,7 @@ TOML
|
|
|
|
|
local sddm_auto="${TARGET}/etc/sddm.conf.d/autologin.conf"
|
|
|
|
|
if [[ "$AUTOLOGIN" == "enabled" ]]; then
|
|
|
|
|
mkdir -p "$(dirname "$sddm_auto")"
|
|
|
|
|
cat > "$sddm_auto" <<EOF
|
|
|
|
|
cat >"$sddm_auto" <<EOF
|
|
|
|
|
[Autologin]
|
|
|
|
|
User=${USERNAME}
|
|
|
|
|
Session=hyprland.desktop
|
|
|
|
@@ -2087,7 +2121,7 @@ EOF
|
|
|
|
|
local gdm_conf="${TARGET}/etc/gdm/custom.conf"
|
|
|
|
|
if [[ "$AUTOLOGIN" == "enabled" ]]; then
|
|
|
|
|
mkdir -p "$(dirname "$gdm_conf")"
|
|
|
|
|
cat > "$gdm_conf" <<EOF
|
|
|
|
|
cat >"$gdm_conf" <<EOF
|
|
|
|
|
[daemon]
|
|
|
|
|
AutomaticLoginEnable=true
|
|
|
|
|
AutomaticLogin=${USERNAME}
|
|
|
|
@@ -2122,6 +2156,53 @@ EOF
|
|
|
|
|
|
|
|
|
|
# ── Cleanup ───────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
# Last install step: the system is already bootable, so every failure here
|
|
|
|
|
# only warns. Re-ranks the Arch mirrorlist (the shipped one is whatever the
|
|
|
|
|
# ISO build had), refreshes the keyrings first so newer packages' signatures
|
|
|
|
|
# verify, then -Syu. That also moves packages pacstrap took from [core] onto
|
|
|
|
|
# the [cachyos] builds the target's pacman.conf now prefers. A new kernel
|
|
|
|
|
# triggers the mkinitcpio hook with the final HOOKS, and grub.cfg names
|
|
|
|
|
# /vmlinuz-linux etc. without versions, so the bootloader stays valid.
|
|
|
|
|
install_update() {
|
|
|
|
|
info "Updating mirrors and the installed system..."
|
|
|
|
|
sync_target_dbs
|
|
|
|
|
|
|
|
|
|
local ml="${TARGET}/etc/pacman.d/mirrorlist"
|
|
|
|
|
if [[ -x "${TARGET}/usr/bin/reflector" ]]; then
|
|
|
|
|
cp -a "$ml" "${ml}.alvin-bak" 2>/dev/null || true
|
|
|
|
|
if run_logged -s dot -t "Ranking Arch mirrors (reflector)" "reflector failed" \
|
|
|
|
|
arch-chroot "$TARGET" reflector --protocol https --age 12 --latest 20 \
|
|
|
|
|
--sort rate --save /etc/pacman.d/mirrorlist; then
|
|
|
|
|
log "Update: mirrorlist re-ranked by reflector (backup: mirrorlist.alvin-bak)"
|
|
|
|
|
else
|
|
|
|
|
[[ -s "${ml}.alvin-bak" ]] && cp -a "${ml}.alvin-bak" "$ml"
|
|
|
|
|
warn "reflector failed (output above) - keeping the shipped mirrorlist."
|
|
|
|
|
fi
|
|
|
|
|
else
|
|
|
|
|
log "Update: reflector not installed in target - mirrorlist left as shipped"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
local -a keyrings=()
|
|
|
|
|
mapfile -t keyrings < <(arch-chroot "$TARGET" pacman -Qq \
|
|
|
|
|
archlinux-keyring cachyos-keyring chaotic-keyring 2>/dev/null || true)
|
|
|
|
|
if ((${#keyrings[@]} > 0)); then
|
|
|
|
|
run_logged -s dot -t "Updating keyrings" "keyring update failed" \
|
|
|
|
|
arch-chroot "$TARGET" pacman -Sy --needed --noconfirm "${keyrings[@]}" ||
|
|
|
|
|
warn "Keyring update failed (output above) - trying the upgrade anyway."
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if run_logged -s dot -t "Updating the installed system (pacman -Syu)" \
|
|
|
|
|
"pacman -Syu failed" \
|
|
|
|
|
arch-chroot "$TARGET" pacman -Syu --noconfirm; then
|
|
|
|
|
UPDATE_STATUS="up to date"
|
|
|
|
|
log "Update: pacman -Syu complete"
|
|
|
|
|
ok "Installed system is up to date."
|
|
|
|
|
else
|
|
|
|
|
UPDATE_STATUS="FAILED - run sudo pacman -Syu after boot"
|
|
|
|
|
warn "pacman -Syu failed (output above) - the system boots; run 'sudo pacman -Syu' after first login."
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
install_cleanup() {
|
|
|
|
|
info "Cleaning up..."
|
|
|
|
|
cp "$LOG" "${TARGET}/var/log/alvin-install.log" 2>/dev/null || true
|
|
|
|
@@ -2143,7 +2224,7 @@ install_cleanup() {
|
|
|
|
|
"Arch Linux installed successfully." \
|
|
|
|
|
"" \
|
|
|
|
|
"Login manager: ${LOGIN_MANAGER} Autologin: ${AUTOLOGIN:-disabled}" \
|
|
|
|
|
"SSH server: ${ssh_state}" \
|
|
|
|
|
"SSH server: ${ssh_state} System update: ${UPDATE_STATUS}" \
|
|
|
|
|
"TPM2 auto-unlock: ${TPM_STATUS:-n/a} unl0kr: ${UNL0KR}"
|
|
|
|
|
if [[ -n "${TPM_DISCLAIMER}" ]]; then
|
|
|
|
|
echo ""
|
|
|
|
@@ -2207,6 +2288,7 @@ main() {
|
|
|
|
|
install_unl0kr
|
|
|
|
|
install_login_manager
|
|
|
|
|
install_autologin
|
|
|
|
|
install_update
|
|
|
|
|
install_cleanup
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|