-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathverify.sh
More file actions
executable file
·286 lines (245 loc) · 9.26 KB
/
verify.sh
File metadata and controls
executable file
·286 lines (245 loc) · 9.26 KB
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
#!/usr/bin/env bash
# verify.sh — Automated verification for spel
#
# Replaces the manual verification checklist. All logic lives here.
#
# Usage:
# ./verify.sh # Full verification (default)
# ./verify.sh --allure # Allure-only (report rendering changes)
# ./verify.sh --quick # Format + lint only (no build/test)
#
# Each step writes:
# .verification/<step>.log — stdout + stderr
# .verification/<step>.code — exit code (0=pass, non-zero=fail, skip=skipped)
# .verification/summary.log — final report
#
# On failure: stops at the failed step. Fix the issue and re-run.
# The .verification/ directory is gitignored — never committed.
set -uo pipefail
# Prevent Playwright from auto-downloading browsers (slow + geo-blocked in some regions).
# Browsers should be installed via `spel install` or system packages.
export PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
VERIFY_DIR=".verification"
rm -rf "$VERIFY_DIR"
mkdir -p "$VERIFY_DIR"
TOTAL=0
PASSED=0
SKIPPED=0
FAILED_STEP=""
# --- Colors (disabled when piped) ---
if [ -t 1 ]; then
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
BLUE='\033[0;34m'; BOLD='\033[1m'; NC='\033[0m'
else
RED=''; GREEN=''; YELLOW=''; BLUE=''; BOLD=''; NC=''
fi
# Use Firefox for tests when Playwright Chromium is not installed.
# Playwright's CDN is geo-blocked in some regions (403 on chrome-for-testing-public).
# System Chromium works for the CLI (--executable-path) but Playwright's Java test
# fixtures require a Playwright-managed browser. Firefox installs via Mozilla CDN and
# is always available after `spel install firefox`.
if [ ! -d "${HOME}/.cache/ms-playwright/chromium_headless_shell-1208" ]; then
if [ -d "${HOME}/.cache/ms-playwright/firefox-1509" ]; then
export SPEL_BROWSER=firefox
printf "${YELLOW}NOTE: Using Firefox for tests (Playwright Chromium not installed)${NC}\n"
fi
fi
# --- Step runner ---
# Runs a command, captures output to .verification/<name>.log,
# writes exit code to .verification/<name>.code.
# Returns 1 on failure (caller should bail).
step() {
local name="$1" desc="$2"
shift 2
TOTAL=$((TOTAL + 1))
printf "${BLUE}[%02d]${NC} %-45s " "$TOTAL" "$desc"
if "$@" > "$VERIFY_DIR/$name.log" 2>&1; then
echo "0" > "$VERIFY_DIR/$name.code"
PASSED=$((PASSED + 1))
printf "${GREEN}PASS${NC}\n"
return 0
else
local code=$?
echo "$code" > "$VERIFY_DIR/$name.code"
FAILED_STEP="$name"
printf "${RED}FAIL${NC} (exit $code)\n"
printf " ${RED}see: .verification/$name.log${NC}\n"
echo ""
tail -20 "$VERIFY_DIR/$name.log" | sed 's/^/ /'
echo ""
return 1
fi
}
# --- Skip a step (logged but not executed) ---
skip() {
local name="$1" desc="$2" reason="$3"
TOTAL=$((TOTAL + 1))
SKIPPED=$((SKIPPED + 1))
PASSED=$((PASSED + 1))
printf "${BLUE}[%02d]${NC} %-45s ${YELLOW}SKIP${NC} (%s)\n" "$TOTAL" "$desc" "$reason"
echo "skip" > "$VERIFY_DIR/$name.code"
echo "Skipped: $reason" > "$VERIFY_DIR/$name.log"
}
# --- Summary ---
summary() {
local failed=$((TOTAL - PASSED))
echo ""
if [ $failed -eq 0 ]; then
printf "${GREEN}${BOLD}All %d steps passed" "$TOTAL"
[ $SKIPPED -gt 0 ] && printf " (%d skipped)" "$SKIPPED"
printf "${NC}\n"
else
printf "${RED}${BOLD}Failed at step: %s (%d of %d passed)${NC}\n" "$FAILED_STEP" "$PASSED" "$TOTAL"
printf "Fix the issue, then re-run: ${BOLD}./verify.sh${NC}\n"
fi
echo ""
# Write machine-readable summary
{
echo "verify.sh — $(date -Iseconds)"
echo "total=$TOTAL passed=$PASSED failed=$failed skipped=$SKIPPED"
[ -n "$FAILED_STEP" ] && echo "stopped_at=$FAILED_STEP"
echo ""
for f in "$VERIFY_DIR"/*.code; do
[ -f "$f" ] || continue
local sname
sname=$(basename "$f" .code)
[ "$sname" = "summary" ] && continue
printf " %-25s %s\n" "$sname" "$(cat "$f")"
done
} > "$VERIFY_DIR/summary.log"
[ $failed -eq 0 ]
}
# =============================================================================
# Custom step functions
# =============================================================================
# Lint: info-level diagnostics are OK (pre-existing, intentional).
# Only fail on error or warning.
_lint() {
local output
output=$(clojure-lsp diagnostics --raw 2>&1) || true
echo "$output"
if echo "$output" | grep -E ": (error|warning):" > /dev/null 2>&1; then
echo ""
echo "FAILED: Lint errors or warnings found (see above)"
return 1
fi
echo ""
echo "OK: info-level diagnostics only (pre-existing, not errors)"
}
# Test with sanity check on test counts.
_test_full() {
local output code=0
output=$(make test 2>&1) || code=$?
echo "$output"
[ $code -ne 0 ] && return $code
# Sanity: lazytest should run ~1795 cases
local lt_count
lt_count=$(echo "$output" | grep -oP 'Ran \K\d+(?= test cases)' | tail -1 || true)
if [ -n "$lt_count" ] && [ "$lt_count" -lt 1000 ]; then
echo ""
echo "WARNING: Only $lt_count lazytest cases (expected ~1795). Possible subset run."
return 1
fi
# Sanity: CLI bash should run ~270 assertions
local cli_count
cli_count=$(echo "$output" | grep -oP 'Total: \K\d+' | tail -1 || true)
if [ -n "$cli_count" ] && [ "$cli_count" -lt 200 ]; then
echo ""
echo "WARNING: Only $cli_count CLI assertions (expected ~270). Possible subset run."
return 1
fi
}
# Secret scan against base branch.
_secret_scan() {
local base
base=$(git merge-base HEAD origin/main 2>/dev/null || git merge-base HEAD main 2>/dev/null || echo "")
if [ -z "$base" ]; then
echo "No base branch found — skipping secret scan"
return 0
fi
local hits
hits=$(git diff "$base"..HEAD | grep -iE "(sk_|lin_api_|nvapi-|AIzaSy|ghp_|password\s*=\s*\S{8})" || true)
if [ -n "$hits" ]; then
echo "FAILED: Potential secrets in diff:"
echo "$hits"
return 1
fi
echo "No secrets found"
}
# =============================================================================
# Detect what changed (for smart skipping)
# =============================================================================
NEEDS_REGEN=true # Default: run everything
detect_regen_triggers() {
local base
base=$(git merge-base HEAD origin/main 2>/dev/null || git merge-base HEAD main 2>/dev/null || echo "")
[ -z "$base" ] && return # Can't detect — run everything
local changed
changed=$(git diff --name-only "$base"..HEAD 2>/dev/null || echo "")
NEEDS_REGEN=false
while IFS= read -r file; do
[[ -z "$file" ]] && continue
if [[ "$file" =~ templates/ || "$file" =~ sci_env\.clj || "$file" =~ gen_docs\.clj ]]; then
NEEDS_REGEN=true
return
fi
done <<< "$changed"
}
# =============================================================================
# Verification modes
# =============================================================================
verify_quick() {
printf "\n${BOLD}Quick verification (format + lint)${NC}\n\n"
step "format" "Format source" make format || return 1
step "lint" "Lint diagnostics" _lint || return 1
summary
}
verify_allure() {
printf "\n${BOLD}Allure-only verification${NC}\n\n"
step "format" "Format source" make format || return 1
step "lint" "Lint diagnostics" _lint || return 1
step "test" "Test + Allure report" make test-allure || return 1
summary
}
verify_full() {
detect_regen_triggers
printf "\n${BOLD}Full verification${NC}\n\n"
# 1. Format (must run BEFORE tests — format changes must be tested)
step "format" "Format source" make format || return 1
# 2. Lint (errors/warnings fail, info is OK)
step "lint" "Lint diagnostics" _lint || return 1
# 3. GraalVM safety
step "validate-graal" "GraalVM safety (reflection/boxed math)" make validate-safe-graal || return 1
# 4. Gen docs (only if regen triggers changed)
if $NEEDS_REGEN; then
step "gen-docs" "Generate API docs" make gen-docs || return 1
else
skip "gen-docs" "Generate API docs" "no regen triggers changed"
fi
# 5. Build native binary
step "install-local" "Build native binary" make install-local || return 1
# 6. Smoke test
step "smoke" "Smoke test (version + help)" bash -c 'spel version && spel --help > /dev/null' || return 1
# 7. Full test suite (lazytest + CLI bash regression)
step "test" "Full test suite (lazytest + CLI)" _test_full || return 1
# 8. Regenerate agent scaffolding (only if regen triggers changed)
if $NEEDS_REGEN; then
step "init-agents" "Regenerate agent scaffolding" make init-agents 'ARGS=--ns com.blockether.spel --force' || return 1
else
skip "init-agents" "Regenerate agent scaffolding" "no regen triggers changed"
fi
# 9. Git hygiene
step "git-check" "Git hygiene (markers, whitespace)" git diff --check || return 1
# 10. Secret scan
step "secrets" "Secret scan" _secret_scan || return 1
summary
}
# =============================================================================
# Entry point
# =============================================================================
MODE="${1:-full}"
case "$MODE" in
--quick|-q) verify_quick ;;
--allure|-a) verify_allure ;;
--full|-f|*) verify_full ;;
esac