diff --git a/README.md b/README.md index 44f5e45..a4290b9 100644 --- a/README.md +++ b/README.md @@ -51,11 +51,12 @@ Distro-agnostic .NET development environment: The setup is broken down into focused scripts: -- `00-system-prep.sh` - System updates and Flathub setup +- `00-system-prep.sh` - System updates, `sudo` bootstrap, Debian `contrib`/`non-free-firmware` + `needrestart`, Flathub setup - `01-package-install.sh` - Core packages, upstream tooling, and Flatpak apps - `02-dev-tools-setup.sh` - Development tools (Neovim, Go, Rust, Oh-My-Posh, Helm, kubectx, stern, dive, trivy, AWS CLI) - `03-dotnet-setup.sh` - .NET SDK + PostgreSQL + Podman (distro-agnostic) - `04-config-symlinks.sh` - Dotfile symlinking +- `verify-debian.sh` - Post-install sanity checker (run manually, no root needed) You can run individual scripts if you only need specific components. diff --git a/docs/debian-13-migration-plan.md b/docs/debian-13-migration-plan.md index 2ddab5d..465349e 100644 --- a/docs/debian-13-migration-plan.md +++ b/docs/debian-13-migration-plan.md @@ -46,87 +46,60 @@ The repo is mostly distro-agnostic already (shell rc files, aliases, nvim via Nv --- -## 3. File-by-file plan +## 3. File-by-file status (as of 2026-09-02) -### `main-setup.sh` -- [ ] Keep existing `detect_linux_distro()` — already returns `debian` for trixie (`ID=debian`). Verify `sudo` present; if missing, warn to `su -`/install `sudo` first (or auto-detect root). -- [ ] Replace the Fedora-only prompt for `03-fedora-dotnet-setup.sh` with a distro-agnostic `.NET` prompt (new `03-dotnet-setup.sh`), OR gate on both `fedora` and `debian`. -- [ ] Consider exporting `DEBIAN_CODENAME`/`DISTRO_VERSION` (from `/etc/os-release`) for downstream repo URLs. +### `main-setup.sh` — DONE +- [x] `detect_linux_distro()` returns `debian` for trixie; `PACKAGE_MANAGER=apt`. +- [x] Preflight: if non-root and `sudo` missing → clear guidance (run as root or install `sudo`); `sudo -n` note. +- [x] `.NET` prompt is now distro-agnostic → runs `03-dotnet-setup.sh` on every supported distro. -### `scripts/00-system-prep.sh` -- [ ] `apt update && apt upgrade` path already correct. Add optional: install `sudo`, `curl`, `gnupg`, `ca-certificates`, `software-properties-common`/`apt-transport-https` prerequisites early. -- [ ] (Optional) enable `contrib`/`non-free-firmware` components in `/etc/apt/sources.list.d/debian.sources` — needed for some firmware/tools. +### `scripts/00-system-prep.sh` — DONE +- [x] `sudo` bootstrap when running as root on a fresh install (apt/dnf). +- [x] Debian: idempotently add `contrib` + `non-free-firmware` to `debian.sources` (deb822) or legacy `sources.list` (skip with `SKIP_NONFREE=1`); `needrestart` installed on apt. -### `scripts/01-package-install.sh` -- [ ] Split `BASE_PACKAGE_LIST` into truly-common + distro lists; on Debian drop anything not in trixie and add Debian equivalents. -- [ ] **Rename fixes (Debian branch of `DISTRO_SPECIFIC_PACKAGES`):** - - `dnsutils` → `bind9-dnsutils` - - add `bat` handling: after install, `ln -sf /usr/bin/batcat /usr/local/bin/bat` - - add `sudo`, `ca-certificates`, `gnupg`, `curl` (ensure present) - - add `libvirt-daemon-system`, `virtinst`, `qemu-kvm`, `bridge-utils` for virt-manager - - add `zsh` post: `chsh -s /usr/bin/zsh` (or instruct) - - `python3-pip` stays, but flag PEP 668 handling (see 02) - - `docker.io` + `docker-compose` **or** wire Docker CE repo (defer to decision in §4) -- [ ] **Upstream repo tools not in Debian main** — install in this script or a new `05-` script: - - `gh` → GitHub CLI apt repo (`https://cli.github.com/packages`) - - `terraform` → HashiCorp apt repo - - `kubectl` → Kubernetes apt repo (`pkgs.k8s.io`) - - `k9s`, `minikube` → GitHub `.deb` releases (minikube official `.deb` exists) -- [ ] Flatpak list unchanged (distro-agnostic). Note VS Code Flatpak vs MS repo decision in §4. +### `scripts/01-package-install.sh` — DONE +- [x] Split base vs distro lists; `dnsutils` → `bind9-dnsutils`; `bat` → `batcat` symlink; libvirt/QEMU stack (`libvirt-daemon-system`, `libvirt-clients`, `qemu-system-x86`, `virtinst`, `bridge-utils`); prereqs (`sudo`, `ca-certificates`, `gnupg`, `curl`). +- [x] `setup_upstream_tooling()`: vendor repos for `gh` (GitHub CLI), `terraform` (HashiCorp), `kubectl` (pkgs.k8s.io); `k9s` (GitHub tarball) + `minikube` (official `.deb`) on apt; best-effort `dnf` on Fedora. +- [x] Kubernetes minor auto-detected from the latest k8s release (fallback `v1.36`; override with `K8S_MINOR`). +- [x] Flatpak list unchanged (VS Code stays Flatpak per decision). -### `scripts/02-dev-tools-setup.sh` -- [ ] **pynvim PEP 668 fix:** replace `pip install --user pynvim` with `python3 -m pip install --user --break-system-packages pynvim`, or (cleaner) install into NvChad's venv, or note `pipx`. -- [ ] Rest is GitHub-download based → distro-agnostic; verify `unzip`, `tar`, `wget` present. -- [ ] `dive` already handles `.deb` via `dpkg -i` on apt — good. -- [ ] Go/Rust/Nerd Font/Oh-My-Posh/Helm/kubectx/stern/trivy/AWS — no changes needed. +### `scripts/02-dev-tools-setup.sh` — DONE +- [x] `pynvim` PEP 668 fix: try `pip install --user`, fall back to `--break-system-packages`. -### `scripts/03-fedora-dotnet-setup.sh` → split/generalize -- [ ] Rename to **`03-dotnet-setup.sh`** and branch by `$PACKAGE_MANAGER`: - - **Fedora:** keep existing RPM/MS-repo path. - - **Debian:** add MS Debian repo `https://packages.microsoft.com/config/debian/13/packages-microsoft-prod.deb` (verify trixie support; fall back to `dotnet-install.sh` if repo is unavailable for trixie) then `apt install dotnet-sdk-10.0` (or 8/9 fallback loop like Fedora). - - Alternatively use official **`dotnet-install.sh`** for both — fully distro-agnostic, no repo dependency. -- [ ] Replace Fedora-specific db/container section with Debian equivalents: - - PostgreSQL: `apt install postgresql` (cluster auto-init — remove `postgresql-setup --initdb` step for Debian; just `systemctl enable --now postgresql`). - - Docker: `docker.io` + `docker-compose` + add user to `docker` group, or Docker CE repo. -- [ ] Update `main-setup.sh` gate accordingly. +### `scripts/03-fedora-dotnet-setup.sh` → `03-dotnet-setup.sh` — DONE +- [x] Renamed to `03-dotnet-setup.sh`, distro-agnostic (apt/dnf). +- [x] `.NET SDK` via official `dotnet-install.sh` (channel `STS`, override `DOTNET_CHANNEL`) → `~/.dotnet`, PATH added to `bashrc`/`zshrc`. +- [x] PostgreSQL: auto-cluster on Debian; `--initdb` only on Fedora. +- [x] Containers = **Podman** + `podman-compose` + `podman-docker` (Docker-CLI shim). No MS repo, no moby/docker-ce. -### `scripts/04-config-symlinks.sh` -- [ ] No functional changes needed (pure symlinking). Optionally handle `~/.bash_aliases` (Debian's default bashrc sources it) or leave as-is since `bashrc` sources `aliases.bash` directly. +### `scripts/04-config-symlinks.sh` — no changes required +- [x] Pure symlinking; distro-agnostic as-is. -### Shell rc files (`bashrc`, `zshrc`, aliases) -- [ ] Already distro-agnostic (PATH for `/usr/local/go`, `~/.local/bin`, cargo, nvm). No Fedora-specific bits. -- [ ] Note: `zshrc` still references **oh-my-zsh** (`$ZSH/oh-my-zsh.sh`, `plugins=(git)`) but no script installs it anymore (repo moved to Oh-My-Posh). On a fresh Debian this will fail silently. Decide: purge oh-my-zsh remnants or restore its install step. Recommend purging on this branch. -- [ ] Ensure `~/.fzf.zsh` / `~/.fzf.bash` generation (`fzf --zsh`/`--bash`) or skip gracefully (already guarded). +### Shell rc files (`bashrc`, `zshrc`, aliases) — DONE +- [x] `zshrc` oh-my-zsh remnants purged (plain zsh + Oh-My-Posh). +- [x] Duplicate Go `PATH` lines (appended by an old 02 run) removed from `bashrc`/`zshrc`. +- [x] `nvim/nvim` + `nushell/nushell` self-symlink targets updated to `/home/blake`. -### `README.md` -- [ ] Update "Supported Distributions" / feature matrix to reflect **Debian 13 = first-class**, Fedora optional. -- [ ] Rename "Fedora-Specific (.NET)" section → ".NET Development" with per-distro notes. -- [ ] Update post-install steps (group names `docker`, `libvirt`; remove Fedora-only log-out note if not applicable; add PEP 668 note). -- [ ] Update file listing for `03-*.sh` renames. +### `README.md` — DONE +- [x] Debian 13 first-class; feature lists, modular architecture, post-install steps, and supported-distro matrix updated (Podman, Flatpak VS Code, libvirt/kvm groups, rootless podman socket). + +### Tooling / quality — DONE (this pass) +- [x] **`scripts/verify-debian.sh`** added — non-destructive end-to-end sanity checker (OS, binaries, Debian renames, upstream tools, dev tools, .NET/Podman/Postgres, groups, flatpak, dotfile symlinks); exit code 0/1. +- [x] **ShellCheck** (v0.11.0) run on all scripts — clean at warning severity (remaining notes are pre-existing info/style only). --- -## 4. Open decisions (need your input) +## 4. Decisions (confirmed) +1. **Container runtime:** Podman (`podman-docker` provides the `docker` shim so existing aliases keep working). +2. **VS Code:** Flatpak `com.visualstudio.code` (no MS editor repo). +3. **.NET:** official `dotnet-install.sh` (no MS vendor repo). +4. **oh-my-zsh:** purged from `zshrc`. +5. **Scope:** full rework (applied). -1. **Container runtime on Debian:** `docker.io` (Debian pkg) vs official **Docker CE** repo vs **Podman** (since Fedora used Podman Desktop + `moby-engine`)? This repo mixes docker aliases and Podman Desktop flatpak. -2. **VS Code source:** keep **Flatpak** `com.visualstudio.code` (already in list) or switch to MS apt repo `.deb` like Fedora's RPM setup did? -3. **.NET SDK version to target:** repo tries 8.0 → 9.0 on Fedora; pick a default for Debian (e.g. SDK 10 on trixie) or keep the fallback loop. -4. **oh-my-zsh remnants in `zshrc`:** purge (recommended) or keep? -5. **Scope of this branch:** full rework of all scripts, or a lighter pass (fix names + PEP 668 + .NET script) to validate on a live trixie box first? - ---- - -## 5. Suggested execution order - -1. Do the cheap, high-confidence fixes: - - `main-setup.sh` gating + `.NET` prompt - - `01-package-install.sh` Debian package renames (`bind9-dnsutils`, `bat` symlink, prerequisites, virt stack) - - `02-dev-tools-setup.sh` PEP 668 pynvim fix -2. Rename/generalize `03-fedora-dotnet-setup.sh` → `03-dotnet-setup.sh`. -3. Add upstream repo/bootstrap logic for `gh`, `terraform`, `kubectl`, `minikube`, `k9s`. -4. Purge oh-my-zsh remnants from `zshrc`. -5. Update `README.md`. -6. Test end-to-end on a Debian 13 VM (or real install) before merging to `master`. +## 5. Remaining work / next steps +- [ ] **Test end-to-end on a real Debian 13 box**: run `./main-setup.sh`, then `bash scripts/verify-debian.sh`, and fix anything the checker flags. +- [ ] Decide whether to keep Fedora/dnf support long-term or trim it later (kept for now). +- [ ] Merge `debian-13` → `master` after validation. --- diff --git a/main-setup.sh b/main-setup.sh index f9d4ef2..7041229 100755 --- a/main-setup.sh +++ b/main-setup.sh @@ -54,7 +54,8 @@ prompt_yes_no() { # Function to run a script if approved run_script() { local script_path="$1" - local script_name="$(basename "$script_path")" + local script_name + script_name="$(basename "$script_path")" if ! bash "$script_path"; then echo "ERROR: $script_name failed." diff --git a/scripts/00-system-prep.sh b/scripts/00-system-prep.sh index 1dc7a07..da8603a 100755 --- a/scripts/00-system-prep.sh +++ b/scripts/00-system-prep.sh @@ -1,7 +1,8 @@ #!/bin/bash # 00-system-prep.sh -# Updates system and sets up Flathub. +# Updates system, bootstraps sudo, enables Debian extra components, +# and sets up Flathub. # Relies on DISTRO and PACKAGE_MANAGER being set by the caller. echo "--- Starting System Preparation ---" @@ -11,6 +12,27 @@ if [ -z "$DISTRO" ] || [ -z "$PACKAGE_MANAGER" ]; then exit 1 fi +# Ensure 'sudo' exists so all later scripts can use it. +# - Running as root on a fresh Debian: installing sudo lets root invoke +# `sudo ...` directly (root is not subject to sudoers). +# - Non-root users without sudo are handled by main-setup.sh's preflight. +if ! command -v sudo &>/dev/null; then + echo "sudo not found. Bootstrapping it..." + if [ "$(id -u)" -eq 0 ]; then + if [ "$PACKAGE_MANAGER" == "apt" ]; then + apt-get update && apt-get install -y sudo + elif [ "$PACKAGE_MANAGER" == "dnf" ]; then + dnf install -y sudo + fi + echo "sudo installed. Add your normal user to the sudo group if desired:" + echo " usermod -aG sudo (then log out/in)" + else + echo "ERROR: 'sudo' is required but missing, and you are not root." + echo "As root, run: apt install -y sudo && usermod -aG sudo \$USER" + exit 1 + fi +fi + # Update system before installing packages echo "Updating system packages..." if [ "$PACKAGE_MANAGER" == "dnf" ]; then @@ -21,6 +43,66 @@ else echo "WARNING: Unknown package manager '$PACKAGE_MANAGER'. Skipping system update." fi +# --------------------------------------------------------------------------- +# Debian extras (only on Debian; idempotent) +# --------------------------------------------------------------------------- +if [ "$DISTRO" == "debian" ]; then + + # Enable contrib + non-free-firmware components on the main archive. + # Needed for some WiFi/GPU firmware and non-free tooling. Skip with SKIP_NONFREE=1. + if [ -z "$SKIP_NONFREE" ]; then + echo "Enabling 'contrib' and 'non-free-firmware' apt components..." + _enable_debian_components() { + local f="/etc/apt/sources.list.d/debian.sources" + local tmp + # deb822 format used by Debian 12+ (trixie ships debian.sources) + if [ -f "$f" ]; then + tmp="$(mktemp)" + if awk ' + /^URIs:[[:space:]]/ { in_archive = ($0 ~ /deb\.debian\.org\/debian/) } + in_archive && /^Components:/ && $0 !~ /contrib/ { + print $0 " contrib non-free-firmware" + in_archive = 0 + next + } + { print } + ' "$f" > "$tmp"; then + if ! diff -q "$f" "$tmp" >/dev/null; then + sudo cp "$f" "${f}.bak" + sudo mv "$tmp" "$f" + echo " -> updated ${f}" + else + rm -f "$tmp" + echo " -> components already present in ${f}" + fi + else + rm -f "$tmp" + echo "WARNING: could not parse ${f}; skipping component changes." + fi + fi + # Legacy single-line /etc/apt/sources.list fallback + if [ -f /etc/apt/sources.list ] \ + && grep -qE '^deb[[:space:]]+https?://deb\.debian\.org/debian' /etc/apt/sources.list \ + && ! grep -qE '^deb[[:space:]]+https?://deb\.debian\.org/debian[^#]*non-free-firmware' /etc/apt/sources.list; then + sudo sed -i -E 's/^(deb[[:space:]]+https?:\/\/deb\.debian\.org\/debian[^#]*main)([[:space:]]|$)/\1 contrib non-free-firmware/' /etc/apt/sources.list + echo " -> updated /etc/apt/sources.list" + fi + sudo apt update + } + _enable_debian_components + else + echo "SKIP_NONFREE is set - leaving apt components unchanged." + fi + + # needrestart prompts (or auto-restarts) services after library upgrades. + if ! dpkg-query -W -f='${Status}' needrestart 2>/dev/null | grep -q "ok installed"; then + echo "Installing needrestart..." + sudo apt install -y needrestart + else + echo "needrestart already installed." + fi +fi + # Setup Flatpak echo "Setting up Flathub repository..." if command -v flatpak &> /dev/null; then diff --git a/scripts/01-package-install.sh b/scripts/01-package-install.sh index 368ab7d..f8bd69f 100755 --- a/scripts/01-package-install.sh +++ b/scripts/01-package-install.sh @@ -76,7 +76,7 @@ fi # Combine package lists PACKAGE_LIST=("${BASE_PACKAGE_LIST[@]}" "${DISTRO_SPECIFIC_PACKAGES[@]}") -PACKAGE_LIST=($(printf "%s\n" "${PACKAGE_LIST[@]}" | LC_ALL=C sort -u)) +mapfile -t PACKAGE_LIST < <(printf "%s\n" "${PACKAGE_LIST[@]}" | LC_ALL=C sort -u) # --- apt helpers --------------------------------------------------------- _apt_is_installed() { @@ -210,10 +210,21 @@ _apt_add_hashicorp_repo() { | sudo tee /etc/apt/sources.list.d/hashicorp.list >/dev/null } -# Kubernetes apt repo. Bump K8S_MINOR to track the current stable minor. -K8S_MINOR="v1.32" +# Kubernetes apt repo. Auto-detects the latest stable minor from the +# kubernetes/kubernetes GitHub release feed, falling back to a pinned minor +# if the API is unreachable. Override explicitly with K8S_MINOR=... +K8S_MINOR_FALLBACK="v1.36" +_k8s_latest_minor() { + local tag minor + tag="$(curl -fsSL --max-time 10 https://api.github.com/repos/kubernetes/kubernetes/releases/latest \ + | grep '"tag_name"' | cut -d'"' -f4)" || return 1 + minor="${tag#v}" # e.g. 1.36.0 + minor="${minor%.*}" # strip patch -> 1.36 + echo "v${minor}" +} _apt_add_kubernetes_repo() { [ -f /etc/apt/sources.list.d/kubernetes.list ] && { echo "Kubernetes repo already configured."; return 0; } + K8S_MINOR="${K8S_MINOR:-$(_k8s_latest_minor || echo "$K8S_MINOR_FALLBACK")}" echo "Adding Kubernetes apt repository (${K8S_MINOR})..." curl -fsSL "https://pkgs.k8s.io/core:/stable:/${K8S_MINOR}/deb/Release.key" \ | sudo gpg --dearmor -o /usr/share/keyrings/kubernetes-archive-keyring.gpg diff --git a/scripts/verify-debian.sh b/scripts/verify-debian.sh new file mode 100755 index 0000000..e7d1b64 --- /dev/null +++ b/scripts/verify-debian.sh @@ -0,0 +1,176 @@ +#!/bin/bash + +# verify-debian.sh +# Non-destructive sanity checks for a freshly set-up Debian 13 machine. +# Verifies the pieces that 00-03 + 04-config-symlinks.sh are expected to leave behind. +# Exit code: 0 = all critical checks passed; 1 = at least one critical check failed. +# +# Usage: bash scripts/verify-debian.sh (no root needed) + +# --- output helpers ------------------------------------------------------- +if [ -t 1 ]; then + C_GREEN=$'\e[32m'; C_RED=$'\e[31m'; C_YELLOW=$'\e[33m'; C_BOLD=$'\e[1m'; C_OFF=$'\e[0m' +else + C_GREEN=""; C_RED=""; C_YELLOW=""; C_BOLD=""; C_OFF="" +fi + +PASS=0 +WARN=0 +FAIL=0 + +ok() { PASS=$((PASS+1)); echo "${C_GREEN}✔${C_OFF} $1"; } +warn() { WARN=$((WARN+1)); echo "${C_YELLOW}⚠${C_OFF} $1"; } +bad() { FAIL=$((FAIL+1)); echo "${C_RED}✘${C_OFF} $1"; } + +# --- helpers -------------------------------------------------------------- +check_cmd() { # check_cmd "label" "command-name" + if command -v "$2" &>/dev/null; then ok "$1: found ($(command -v "$2"))"; else bad "$1: NOT FOUND"; fi +} + +check_symlink_target() { # check_symlink_target "label" "link-path" "expected-target" + if [ -L "$2" ]; then + local target; target="$(readlink -f "$2")" + if [ "$target" = "$3" ] || [ "$(basename "$target")" = "$(basename "$3")" ]; then + ok "$1: $2 -> $target" + else + warn "$1: $2 points to $target (expected $3)" + fi + else + warn "$1: $2 is not a symlink" + fi +} + +check_service() { # check_service "label" "unit" + if systemctl is-active --quiet "$2" 2>/dev/null; then ok "$1: active"; else warn "$1: not active"; fi +} + +# --- 1. OS ------------------------------------------------------------------ +echo "${C_BOLD}== OS ==${C_OFF}" +. /etc/os-release +echo " distro: $PRETTY_NAME (id=$ID, codename=${VERSION_CODENAME:-unknown})" +if [ "$ID" = "debian" ]; then + ok "Debian detected" +else + warn "Not Debian (id=$ID) - this script targets Debian 13" +fi + +# --- 2. Package manager ------------------------------------------------------ +echo "" +echo "${C_BOLD}== Package manager ==${C_OFF}" +if command -v apt-get &>/dev/null; then ok "apt present"; else bad "apt missing"; fi +if command -v dpkg-query &>/dev/null; then :; else bad "dpkg missing"; fi + +# --- 3. Core packages --------------------------------------------------------- +echo "" +echo "${C_BOLD}== Core tools ==${C_OFF}" +for c in git curl wget jq ripgrep tmux zsh fzf zoxide eza unzip btop direnv tldr socat mtr nmap whois traceroute tcpdump iperf3 httpie ansible virt-manager flatpak python3; do + check_cmd "$c" "$c" +done + +# --- 4. Debian binary renames ------------------------------------------------- +echo "" +echo "${C_BOLD}== Debian binary renames ==${C_OFF}" +if command -v batcat &>/dev/null; then + check_symlink_target "bat -> batcat" "/usr/local/bin/bat" "/usr/bin/batcat" +elif command -v bat &>/dev/null; then + ok "bat present directly" +else + bad "bat/batcat not found" +fi +if command -v fdfind &>/dev/null; then + check_symlink_target "fd -> fdfind" "/usr/local/bin/fd" "/usr/bin/fdfind" +elif command -v fd &>/dev/null; then + ok "fd present directly" +else + bad "fd/fdfind not found" +fi +if command -v dig &>/dev/null; then ok "dig present"; else bad "dig missing (install bind9-dnsutils)"; fi + +# --- 5. Upstream-only tools ---------------------------------------------------- +echo "" +echo "${C_BOLD}== Upstream tooling ==${C_OFF}" +for c in gh terraform kubectl k9s minikube; do check_cmd "$c" "$c"; done + +# --- 6. Development tools ------------------------------------------------------- +echo "" +echo "${C_BOLD}== Development tools ==${C_OFF}" +for c in nvim go rustc cargo oh-my-posh helm kubectx kubens stern dive trivy aws; do check_cmd "$c" "$c"; done + +# Go binary path sanity +if command -v go &>/dev/null; then ok "GOPATH=$(go env GOPATH 2>/dev/null)"; fi + +# pynvim (PEP 668 note: system pip is externally managed on Debian 13) +if /usr/bin/python3 -m pip show pynvim &>/dev/null; then + ok "pynvim installed for python3" +else + warn "pynvim not found for /usr/bin/python3 (install with --break-system-packages if wanted)" +fi + +# --- 7. .NET / backend ----------------------------------------------------------- +echo "" +echo "${C_BOLD}== .NET / backend services ==${C_OFF}" +if command -v dotnet &>/dev/null; then + ok "dotnet: $(dotnet --version 2>/dev/null)" +else + bad "dotnet not on PATH (install via 03-dotnet-setup.sh or add ~/.dotnet to PATH)" +fi +check_cmd "podman" "podman" +if command -v docker &>/dev/null; then + ok "docker shim present ($(readlink -f "$(command -v docker)" 2>/dev/null || echo docker))" +else + warn "docker shim missing (install podman-docker)" +fi +if command -v podman-compose &>/dev/null; then ok "podman-compose present"; else warn "podman-compose missing"; fi +check_service "postgresql" "postgresql" +check_service "libvirtd" "libvirtd" + +# --- 8. Groups ------------------------------------------------------------------- +echo "" +echo "${C_BOLD}== Group membership ==${C_OFF}" +for g in libvirt kvm; do + if id -nG 2>/dev/null | tr ' ' '\n' | grep -qx "$g"; then + ok "in group '$g'" + else + warn "not in group '$g' (needed for virt-manager/KVM; log out/in after usermod)" + fi +done + +# --- 9. Flatpak -------------------------------------------------------------------- +echo "" +echo "${C_BOLD}== Flatpak ==${C_OFF}" +if command -v flatpak &>/dev/null && flatpak remotes 2>/dev/null | grep -q flathub; then + ok "flathub remote present" +else + warn "flathub remote not configured" +fi + +# --- 10. Shell rc symlinks ---------------------------------------------------------- +echo "" +echo "${C_BOLD}== Dotfile symlinks ==${C_OFF}" +for d in bashrc zshrc gitconfig aliases.bash aliases.zsh; do + if [ -e "$HOME/.$d" ] || [ -L "$HOME/.$d" ]; then + # shellcheck disable=SC2088 + ok "~/.$d linked" + else + # shellcheck disable=SC2088 + warn "~/.$d missing" + fi +done + +check_symlink_target "oh-my-posh theme" "$HOME/.config/oh-my-posh/theme.omp.json" "theme.omp.json" +check_symlink_target "nvim config" "$HOME/.config/nvim" "nvim" +check_symlink_target "nushell config" "$HOME/.config/nushell" "nushell" + +# --- Summary ------------------------------------------------------------------------ +echo "" +echo "${C_BOLD}========================================${C_OFF}" +echo "${C_GREEN}✔${C_OFF} ${PASS} passed ${C_YELLOW}⚠${C_OFF} ${WARN} warnings ${C_RED}✘${C_OFF} ${FAIL} failed" +echo "${C_BOLD}========================================${C_OFF}" +echo "" +if [ "$FAIL" -gt 0 ]; then + echo "Some critical checks failed. See ✘ items above." + exit 1 +else + echo "All critical checks passed. Warnings above are optional/suggested." + exit 0 +fi