Thanatos-27B / scripts /check.sh
FoolDev's picture
Rename back: Thanatos-27B-Heretic → Thanatos-27B (HF repo also renamed)
7197abd
#!/usr/bin/env bash
# Thanatos-27B — repo-local sanity checks.
#
# Runs everything that's cheap and catches a real-world bug we've already hit:
#
# 1. bash -n on every *.sh (catches syntax errors)
# 2. shellcheck on every *.sh, if installed (catches quoting/SC2086 bugs)
# 3. python3 -m pyflakes on every *.py (catches NameError, unused imports)
# 4. python3 -m py_compile on every *.py (catches actual syntax errors)
# 5. grep -E for the dash/dot filename footgun (the one we shipped in 6f2884f
# and had to fix in 82677d0)
# 6. grep -E for the VAR="$(cmd 2>/dev/null | filter)" silent-exit pattern
# under set -e + pipefail (the one we shipped in d87bc64 and fixed in 385ed94)
#
# Exit non-zero on any failure. Designed to run from .git/hooks/pre-commit.
#
# Usage:
# ./scripts/check.sh # one-shot
# ./scripts/install-hooks.sh # install as pre-commit hook
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "${ROOT}"
red() { printf "\033[31m%s\033[0m\n" "$*"; }
green() { printf "\033[32m%s\033[0m\n" "$*"; }
yellow() { printf "\033[33m%s\033[0m\n" "$*"; }
blue() { printf "\033[34m%s\033[0m\n" "$*"; }
FAIL=0
# ---- collect targets -------------------------------------------------------
# Find every shell script and every python file, ignoring vendored / hidden dirs.
mapfile -t SH_FILES < <(find . -type f -name '*.sh' -not -path './.git/*')
mapfile -t PY_FILES < <(find . -type f -name '*.py' -not -path './.git/*' -not -path '*/__pycache__/*')
blue "[*] checking ${#SH_FILES[@]} shell file(s) and ${#PY_FILES[@]} python file(s)"
# ---- 1. bash -n ------------------------------------------------------------
if (( ${#SH_FILES[@]} )); then
blue "[*] bash -n"
for f in "${SH_FILES[@]}"; do
if ! bash -n "$f" 2>/dev/null; then
red " [FAIL] bash -n $f"
bash -n "$f" || true
FAIL=1
else
echo " [ ok ] $f"
fi
done
fi
# ---- 2. shellcheck (optional) ---------------------------------------------
if command -v shellcheck >/dev/null 2>&1; then
blue "[*] shellcheck"
for f in "${SH_FILES[@]}"; do
if ! shellcheck -S warning "$f"; then
red " [FAIL] shellcheck $f"
FAIL=1
else
echo " [ ok ] $f"
fi
done
else
yellow "[~] shellcheck not installed (skip). Install with: apt install shellcheck"
fi
# ---- 3. pyflakes -----------------------------------------------------------
if (( ${#PY_FILES[@]} )); then
if python3 -c 'import pyflakes' 2>/dev/null; then
blue "[*] pyflakes"
for f in "${PY_FILES[@]}"; do
if ! python3 -m pyflakes "$f"; then
red " [FAIL] pyflakes $f"
FAIL=1
else
echo " [ ok ] $f"
fi
done
else
yellow "[~] pyflakes not installed (skip). Install with: pip install pyflakes"
fi
fi
# ---- 4. py_compile ---------------------------------------------------------
if (( ${#PY_FILES[@]} )); then
blue "[*] python3 -m py_compile"
for f in "${PY_FILES[@]}"; do
if ! python3 -m py_compile "$f" 2>&1; then
red " [FAIL] py_compile $f"
FAIL=1
else
echo " [ ok ] $f"
fi
done
# py_compile leaves __pycache__ artifacts; clean them up.
find . -type d -name __pycache__ -not -path './.git/*' -exec rm -rf {} + 2>/dev/null || true
fi
# ---- 5. footgun: dot-vs-dash filename -------------------------------------
#
# Upstream unsloth/Qwen3.6-27B-GGUF uses dashes (Qwen3.6-27B-Q4_K_M.gguf).
# Earlier commits used the wrong dot-separated pattern, which 404s.
# Block re-introduction.
blue "[*] grep: forbidden Qwen3.6-27B.Q* filename pattern"
if grep -RnE 'Qwen3\.6-27B\.Q[0-9A-Z_]+\.gguf' \
--include='*.sh' --include='*.py' --include='*.md' \
--include='Modelfile*' --include='*.cff' \
--exclude-dir=.git --exclude-dir=__pycache__ \
. ; then
red " [FAIL] found banned dot pattern (use 'Qwen3.6-27B-Q*.gguf')"
FAIL=1
else
echo " [ ok ] no stale dot-pattern matches"
fi
# ---- 6. footgun: VAR="$(cmd 2>/dev/null | filter)" silent-exit pattern -----
#
# Under `set -euo pipefail`, a direct command substitution like
# VAR="$(ollama show "${TAG}" 2>/dev/null | awk ...)"
# silently kills the script when ollama show fails: pipefail
# propagates the non-zero exit through the pipeline, set -e
# aborts on the assignment, and the explicit `[[ -z "${VAR}" ]]`
# check below it never runs. The user sees only
# make: *** [Makefile:N: <target>] Error 1
# with no diagnostic. We shipped this exact bug in
# scripts/heal_hf_pull.sh and only caught it this session by
# running `make heal-hf` against an empty store — see commit
# 385ed94 for the fix recipe: split the assignment with
# if VAR="$(cmd 2>/dev/null)"; then
# VAR2="$(filter <<<"${VAR}")"
# fi
# The `2>/dev/null` is the tell — its presence says "this command
# can fail loudly, we want to suppress the noise" — which is
# exactly the case where set -e + pipefail silently kills.
blue "[*] grep: forbidden VAR=\$(... 2>/dev/null | ...) silent-exit pattern"
if grep -RnE '^\s*[A-Za-z_][A-Za-z0-9_]*=("?)\$\([^)]*2>/dev/null[^|]*\|' \
--include='*.sh' \
--exclude-dir=.git \
scripts/ ; then
red " [FAIL] silent-exit substitution under set -e + pipefail."
red " Rewrite as 'if VAR=\$(cmd 2>/dev/null); then VAR2=\$(filter <<<\"\${VAR}\"); fi'."
red " See commit 385ed94 for the bug we shipped and the fix recipe."
FAIL=1
else
echo " [ ok ] no silent-exit substitution patterns"
fi
# ---- 7. Modelfile <-> bridge files sync -----------------------------------
#
# 'Modelfile' (consumed by 'ollama create -f Modelfile') and the root-level
# 'template' / 'system' / 'params' files (consumed by HF's Ollama bridge,
# which does NOT read Modelfile) must stay in sync. If they drift, hf.co/...
# users and 'make build' users get different behaviour.
if [[ -f "${ROOT}/Modelfile" && -f "${ROOT}/template" \
&& -f "${ROOT}/system" && -f "${ROOT}/params" ]]; then
blue "[*] python: Modelfile <-> bridge files sync"
if ! python3 "${ROOT}/scripts/check_bridge_sync.py"; then
FAIL=1
fi
else
yellow "[~] bridge files missing; skipping sync check"
fi
# ---- result ----------------------------------------------------------------
echo
if (( FAIL )); then
red "[!] FAIL"
exit 1
fi
green "[+] all checks passed"