#!/usr/bin/env bash # install.sh — idempotent fleet package installer for the ~/.omp repo deployment. # # Model: packages/*.pkgs declare DESIRED state per host. This script computes # (desired − installed) and installs only that delta via pacman/yay. It NEVER # removes anything. Re-running after a fresh install, or casually months later, # is safe by construction. # # Modes: # install.sh check (default): print the plan for this host, write nothing # install.sh --apply install the delta + run host-gated enablement actions # # Host selection: --host overrides $(hostname); an unknown host gets the # common manifest only — wrong-host runs are a no-op, not a hazard. # # Sudo: set FRANK_SUDO_PW in the environment for non-interactive runs; # otherwise sudo prompts normally. The value is never stored in this repo. set -euo pipefail REPO="$(git -C "$(dirname "$(realpath "$0")")" rev-parse --show-toplevel 2>/dev/null || true)" REPO="${INSTALL_REPO:-$REPO}" [ -n "$REPO" ] && [ -d "$REPO/packages" ] || { echo "install.sh: no packages/ under ${REPO:-}" >&2; exit 1; } MODE="check" HOST="$(hostname)" while [[ $# -gt 0 ]]; do case "$1" in --apply) MODE="apply"; shift ;; --host) HOST="$2"; shift 2 ;; *) echo "usage: install.sh [--apply] [--host ]" >&2; exit 2 ;; esac done SUDO() { if [[ -n "${FRANK_SUDO_PW:-}" ]]; then printf '%s' "$FRANK_SUDO_PW" | sudo -S -p '' "$@" else sudo "$@"; fi } read_manifest() { # $1 = manifest path; emits package names, strips comments/blanks sed 's/#.*//' "$1" | tr -d '\r' | awk '{if ($1) print $1}' } # ---- desired-minus-installed delta ------------------------------------------ PKGS=() for m in common.pkgs "$HOST.pkgs"; do [[ -f "$REPO/packages/$m" ]] || continue mapfile -t _pkgs < <(read_manifest "$REPO/packages/$m") PKGS+=("${_pkgs[@]}") done # AUR phase only when the helper exists or can be bootstrapped from a repo pkg AUR_PKGS=() if [[ -f "$REPO/packages/aur.pkgs" ]]; then mapfile -t AUR_PKGS < <(read_manifest "$REPO/packages/aur.pkgs") fi # Membership via direct DB query per package — deterministic, no set-diff plumbing. DELTAC=() for p in "${PKGS[@]}"; do pacman -Qq --quiet -- "$p" >/dev/null || DELTAC+=("$p") done echo "install.sh [$MODE] host=$HOST" if [[ ${#DELTAC[@]} -eq 0 ]]; then echo "repo pkgs: delta empty — nothing to install" else echo "repo pkgs to install: ${DELTAC[*]}" fi for p in "${AUR_PKGS[@]}"; do pacman -Qq --quiet -- "$p" >/dev/null || echo "aur pkg pending: $p" done [[ "$MODE" == "check" ]] && exit 0 # ---- apply ------------------------------------------------------------------ if [[ ${#DELTAC[@]} -gt 0 ]]; then SUDO pacman -S --needed --noconfirm -- "${DELTAC[@]}" fi # Helper bootstrap: yay itself ships in the cachyos repo (no chicken-and-egg). if [[ ${#AUR_PKGS[@]} -gt 0 ]] && ! pacman -Qq yay >/dev/null 2>&1; then SUDO pacman -S --needed --noconfirm -- yay fi # AUR builds must NOT run as root (makepkg refuses); yay escalates only for # the final package install via its internal sudo — prime the timestamp cache. if [[ ${#AUR_PKGS[@]} -gt 0 ]]; then if [[ -n "${FRANK_SUDO_PW:-}" ]]; then printf '%s' "$FRANK_SUDO_PW" | sudo -S -p '' true; fi for p in "${AUR_PKGS[@]}"; do printf '%s\n' "${INSTALLED[@]}" | grep -qx -- "$p" || { YAYFLAGS="--answerclean None --answerdiff None" yay -S --needed --noconfirm --aur -- "$p" } done fi # ---- host-gated enablement --------------------------------------------------- case "$HOST" in dankware) # nvidia sleep fix: package hooks already ship /usr/lib/modprobe.d/nvidia-sleep.conf # (UseKernelSuspendNotifiers=1 + TemporaryFilePath); enable the units idempotently. SUDO systemctl enable --now nvidia-suspend nvidia-resume 2>/dev/null || true ;; blankpad) # Cezanne iGPU: amdgpu is in-kernel, amd-ucode carried by kernel pkg; no module options. : ;; esac # sshd + firewall — same posture on both hosts (decision 2026-09: ssh open in default zone) if command -v sshd >/dev/null 2>&1 || pacman -Qq openssh >/dev/null 2>&1; then SUDO systemctl enable --now sshd fi if command -v firewall-cmd >/dev/null 2>&1 && firewall-cmd --state >/dev/null 2>&1; then if ! firewall-cmd --query-service ssh >/dev/null 2>&1; then SUDO firewall-cmd --permanent --zone=public --add-service=ssh SUDO firewall-cmd --reload fi fi echo "install.sh [$MODE] host=$HOST done"