From 779b65af8cdf465dc5da3ba96322b9e262896bad Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 10 Sep 2026 19:42:57 -0700 Subject: [PATCH] feat(install): idempotent per-host package installer with host-gated enablement --- packages/aur.pkgs | 8 +++ packages/blankpad.pkgs | 5 ++ packages/common.pkgs | 31 +++++++++++ packages/dankware.pkgs | 8 +++ scripts/install.sh | 115 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 167 insertions(+) create mode 100644 packages/aur.pkgs create mode 100644 packages/blankpad.pkgs create mode 100644 packages/common.pkgs create mode 100644 packages/dankware.pkgs create mode 100755 scripts/install.sh diff --git a/packages/aur.pkgs b/packages/aur.pkgs new file mode 100644 index 0000000..9d72778 --- /dev/null +++ b/packages/aur.pkgs @@ -0,0 +1,8 @@ +# packages/aur.pkgs — AUR-only desired state, installed via yay (bootstrapped +# from the cachyos repo by install.sh). pkgbase names; runtime resolution by yay. + +oh-my-zsh-git # system-wide oh-my-zsh at /usr/share/oh-my-zsh; sourced by .zshrc guard +herdr-bin # AI-agent terminal workspace manager (prebuilt binary) +proton-pass # Proton Pass desktop client +handy-bin # Handy voice dictation (AppImage repackaged as pkg) +typora # markdown editor diff --git a/packages/blankpad.pkgs b/packages/blankpad.pkgs new file mode 100644 index 0000000..b13c7ea --- /dev/null +++ b/packages/blankpad.pkgs @@ -0,0 +1,5 @@ +# packages/blankpad.pkgs — desired state for the AMD iGPU laptop (Cezanne/Vega). +# amdgpu is in-kernel; the only GPU-relevant package is the CPU microcode, +# pkgbase amd-u-code (split pkgname amd-ucode). No module options needed. + +amd-u-code diff --git a/packages/common.pkgs b/packages/common.pkgs new file mode 100644 index 0000000..470f2a5 --- /dev/null +++ b/packages/common.pkgs @@ -0,0 +1,31 @@ +# packages/common.pkgs — desired-state package list for EVERY host in the fleet. +# install.sh installs (desired − installed); already-installed names are no-ops. +# pkgbase names only; AUR lives in aur.pkgs. + +# terminals + shells +ghostty +zoxide + +# dev tooling +neovim +lazygit +uv +fzf +ripgrep +fd +eza +btop + +# system / fleet tooling +yay # AUR helper bootstrap: ships in the cachyos repo; aur.pkgs installs need it +git-lfs # LFS smudge/clean for tracked wallpapers (see .gitattributes) +swayidle # idle lock + lid policy via swayidle-lock.service unit + +# desktop apps +flameshot # screenshot tool; bound to CTRL+Shift+1/2/3 in niri keybinds +wl-clipboard # clipboard provider flameshot copies through on wayland +filezilla +podman +firefox +vlc +steam diff --git a/packages/dankware.pkgs b/packages/dankware.pkgs new file mode 100644 index 0000000..11b0cd6 --- /dev/null +++ b/packages/dankware.pkgs @@ -0,0 +1,8 @@ +# packages/dankware.pkgs — desired state for the nvidia hybrid-GPU laptop. +# GPU stack: open-kernel-module DKMS kernels; nvidia-utils/lib32/libxnvctrl/ +# nvidia-prime/nvidia-settings arrive as dependencies of these pkgbases. +# Sleep fix (NVreg_UseKernelSuspendNotifiers=1 + nvidia-suspend/resume units) +# ships with these packages; install.sh --apply enables the units idempotently. + +linux-cachyos-nvidia-open +linux-cachyos-lts-nvidia-open diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 0000000..b7bce2c --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,115 @@ +#!/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 'NF' +} + +# ---- 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 + +mapfile -t INSTALLED < <(pacman -Qq | sort) +DELTAC=() +for p in "${PKGS[@]}"; do + printf '%s\n' "${INSTALLED[@]}" | grep -qx -- "$p" || 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 + printf '%s\n' "${INSTALLED[@]}" | grep -qx -- "$p" || 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 + +for p in "${AUR_PKGS[@]}"; do + printf '%s\n' "${INSTALLED[@]}" | grep -qx -- "$p" || { + YAYFLAGS="--answerclean None --answerdiff None" SUDO yay -S --needed --noconfirm -- "$p" + } +done + +# ---- 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"