Phase 4: the optional application categories, and the Firefox chrome. Everything Panama installed until now was what every machine gets, which meant a work laptop acquired emulators and a desktop that wanted Steam had to be told about it by hand. The interview now offers the categories in setup/packages/extras/ as a checklist -- gaming, creative, communication, virtualization -- and nothing is preselected, because a default here installs applications nobody chose on a machine whose owner answered a question they thought was about something else. A category is one file, and a category mixes both package managers because the applications do: Steam is in RPM Fusion, Slack publishes only a flatpak. So a bare line is a dnf package and a flatpak: line is a Flathub ID, and one file holds the whole answer rather than splitting each category across two. The menu is read from the directory rather than written down, so adding a category is adding a file. Every name in all four was resolved against the actual repositories before being written down, and the contract re-resolves them -- the point of admitting applications one at a time is that they stay installable, and a typo here fails on somebody else's machine, not this one. Firefox is declared, and its chrome is Edge-Frfox, vendored into config/firefox. sunhat carried that theme with no license and no attribution; it is MIT, and now it says so and says whose it is. It is the only piece of Panama's configuration that does not go to a path this repository chooses. Firefox owns the profile directory, names it with a random salt, and does not create one until the browser has been run -- so link-dotfiles finds or creates a profile and links both halves into it. Both, or neither works: chrome/ is the CSS and user.js sets the preference that makes Firefox read chrome/ at all, without which the theme is a directory of dead files. Two assumptions there were wrong, and the contract exists for both. Firefox has moved to the XDG directories -- the profile root is ~/.config/mozilla/firefox on this build, not ~/.mozilla/firefox, and writing to the wrong one themes nothing and says nothing about it. And -CreateProfile turns out to be non-interactive, so a fresh machine gets the theme on the first install rather than the second. The contract runs link-dotfiles for real against a throwaway home with no profile in it and looks at what came out; it was checked by pointing the search at the legacy path only and watching it fail. Also: the enrolment/enrollment spellings from the last commit are corrected. This repository is US-spelled everywhere else -- color 1131 times against colour never -- and consistency in prose is worth as much as it is in code. Claude-Session: https://claude.ai/code/session_01NvgBuSWB5sE43yWmg21ozj
127 lines
6.0 KiB
Bash
Executable File
127 lines
6.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# Panama's installer. Safe to re-run: every stage is idempotent, and this is
|
|
# also the upgrade path.
|
|
|
|
set -uo pipefail
|
|
|
|
PANAMA_PATH="${PANAMA_PATH:-$HOME/.local/share/Panama}"
|
|
source "$PANAMA_PATH/bin/ascii"
|
|
|
|
# ── The interview ────────────────────────────────────────────────────────────
|
|
#
|
|
# Everything Panama needs to be told is asked here, before a single package is
|
|
# installed, and nothing asks again afterwards. That is the whole bargain: the
|
|
# rest of the run takes twenty minutes and needs nobody watching it.
|
|
#
|
|
# gum is bootstrapped first because the interview is built on it and it cannot
|
|
# install itself -- it is declared in initial-packages, which install-packages
|
|
# installs, which runs after this. One small dnf call buys a real interface for
|
|
# the only part of the install a person actually interacts with.
|
|
if ! command -v gum >/dev/null 2>&1; then
|
|
echo "Installing gum, which the setup questions are built on"
|
|
sudo dnf install -y gum >/dev/null || {
|
|
echo "Could not install gum, so the setup questions cannot be asked." >&2
|
|
exit 1
|
|
}
|
|
fi
|
|
|
|
# ── Keep the machine awake for the duration ──────────────────────────────────
|
|
# Package installation takes long enough to hit an idle lock, and being locked
|
|
# out mid-transaction is unpleasant. Restored on every exit path, including
|
|
# failure and Ctrl-C, so an interrupted install does not leave the screen
|
|
# permanently awake.
|
|
cleanup() {
|
|
gsettings set org.gnome.desktop.screensaver lock-enabled true 2>/dev/null || true
|
|
gsettings set org.gnome.desktop.session idle-delay 300 2>/dev/null || true
|
|
# Deleted on every exit path, including Ctrl-C. The answers are transient by
|
|
# design, and one of them is an email address.
|
|
[[ -n "${PANAMA_ANSWERS:-}" ]] && rm -f "$PANAMA_ANSWERS"
|
|
}
|
|
trap cleanup EXIT INT TERM
|
|
|
|
gsettings set org.gnome.desktop.screensaver lock-enabled false 2>/dev/null || true
|
|
gsettings set org.gnome.desktop.session idle-delay 0 2>/dev/null || true
|
|
|
|
# ── Stages ───────────────────────────────────────────────────────────────────
|
|
# Each runs in its own process so strict-shell options and helper variables stay
|
|
# local to the script that owns them. A failing stage is reported and the rest
|
|
# still run: a missing optional package should not stop the dotfiles being
|
|
# linked. The summary at the end is what decides whether the install worked,
|
|
# because a failure scrolled past twenty minutes ago is a failure nobody saw.
|
|
#
|
|
# Explicit order, not glob order: change-settings runs `vicinae theme set`,
|
|
# which needs both vicinae itself (installed by install-packages) and the
|
|
# theme files it selects among (symlinked into place by link-dotfiles);
|
|
# setup-identity needs the gh and git-all that install-packages provides; and
|
|
# install-hardware is last because MOK enrollment arms a prompt consumed at the
|
|
# next boot and a firmware update may ask for a reboot -- a machine that reboots
|
|
# out of the final stage has already been completely configured. New scripts
|
|
# must be added here explicitly, or they will not run at all.
|
|
|
|
# The interview is not in that list, because it is the one stage whose output the
|
|
# installer reads back -- and because declining it must stop everything rather
|
|
# than be recorded as one failure among several.
|
|
#
|
|
# The answers live for exactly one run. There is no state file to go stale and
|
|
# nothing personal reaches a durable path, which is what keeps this repository
|
|
# something somebody else could clone. Created here rather than earlier so the
|
|
# trap that deletes it is already armed before the file exists.
|
|
PANAMA_ANSWERS="$(mktemp -t panama-answers.XXXXXX)"
|
|
export PANAMA_ANSWERS
|
|
|
|
if ! "$PANAMA_PATH/setup/scripts/interview"; then
|
|
exit 1
|
|
fi
|
|
# shellcheck source=/dev/null
|
|
source "$PANAMA_ANSWERS"
|
|
export PANAMA_HOSTNAME PANAMA_GIT_NAME PANAMA_GIT_EMAIL PANAMA_GIT_EDITOR \
|
|
PANAMA_GH_LOGIN PANAMA_SSH_KEY PANAMA_NVIDIA PANAMA_MOK_HASH \
|
|
PANAMA_DEBLOAT PANAMA_FIRMWARE PANAMA_EXTRAS
|
|
|
|
# Applied here rather than in a stage, and applied early: it needs sudo, and
|
|
# sudo is warm right now. At the end of a long unattended run the timestamp has
|
|
# expired, and a password prompt then is exactly the interruption the interview
|
|
# exists to prevent.
|
|
if [[ -n "${PANAMA_HOSTNAME:-}" ]]; then
|
|
sudo hostnamectl set-hostname "$PANAMA_HOSTNAME"
|
|
echo "Hostname set to: $(hostname)"
|
|
fi
|
|
|
|
STAGES=(install-packages link-dotfiles change-settings link-vicinae-scripts setup-identity install-hardware)
|
|
failed=()
|
|
for stage in "${STAGES[@]}"; do
|
|
script="$PANAMA_PATH/setup/scripts/$stage"
|
|
[[ -x "$script" ]] || continue
|
|
printf '\n=== %s ===\n' "$stage"
|
|
if ! "$script"; then
|
|
failed+=("$stage")
|
|
printf '!!! %s failed\n' "$stage" >&2
|
|
fi
|
|
done
|
|
|
|
# ── Did it actually work? ────────────────────────────────────────────────────
|
|
#
|
|
# A failed-stage count only reports what exited non-zero. It says nothing about a
|
|
# service that did not start or a font that did not land, and those are the
|
|
# failures that survive an install unnoticed. Doctor answers the question the
|
|
# stage list cannot.
|
|
#
|
|
# It never changes the exit code. On a fresh machine it legitimately reports
|
|
# things as unconfigured -- no Home Assistant token yet, Nextcloud not signed in
|
|
# -- and failing an install over those would be crying wolf.
|
|
doctor="$PANAMA_PATH/config/dot/quickshell/scripts/panama-doctor"
|
|
if [[ -x "$doctor" ]]; then
|
|
printf '\n=== health ===\n'
|
|
"$doctor" --summary || true
|
|
fi
|
|
|
|
printf '\n'
|
|
if (( ${#failed[@]} == 0 )); then
|
|
echo "Panama installed. Log out and choose the Hyprland session to start it."
|
|
else
|
|
printf 'Panama installed with %d failed stage(s): %s\n' "${#failed[@]}" "${failed[*]}" >&2
|
|
printf 'Re-running ./install is safe and will retry them.\n' >&2
|
|
exit 1
|
|
fi
|