#!/usr/bin/env bash # scripts/quality/shellcheck.sh # # Run shellcheck across every Bash script we own (scripts/**/*.sh). # Skips the macOS duplicate artefacts (` 2.sh`). # # Local-only. CI promotion once every script is shellcheck-clean. # # Usage: # bash scripts/quality/shellcheck.sh # warn + advisory exit 0 # bash scripts/quality/shellcheck.sh --strict # fail on any finding # # Exit codes: # 0 no findings, or findings but --strict not set # 1 --strict was set and shellcheck reported findings # 2 prerequisite missing set -uo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" cd "$ROOT" || exit 2 command -v shellcheck >/dev/null 2>&1 || { echo "FAIL: shellcheck not in PATH." >&2 echo " macOS: brew install shellcheck" >&2 echo " Linux: sudo apt install shellcheck" >&2 exit 2 } STRICT=0 for arg in "$@"; do case "$arg" in --strict) STRICT=1 ;; *) echo "Unknown arg: $arg" >&2; exit 2 ;; esac done FILES="$(find scripts -name "*.sh" -type f 2>/dev/null \ | grep -v " 2\.sh" \ | sort)" if [ -z "$FILES" ]; then echo "FAIL: no shell scripts found under scripts/" >&2 exit 2 fi echo "shellcheck ($(shellcheck --version | sed -n '2p'))" echo "Scanning shell scripts under scripts/" echo n_total=0 n_with_findings=0 total_findings=0 while IFS= read -r f; do [ -z "$f" ] && continue n_total=$((n_total + 1)) # -S style: warnings + above (skip "info" and "style" noise). out="$(shellcheck --severity=warning --shell=bash "$f" 2>&1)" if [ -n "$out" ]; then echo "── $f ──" echo "$out" echo n_with_findings=$((n_with_findings + 1)) # rough count: one finding per "In line N:" block cnt=$(printf '%s' "$out" | grep -c "^In .* line") total_findings=$((total_findings + cnt)) fi done <