-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathverify.sh
More file actions
executable file
·411 lines (368 loc) · 17.7 KB
/
verify.sh
File metadata and controls
executable file
·411 lines (368 loc) · 17.7 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
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
#!/bin/bash
set -euo pipefail
PHASE=${1:-all}
PROJECT_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$PROJECT_DIR"
echo "=== MarkView Verification ==="
# Version sync check
echo ""
echo "--- Version Sync ---"
bash "$PROJECT_DIR/scripts/check-version-sync.sh"
# Tier 0: Build all targets
echo ""
echo "--- Tier 0: Build ---"
BUILD_OUTPUT=$(swift build 2>&1)
if echo "$BUILD_OUTPUT" | tail -3 | grep -q "Build complete"; then
echo "✓ All targets build successfully"
else
echo "✗ Build failed"
echo "$BUILD_OUTPUT" | tail -10
exit 1
fi
[ "$PHASE" = "0" ] && exit 0
# Tier 0b: Bundle structure (if bundle.sh has been run)
if [ -d "$PROJECT_DIR/MarkView.app" ]; then
echo ""
echo "--- Tier 0b: Bundle Verification ---"
BUNDLE_OK=true
[ -f "$PROJECT_DIR/MarkView.app/Contents/MacOS/MarkView" ] && echo "✓ Executable in bundle" || { echo "✗ Missing executable"; BUNDLE_OK=false; }
[ -f "$PROJECT_DIR/MarkView.app/Contents/Info.plist" ] && echo "✓ Info.plist in bundle" || { echo "✗ Missing Info.plist"; BUNDLE_OK=false; }
if plutil -lint "$PROJECT_DIR/MarkView.app/Contents/Info.plist" > /dev/null 2>&1; then
echo "✓ Info.plist is valid"
else
echo "✗ Info.plist is invalid"
BUNDLE_OK=false
fi
if grep -q "CFBundleDocumentTypes" "$PROJECT_DIR/MarkView.app/Contents/Info.plist"; then
echo "✓ Document types registered"
else
echo "✗ Missing document types"
BUNDLE_OK=false
fi
# Resource integrity check — catches xcodegen-not-rerun after adding resource files
BUNDLE_RSRC="$PROJECT_DIR/MarkView.app/Contents/Resources/MarkView_MarkViewCore.bundle/Contents/Resources"
for rsrc in mermaid.min.js prism-bundle.min.js template.html; do
if [ -f "$BUNDLE_RSRC/$rsrc" ]; then
echo "✓ Resource bundled: $rsrc"
else
echo "✗ Missing resource in bundle: $rsrc (run: xcodegen generate && bash scripts/bundle.sh)"
BUNDLE_OK=false
fi
done
# Same resources must also be in the QuickLook extension
APPEX_RSRC="$PROJECT_DIR/MarkView.app/Contents/PlugIns/MarkViewQuickLook.appex/Contents/Resources/MarkView_MarkViewCore.bundle/Contents/Resources"
for rsrc in mermaid.min.js prism-bundle.min.js template.html; do
if [ -f "$APPEX_RSRC/$rsrc" ]; then
echo "✓ QL resource bundled: $rsrc"
else
echo "✗ Missing QL resource: $rsrc"
BUNDLE_OK=false
fi
done
# Quick Look extension verification
APPEX_DIR="$PROJECT_DIR/MarkView.app/Contents/PlugIns/MarkViewQuickLook.appex"
if [ -d "$APPEX_DIR" ]; then
[ -f "$APPEX_DIR/Contents/MacOS/MarkViewQuickLook" ] && echo "✓ Quick Look extension executable" || { echo "✗ Missing QL executable"; BUNDLE_OK=false; }
[ -f "$APPEX_DIR/Contents/Info.plist" ] && echo "✓ Quick Look extension Info.plist" || { echo "✗ Missing QL Info.plist"; BUNDLE_OK=false; }
if plutil -lint "$APPEX_DIR/Contents/Info.plist" > /dev/null 2>&1; then
echo "✓ Quick Look Info.plist is valid"
else
echo "✗ Quick Look Info.plist is invalid"
BUNDLE_OK=false
fi
if codesign --verify --no-strict "$APPEX_DIR" 2>/dev/null; then
echo "✓ Quick Look extension is signed"
else
echo "⚠ Quick Look extension is unsigned (non-fatal)"
fi
else
echo "⚠ Quick Look extension not in bundle (run: bash scripts/bundle.sh)"
fi
# Code signing verification
echo ""
echo " --- Signing Verification ---"
BUNDLE_APP="$PROJECT_DIR/MarkView.app"
if codesign --verify --deep --strict "$BUNDLE_APP" 2>/dev/null; then
echo " ✓ Code signature valid (deep + strict)"
else
echo " ⚠ Strict signature verification failed (expected for ad-hoc)"
fi
# Display signing identity
SIGN_INFO=$(codesign -d --verbose=2 "$BUNDLE_APP" 2>&1 || true)
AUTHORITY=$(echo "$SIGN_INFO" | grep "Authority=" | head -1 || true)
if echo "$SIGN_INFO" | grep -q "Signature=adhoc"; then
echo " Signing: ad-hoc"
elif echo "$AUTHORITY" | grep -q "Developer ID"; then
echo " Signing: $AUTHORITY"
else
echo " Signing: unknown"
fi
# Gatekeeper assessment (only meaningful for Developer ID signed apps)
if echo "$AUTHORITY" | grep -q "Developer ID"; then
if spctl --assess --type execute "$BUNDLE_APP" 2>/dev/null; then
echo " ✓ Gatekeeper: accepted"
else
echo " ⚠ Gatekeeper: rejected (may need notarization)"
fi
fi
# Notarization ticket check
if xcrun stapler validate "$BUNDLE_APP" 2>/dev/null; then
echo " ✓ Notarization ticket: stapled"
else
echo " Notarization ticket: not stapled (use --notarize with bundle.sh)"
fi
[ "$BUNDLE_OK" = true ] || exit 1
fi
# Tier 1-3: Run full test suite (unit, GFM compliance, performance, golden files, E2E)
echo ""
echo "--- Tier 1-3: Full Test Suite ---"
TEST_OUTPUT=$(swift run MarkViewTestRunner 2>&1)
# Display test output (filter build noise)
echo "$TEST_OUTPUT" | grep -v "^Building\|^Build of\|^\[" | grep -v "^$"
# Check result from the last line
RESULT=$(echo "$TEST_OUTPUT" | tail -1)
if echo "$RESULT" | grep -q "0 failed"; then
true
else
echo ""
echo "=== Some checks failed ==="
exit 1
fi
# PDF behavioral tests — validates actual PDF output (not source inspection)
# Catches: viewport-only capture, NSPrintOperation object explosion, corrupt output
echo ""
echo "--- PDF Behavioral Tests ---"
PDF_OUTPUT=$(swift run MarkViewPDFTester 2>&1)
echo "$PDF_OUTPUT" | grep -v "^Building\|^Build of\|^\[" | grep -v "^$"
PDF_RESULT=$(echo "$PDF_OUTPUT" | tail -2)
if echo "$PDF_RESULT" | grep -q "0 failed"; then
true
else
echo ""
echo "=== PDF tests failed ==="
exit 1
fi
# Golden baseline drift check — catches the same issue CI catches
echo ""
echo "--- Golden Drift Check ---"
swift run MarkViewTestRunner --generate-goldens 2>&1 | grep -v "^\[" | grep -v "^$" | grep -v "^Building\|^Build of\|^warning:"
if git diff --quiet Tests/TestRunner/Fixtures/expected/ 2>/dev/null; then
echo "✓ Golden baselines are up to date"
else
echo "✗ Golden baselines are stale — commit the updated files:"
git diff --stat Tests/TestRunner/Fixtures/expected/
exit 1
fi
# CLI smoke test
echo ""
echo "--- CLI Check ---"
if [ -x "$HOME/.local/bin/mdpreview" ] && file "$HOME/.local/bin/mdpreview" | grep -q "text"; then
echo "✓ mdpreview CLI is installed (shell script)"
# Behavioral: verify it actually launches MarkView.app
if [ -d "/Applications/MarkView.app" ] || [ -d "$HOME/Applications/MarkView.app" ]; then
_CLI_TMP=$(mktemp /tmp/markview-cli-test-XXXXXXXX)".md"
echo "# CLI test" > "$_CLI_TMP"
"$HOME/.local/bin/mdpreview" "$_CLI_TMP" &
_CLI_PID=$!
sleep 3
if pgrep -x "MarkView" > /dev/null 2>&1; then
echo "✓ mdpreview CLI launches MarkView.app"
else
echo "✗ mdpreview CLI did not launch MarkView.app within 3s"
fi
kill "$_CLI_PID" 2>/dev/null; rm -f "$_CLI_TMP"
else
echo " ⊘ mdpreview behavioral test skipped — MarkView.app not installed"
fi
else
echo "⚠ mdpreview not installed or is not a shell script (run: bash scripts/install-cli.sh)"
fi
echo ""
echo "=== All checks passed ==="
# Write per-repo verify stamp so commit-gate allows the next commit without a
# separate manual date +%s > stamp call. The commit-gate checks this file first
# alongside the global claude-loop stamp.
date +%s > "$PROJECT_DIR/.last-verify-at"
# Extended tests (fuzz + differential) — only with --extended flag
if [ "$PHASE" = "--extended" ]; then
echo ""
echo "--- Extended: Fuzz Testing ---"
swift run MarkViewFuzzTester 2>&1 | grep -v "^\[" | grep -v "^$"
echo ""
echo "--- Extended: Differential Testing ---"
swift run MarkViewDiffTester 2>&1 | grep -v "^\[" | grep -v "^$"
echo ""
echo "--- Extended: Visual Regression Tests ---"
# Generate goldens first (idempotent), then compare
swift run MarkViewVisualTester --generate-goldens 2>&1 | grep -v "^\[" | grep -v "^$" | grep -v "^Building\|^Build of\|^warning:"
swift run MarkViewVisualTester 2>&1 | grep -v "^\[" | grep -v "^$" | grep -v "^Building\|^Build of\|^warning:"
# Quick Look system integration tests
echo ""
echo "--- Extended: Quick Look System Integration ---"
QL_APPEX="/Applications/MarkView.app/Contents/PlugIns/MarkViewQuickLook.appex"
QL_PASS=0
QL_FAIL=0
QL_SKIP=0
if [ -d "$QL_APPEX" ]; then
# 1. Bundle structure
[ -f "$QL_APPEX/Contents/MacOS/MarkViewQuickLook" ] && { echo " ✓ Extension executable exists"; QL_PASS=$((QL_PASS + 1)); } || { echo " ✗ Missing extension executable"; QL_FAIL=$((QL_FAIL + 1)); }
[ -f "$QL_APPEX/Contents/Info.plist" ] && { echo " ✓ Extension Info.plist exists"; QL_PASS=$((QL_PASS + 1)); } || { echo " ✗ Missing extension Info.plist"; QL_FAIL=$((QL_FAIL + 1)); }
[ -f "$QL_APPEX/Contents/PkgInfo" ] && { echo " ✓ Extension PkgInfo exists"; QL_PASS=$((QL_PASS + 1)); } || { echo " ✗ Missing PkgInfo"; QL_FAIL=$((QL_FAIL + 1)); }
# 2. PkgInfo type
PKGTYPE=$(cat "$QL_APPEX/Contents/PkgInfo" 2>/dev/null || true)
if [[ "$PKGTYPE" == "XPC!????" ]]; then
echo " ✓ PkgInfo declares XPC service type"; QL_PASS=$((QL_PASS + 1))
else
echo " ✗ PkgInfo wrong type: got '$PKGTYPE', expected 'XPC!????'"; QL_FAIL=$((QL_FAIL + 1))
fi
# 3. Info.plist validity
if plutil -lint "$QL_APPEX/Contents/Info.plist" > /dev/null 2>&1; then
echo " ✓ Extension Info.plist is valid XML"; QL_PASS=$((QL_PASS + 1))
else
echo " ✗ Extension Info.plist is invalid"; QL_FAIL=$((QL_FAIL + 1))
fi
# 4. Required plist keys
PLIST_CONTENT=$(plutil -convert xml1 -o - "$QL_APPEX/Contents/Info.plist" 2>/dev/null || true)
for KEY in NSExtensionPointIdentifier NSExtensionPrincipalClass QLSupportedContentTypes CFBundleIdentifier; do
if echo "$PLIST_CONTENT" | grep -q "$KEY"; then
echo " ✓ Info.plist has $KEY"; QL_PASS=$((QL_PASS + 1))
else
echo " ✗ Info.plist missing $KEY"; QL_FAIL=$((QL_FAIL + 1))
fi
done
# 5. Extension point identifier
EXT_POINT=$(plutil -extract NSExtension.NSExtensionPointIdentifier raw "$QL_APPEX/Contents/Info.plist" 2>/dev/null || true)
if [[ "$EXT_POINT" == "com.apple.quicklook.preview" ]]; then
echo " ✓ Extension point: com.apple.quicklook.preview"; QL_PASS=$((QL_PASS + 1))
else
echo " ✗ Wrong extension point: '$EXT_POINT'"; QL_FAIL=$((QL_FAIL + 1))
fi
# 6. Supported content types include markdown
if echo "$PLIST_CONTENT" | grep -q "net.daringfireball.markdown"; then
echo " ✓ Supports net.daringfireball.markdown"; QL_PASS=$((QL_PASS + 1))
else
echo " ✗ Missing markdown content type"; QL_FAIL=$((QL_FAIL + 1))
fi
# 7. Code signing
if codesign --verify --no-strict "$QL_APPEX" 2>/dev/null; then
echo " ✓ Extension is code-signed"; QL_PASS=$((QL_PASS + 1))
else
echo " ⚠ Extension is unsigned (ad-hoc signing may have been stripped)"; QL_SKIP=$((QL_SKIP + 1))
fi
# 8. Signing identity check (Developer ID vs ad-hoc)
SIGN_INFO=$(codesign -dvv "$QL_APPEX" 2>&1 || true)
if echo "$SIGN_INFO" | grep -q "Signature=adhoc"; then
echo " ⚠ Ad-hoc signed — Finder spacebar preview requires Developer ID signing + notarization"; QL_SKIP=$((QL_SKIP + 1))
elif echo "$SIGN_INFO" | grep -q "Developer ID"; then
echo " ✓ Developer ID signed — Finder spacebar preview should work"; QL_PASS=$((QL_PASS + 1))
fi
# 9. Binary architecture
ARCH=$(file "$QL_APPEX/Contents/MacOS/MarkViewQuickLook" 2>/dev/null || true)
if echo "$ARCH" | grep -q "arm64"; then
echo " ✓ Binary is arm64"; QL_PASS=$((QL_PASS + 1))
elif echo "$ARCH" | grep -q "Mach-O"; then
echo " ✓ Binary is valid Mach-O"; QL_PASS=$((QL_PASS + 1))
else
echo " ✗ Binary architecture issue: $ARCH"; QL_FAIL=$((QL_FAIL + 1))
fi
# 10. Parent app bundle identifier consistency
PARENT_ID=$(plutil -extract CFBundleIdentifier raw "/Applications/MarkView.app/Contents/Info.plist" 2>/dev/null || true)
EXT_ID=$(plutil -extract CFBundleIdentifier raw "$QL_APPEX/Contents/Info.plist" 2>/dev/null || true)
if [[ "$EXT_ID" == "$PARENT_ID."* ]]; then
echo " ✓ Extension ID ($EXT_ID) is child of parent ($PARENT_ID)"; QL_PASS=$((QL_PASS + 1))
else
echo " ⚠ Extension ID ($EXT_ID) is not prefixed by parent ($PARENT_ID) — may cause signing issues"; QL_SKIP=$((QL_SKIP + 1))
fi
# 11. Version sync between extension and parent
PARENT_VER=$(plutil -extract CFBundleShortVersionString raw "/Applications/MarkView.app/Contents/Info.plist" 2>/dev/null || true)
EXT_VER=$(plutil -extract CFBundleShortVersionString raw "$QL_APPEX/Contents/Info.plist" 2>/dev/null || true)
if [[ "$PARENT_VER" == "$EXT_VER" ]]; then
echo " ✓ Version sync: parent=$PARENT_VER, extension=$EXT_VER"; QL_PASS=$((QL_PASS + 1))
else
echo " ✗ Version mismatch: parent=$PARENT_VER, extension=$EXT_VER"; QL_FAIL=$((QL_FAIL + 1))
fi
# 12. pluginkit registration status
if pluginkit -m -p com.apple.quicklook.preview 2>/dev/null | grep -q "com.markview"; then
echo " ✓ Extension registered with pluginkit"; QL_PASS=$((QL_PASS + 1))
else
# Developer ID signed apps MUST register — ad-hoc gets a pass
APPEX_SIGN_INFO=$(codesign -dvv "$QL_APPEX" 2>&1 || true)
if echo "$APPEX_SIGN_INFO" | grep -q "Developer ID"; then
echo " ✗ Extension NOT registered with pluginkit (Developer ID signed — this is a bug)"; QL_FAIL=$((QL_FAIL + 1))
else
echo " ⚠ Extension NOT registered with pluginkit (expected for ad-hoc signed apps)"; QL_SKIP=$((QL_SKIP + 1))
fi
fi
# 13. UTType recognition for .md files
FIXTURE="$PROJECT_DIR/Tests/TestRunner/Fixtures/basic.md"
if [ -f "$FIXTURE" ]; then
MD_TYPE=$(mdls -attr kMDItemContentType "$FIXTURE" 2>/dev/null | grep -o '".*"' | tr -d '"' || true)
if [[ "$MD_TYPE" == "net.daringfireball.markdown" ]]; then
echo " ✓ System recognizes .md as net.daringfireball.markdown"; QL_PASS=$((QL_PASS + 1))
elif [[ -n "$MD_TYPE" ]]; then
echo " ⚠ System sees .md as '$MD_TYPE' (not net.daringfireball.markdown)"; QL_SKIP=$((QL_SKIP + 1))
else
echo " ⚠ Could not determine UTType for .md files"; QL_SKIP=$((QL_SKIP + 1))
fi
fi
echo ""
echo " Quick Look E2E: $QL_PASS passed, $QL_FAIL failed, $QL_SKIP skipped/advisory"
if (( QL_FAIL > 0 )); then
echo " ✗ Quick Look integration has failures"
exit 1
fi
else
echo " ⊘ Quick Look extension not installed — skipping (run: bash scripts/bundle.sh --install)"
fi
# qlmanage smoke test — verify Quick Look can preview a markdown file without crashing
echo ""
echo "--- Extended: qlmanage Smoke Test ---"
QL_FIXTURE="$PROJECT_DIR/Tests/TestRunner/Fixtures/basic.md"
if [ -f "$QL_FIXTURE" ]; then
# Run qlmanage with a 10-second timeout; -p triggers preview generation
if timeout 10 qlmanage -p "$QL_FIXTURE" > /dev/null 2>&1; then
echo " ✓ qlmanage -p returned success for basic.md"
else
QL_EXIT=$?
if [ "$QL_EXIT" -eq 124 ]; then
echo " ⚠ qlmanage -p timed out (10s) — may need manual investigation"
else
echo " ✗ qlmanage -p failed with exit code $QL_EXIT"
exit 1
fi
fi
else
echo " ⊘ Fixture not found: $QL_FIXTURE — skipping"
fi
# Window lifecycle smoke test (no AX permissions needed)
echo ""
echo "--- Extended: Window Lifecycle Smoke Test ---"
bash "$PROJECT_DIR/scripts/test-window-lifecycle.sh"
# E2E UI automation tests (require .app bundle + AX permissions)
if [ -d "$PROJECT_DIR/MarkView.app" ] || [ -d "/Applications/MarkView.app" ]; then
echo ""
echo "--- Extended: E2E Tests ---"
swift run MarkViewE2ETester 2>&1 | grep -v "^\[" | grep -v "^$" | grep -v "^Building\|^Build of\|^warning:"
else
echo ""
echo "--- Extended: E2E Tests ---"
echo " ⊘ Skipped — no .app bundle found (run: bash scripts/bundle.sh)"
fi
# Playwright DOM tests (Tier 5 — require fixtures to be generated first)
echo ""
echo "--- Extended: Playwright DOM Tests ---"
if [ -d "$PROJECT_DIR/Tests/playwright/node_modules" ]; then
if [ -f "$PROJECT_DIR/Tests/playwright/fixtures/golden-corpus.html" ]; then
(cd "$PROJECT_DIR/Tests/playwright" && npx playwright test 2>&1) && \
bash "$PROJECT_DIR/scripts/bundle.sh" --install 2>/dev/null && \
echo " ✓ Playwright tests passed + MarkView.app updated" || \
echo " ✗ Playwright tests failed"
else
echo " ⊘ Fixtures not generated — run: make playwright-fixtures"
fi
else
echo " ⊘ Playwright not installed — run: make playwright-install"
fi
echo ""
echo "=== Extended verification complete ==="
fi