Files
Panama/setup/scripts/install-hardware
T
Gabriel Brown 88497826ec Let a machine say what it is for, and give Firefox its face back
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
2026-08-20 21:24:15 -04:00

137 lines
6.3 KiB
Bash
Executable File

#!/usr/bin/env bash
# The parts of an install that depend on what the machine actually is: the
# NVIDIA driver, the machine owner key that lets it load under Secure Boot,
# Fedora's preinstalled extras, and firmware.
#
# Runs last. MOK enrollment arms a prompt consumed at the next boot and firmware
# updates can ask for a reboot, so neither belongs in front of the package work
# or the dotfiles -- a machine that reboots out of this stage has already been
# fully configured.
#
# Nothing here decides anything. Every branch is an answer the interview
# collected before the run began, and an absent answer means no, which is what
# makes this safe to re-run by hand while repairing one piece of a machine.
#
# sunhat's version of this opened an editor in the middle of the run so grub
# could be hand-corrected. That is the exact failure this repository exists to
# avoid, and it is unnecessary: grubby replaces an argument that already exists
# rather than appending a second copy, so the duplicates that had to be cleaned
# up by hand cannot accumulate in the first place.
set -uo pipefail
log() { echo -e "\033[1;34m[INFO]\033[0m $*"; }
warn() { echo -e "\033[1;33m[WARN]\033[0m $*" >&2; }
# Fedora ships these and Panama uses none of them. Named here rather than in the
# interview so there is one list: the interview asks for it with --debloat-list
# to name what it is about to remove, and this stage removes it. A package that
# is not installed is skipped rather than passed to dnf, so the list can outlive
# a Fedora release -- totem left in Fedora 43 and the list should not start
# failing because of it.
DEBLOAT=(gnome-contacts gnome-tour gnome-maps showtime)
if [[ "${1:-}" == "--debloat-list" ]]; then
printf '%s\n' "${DEBLOAT[@]}"
exit 0
fi
# ── NVIDIA ───────────────────────────────────────────────────────────────────
if [[ "${PANAMA_NVIDIA:-no}" == yes ]]; then
log "Installing the NVIDIA driver"
if sudo dnf install -y akmod-nvidia xorg-x11-drv-nvidia-cuda; then
# nouveau has to be out of the way before the kernel would otherwise
# bind it, which is why these are kernel arguments and not a modprobe
# drop-in. modeset=1 is what makes the Wayland session work at all.
if command -v grubby >/dev/null 2>&1; then
sudo grubby --update-kernel=ALL \
--args="rd.driver.blacklist=nouveau modprobe.blacklist=nouveau nvidia-drm.modeset=1"
log "Kernel arguments set for every installed kernel"
else
warn "grubby is not installed; nouveau was not blacklisted"
fi
# Suspend and resume are where a proprietary driver most visibly fails.
# These units save and restore VRAM across it.
sudo systemctl enable nvidia-hibernate.service nvidia-suspend.service \
nvidia-resume.service nvidia-powerd.service
log "NVIDIA power management services enabled"
else
warn "The NVIDIA driver did not install; skipping its kernel arguments and services"
fi
fi
# ── Secure Boot ──────────────────────────────────────────────────────────────
#
# akmods signs the modules it builds with a key it generates on installation.
# Under Secure Boot that key means nothing until it is enrolled, and enrollment
# is deliberately a thing only somebody at the physical machine can complete:
# the request is queued here, and the next boot shows a blue screen asking for
# the password before it will trust the key.
#
# The password itself never reaches this stage. The interview hashed it and
# recorded the hash, so nothing readable is passed on a command line or left in
# this process's environment -- which is the whole reason mokutil has
# --generate-hash and --hash-file.
mok_hash="${PANAMA_MOK_HASH:-}"
if [[ -n "$mok_hash" ]]; then
# Overridable so the contract can exercise this against a certificate it is
# allowed to create. Nothing else sets it.
cert="${PANAMA_MOK_CERT:-/etc/pki/akmods/certs/public_key.der}"
if [[ ! -r "$cert" ]]; then
warn "No akmods certificate at $cert, so there is no key to enroll"
elif mokutil --test-key "$cert" 2>/dev/null | grep -q 'already enrolled'; then
log "The akmods key is already enrolled"
else
hash_file="$(mktemp -t panama-mok.XXXXXX)"
chmod 600 "$hash_file"
printf '%s\n' "$mok_hash" >"$hash_file"
if sudo mokutil --import "$cert" --hash-file "$hash_file"; then
log "Key enrollment requested"
log "At the next boot, choose 'Enroll MOK' and enter the password you gave the installer"
else
warn "Key enrollment failed; the NVIDIA module will not load until it is enrolled"
fi
rm -f "$hash_file"
fi
fi
# ── Fedora's preinstalled extras ─────────────────────────────────────────────
if [[ "${PANAMA_DEBLOAT:-no}" == yes ]]; then
present=()
for package in "${DEBLOAT[@]}"; do
rpm -q "$package" >/dev/null 2>&1 && present+=("$package")
done
if (( ${#present[@]} > 0 )); then
log "Removing ${present[*]}"
sudo dnf remove -y "${present[@]}" >/dev/null \
|| warn "Some packages could not be removed"
else
log "None of Fedora's extras are installed"
fi
fi
# ── Firmware ─────────────────────────────────────────────────────────────────
#
# Panama's Updates page covers this from then on; the installer covers the
# first run. --no-reboot-check because deciding to reboot is not this stage's
# call to make in the middle of an unattended install.
if [[ "${PANAMA_FIRMWARE:-no}" == yes ]]; then
if command -v fwupdmgr >/dev/null 2>&1; then
log "Refreshing firmware metadata"
sudo fwupdmgr refresh --force >/dev/null 2>&1
log "Applying firmware updates"
sudo fwupdmgr update -y --no-reboot-check \
|| log "No firmware updates were applied"
else
warn "fwupdmgr is not installed; skipping firmware"
fi
fi