#!/usr/bin/env bash # Shared helpers for docs/setup/scripts/*.sh — sourced from each script. # Idempotency utilities + nice logging. set -euo pipefail if [[ -t 1 ]]; then C_GREEN=$'\033[1;32m' C_YELLOW=$'\033[1;33m' C_RED=$'\033[1;31m' C_BLUE=$'\033[1;34m' C_DIM=$'\033[2m' C_RESET=$'\033[0m' else C_GREEN= C_YELLOW= C_RED= C_BLUE= C_DIM= C_RESET= fi log() { printf '%s[%s]%s %s\n' "$C_BLUE" "$(date +%H:%M:%S)" "$C_RESET" "$*"; } ok() { printf '%s✓%s %s\n' "$C_GREEN" "$C_RESET" "$*"; } warn() { printf '%s⚠%s %s\n' "$C_YELLOW" "$C_RESET" "$*" >&2; } err() { printf '%s✗%s %s\n' "$C_RED" "$C_RESET" "$*" >&2; } skip() { printf '%s↪ skip%s %s\n' "$C_DIM" "$C_RESET" "$*"; } require_debian() { if ! grep -q '^ID=debian' /etc/os-release 2>/dev/null; then err "This script targets Debian (read from /etc/os-release)." exit 1 fi } require_not_root() { if [[ $EUID -eq 0 ]]; then err "Run as your regular user (sudo is invoked where needed)." exit 1 fi } confirm() { local prompt="${1:-Proceed?}" read -r -p "$prompt [y/N] " ans [[ "$ans" =~ ^[yY]$ ]] } # Install apt packages, skipping those already present. Idempotent. apt_install() { local missing=() for pkg in "$@"; do if ! dpkg -s "$pkg" >/dev/null 2>&1; then missing+=("$pkg") fi done if (( ${#missing[@]} == 0 )); then skip "All packages already present: $*" return 0 fi log "apt install: ${missing[*]}" sudo apt-get install -y "${missing[@]}" ok "Installed: ${missing[*]}" } # Append a line to a file iff not already present (exact match). Idempotent. ensure_line() { local line="$1" local file="$2" if grep -qxF -- "$line" "$file" 2>/dev/null; then skip "Already in $(basename "$file"): $line" else echo "$line" >> "$file" ok "Appended to $file: $line" fi } # Same but with sudo (system files). ensure_line_sudo() { local line="$1" local file="$2" if sudo grep -qxF -- "$line" "$file" 2>/dev/null; then skip "Already in $(basename "$file"): $line" else echo "$line" | sudo tee -a "$file" >/dev/null ok "Appended to $file: $line" fi }