chore: enforce shellcheck across the repo

Establish shellcheck as a mandatory pre-commit quality gate and bring all 93
shell scripts to a clean state.

- tests/shellcheck.sh: wrapper that runs koalaman/shellcheck:stable via Docker
  (no native binary needed), skips vendored + upstream librenms-agent scripts.
- .shellcheckrc: documents intentional codebase-wide disables (dynamic source
  paths SC1090/SC1091, client-side ssh expansion SC2029).
- AGENTS.md: new Git Policy rule mandating clean shellcheck for every shell
  script before commit.

Fixes applied (real bugs + quality): missing quote in netinfra/gather-configs.sh
(caused cascading parse errors), unquoted expansions, declare-and-assign masking,
egrep -> grep -E, $FUNCNAME array indexing, unused variable removal, cd || exit.
Intentional patterns (sourced config, sysfs/ps diagnostics, ssh heredocs that
expand local config) get justified targeted disables.

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
This commit is contained in:
2026-07-30 08:56:31 -05:00
parent 54e9927167
commit 0fa0692c37
37 changed files with 226 additions and 89 deletions
+3 -1
View File
@@ -41,6 +41,7 @@ trap 'rm -rf "$TMP"' EXIT
# plus all error-like counters. Falls back gracefully if a counter doesn't
# exist (different NIC drivers expose different names).
nic_snapshot() {
# shellcheck disable=SC2013 # interface names contain no spaces; word-splitting is safe here
for s in $(awk '/^Slave Interface:/{print $3}' "/proc/net/bonding/$BOND"); do
[ -n "$s" ] || continue
echo "[$s]"
@@ -144,9 +145,10 @@ echo "Bond: $BOND"
echo ""
echo "--- bond0 hash + driver ---"
grep -E "Bonding Mode|Transmit Hash|Number of ports|Partner Mac" /proc/net/bonding/$BOND
# shellcheck disable=SC2013 # interface names contain no spaces; word-splitting is safe here
for s in $(awk '/^Slave Interface:/{print $3}' /proc/net/bonding/$BOND); do
drv=$(ethtool -i "$s" 2>/dev/null | awk -F: '/^driver:/{print $2}' | sed 's/^ *//')
speed=$(cat /sys/class/net/$s/speed 2>/dev/null)
speed=$(cat "/sys/class/net/$s/speed" 2>/dev/null)
ring=$(ethtool -g "$s" 2>/dev/null | awk '/RX:/{print $2; exit}')
printf " %-8s driver=%-20s speed=%-6s current RX ring=%s\n" "$s" "$drv" "$speed" "$ring"
done
+1
View File
@@ -68,6 +68,7 @@ echo "[$(ts)] switch=$SWITCH device=$DEVICE baud=$BAUD host=$HOST"
# 1. De-conflict: any local ssh to pfv-tsys4 in flight?
echo "[$(ts)] checking for in-flight ssh to pfv-tsys4..."
# shellcheck disable=SC2009 # intentional: need full ps columns filtered by process args
if ps -eo pid,etime,args | grep -E 'ssh.*pfv-tsys4|scp.*pfv-tsys4' | grep -v grep >/tmp/.swcap.ps 2>&1; then
cat /tmp/.swcap.ps
echo "[$(ts)] ABORT: another ssh/scp to pfv-tsys4 is running (other agent?)." >&2
+1
View File
@@ -5,6 +5,7 @@ set -u
# De-conflict: any ssh to pfv-tsys4 right now?
echo "===== LOCAL ssh activity ====="
# shellcheck disable=SC2009 # intentional: need full ps columns filtered by process args
ps -eo pid,etime,args | grep -E 'ssh.*pfv-tsys' | grep -v grep || echo "(none to pfv-tsys4)"
echo
+1
View File
@@ -5,6 +5,7 @@
set -u
echo "===== LOCAL ssh/scp activity (other-agent de-confliction) ====="
# shellcheck disable=SC2009 # intentional: need full ps columns (etime,args) filtered by process args
ps -eo pid,ppid,etime,user,args | grep -E 'ssh|scp' | grep -v grep || echo "(none)"
echo
+3 -3
View File
@@ -1,5 +1,5 @@
#!/bin/bash
# shellcheck.sh - permanent wrapper to lint every shell script in this project.
# Lint wrapper - permanent wrapper to lint every shell script in this project.
#
# Uses the koalaman/shellcheck:stable docker image so nothing is installed
# on the host. Run from anywhere; lints scripts/ and any .sh under switches/.
@@ -45,7 +45,7 @@ echo
# Make paths relative to ROOT so docker volume maps cleanly
REL_TARGETS=()
for t in "${TARGETS[@]}"; do
rel="${t#$ROOT/}"
rel="${t#"$ROOT"/}"
[ "$rel" = "$t" ] && rel="$t"
REL_TARGETS+=("$rel")
done
@@ -61,5 +61,5 @@ if [ "$STRICT" -eq 1 ]; then
exit $RC
fi
# Non-strict: only fail on parse errors / errors, not style notes.
# shellcheck exit code 1 means "findings"; re-run with severity to distinguish.
# (a shellcheck exit code of 1 means "findings"; re-run with severity to distinguish.)
exit 0
+2 -4
View File
@@ -75,8 +75,7 @@ for HOST in "$@"; do
for vmid in $VM_LIST; do
name=$(ssh "${SSH_OPTS[@]}" "root@$HOST" "qm config $vmid 2>/dev/null | awk -F: '/^name:/{gsub(/^ /,\"\");print \$2}'")
echo -n " [$vmid $name] starting... "
start_output=$(ssh "${SSH_OPTS[@]}" "root@$HOST" "qm start $vmid" 2>&1)
if [ $? -eq 0 ]; then
if start_output=$(ssh "${SSH_OPTS[@]}" "root@$HOST" "qm start $vmid" 2>&1); then
echo "OK"
else
echo "FAILED: $start_output"
@@ -115,8 +114,7 @@ for HOST in "$@"; do
if [ "$agent" = "1" ]; then
name=$(ssh "${SSH_OPTS[@]}" "root@$HOST" "qm config $vmid 2>/dev/null | awk -F: '/^name:/{gsub(/^ /,\"\");print \$2}'")
echo -n " [$vmid $name] agent ping... "
result=$(ssh "${SSH_OPTS[@]}" "root@$HOST" "timeout 10 qm agent $vmid ping 2>&1")
if [ $? -eq 0 ]; then
if ssh "${SSH_OPTS[@]}" "root@$HOST" "timeout 10 qm agent $vmid ping" >/dev/null 2>&1; then
echo "OK"
else
echo "no response (VM may still be booting)"