diff --git a/.chezmoi.yaml.tmpl b/.chezmoi.yaml.tmpl index 842c57f..0cab74f 100644 --- a/.chezmoi.yaml.tmpl +++ b/.chezmoi.yaml.tmpl @@ -37,7 +37,17 @@ age: # Arch's pacman and Debian's apt both put it there. Using the absolute # path means chezmoi can find age even if PATH isn't set correctly # (which happens in some non-interactive SSH contexts). + # + # On a per-machine bootstrap, this might not be installed yet (sudo + # required). The bootstrap detects the fallback path and re-renders + # the config to use it. See run_once_00-install-bootstrap-tools.sh.tmpl. + {{- if stat "/usr/bin/age" }} command: "/usr/bin/age" + {{- else if stat (joinPath .chezmoi.homeDir ".local/bin/age") }} + command: "{{ joinPath .chezmoi.homeDir ".local/bin/age" }}" + {{- else if stat "/usr/local/bin/age" }} + command: "/usr/local/bin/age" + {{- end }} identity: "~/.config/chezmoi/key.txt" # Multiple recipients: every listed recipient can decrypt every *.age file. diff --git a/run_once_00-install-bootstrap-tools.sh.tmpl b/run_once_00-install-bootstrap-tools.sh.tmpl index c58e728..587333c 100755 --- a/run_once_00-install-bootstrap-tools.sh.tmpl +++ b/run_once_00-install-bootstrap-tools.sh.tmpl @@ -14,23 +14,45 @@ die() { printf '\033[1;31m[bootstrap ERROR]\033[0m %s\n' "$*" >&2; exit 1; } # will be prompted once per sudo invocation. {{ if eq .os_family "arch" -}} -log "pacman-sync" -sudo pacman -Sy --noconfirm +# Only sync the package DB if anything is missing. Avoids a no-op sudo +# (which would still prompt for a password even when there's nothing to +# install) on boxes where all the bootstrap tools are already present. +MISSING_PKGS=() +for p in age curl ca-certificates git base-devel wget; do + if ! command -v "$p" >/dev/null 2>&1 && ! pacman -Qi "$p" >/dev/null 2>&1; then + MISSING_PKGS+=("$p") + fi +done -log "install base tools (arch)" -PACMAN_PKGS=(age curl ca-certificates git base-devel wget) -sudo pacman -S --needed --noconfirm "${PACMAN_PKGS[@]}" +if (( ${#MISSING_PKGS[@]} > 0 )); then + log "pacman-sync (missing: ${MISSING_PKGS[*]})" + sudo pacman -Sy --noconfirm + log "install base tools (arch)" + sudo pacman -S --needed --noconfirm "${MISSING_PKGS[@]}" +else + log "all base tools already installed; skipping pacman" +fi {{ else if eq .os_family "debian" -}} export DEBIAN_FRONTEND=noninteractive -log "apt-update" -sudo apt-get update -y -log "apt-upgrade" -sudo apt-get upgrade -y +# Only run apt if anything is missing, so a no-op sudo isn't required. +MISSING_PKGS=() +for p in age curl ca-certificates git wget gnupg libssl-dev pkg-config; do + if ! command -v "$p" >/dev/null 2>&1; then + MISSING_PKGS+=("$p") + fi +done -log "install base tools (debian)" -APT_PKGS=(age curl ca-certificates git wget gnupg libssl-dev pkg-config) -sudo apt-get install -y --no-install-recommends "${APT_PKGS[@]}" +if (( ${#MISSING_PKGS[@]} > 0 )); then + log "apt-update (missing: ${MISSING_PKGS[*]})" + sudo apt-get update -y + log "apt-upgrade" + sudo apt-get upgrade -y + log "install base tools (debian)" + sudo apt-get install -y --no-install-recommends "${MISSING_PKGS[@]}" +else + log "all base tools already installed; skipping apt" +fi {{ else -}} die "unsupported os_family: {{ .os_family }} (this script supports arch or debian)" diff --git a/run_once_05-install-hosts.sh.tmpl b/run_once_05-install-hosts.sh.tmpl index 7462ec0..21a7e96 100644 --- a/run_once_05-install-hosts.sh.tmpl +++ b/run_once_05-install-hosts.sh.tmpl @@ -54,9 +54,18 @@ if [[ ! -f "$HOSTS_FILE" ]]; then exit 1 fi -# If our block already exists, remove it first (so re-runs don't duplicate) +# If our block already exists with all entries, skip. Otherwise rewrite. +# This avoids a no-op sudo prompt on boxes that already have the block. +if grep -q "$LAN_BLOCK_BEGIN" "$HOSTS_FILE" 2>/dev/null \ + && grep -q "miche.local" "$HOSTS_FILE" 2>/dev/null \ + && grep -q "bit.local" "$HOSTS_FILE" 2>/dev/null; then + log "LAN block already present in $HOSTS_FILE; skipping" + exit 0 +fi + +# If our block exists but is stale (missing some entries), remove it first if grep -q "$LAN_BLOCK_BEGIN" "$HOSTS_FILE"; then - log "removing old LAN block" + log "stale LAN block detected; removing before re-adding" sudo cp "$HOSTS_FILE" "${HOSTS_FILE}.bak.$(date +%s)" sudo sed -i "/$LAN_BLOCK_BEGIN/,/$LAN_BLOCK_END/d" "$HOSTS_FILE" fi diff --git a/run_once_20-install-user-packages.sh.tmpl b/run_once_20-install-user-packages.sh.tmpl index aceba16..1ceb58e 100755 --- a/run_once_20-install-user-packages.sh.tmpl +++ b/run_once_20-install-user-packages.sh.tmpl @@ -20,9 +20,9 @@ ZSH_CUSTOM="${ZSH_CUSTOM:-$USER_HOME/.oh-my-zsh/custom}" {{ if eq .os_family "arch" -}} # ----------------------------- ARCH --------------------------------------- -log "pacman -Syu" -sudo pacman -Syu --noconfirm - +# Only run pacman if anything is actually missing. Avoids a no-op sudo +# (which would still prompt for a password even when there's nothing to +# install) on boxes where all the user packages are already present. PACMAN_PKGS=( zsh tmux neovim git base-devel bat btop htop fastfetch @@ -32,9 +32,20 @@ PACMAN_PKGS=( openssh bun ) - -log "installing pacman packages" -sudo pacman -S --needed --noconfirm "${PACMAN_PKGS[@]}" +MISSING_PKGS=() +for p in "${PACMAN_PKGS[@]}"; do + if ! command -v "$p" >/dev/null 2>&1 && ! pacman -Qi "$p" >/dev/null 2>&1; then + MISSING_PKGS+=("$p") + fi +done +if (( ${#MISSING_PKGS[@]} > 0 )); then + log "pacman -Syu (missing: ${MISSING_PKGS[*]})" + sudo pacman -Syu --noconfirm + log "installing pacman packages" + sudo pacman -S --needed --noconfirm "${MISSING_PKGS[@]}" +else + log "all user packages already installed; skipping pacman" +fi # --------------------------- Pi coding agent + oh-my-pi --------------------- # Arch: bun comes from pacman (above), used here for the global install. @@ -50,9 +61,6 @@ fi {{ else if eq .os_family "debian" -}} # ----------------------------- DEBIAN -------------------------------------- export DEBIAN_FRONTEND=noninteractive -sudo apt-get update -y -sudo apt-get upgrade -y - APT_PKGS=( zsh tmux git build-essential btop htop fastfetch @@ -63,9 +71,22 @@ APT_PKGS=( ca-certificates curl wget fontconfig ) - -log "installing apt packages" -sudo apt-get install -y --no-install-recommends "${APT_PKGS[@]}" +MISSING_PKGS=() +for p in "${APT_PKGS[@]}"; do + if ! command -v "$p" >/dev/null 2>&1; then + MISSING_PKGS+=("$p") + fi +done +if (( ${#MISSING_PKGS[@]} > 0 )); then + log "apt-update (missing: ${MISSING_PKGS[*]})" + sudo apt-get update -y + log "apt-upgrade" + sudo apt-get upgrade -y + log "installing apt packages" + sudo apt-get install -y --no-install-recommends "${MISSING_PKGS[@]}" +else + log "all user packages already installed; skipping apt" +fi # bun isn't in debian repos. Install via official script into ~/.local # (so the binary lands at ~/.local/bin/bun, which is already in PATH diff --git a/run_once_40-install-sway.sh.tmpl b/run_once_40-install-sway.sh.tmpl index 0ab0ada..53e90b7 100644 --- a/run_once_40-install-sway.sh.tmpl +++ b/run_once_40-install-sway.sh.tmpl @@ -36,14 +36,28 @@ SWAY_PKGS+=(dunst) log "WARNING: sway packages not configured for os_family={{ .os_family }}" {{ end -}} -log "installing sway stack: ${SWAY_PKGS[*]}" +log "checking sway stack: ${SWAY_PKGS[*]}" -{{ if eq .os_family "arch" -}} -sudo pacman -S --needed --noconfirm "${SWAY_PKGS[@]}" -{{ else if eq .os_family "debian" -}} -export DEBIAN_FRONTEND=noninteractive -sudo apt-get install -y --no-install-recommends "${SWAY_PKGS[@]}" -{{ end -}} +# Only invoke sudo if any of the packages are missing. Bit has most +# already; byte/kaiser are full. This avoids a no-op sudo prompt. +MISSING_PKGS=() +for p in "${SWAY_PKGS[@]}"; do + if ! command -v "$p" >/dev/null 2>&1; then + MISSING_PKGS+=("$p") + fi +done + +if (( ${#MISSING_PKGS[@]} > 0 )); then + log "missing: ${MISSING_PKGS[*]}" + {{ if eq .os_family "arch" -}} + sudo pacman -S --needed --noconfirm "${MISSING_PKGS[@]}" + {{ else if eq .os_family "debian" -}} + export DEBIAN_FRONTEND=noninteractive + sudo apt-get install -y --no-install-recommends "${MISSING_PKGS[@]}" + {{ end -}} +else + log "all sway packages already installed; skipping" +fi log "sway stack installed" sway --version 2>&1 | head -1