test suite with docker container and linter
This commit is contained in:
@@ -0,0 +1,9 @@
|
|||||||
|
# Keep image builds light: the runtime mounts the live repo via -v.
|
||||||
|
# NOTE: do NOT exclude test/ - the dev stage COPYs the repo and then runs
|
||||||
|
# test/lint.sh from it.
|
||||||
|
.git
|
||||||
|
**/node_modules
|
||||||
|
**/lazy-lock.json
|
||||||
|
**/fish_variables
|
||||||
|
kreation/.vst3
|
||||||
|
term/.config/tmux/plugins
|
||||||
@@ -7,15 +7,15 @@ Only entries that would be missed without explicit help are included.
|
|||||||
## 1. Project Scope
|
## 1. Project Scope
|
||||||
|
|
||||||
- **Dotfiles** for a personal workstation (hyprland + Neovim + VST plugin).
|
- **Dotfiles** for a personal workstation (hyprland + Neovim + VST plugin).
|
||||||
- No tests, CI or build scripts – the repo is purely configuration data.
|
- No CI or build scripts – the repo is purely configuration data; the only executable content is the `install_*` scripts and the `test/` harness.
|
||||||
- The only executable content is the `install_*` shell scripts that install system packages.
|
- **Testing** lives in `test/`. Run `make test-lint` / `make test-stow` (offline, no Docker) or `make test-smoke` (Docker-based, see `test/Dockerfile` and `README.md → Testing`). The harness currently surfaces pre-existing Makefile bugs by design (missing `/` in `install_*` script paths, `stow nvim` with no `nvim/` package).
|
||||||
|
|
||||||
---
|
---
|
||||||
## 2. System Setup
|
## 2. System Setup
|
||||||
|
|
||||||
| Task | Command | Why it matters |
|
| Task | Command | Why it matters |
|
||||||
|------|---------|----------------|
|
|------|---------|----------------|
|
||||||
| **Install required programs** | `bash term/setup.sh` <br>`bash .install_skripte/install_basic_pcks.sh` <br>`bash .install_skripte/install_hypr_pcks.sh` <br>`bash .install_skripte/install_kreation_pcks.sh` | These scripts install Neovim, hyprland, VST runtime, and other utilities. Edit `term/packages.txt` to add/remove terminal packages. |
|
| **Install required programs** | `bash term/setup.sh` <br>`bash _install_skripte/install_basic_pcks.sh` <br>`bash _install_skripte/install_hypr_pcks.sh` <br>`bash _install_skripte/install_kreation_pcks.sh` | These scripts install Neovim, hyprland, VST runtime, and other utilities. Edit `term/packages.txt` to add/remove terminal packages. |
|
||||||
| **Link configuration directories** | `ln -s ~/.dotfiles/hypr/.config/hypr ~/.config/hypr` <br>`ln -s ~/.dotfiles/term/.config/nvim ~/.config/nvim` | The repository expects the configs to live in `~/.config`. |
|
| **Link configuration directories** | `ln -s ~/.dotfiles/hypr/.config/hypr ~/.config/hypr` <br>`ln -s ~/.dotfiles/term/.config/nvim ~/.config/nvim` | The repository expects the configs to live in `~/.config`. |
|
||||||
| **Add VST plugin** | Copy `kreation/.vst3/MT-PowerDrumKit.vst3` into your DAW’s plugins folder (usually `~/.vst`). | The repo contains a pre‑built VST for use in audio software. |
|
| **Add VST plugin** | Copy `kreation/.vst3/MT-PowerDrumKit.vst3` into your DAW’s plugins folder (usually `~/.vst`). | The repo contains a pre‑built VST for use in audio software. |
|
||||||
|
|
||||||
|
|||||||
@@ -19,3 +19,44 @@ kreation: basic
|
|||||||
stow kreation
|
stow kreation
|
||||||
bunters-monster: basic hypr kreation
|
bunters-monster: basic hypr kreation
|
||||||
_install_skripte/buntes-monster-links.sh
|
_install_skripte/buntes-monster-links.sh
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Testing (see README.md "Testing" and test/ for details)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
.PHONY: test test-lint test-smoke test-stow test-shellcheck test-lint-docker
|
||||||
|
|
||||||
|
# Full test suite: fast offline lint first, then the optional Docker smoke.
|
||||||
|
test: test-lint test-smoke
|
||||||
|
|
||||||
|
# Fast, offline integrity checks. No Docker required.
|
||||||
|
# shellcheck is used if installed, otherwise only syntax checks run.
|
||||||
|
test-lint:
|
||||||
|
bash test/lint.sh
|
||||||
|
|
||||||
|
# Check stow package resolution without creating symlinks.
|
||||||
|
test-stow:
|
||||||
|
bash test/probe_stow.sh
|
||||||
|
|
||||||
|
# Run shellcheck over all tracked repo scripts (needs shellcheck installed).
|
||||||
|
test-shellcheck:
|
||||||
|
shellcheck -S warning $$(git ls-files '*.sh' | grep -v '/tpm/')
|
||||||
|
|
||||||
|
# Test shellcheck: build the /dev stage with shellcheck and run lint.sh.
|
||||||
|
# docker build context is the repo root; .dockerignore excludes heavy dirs.
|
||||||
|
test-lint-docker:
|
||||||
|
docker build --target dev -t dotfiles-lint -f test/Dockerfile .
|
||||||
|
docker run --rm dotfiles-lint
|
||||||
|
|
||||||
|
# Docker smoke test: builds the Arch container and runs the dry probes.
|
||||||
|
# RUN_SMOKE=1 also installs the real pacman packages inside the container.
|
||||||
|
# RUN_SMOKE=1 RUN_FULL=1 additionally attempts AUR/npm + hypr/kreation.
|
||||||
|
# SKIP_NET=1 skips anything that contacts the network.
|
||||||
|
# Example: make test-smoke RUN_SMOKE=1
|
||||||
|
test-smoke:
|
||||||
|
docker build --target test -t dotfiles-test -f test/Dockerfile .
|
||||||
|
docker run --rm \
|
||||||
|
-e RUN_SMOKE="$${RUN_SMOKE:-0}" \
|
||||||
|
-e RUN_FULL="$${RUN_FULL:-0}" \
|
||||||
|
-e SKIP_NET=$${SKIP_NET:-0} \
|
||||||
|
-v "$$PWD":/dotfiles \
|
||||||
|
dotfiles-test
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ Personal configuration files for an Arch Linux workstation (Hyprland + Neovim +
|
|||||||
| `term/` | Fish + kitty + starship + nvim + tmux + yazi + fastfetch + opencode terminal environment |
|
| `term/` | Fish + kitty + starship + nvim + tmux + yazi + fastfetch + opencode terminal environment |
|
||||||
| `hypr/` | Hyprland compositor config, kitty theme, wpaperd wallpapers |
|
| `hypr/` | Hyprland compositor config, kitty theme, wpaperd wallpapers |
|
||||||
| `kreation/`| VST plugin (`MT-PowerDrumKit.vst3`) |
|
| `kreation/`| VST plugin (`MT-PowerDrumKit.vst3`) |
|
||||||
|
| `local_ai/`| Local AI / MCP tooling config |
|
||||||
| `_install_skripte/` | Miscellaneous install helper scripts |
|
| `_install_skripte/` | Miscellaneous install helper scripts |
|
||||||
|
| `test/` | Docker test harness (lint + smoke tests) |
|
||||||
|
|
||||||
Each top-level directory is a **stow package**. `stow` symlinks the files from the repository into your home directory (e.g. `term/.config/...` → `~/.config/...`).
|
Each top-level directory is a **stow package**. `stow` symlinks the files from the repository into your home directory (e.g. `term/.config/...` → `~/.config/...`).
|
||||||
|
|
||||||
@@ -125,4 +127,56 @@ stow --ignore='\.config/(local|misc)' term
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
The `test/` directory contains a Docker-based test harness that verifies the
|
||||||
|
install scripts, the `Makefile`, and the stow packages.
|
||||||
|
|
||||||
|
There are **two tiers**:
|
||||||
|
|
||||||
|
| Target | What it does | Docker required? |
|
||||||
|
|--------|--------------|------------------|
|
||||||
|
| `make test-lint` | Fast, offline integrity checks: `bash -n` syntax pass over every script, `shellcheck` (if installed), `packages.txt` marker validation, and Makefile ↔ script/stow-package name consistency. Catches typos like `_install_skripte_install_basic_pcks.sh` (missing `/`). | No |
|
||||||
|
| `make test-stow` | `stow --simulate` for every stow-able package; fails if a package the Makefile stows doesn't exist (e.g. `stow nvim` — nvim lives under `term/`). | No |
|
||||||
|
| `make test-shellcheck` | Runs shellcheck over all tracked scripts (shellcheck must be installed). | No |
|
||||||
|
| `make test-lint-docker` | Builds the `/dev` stage (`archlinux:latest` + shellcheck) and runs `test/lint.sh` inside it. | Yes |
|
||||||
|
| `make test-smoke` | Builds the `/test` stage and runs the container's dry probes: `make -n` over every target, `stow --simulate` over every Makefile-stowed package, plus `test/lint.sh`. A network-enabled `RUN_SMOKE=1` variant installs the real pacman packages. | Yes |
|
||||||
|
| `make test` | `test-lint` + `test-smoke`. | Partially |
|
||||||
|
|
||||||
|
### Docker smoke details
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make test-smoke # dry probes only (fast, no installs)
|
||||||
|
make test-smoke RUN_SMOKE=1 # + real pacman installs of the term packages
|
||||||
|
make test-smoke RUN_SMOKE=1 RUN_FULL=1 # + AUR/npm + hypr/kreation/buntes-monster
|
||||||
|
make test-smoke SKIP_NET=1 # skip anything that contacts the network
|
||||||
|
```
|
||||||
|
|
||||||
|
The container:
|
||||||
|
|
||||||
|
- uses `archlinux:latest` (the scripts assume Arch + pacman),
|
||||||
|
- creates a non-root user (`mathias`, uid 1000) with passwordless sudo so the
|
||||||
|
scripts don't hang on a password prompt,
|
||||||
|
- mounts the live repo at `/dotfiles` (`-v "$PWD":/dotfiles`), so the tests
|
||||||
|
always run against your current checkout,
|
||||||
|
- stubs host daemon commands (`systemctl`, `updatedb`, `kbuildsycoca6`,
|
||||||
|
`dbus-update-activation-environment`) as no-ops so nothing talks to a real
|
||||||
|
systemd/dbus inside the container.
|
||||||
|
|
||||||
|
In the `RUN_SMOKE=1` install path, the harness additionally **verifies the
|
||||||
|
installed packages** (`test/verify_packages.sh`):
|
||||||
|
- every `@pacman` entry is confirmed via `pacman -Q <pkg>` (covers fonts,
|
||||||
|
libs and python modules that ship no binary),
|
||||||
|
- a curated mapping probes each package's binary with a real
|
||||||
|
`--version`-style call (e.g. `fish --version`, `nvim --version`,
|
||||||
|
`ssh -V`), failing if the binary is missing or does not report a version.
|
||||||
|
|
||||||
|
> **Note for the current repo state:** `make test-lint` and `make test-smoke`
|
||||||
|
> currently **fail** because the harness surfaces pre-existing Makefile bugs
|
||||||
|
> (missing `/` in the `install_*` script paths and `stow nvim` for a
|
||||||
|
> non-existent `nvim/` package). That is the intended behaviour — fix the
|
||||||
|
> Makefile and the tests should go green.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
**When in doubt:** run `stow --verbose --simulate <target>` first — it prints what would happen (linking) without changing anything. Never `rm -rf` the `~/.config` of something you still want to keep; move it to a backup directory instead.
|
**When in doubt:** run `stow --verbose --simulate <target>` first — it prints what would happen (linking) without changing anything. Never `rm -rf` the `~/.config` of something you still want to keep; move it to a backup directory instead.
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# test/Dockerfile
|
||||||
|
#
|
||||||
|
# Docker test harness for the dotfiles repo.
|
||||||
|
#
|
||||||
|
# Two stages:
|
||||||
|
# --target dev : fast shellcheck/lint environment (no heavy installs)
|
||||||
|
# --target test : smoke-test environment that can run the install scripts
|
||||||
|
#
|
||||||
|
# Build with (repo root as context; .dockerignore trims heavy/binary dirs):
|
||||||
|
# docker build --target test -t dotfiles-test -f test/Dockerfile .
|
||||||
|
# Run with:
|
||||||
|
# docker run --rm -v "$PWD":/dotfiles dotfiles-test
|
||||||
|
#
|
||||||
|
# Or simply use the Makefile targets:
|
||||||
|
# make test-lint-docker # build dev stage, run shellcheck+lint
|
||||||
|
# make test-smoke # dry probes; add RUN_SMOKE=1 for real installs
|
||||||
|
#
|
||||||
|
# See README.md ("Testing") and test/entrypoint-test.sh for details.
|
||||||
|
|
||||||
|
FROM archlinux:latest AS base
|
||||||
|
|
||||||
|
# Non-root test user that mirrors the workstation account.
|
||||||
|
ARG UID_ARG=1000
|
||||||
|
ARG USERNAME=mathias
|
||||||
|
|
||||||
|
RUN pacman -Syu --noconfirm \
|
||||||
|
base-devel \
|
||||||
|
git \
|
||||||
|
stow \
|
||||||
|
curl \
|
||||||
|
which \
|
||||||
|
sudo \
|
||||||
|
&& pacman -Scc --noconfirm
|
||||||
|
|
||||||
|
RUN useradd -m -u "$UID_ARG" -s /bin/bash -G wheel "$USERNAME"
|
||||||
|
|
||||||
|
# Passwordless sudo so scripts that call `sudo pacman`, `sudo systemctl`, etc.
|
||||||
|
# do not hang on a password prompt inside the container.
|
||||||
|
RUN printf '%%wheel ALL=(ALL) NOPASSWD: ALL\n' > /etc/sudoers.d/wheel-nopasswd \
|
||||||
|
&& chmod 440 /etc/sudoers.d/wheel-nopasswd
|
||||||
|
|
||||||
|
USER "$USERNAME"
|
||||||
|
ENV HOME="/home/$USERNAME"
|
||||||
|
WORKDIR /dotfiles
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# dev stage: shellcheck/lint only. Fast, no install heavy-lifting.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
FROM base AS dev
|
||||||
|
|
||||||
|
USER root
|
||||||
|
RUN pacman -S --noconfirm --needed shellcheck
|
||||||
|
|
||||||
|
USER "$USERNAME"
|
||||||
|
WORKDIR /dotfiles
|
||||||
|
|
||||||
|
COPY --chown="$USERNAME" . /dotfiles
|
||||||
|
|
||||||
|
CMD ["bash", "test/lint.sh"]
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# test stage: prepared to actually run the install scripts & probe stow.
|
||||||
|
# Uses the mounted repo (see -v $PWD:/dotfiles in the Makefile) so scripts
|
||||||
|
# see the live workspace checkout.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
FROM base AS test
|
||||||
|
|
||||||
|
USER "$USERNAME"
|
||||||
|
WORKDIR /dotfiles
|
||||||
|
|
||||||
|
# The entrypoint drives the smoke tests. The repo is mounted at /dotfiles
|
||||||
|
# via the Makefile's `docker run -v`.
|
||||||
|
ENTRYPOINT ["bash", "/dotfiles/test/entrypoint-test.sh"]
|
||||||
Executable
+252
@@ -0,0 +1,252 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# test/entrypoint-test.sh
|
||||||
|
#
|
||||||
|
# Runs inside the docker `test` stage. The repo is mounted read-write at
|
||||||
|
# /dotfiles (see the Makefile's `docker run -v "$PWD":/dotfiles`).
|
||||||
|
#
|
||||||
|
# Two tiers:
|
||||||
|
# 1. DRY PROBES (always, fast, offline)
|
||||||
|
# - `make -n` dry-runs to catch Makefile typos / missing scripts
|
||||||
|
# - `stow --simulate` to prove every stow target resolves
|
||||||
|
# 2. REAL SMOKE (guarded by RUN_SMOKE=1)
|
||||||
|
# - exercises term/setup.sh package parsing and (optionally) installs
|
||||||
|
# - verifies stow actually creates symlinks into $HOME
|
||||||
|
# Full AUR/npm + kreation/buntes-monster only run with RUN_FULL=1.
|
||||||
|
#
|
||||||
|
# Env flags (set via `docker run -e`):
|
||||||
|
# RUN_SMOKE=1 run the real pacman-install smoke
|
||||||
|
# RUN_FULL=1 also attempt AUR/npm + hypr/kreation/buntes-monster
|
||||||
|
# SKIP_NET=1 pretend no network; skip anything that needs the mirror
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
# Resolve the repo root: prefer the container's /dotfiles mount, otherwise
|
||||||
|
# fall back to the directory above this script (a local checkout).
|
||||||
|
if [[ -d /dotfiles ]]; then
|
||||||
|
cd /dotfiles || { echo "FATAL: repo not mounted at /dotfiles"; exit 1; }
|
||||||
|
else
|
||||||
|
cd "$(dirname "${BASH_SOURCE[0]}")/.." || { echo "FATAL: cannot locate repo root"; exit 1; }
|
||||||
|
fi
|
||||||
|
REPO="$(pwd)"
|
||||||
|
|
||||||
|
# Guard: both resolution paths must find a real checkout.
|
||||||
|
if [[ ! -f "$REPO/Makefile" || ! -d "$REPO/test" ]]; then
|
||||||
|
echo "FATAL: '$REPO' does not look like the dotfiles repo root." >&2
|
||||||
|
echo " In docker, run with -v \"\$PWD\":/dotfiles ." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
PASS=0
|
||||||
|
FAIL=0
|
||||||
|
say_ok() { printf ' ok: %s\n' "$*"; PASS=$((PASS+1)); }
|
||||||
|
say_fail(){ printf ' FAIL: %s\n' "$*"; FAIL=$((FAIL+1)); }
|
||||||
|
header() { printf '\n==== %s ====\n' "$*"; }
|
||||||
|
|
||||||
|
if command -v git >/dev/null 2>&1 && [[ ! -d .git ]]; then
|
||||||
|
# when mounted from a worktree, .git may be a file; fine.
|
||||||
|
:
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "entrypoint-test.sh: running as $(id -un) in $(pwd)"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 1. DRY PROBES - always run, cheap, offline.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
header "Dry probes"
|
||||||
|
|
||||||
|
# 1a. Makefile dry runs for every target.
|
||||||
|
# Discover targets dynamically so we never drift from the Makefile.
|
||||||
|
targets=$(grep -oE '^[A-Za-z0-9_-]+:' Makefile | tr -d ':' | grep -v '^test' | sort -u)
|
||||||
|
for target in $targets; do
|
||||||
|
# `make -n` must not error out from missing scripts / dirs.
|
||||||
|
if ( cd "$REPO" && make -n "$target" >/dev/null 2>err.txt ); then
|
||||||
|
say_ok "make -n $target parses"
|
||||||
|
else
|
||||||
|
say_fail "make -n $target (typo? missing script?)"
|
||||||
|
sed 's/^/ /' err.txt 2>/dev/null
|
||||||
|
fi
|
||||||
|
rm -f err.txt
|
||||||
|
done
|
||||||
|
|
||||||
|
# 1b. stow --simulate for every package the Makefile stows.
|
||||||
|
while IFS= read -r pkg; do
|
||||||
|
[[ -z "$pkg" ]] && continue
|
||||||
|
if stow --simulate --dir="$REPO" --target="$REPO" "$pkg" 2>err.txt; then
|
||||||
|
say_ok "stow --simulate $pkg resolves"
|
||||||
|
else
|
||||||
|
say_fail "stow --simulate $pkg (non-existent package or conflict)"
|
||||||
|
sed 's/^/ /' err.txt 2>/dev/null
|
||||||
|
fi
|
||||||
|
rm -f err.txt
|
||||||
|
done < <(grep -P '^\t' Makefile | grep -oE 'stow [A-Za-z0-9_-]+' | awk '{print $2}' | sort -u)
|
||||||
|
|
||||||
|
# 1c. run the repo's own lint + probe scripts (they're mounted here).
|
||||||
|
if [[ -x test/lint.sh ]] || [[ -f test/lint.sh ]]; then
|
||||||
|
if bash test/lint.sh >/dev/null 2>&1; then
|
||||||
|
say_ok "test/lint.sh"
|
||||||
|
else
|
||||||
|
say_fail "test/lint.sh (see logs above / rerun outside container)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
say_fail "test/lint.sh not present in mount"
|
||||||
|
fi
|
||||||
|
|
||||||
|
header "Smoke gates"
|
||||||
|
echo " RUN_SMOKE=${RUN_SMOKE:-0} RUN_FULL=${RUN_FULL:-0} SKIP_NET=${SKIP_NET:-0}"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 2. REAL SMOKE (RUN_SMOKE=1)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
if [[ "${RUN_SMOKE:-0}" == "1" ]]; then
|
||||||
|
header "Real smoke (RUN_SMOKE=1)"
|
||||||
|
|
||||||
|
# 2a. Allow scripts to use sudo without a password (already NOPASSWD via
|
||||||
|
# the Dockerfile for the wheel group).
|
||||||
|
#
|
||||||
|
# 2b. Override host-daemon commands with no-ops so nothing tries to talk
|
||||||
|
# to systemd/dbus/dbus in the container.
|
||||||
|
mkdir -p "$HOME/bin"
|
||||||
|
cat > "$HOME/bin/systemctl" <<'NOOP'
|
||||||
|
#!/bin/bash
|
||||||
|
echo "[stub] systemctl $*"
|
||||||
|
exit 0
|
||||||
|
NOOP
|
||||||
|
cat > "$HOME/bin/dbus-update-activation-environment" <<'NOOP'
|
||||||
|
#!/bin/bash
|
||||||
|
echo "[stub] dbus-update-activation-environment $*"
|
||||||
|
exit 0
|
||||||
|
NOOP
|
||||||
|
cat > "$HOME/bin/kbuildsycoca6" <<'NOOP'
|
||||||
|
#!/bin/bash
|
||||||
|
echo "[stub] kbuildsycoca6 $*"
|
||||||
|
exit 0
|
||||||
|
NOOP
|
||||||
|
cat > "$HOME/bin/updatedb" <<'NOOP'
|
||||||
|
#!/bin/bash
|
||||||
|
echo "[stub] updatedb $*"
|
||||||
|
exit 0
|
||||||
|
NOOP
|
||||||
|
chmod +x "$HOME/bin/"*
|
||||||
|
export PATH="$HOME/bin:$PATH"
|
||||||
|
|
||||||
|
# 2c. term/setup.sh package parsing sanity (mirrors read_packages).
|
||||||
|
if ( cd "$REPO" && bash -n term/setup.sh ); then
|
||||||
|
say_ok "term/setup.sh syntax OK"
|
||||||
|
else
|
||||||
|
say_fail "term/setup.sh syntax error"
|
||||||
|
fi
|
||||||
|
pacman_count=$(awk '
|
||||||
|
/^@pacman/{s="pacman"}
|
||||||
|
/^@aur/{s="aur"}
|
||||||
|
/^@npm/{s="npm"}
|
||||||
|
/^[[:space:]]*#/{next}
|
||||||
|
/^[[:space:]]*$/{next}
|
||||||
|
s=="pacman"{pc++} s=="aur"{ac++} s=="npm"{nc++}
|
||||||
|
END{printf "%d %d %d", pc,ac,nc}
|
||||||
|
' $REPO/term/packages.txt)
|
||||||
|
read -r PC AC NC <<< "$pacman_count"
|
||||||
|
say_ok "packages.txt -> ${PC} pacman, ${AC} aur, ${NC} npm entries"
|
||||||
|
|
||||||
|
# 2d. If network allowed and not skipped, actually install the pacman
|
||||||
|
# section of term/setup.sh (mirrors `make term` without AUR/npm).
|
||||||
|
if [[ "${SKIP_NET:-0}" != "1" ]]; then
|
||||||
|
header "make term (pacman packages, AUR/npm skipped)"
|
||||||
|
# Patch packages.txt to drop @aur/@npm so we don't hit AUR/npm.
|
||||||
|
cp "$REPO/term/packages.txt" "$HOME/packages.txt.full"
|
||||||
|
awk '
|
||||||
|
/^@aur$/{s="skip"}; /^@npm$/{s="skip"}; /^@pacman$/{s="pacman"}
|
||||||
|
{ if (s!="skip" && NF>0 && $1 !~ /^#/) print }
|
||||||
|
' "$REPO/term/packages.txt" > "$HOME/packages.txt.term-only"
|
||||||
|
# set a scratch HOME for setup.sh so it doesn't touch the real one.
|
||||||
|
mkdir -p "$HOME/term"
|
||||||
|
cp "$REPO/term/setup.sh" "$HOME/term/setup.sh"
|
||||||
|
cp "$HOME/packages.txt.term-only" "$HOME/term/packages.txt"
|
||||||
|
# chsh is stubbed via $HOME/bin so even `chsh` works without a pty.
|
||||||
|
setup_ok=0
|
||||||
|
if ( cd "$HOME/term" && bash setup.sh ) ; then
|
||||||
|
say_ok "term/setup.sh (term-only) completed"
|
||||||
|
setup_ok=1
|
||||||
|
else
|
||||||
|
say_fail "term/setup.sh (term-only) failed (exit $?)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2d2. Verify that every package is really installed and its binary
|
||||||
|
# runs. Only meaningful if the install itself succeeded.
|
||||||
|
if [[ "$setup_ok" == "1" ]]; then
|
||||||
|
if [[ -x test/verify_packages.sh ]]; then
|
||||||
|
header "verify installed packages"
|
||||||
|
if bash test/verify_packages.sh "$HOME/packages.txt.term-only"; then
|
||||||
|
say_ok "verify_packages.sh: all packages verified"
|
||||||
|
else
|
||||||
|
say_fail "verify_packages.sh: one or more packages failed verification (see above)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
say_fail "test/verify_packages.sh missing"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " skipping package verification (install did not complete)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " SKIP_NET=1 -> skipping real package install and verification"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2e. stow --actual into $HOME to prove real symlinks are created.
|
||||||
|
header "stow real symlinks into \$HOME"
|
||||||
|
for pkg in term hypr kreation local_ai; do
|
||||||
|
if [[ -d "$REPO/$pkg" ]]; then
|
||||||
|
if stow --dir="$REPO" --target="$HOME" "$pkg" 2>err.txt; then
|
||||||
|
say_ok "stow $pkg into \$HOME"
|
||||||
|
else
|
||||||
|
say_fail "stow $pkg into \$HOME"
|
||||||
|
sed 's/^/ /' err.txt 2>/dev/null
|
||||||
|
fi
|
||||||
|
rm -f err.txt
|
||||||
|
else
|
||||||
|
say_fail "package dir missing: $pkg"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
# sanity: representative symlinks must now exist in $HOME.
|
||||||
|
missing=""
|
||||||
|
for link in .config/fish .config/hypr .vst3/MT-PowerDrumKit.vst3; do
|
||||||
|
[[ -e "$HOME/$link" ]] || missing="$missing $link"
|
||||||
|
done
|
||||||
|
if [[ -z "$missing" ]]; then
|
||||||
|
say_ok "stow created representative \$HOME symlinks"
|
||||||
|
else
|
||||||
|
say_fail "missing expected stow symlinks:$missing"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2f. kreation package: verify the VST path is stow-able and present.
|
||||||
|
header "kreation VST"
|
||||||
|
if [[ -f "$REPO/kreation/.vst3/MT-PowerDrumKit.vst3/Contents/x86_64-linux/MT-PowerDrumKit.so" ]]; then
|
||||||
|
say_ok "MT-PowerDrumKit.vst3 binary present"
|
||||||
|
else
|
||||||
|
say_fail "MT-PowerDrumKit.vst3 binary missing"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " RUN_SMOKE not set -> skipping real installs & symlink creation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 3. FULL (RUN_FULL=1) - opt-in heavy path (AUR/npm/hypr/kreation)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
if [[ "${RUN_FULL:-0}" == "1" ]] && [[ "${SKIP_NET:-0}" != "1" ]]; then
|
||||||
|
header "Full smoke (RUN_FULL=1)"
|
||||||
|
if ( cd "$REPO" && make buntes-monster ); then
|
||||||
|
say_ok "make buntes-monster"
|
||||||
|
else
|
||||||
|
say_fail "make buntes-monster (exit $?)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " RUN_FULL not set (or SKIP_NET) -> skipping full/hypr/kreation install."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Summary & exit code
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
echo
|
||||||
|
echo "=============================================="
|
||||||
|
echo "entrypoint-test.sh: $PASS passed, $FAIL failed"
|
||||||
|
echo "=============================================="
|
||||||
|
# Exit non-zero if anything failed.
|
||||||
|
[[ $FAIL -eq 0 ]]
|
||||||
Executable
+145
@@ -0,0 +1,145 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# test/lint.sh
|
||||||
|
#
|
||||||
|
# Fast, offline integrity checks for the dotfiles repo.
|
||||||
|
# Does NOT require Docker or any packages to be installed beyond:
|
||||||
|
# - shellcheck (silently skipped if unavailable)
|
||||||
|
# - bash (for `bash -n` syntax checks)
|
||||||
|
#
|
||||||
|
# Run with: make test-lint
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
CD="$(pwd)"
|
||||||
|
cd "$REPO_ROOT"
|
||||||
|
|
||||||
|
PASS=0
|
||||||
|
FAIL=0
|
||||||
|
|
||||||
|
say_fail() { printf 'FAIL: %s\n' "$*"; FAIL=$((FAIL+1)); }
|
||||||
|
say_pass() { printf 'ok: %s\n' "$*"; PASS=$((PASS+1)); }
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 1. Collect scripts to check.
|
||||||
|
# Only repo-owned scripts; skip vendored tpm/ plugin shell scripts.
|
||||||
|
# Preferred source is git ls-files (the repo checkout); fall back to a
|
||||||
|
# filesystem scan when .git is not present (e.g. inside the docker dev
|
||||||
|
# image, where the repo is COPYied without .git).
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
mapfile -t SCRIPTS < <(git ls-files '*.sh' 2>/dev/null | grep -v '/tpm/' | grep -vE '/plugins/[^/]+/scripts/')
|
||||||
|
if [[ ${#SCRIPTS[@]} -eq 0 ]]; then
|
||||||
|
mapfile -t SCRIPTS < <(find . -name '*.sh' -not -path './.git/*' -not -path '*/tpm/*' -not -path '*/.git/*' | sort)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ${#SCRIPTS[@]} -eq 0 ]]; then
|
||||||
|
say_fail "no *.sh files found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "== Checking $((${#SCRIPTS[@]})) tracked shell scripts =="
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 2. Syntax check (bash -n) - always runs, no external deps.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
for s in "${SCRIPTS[@]}"; do
|
||||||
|
if bash -n "$s" 2>/dev/null; then
|
||||||
|
:
|
||||||
|
else
|
||||||
|
say_fail "bash -n $s"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
[[ $FAIL -eq 0 ]] && say_pass "bash -n syntax check passed"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 3. shellcheck (optional dependency).
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
if command -v shellcheck >/dev/null 2>&1; then
|
||||||
|
echo "== Running shellcheck =="
|
||||||
|
if shellcheck --version >/dev/null 2>&1; then
|
||||||
|
if shellcheck -S warning "${SCRIPTS[@]}" ; then
|
||||||
|
say_pass "shellcheck (warning severity)"
|
||||||
|
else
|
||||||
|
say_fail "shellcheck reported issues (warning severity)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
say_fail "shellcheck binary present but not functional"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "== shellcheck not installed; skipping (install with: pacman -S shellcheck) =="
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 4. packages.txt parsing sanity (mirrors term/setup.sh read_packages).
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
echo "== Checking term/packages.txt markers =="
|
||||||
|
if grep -q '^@pacman' term/packages.txt; then
|
||||||
|
say_pass "packages.txt declares @pacman section"
|
||||||
|
else
|
||||||
|
say_fail "packages.txt missing @pacman marker"
|
||||||
|
fi
|
||||||
|
# every @aur/@npm marker must be matched by a real consumer in setup.sh
|
||||||
|
for marker in '@aur' '@npm'; do
|
||||||
|
if grep -q "^$marker" term/packages.txt; then
|
||||||
|
grep -q "$marker" term/setup.sh \
|
||||||
|
&& say_pass "packages.txt $marker handled by term/setup.sh" \
|
||||||
|
|| say_fail "packages.txt uses $marker but term/setup.sh does not handle it"
|
||||||
|
else
|
||||||
|
say_pass "packages.txt has no $marker section"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 5. Makefile <-> install script name consistency.
|
||||||
|
# Catches typos such as `_install_skripte_install_basic_pcks.sh`
|
||||||
|
# (missing '/') in Makefile targets.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
echo "== Checking Makefile references install scripts that exist =="
|
||||||
|
# Pull out every `*_install_*_pcks.sh` token mentioned in the Makefile.
|
||||||
|
while IFS= read -r tok; do
|
||||||
|
[[ -z "$tok" ]] && continue
|
||||||
|
if [[ -e "$REPO_ROOT/$tok" ]]; then
|
||||||
|
say_pass "Makefile references existing script: $tok"
|
||||||
|
else
|
||||||
|
say_fail "Makefile references missing script: $tok"
|
||||||
|
fi
|
||||||
|
done < <(grep -P '^\t' Makefile | grep -oE '[_A-Za-z0-9/-]*install_[A-Za-z0-9_-]*\.sh' | sort -u)
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 6. Makefile <-> stow package name consistency.
|
||||||
|
# Catches bugs such as `stow nvim` when the actual package dir is `term/`.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
echo "== Checking Makefile stow targets reference real package dirs =="
|
||||||
|
# A 'package' is any tracked top-level directory (a .config/ subtree is stowable).
|
||||||
|
have_pkg() {
|
||||||
|
local name="$1"
|
||||||
|
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||||
|
git ls-files 2>/dev/null | awk -F/ 'NF>1 {print $1}' | sort -u | grep -qx "$name"
|
||||||
|
else
|
||||||
|
# git-less checkout (e.g. docker dev image): fall back to filesystem.
|
||||||
|
# Only verify the dir exists; "is it stowable" is validated by stow
|
||||||
|
# itself during the smoke test.
|
||||||
|
[[ -d "$name" ]]
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
while IFS= read -r pkg; do
|
||||||
|
[[ -z "$pkg" ]] && continue
|
||||||
|
case "$pkg" in
|
||||||
|
\#*|"") continue ;;
|
||||||
|
esac
|
||||||
|
if have_pkg "$pkg" && [[ -d "$REPO_ROOT/$pkg" ]]; then
|
||||||
|
say_pass "Makefile stows existing package: $pkg"
|
||||||
|
else
|
||||||
|
say_fail "Makefile stows non-existent package: $pkg"
|
||||||
|
fi
|
||||||
|
done < <(grep -P '^\t' Makefile | grep -oE 'stow [A-Za-z0-9_-]+' | awk '{print $2}' | sort -u)
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Summary
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
echo
|
||||||
|
echo "=============================================="
|
||||||
|
echo "lint.sh: $PASS passed, $FAIL failed"
|
||||||
|
echo "=============================================="
|
||||||
|
[[ $FAIL -eq 0 ]]
|
||||||
Executable
+79
@@ -0,0 +1,79 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# test/probe_stow.sh
|
||||||
|
#
|
||||||
|
# Verifies that each package the Makefile stows actually resolves and that
|
||||||
|
# stow can create all symlinks into the target without conflicts.
|
||||||
|
#
|
||||||
|
# This is a DRY RUN: it uses `stow --simulate` and creates no real symlinks.
|
||||||
|
# It catches bugs like `stow nvim` for a non-existent `nvim/` package.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# bash test/probe_stow.sh # probe every stow-able package
|
||||||
|
# bash test/probe_stow.sh <pkg>... # probe specific packages
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
cd "$REPO_ROOT"
|
||||||
|
|
||||||
|
# A stow-able package is any tracked top-level directory that contains a
|
||||||
|
# dot-prefixed config subtree (or a dot-prefixed top-level file/dir).
|
||||||
|
tracked_basenames() {
|
||||||
|
git ls-files | awk -F/ 'NF>1 {print $1}' | sort -u
|
||||||
|
}
|
||||||
|
|
||||||
|
is_stowable() {
|
||||||
|
local pkg="$1"
|
||||||
|
[[ -d "$REPO_ROOT/$pkg" ]] || return 1
|
||||||
|
# stow needs at least one dot-prefixed entry at the package root
|
||||||
|
git ls-files "$pkg/" | grep -qE "^$pkg/\." || return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Determine which packages to probe.
|
||||||
|
PACKAGES=()
|
||||||
|
if [[ $# -eq 0 ]]; then
|
||||||
|
while IFS= read -r b; do
|
||||||
|
[[ -z "$b" ]] && continue
|
||||||
|
is_stowable "$b" && PACKAGES+=("$b")
|
||||||
|
done < <(tracked_basenames)
|
||||||
|
else
|
||||||
|
PACKAGES=("$@")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ${#PACKAGES[@]} -eq 0 ]]; then
|
||||||
|
echo "probe_stow.sh: no stow-able packages found" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "== Probing stow packages: ${PACKAGES[*]} =="
|
||||||
|
|
||||||
|
FAIL=0
|
||||||
|
for pkg in "${PACKAGES[@]}"; do
|
||||||
|
echo "--- stow --simulate: $pkg ---"
|
||||||
|
if stow --simulate --verbose=1 --dir="$REPO_ROOT" --target="$REPO_ROOT" "$pkg" >/dev/null 2>probe_err.txt; then
|
||||||
|
echo "ok: stow --simulate '$pkg' resolved without conflicts"
|
||||||
|
else
|
||||||
|
echo "FAIL: stow --simulate '$pkg'"
|
||||||
|
sed 's/^/ /' probe_err.txt
|
||||||
|
FAIL=$((FAIL+1))
|
||||||
|
fi
|
||||||
|
rm -f probe_err.txt
|
||||||
|
done
|
||||||
|
|
||||||
|
# Cross-check: does the Makefile stow anything that is NOT in our package set?
|
||||||
|
echo "== Checking Makefile stow targets against stow-able packages =="
|
||||||
|
make_pkgs="$(grep -P '^\t' Makefile | grep -oE 'stow [A-Za-z0-9_-]+' | awk '{print $2}' | sort -u)"
|
||||||
|
for mp in $make_pkgs; do
|
||||||
|
if is_stowable "$mp"; then
|
||||||
|
echo "ok: Makefile 'stow $mp' matches a stow-able package"
|
||||||
|
else
|
||||||
|
echo "FAIL: Makefile 'stow $mp' does not match a stow-able package"
|
||||||
|
FAIL=$((FAIL+1))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=============================================="
|
||||||
|
echo "probe_stow.sh: $(( ${#PACKAGES[@]} + $(printf '%s\n' $make_pkgs | grep -c . || true) )) checks, $FAIL failed"
|
||||||
|
echo "=============================================="
|
||||||
|
[[ $FAIL -eq 0 ]]
|
||||||
Executable
+121
@@ -0,0 +1,121 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# test/verify_packages.sh
|
||||||
|
#
|
||||||
|
# Post-install verification of the packages the smoke test installs.
|
||||||
|
# Given a package-list file (one package per line, `@section` markers
|
||||||
|
# allowed - only the section content is verified), it checks that every
|
||||||
|
# listed package is:
|
||||||
|
# 1. installed in the pacman DB -> `pacman -Q <pkg>`
|
||||||
|
# 2. its expected binary exists and reports a version (curated mapping)
|
||||||
|
#
|
||||||
|
# Layer 2 runs a real `--version`-style probe for packages that install a
|
||||||
|
# binary, so a broken/renamed executable is caught even if pacman reports
|
||||||
|
# the package as installed. Fonts/libs/modules without a binary are verified
|
||||||
|
# by Layer 1 only.
|
||||||
|
#
|
||||||
|
# Usage: bash test/verify_packages.sh <package-list-file>
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
PKGS_FILE="${1:?usage: verify_packages.sh <package-list-file>}"
|
||||||
|
[[ -f "$PKGS_FILE" ]] || { echo "verify_packages.sh: not a file: $PKGS_FILE" >&2; exit 1; }
|
||||||
|
|
||||||
|
PASS=0
|
||||||
|
FAIL=0
|
||||||
|
say_ok() { printf ' ok: %s\n' "$*"; PASS=$((PASS+1)); }
|
||||||
|
say_fail() { printf ' FAIL: %s\n' "$*"; FAIL=$((FAIL+1)); }
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Curated mapping: package -> "binary version_flag"
|
||||||
|
# - packages not listed here are checked with pacman -Q only
|
||||||
|
# - flag missing (empty) => existence check only
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
declare -A BINS=(
|
||||||
|
[fish]="fish --version"
|
||||||
|
[kitty]="kitty --version"
|
||||||
|
[neovim]="nvim --version"
|
||||||
|
[openssh]="ssh -V"
|
||||||
|
[rclone]="rclone --version"
|
||||||
|
[rsync]="rsync --version"
|
||||||
|
[unison]="unison -version"
|
||||||
|
[stow]="stow --version"
|
||||||
|
[nodejs]="node --version"
|
||||||
|
[npm]="npm --version"
|
||||||
|
[starship]="starship --version"
|
||||||
|
[fastfetch]="fastfetch --version"
|
||||||
|
[tree-sitter-cli]="tree-sitter --version"
|
||||||
|
[python]="python --version"
|
||||||
|
[python-pip]="pip3 --version"
|
||||||
|
[unzip]="unzip --version"
|
||||||
|
[selene]="selene --version"
|
||||||
|
[tmux]="tmux -V"
|
||||||
|
[yazi]="yazi --version"
|
||||||
|
[zoxide]="zoxide --version"
|
||||||
|
[fzf]="fzf --version"
|
||||||
|
[chromium]="chromium --version"
|
||||||
|
[locate]="locate --version"
|
||||||
|
)
|
||||||
|
|
||||||
|
# python-pip may expose `pip` instead of `pip3` on some setups; treat them
|
||||||
|
# as interchangeable so the probe does not spuriously fail.
|
||||||
|
probe_pip() {
|
||||||
|
for b in pip3 pip; do
|
||||||
|
if command -v "$b" >/dev/null 2>&1; then
|
||||||
|
out=$("$b" --version 2>&1) && [[ -n "$out" ]] && { say_ok "$b --version ($out)"; return 0; }
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
say_fail "python-pip: neither pip3 nor pip found/probe failed"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "== Verifying installed packages from: $PKGS_FILE =="
|
||||||
|
|
||||||
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||||
|
line="${line%%#*}" # strip trailing comments
|
||||||
|
line="${line// /}" # drop inline whitespace
|
||||||
|
[[ -z "$line" ]] && continue
|
||||||
|
[[ "$line" == @* ]] && continue # skip @pacman/@aur/@npm section markers
|
||||||
|
|
||||||
|
pkg="${line#*/}" # strip repo prefix (extra/kitty -> kitty)
|
||||||
|
if [[ -z "$pkg" ]]; then
|
||||||
|
say_fail "empty package name in '$line'"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Layer 1: installed in the pacman DB?
|
||||||
|
if pacman -Q "$pkg" >/dev/null 2>&1; then
|
||||||
|
say_ok "pacman -Q $pkg (installed)"
|
||||||
|
else
|
||||||
|
say_fail "pacman -Q $pkg (not installed)"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Layer 2: runtime binary probe (curated mapping)?
|
||||||
|
spec="${BINS[$pkg]:-}"
|
||||||
|
[[ -z "$spec" ]] && continue # no binary (font/lib/module) - Layer 1 only
|
||||||
|
|
||||||
|
# python-pip has special multi-candidate handling
|
||||||
|
[[ "$pkg" == "python-pip" ]] && { probe_pip; continue; }
|
||||||
|
|
||||||
|
read -r bin flag <<< "$spec"
|
||||||
|
if command -v "$bin" >/dev/null 2>&1; then
|
||||||
|
if [[ -z "$flag" ]]; then
|
||||||
|
say_ok "command -v $bin (present)"
|
||||||
|
else
|
||||||
|
out=$("$bin" $flag 2>&1) && [[ -n "$out" ]]
|
||||||
|
if [[ $? -eq 0 ]]; then
|
||||||
|
say_ok "$bin $flag ($out)"
|
||||||
|
else
|
||||||
|
say_fail "$bin $flag (binary present but probe failed)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
say_fail "command -v $bin (expected from package '$pkg')"
|
||||||
|
fi
|
||||||
|
done < "$PKGS_FILE"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "----------------------------------------------"
|
||||||
|
echo "verify_packages.sh: $PASS passed, $FAIL failed"
|
||||||
|
echo "----------------------------------------------"
|
||||||
|
[[ $FAIL -eq 0 ]]
|
||||||
Reference in New Issue
Block a user