Files
Panama/setup/scripts/change-settings
T
Gabriel Brown 153554b5df Make ./install something you could hand a stranger
The audit's third tier: everything between this installer and a fresh machine
it has never met.

The one path that could cost a person their display: the interview probes
Secure Boot with mokutil, which install-packages had not installed yet, so on
a minimal base the MOK question silently never fired -- and install-hardware
still installed akmod-nvidia and blacklisted nouveau, arming a reboot into an
unloadable driver with its fallback disabled. The probe tools (pciutils,
mokutil, fwupd) now bootstrap beside gum, and install-hardware re-checks
Secure Boot for itself and refuses the driver rather than the display.

Secrets leave the checkout: the personal environment moves to
~/.config/panama/env at mode 600 by migration, and .bashrc sources it with a
permission check that quietly re-tightens drift. change-settings no longer
overwrites /etc/dnf/dnf.conf -- two performance keys are set additively, the
defaultyes=True that made every `dnf remove` treat Enter as yes is gone, and
a migration strips it from machines that already received it.

Package installation survives the world changing: the initial and desktop
lists run with --skip-unavailable and a report_missing pass that names what
was skipped (resolved through --whatprovides, so capability names like awk
do not cry wolf); the openh264, appstream and core-group extras go through
soft; RustDesk resolves its RPM for the machine's own architecture; and the
Claude Desktop repository script is fetched to a kept file and run, never
piped from the network into root.

The hardware predicates stop guessing: a wireless mouse's scope=Device
battery no longer turns a tower into a laptop, USB-PD-only machines read
their power state from the battery's own status instead of being permanently
"on AC", the lid falls back to logind's LidClosed where ACPI is silent, and
charge limits reach every pack of a two-battery machine in one authorization
-- with the reported percentage summed across packs.

And the parsers stop assuming this machine: snapper is read through
--machine-readable csv with named columns instead of a localized box-drawing
table, and reports whether snapshots are even possible so ext4 and
unconfigured-btrfs stop looking identical; fprintd is parsed under LC_ALL=C;
the hypridle drop-in resolves the binary it points at; the recorder's render
node became an "auto" token resolved at record time; update-grub writes the
config its firmware actually boots; the nvm prompt hook and the SSH tmux
takeover are guarded; hipblas and rocm-opencl move to an opt-in gpu-compute
category; and the two interactive python tools' libraries are declared.

Claude-Session: https://claude.ai/code/session_01Epx9ZC1gwm81K3jm9x9CKh
2026-08-23 12:10:03 -04:00

122 lines
6.4 KiB
Bash
Executable File

#!/usr/bin/env bash
# Strict: the copy over / below is the one thing this stage exists to do, and
# without set -e its failure fell through to guarded no-ops and exited 0 --
# a failed stage the installer's summary could never see.
set -euo pipefail
# --- Helper functions ---
log() { echo -e "\033[1;34m[INFO]\033[0m $*"; }
exists() { command -v "$1" >/dev/null 2>&1; }
# --- Defined Paths ---
# The default, not an assignment: an exported PANAMA_PATH from a clone at
# another location must win, or the copy over / below reads the wrong tree.
PANAMA_PATH="${PANAMA_PATH:-$HOME/.local/share/Panama}"
echo -e "\n--- Copying System & User files ---"
# dnf options are applied as individual keys rather than shipped as a file.
# config/copy once carried a whole /etc/dnf/dnf.conf, which clobbered proxies
# and mirrors a machine already had -- and set defaultyes=True system-wide, so
# every `dnf remove` for every user treated a bare Enter as yes. These two are
# performance knobs with no behavior change; anything else stays the machine's
# own business. (fastestmirror and deltarpm were dnf4-era keys dnf5 ignores,
# so they simply stop being written.)
log "Setting dnf performance options (max_parallel_downloads, keepcache)"
sudo dnf config-manager setopt max_parallel_downloads=10 keepcache=True 2>/dev/null \
|| log "Could not set dnf options; defaults apply"
log "Copying system files (udev rules, systemd drop-ins)"
sudo cp -r "$PANAMA_PATH/config/copy/." "/"
# The GPU symlink rules land via the copy above. Apply them without a reboot so
# AQ_DRM_DEVICES resolves on the first Hyprland login rather than the second.
if [[ -f /etc/udev/rules.d/99-panama-gpu.rules ]]; then
log "Reloading udev rules for stable GPU symlinks"
sudo udevadm control --reload
sudo udevadm trigger --subsystem-match=drm
fi
# The document-portal drop-in lands via the same copy. A user unit's drop-ins
# are read at daemon-reload, so without this the guard applies from the next
# login rather than from now -- and the failure it guards against is one you
# only find days later, when you next open a flatpak you had not opened yet.
if [[ -f /etc/systemd/user/xdg-document-portal.service.d/panama-stale-mount.conf ]]; then
log "Reloading the user manager so the document-portal drop-in applies"
systemctl --user daemon-reload || log "Could not reload the user manager"
fi
echo -e "\n--- Desktop appearance ---"
# GTK and Qt apps read these directly from gsettings; there is no settings
# daemon outside GNOME, so they must be correct rather than merely overridden.
# These match config/dot/gtk-3.0/settings.ini and quickshell's Theme.qml.
if exists gsettings; then
log "Applying interface settings"
gsettings set org.gnome.desktop.interface gtk-theme 'adw-gtk3-dark'
gsettings set org.gnome.desktop.interface icon-theme 'Adwaita'
gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark'
gsettings set org.gnome.desktop.interface font-name 'Adwaita Sans 11'
gsettings set org.gnome.desktop.interface monospace-font-name 'VictorMono Nerd Font 10'
gsettings set org.gnome.desktop.interface cursor-theme 'oreo_blue_cursors'
# Deliberately NOT setting cursor-size. GNOME here is on 16 (smaller than
# GNOME's own default of 24) and changing it would visibly alter the working
# GNOME session. Hyprland uses XCURSOR_SIZE=24 from uwsm/env; if the cursor
# feels too large there, change that one value rather than this one.
fi
echo -e "\n--- Hyprland session services ---"
# Deliberately NOT enabling these.
#
# hyprpaper / hypridle / hyprpolkitagent / vicinae all ship units that are
# WantedBy=graphical-session.target -- and that target is active under GNOME
# too. Enabling them would start hypridle alongside GNOME's own idle handling
# (two daemons blanking and locking the screen on different timers) and start
# hyprpaper where there is no layer shell for it to draw on.
#
# Instead hypr/autostart.lua runs `systemctl --user start ...` on
# hyprland.start, which scopes them to the Hyprland session. They still get the
# correct uwsm activation environment and still stop in order, because they are
# PartOf=graphical-session.target.
log "Hyprland services are started per-session by hypr/autostart.lua, not enabled globally"
# espanso is the exception to the rule above: text expansion is wanted under
# any compositor and conflicts with nothing GNOME runs, and upstream's own
# mechanism is a registered user unit. The RPM ships no unit -- without this,
# the package installs, the config links, and no trigger ever fires.
# `service register` writes and enables the unit and is idempotent on re-runs;
# autostart.lua also starts it so the first Hyprland login after a fresh
# install expands text without waiting for the next one.
if exists espanso && ! systemctl --user cat espanso.service >/dev/null 2>&1; then
log "Registering the espanso text-expansion service"
espanso service register >/dev/null 2>&1 || log "Could not register espanso"
fi
# Notification daemons must NOT be installed: mako, dunst and swaync all
# register Name=org.freedesktop.Notifications for D-Bus activation, which races
# Quickshell's own notification server at login. Whoever wins keeps the name.
for pkg in mako dunst SwayNotificationCenter; do
if rpm -q "$pkg" >/dev/null 2>&1; then
log "WARNING: $pkg is installed and will fight Quickshell for the notification D-Bus name."
log " Fix with: systemctl --user mask ${pkg,,}.service"
fi
done
# Select the launcher theme once vicinae exists.
if exists vicinae; then
log "Setting vicinae theme"
vicinae theme set tokyonight-moon >/dev/null 2>&1 || true
fi
# A machine with a battery gets managed idle timings from the start. The
# shipped hypridle.conf deliberately never suspends -- correct for a desktop,
# and exactly wrong for a laptop in a bag. panama-idle generates the
# power-source-aware config and points hypridle at it with a drop-in; it does
# not start hypridle here (the Hyprland session does that), so running it
# under GNOME during install is safe. A desktop skips this entirely and keeps
# the shipped file, as before.
if "${PANAMA_PATH:-$HOME/.local/share/Panama}/bin/panama-hw" battery 2>/dev/null; then
log "Battery detected: enabling managed idle timings (suspend on battery works out of the box)"
"${PANAMA_PATH:-$HOME/.local/share/Panama}/config/dot/quickshell/scripts/panama-idle" install \
|| log "Could not enable managed idle timings; the Power page can turn them on later"
fi