File size: 12,537 Bytes
ab7a8a6
f48fa07
ab7a8a6
 
 
 
 
 
 
f48fa07
 
edc32fb
e023532
 
588ee65
be7f527
 
ab7a8a6
f48fa07
 
 
ab7a8a6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c847255
f48fa07
ab7a8a6
 
 
 
e023532
 
 
 
 
 
 
 
ab7a8a6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c847255
ab7a8a6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ec7ddfa
 
 
 
 
 
 
 
 
 
 
c847255
ab7a8a6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f48fa07
ab7a8a6
f48fa07
 
 
 
 
 
 
ab7a8a6
f48fa07
 
 
 
 
c847255
f48fa07
 
 
 
ab7a8a6
ee4c9b0
 
 
 
 
 
 
 
 
 
 
 
f48fa07
 
 
ee4c9b0
 
f48fa07
 
 
 
 
 
 
 
 
 
 
 
 
ab7a8a6
 
 
 
 
 
 
 
 
 
 
e023532
 
 
 
ab7a8a6
 
 
 
 
 
 
edc32fb
e023532
 
 
edc32fb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ab7a8a6
 
e023532
 
ab7a8a6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c847255
ab7a8a6
 
f48fa07
2e2b8f8
 
 
 
 
 
 
 
be7f527
2e2b8f8
 
 
 
 
c847255
2e2b8f8
 
ab7a8a6
 
 
 
 
 
 
c847255
f48fa07
c847255
f48fa07
c847255
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
#!/usr/bin/env bash
# Janus-35B — 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. the Modelfile's FROM target is the GGUF this repo actually ships
#      (the local-build path da34705 broke, with nothing here to catch it)
#   6. multi-line-aware scan for the VAR="$(cmd 2>/dev/null | filter)" silent-exit
#      under set -e + pipefail (the bug the sibling repo shipped and fixed; those
#      commits live in Thanatos-27B-HERETIC, not here)
#   7. Modelfile <-> template/system/params bridge-file sync
#   8. Go template keeps Ollama's thinking detection, the replay condition and
#      the reasoning-effort mapping
#
# Exit non-zero on any failure; a check whose tooling or input is missing is
# reported as skipped and never counted as a pass. 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
SKIPPED=0
CHECKS=8          # the numbered checks above; keep in sync with that list

# ---- collect targets -------------------------------------------------------

# Find every shell script and every python file, ignoring vendored / hidden dirs.
# The exclusions below are the ones .gitignore anticipates: without them a repo-local
# virtualenv would be fed to shellcheck and py_compile, and the __pycache__ sweep further
# down would delete bytecode caches inside it.
mapfile -t SH_FILES < <(find . -type f -name '*.sh' \
    -not -path './.git/*' -not -path './.venv/*' -not -path './venv/*' -not -path './.cache/*')
mapfile -t PY_FILES < <(find . -type f -name '*.py' \
    -not -path './.git/*' -not -path './.venv/*' -not -path './venv/*' -not -path './.cache/*' \
    -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"
    SKIPPED=$((SKIPPED + 1))
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
        # The hint matters more than it looks: this check runs `python3 -m pyflakes`,
        # so pyflakes has to be importable by the SYSTEM python3. A venv or a pipx
        # install - which is exactly what PEP 668's own error message suggests -
        # leaves this check skipping forever. And on a PEP 668 distro (Arch,
        # Debian 12+, Fedora) the plain pip command below is refused outright.
        yellow "[~] pyflakes not installed (skip). This check runs 'python3 -m pyflakes',"
        yellow "    so it must be importable by the SYSTEM python3 - a venv or pipx install"
        yellow "    does not satisfy it. Install with:  pip install pyflakes"
        yellow "    PEP 668 distro (Arch, Debian 12+, Fedora), either:"
        yellow "      pip install --user --break-system-packages pyflakes   # ~/.local, per python version"
        yellow "      <your package manager> python-pyflakes                # e.g. pacman -S python-pyflakes"
        SKIPPED=$((SKIPPED + 1))
    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: Modelfile FROM target vs the bundled GGUF -----------------
#
# 'ollama create janus -f Modelfile' resolves the FROM line against the repo
# root, so that filename and the blob this repo actually ships have to be the
# same file. The 0.2.0 rebase renamed FROM ahead of the blob: for a day the
# repo tracked one bundle and FROM named another, so the local-build path
# failed outright from a fresh clone until the blob swap in da34705 caught up
# — and nothing here noticed (CHANGELOG: "the broken Modelfile FROM path
# shipped unnoticed"). A rename on either side alone breaks it again.
#
# This replaces the '-MTP-Preserved-Q<quant>.gguf' grep that used to sit here:
# no Qwen 3.6 quant filename carries that substring, so the guard was green by
# construction rather than by inspection. What still guards the MTP footgun is
# scripts/strip_mtp.py itself — build.sh runs every fetched quant through it,
# and it no-ops on the MTP-clean quant this repo bundles.

blue "[*] Modelfile FROM target is the GGUF this repo ships"
if [[ ! -f "${ROOT}/Modelfile" ]]; then
    yellow "[~] Modelfile missing; skipping FROM target check"
    SKIPPED=$((SKIPPED + 1))
else
    # Skip build byproducts git ignores. build.sh defaults GGUF_PATH into the repo
    # root and writes a *.stripped.gguf sibling beside it, and .gitignore covers
    # both - so after any `make build` this check counted three GGUFs, failed, and
    # (being wired into the pre-commit hook) rejected every commit, while git
    # status stayed clean because the extras are ignored. An untracked file that
    # git does NOT ignore still counts: that is the case the check exists for.
    mapfile -t BUNDLES < <(
        find . -maxdepth 1 -type f -name '*.gguf' | sed 's|^\./||' | sort |
        while IFS= read -r f; do
            git -C "${ROOT}" check-ignore -q -- "${f}" || printf '%s\n' "${f}"
        done
    )
    FROM_TARGET="$(awk '/^FROM[[:space:]]/{print $2; exit}' "${ROOT}/Modelfile")"
    if (( ${#BUNDLES[@]} != 1 )); then
        red "  [FAIL] expected exactly one bundled *.gguf in the repo root, found ${#BUNDLES[@]}"
        (( ${#BUNDLES[@]} )) && red "    found: ${BUNDLES[*]}"
        (( ${#BUNDLES[@]} > 1 )) && red "    Leftover build output that git does not ignore? 'make clean' clears it."
        FAIL=1
    elif [[ "${FROM_TARGET#./}" != "${BUNDLES[0]}" ]]; then
        red "  [FAIL] Modelfile FROM '${FROM_TARGET}' is not the bundled '${BUNDLES[0]}'"
        red "    'ollama create janus -f Modelfile' cannot resolve it from a fresh clone."
        FAIL=1
    elif ! git -C "${ROOT}" ls-files --error-unmatch "${BUNDLES[0]}" >/dev/null 2>&1; then
        red "  [FAIL] bundled '${BUNDLES[0]}' is not tracked by git"
        red "    It exists on disk but would be absent from a fresh clone and from the"
        red "    published repo. Stage it: git add '${BUNDLES[0]}'"
        FAIL=1
    else
        echo "  [ ok ] FROM ${FROM_TARGET} == bundled ${BUNDLES[0]} (tracked)"
    fi
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. The sibling repo (Thanatos-27B-HERETIC) shipped
# this exact bug in a script never ported here, and caught it only by
# running that script against an empty store. 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.

# NOTE: this guard used to be a line-anchored grep. A command substitution
# split across backslash-continued lines is invisible to that, and one lived in
# the sibling's scripts/heal_hf_pull.sh (never ported here) the entire time the
# check reported green.
# Joining continuations into logical lines before matching is the whole point.
blue "[*] python: forbidden VAR=\$(... 2>/dev/null | ...) silent-exit pattern"
if python3 - <<'SILENTEXIT'
import glob, re, sys

pat = re.compile(r'^\s*[A-Za-z_]\w*="?\$\(.*2>/dev/null.*\|')
bad = []
for path in sorted(glob.glob('scripts/*.sh')):
    lines = open(path, encoding='utf-8').read().splitlines()
    i = 0
    while i < len(lines):
        start, logical = i + 1, lines[i]
        while logical.rstrip().endswith('\\') and i + 1 < len(lines):
            i += 1
            logical = logical.rstrip()[:-1] + ' ' + lines[i].strip()
        if pat.search(logical):
            bad.append(f"{path}:{start}: {logical.strip()}")
        i += 1
for b in bad:
    print(b)
sys.exit(1 if bad else 0)
SILENTEXIT
then
    echo "  [ ok ] no silent-exit substitution patterns"
else
    red "  [FAIL] silent-exit substitution under set -e + pipefail."
    red "    Rewrite as 'if VAR=\$(cmd 2>/dev/null); then VAR2=\$(filter <<<\"\${VAR}\"); fi'."
    red "    Split it: if VAR=\$(cmd 2>/dev/null); then ...; fi - never pipe inside the" 
    red "    same substitution, or a failing cmd exits the script with no diagnostic."
    FAIL=1
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"
    SKIPPED=$((SKIPPED + 1))
fi

# ---- 8. Go template: Ollama's thinking detection + replay condition ------
#
# Ollama picks between the Go 'template' and the GGUF's embedded Jinja template
# by comparing their capabilities, and infers the Go template's "thinking" from
# one .Thinking reference. Losing it once switched Ollama's template silently
# while every render test passed; restricting it once dropped a tool-call
# chain's reasoning. check_go_template.py fails on both.

if [[ -f "${ROOT}/template" ]]; then
    blue "[*] python: Go template thinking detection + replay + effort mapping"
    if ! python3 "${ROOT}/scripts/check_go_template.py"; then
        FAIL=1
    fi
else
    yellow "[~] template missing; skipping Go template check"
    SKIPPED=$((SKIPPED + 1))
fi

# ---- result ----------------------------------------------------------------

echo
if (( FAIL )); then
    red "[!] FAIL"
    exit 1
fi
if (( SKIPPED )); then
    yellow "[+] $(( CHECKS - SKIPPED )) of ${CHECKS} checks passed, ${SKIPPED} skipped"
else
    green "[+] all ${CHECKS} checks passed"
fi