把交付目录的管理台扫描迁入源仓库 tests/admin-axe-audit.mjs(自包含:Node 内置 http 静态服务 + 浏览器自动探测 + CDP 注入 axe-core),矩阵与口径零放宽(3 视口 × 2 皮肤 × 6 页 + E-Ink 6 格 = 42 格,禁用规则 0 条,incomplete 不记 PASS)。run-guards.sh 增第 6 套件,默认不跑以保住 13s 快跑路径,RUN_AXE=1 才执行,未设时汇总表显式标 SKIPPED (RUN_AXE not set) 并打印原因与启用命令。workflow 支持 workflow_dispatch 输入与仓库变量 IW_RUN_AXE 两种启用方式。结果 JSON 与截图只写 /tmp,不入库。本地实跑:默认 PASS 5 / SKIPPED 1 / 13s / exit 0;RUN_AXE=1 后 PASS 6 / 0 / 60s / exit 0(管理台 42/42 格 violations=0、incomplete=12 均为 mobile-375 几何遮挡)。
164 lines
9.1 KiB
Bash
Executable File
164 lines
9.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# 墨观 InkWatch V4.0 · 设计系统双向护栏统一入口
|
||
#
|
||
# cd <repo> && bash tests/run-guards.sh
|
||
#
|
||
# 串行跑 6 个套件(第 6 个默认不跑,见 RUN_AXE);任一非零退出则整体失败。
|
||
# 无论中途是否有失败,都跑完全部套件并在末尾打印汇总表,便于一次看清全貌
|
||
#(不「遇错即停」)。
|
||
#
|
||
# 套件顺序:
|
||
# 1. web-token-guard.js 源仓库令牌层轻量守卫(本目录新增)
|
||
# 2. phase3b-static-guard.js 管理台深色落地静态护栏
|
||
# 3. dockerfile-coverage-test.js 镜像静态资源 COPY 覆盖
|
||
# 4. schema-consistency-test.js 指标字段三层一致性
|
||
# 5. client/scripts/test.sh 全量 Dart(含 token_parity_test.dart 双向护栏)
|
||
# 6. admin-axe-audit.mjs 管理台 6 页 axe-core 42 格矩阵(默认不跑)
|
||
#
|
||
# 环境变量:
|
||
# PHASE3B_PREVIEW=<path> preview.html 路径;存在则 phase3b 启用 7 处 hash
|
||
# 比对(87 PASS)。未设时脚本自动探测本地交付目录
|
||
# 候选;CI 上不存在即走「不带 preview」分支并打印
|
||
# SKIP 原因(85 PASS / 0 FAIL + 1 SKIP,属预期)。
|
||
# SKIP_FLUTTER_TESTS=1 跳过全量 Dart。默认必须跑;仅 Flutter 不可用时
|
||
# 显式降级,汇总表标 SKIPPED (explicit),绝不静默。
|
||
# FLUTTER_BIN=<path> 透传给 client/scripts/test.sh 指定 flutter 可执行文件
|
||
# (CI runner 上 flutter 不在 $HOME/development/flutter)。
|
||
# RUN_AXE=1 启用第 6 套件「管理台 axe 42 格矩阵」。默认不跑
|
||
# (保护约 14s 的本地快跑体验);未设时汇总表显式标
|
||
# SKIPPED (RUN_AXE not set) 并打印跳过原因,绝不静默。
|
||
# 前置:本机有 Edge/Chrome/Chromium + 能装 axe-core
|
||
# (/tmp/iw-admin-axe),见 tests/README.md。
|
||
#
|
||
# 本地复现无需 Gitea Actions;Gitea Actions 启用后由
|
||
# .gitea/workflows/design-system-guard.yml 调用同一入口。
|
||
|
||
set -u
|
||
|
||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||
cd "$ROOT" || exit 1
|
||
|
||
TMP="$(mktemp -d "${TMPDIR:-/tmp}/inkwatch-guards.XXXXXX")"
|
||
trap 'rm -rf "$TMP"' EXIT
|
||
|
||
# 结果行:名称|状态|退出码|耗时|指标
|
||
RESULTS=()
|
||
OVERALL=0
|
||
START=$SECONDS
|
||
|
||
add_result() { RESULTS+=("$1|$2|$3|$4|$5"); }
|
||
|
||
# 从套件日志里尽力提取一行短指标(best-effort,提取不到记 -)
|
||
metric_of() {
|
||
local name="$1" log="$2" line=""
|
||
case "$name" in
|
||
*web-token-guard*|*phase3b*)
|
||
line="$(grep -Eo 'PASS [0-9]+ +FAIL [0-9]+' "$log" | tail -1)" ;;
|
||
*dockerfile*|*schema*)
|
||
line="$(grep -Eo '测试通过: [0-9]+ +失败: [0-9]+' "$log" | tail -1)" ;;
|
||
*Dart*)
|
||
line="$(grep -E 'All tests passed' "$log" | tail -1 | sed -E 's/^[[:space:]]+//' | cut -c1-44)" ;;
|
||
*admin-axe*)
|
||
local grid viol inc
|
||
grid="$(grep -Eo '矩阵:[0-9]+/[0-9]+ 格' "$log" | tail -1)"
|
||
viol="$(grep -Eo 'violations 合计 [0-9]+ 条' "$log" | tail -1 | grep -Eo '[0-9]+')"
|
||
inc="$(grep -Eo 'incomplete 合计 [0-9]+ 条' "$log" | tail -1 | grep -Eo '[0-9]+')"
|
||
[ -n "$grid" ] && line="$grid viol=${viol:--} inc=${inc:--}" ;;
|
||
esac
|
||
printf '%s' "${line:--}"
|
||
}
|
||
|
||
run_suite() {
|
||
local name="$1"; shift
|
||
local log="$TMP/${#RESULTS[@]}.log"
|
||
local t0=$SECONDS rc=0
|
||
printf '\n════════════════════════════════════════════════════════\n'
|
||
printf ' ▶ %s\n' "$name"
|
||
printf '════════════════════════════════════════════════════════\n'
|
||
"$@" 2>&1 | tee "$log"
|
||
rc=${PIPESTATUS[0]}
|
||
local dur=$((SECONDS - t0))
|
||
local metric
|
||
metric="$(metric_of "$name" "$log")"
|
||
local status="PASS"
|
||
if [ "$rc" -ne 0 ]; then status="FAIL"; OVERALL=1; fi
|
||
add_result "$name" "$status" "$rc" "${dur}s" "$metric"
|
||
printf ' ── %s:%s(退出码 %s,耗时 %ss,指标 %s)\n' \
|
||
"$name" "$status" "$rc" "$dur" "$metric"
|
||
}
|
||
|
||
# ── 1. 源仓库 Web 令牌层轻量守卫 ──────────────────────────────
|
||
run_suite "web-token-guard.js" node tests/web-token-guard.js
|
||
|
||
# ── 2. phase3b 静态护栏(条件性带 PHASE3B_PREVIEW)────────────
|
||
# 交付目录 preview.html 候选;CI 上通常都不存在,自动降级为「不带」分支。
|
||
PREVIEW="${PHASE3B_PREVIEW:-}"
|
||
if [ -z "$PREVIEW" ]; then
|
||
for c in \
|
||
"$ROOT/../inkwatch-design-system/preview.html" \
|
||
"$HOME/WorkBuddy/2026-11-08-14-54-48/inkwatch-design-system/preview.html"; do
|
||
if [ -f "$c" ]; then PREVIEW="$c"; break; fi
|
||
done
|
||
fi
|
||
if [ -n "$PREVIEW" ] && [ -f "$PREVIEW" ]; then
|
||
printf ' ℹ PHASE3B_PREVIEW = %s\n' "$PREVIEW"
|
||
printf ' phase3b 启用 7 处首屏脚本 hash 比对(预期 87 PASS / 0 FAIL)。\n'
|
||
run_suite "phase3b-static-guard.js" \
|
||
env "PHASE3B_PREVIEW=$PREVIEW" node tests/phase3b-static-guard.js
|
||
else
|
||
printf ' ℹ SKIP 原因:未找到交付目录 preview.html,PHASE3B_PREVIEW 不设置。\n'
|
||
printf ' phase3b 走「不带 preview」分支(85 PASS / 0 FAIL + 1 SKIP,属预期)。\n'
|
||
printf ' 如需启用:PHASE3B_PREVIEW=<path> bash tests/run-guards.sh\n'
|
||
run_suite "phase3b-static-guard.js" node tests/phase3b-static-guard.js
|
||
fi
|
||
|
||
# ── 3. Dockerfile 静态资源覆盖 ────────────────────────────────
|
||
run_suite "dockerfile-coverage-test.js" node tests/dockerfile-coverage-test.js
|
||
|
||
# ── 4. 指标字段三层一致性 ─────────────────────────────────────
|
||
run_suite "schema-consistency-test.js" node tests/schema-consistency-test.js
|
||
|
||
# ── 5. 全量 Dart(默认必须跑)─────────────────────────────────
|
||
if [ "${SKIP_FLUTTER_TESTS:-0}" = "1" ]; then
|
||
printf '\n ⚠ SKIPPED (explicit):SKIP_FLUTTER_TESTS=1,按要求跳过全量 Dart 测试。\n'
|
||
printf ' 默认(不设该变量)必须跑 client/scripts/test.sh;此跳过会在汇总表显式标注。\n'
|
||
add_result "client/scripts/test.sh (Dart)" "SKIPPED (explicit)" "-" "0s" "SKIPPED (explicit)"
|
||
else
|
||
run_suite "client/scripts/test.sh (Dart)" bash -c 'cd client && bash scripts/test.sh'
|
||
fi
|
||
|
||
# ── 6. 管理台 axe-core 42 格矩阵(默认不跑)───────────────────
|
||
# 理由:需要本机浏览器 + 联网装 axe-core,单跑约 45s;默认路径保持约 14s 快跑。
|
||
# RUN_AXE=1 才执行;未设时显式标 SKIPPED,绝不静默跳过。
|
||
if [ "${RUN_AXE:-0}" = "1" ]; then
|
||
printf '\n ℹ RUN_AXE=1:启用管理台 axe 42 格矩阵(前置:浏览器 + axe-core,见 tests/README.md)。\n'
|
||
run_suite "admin-axe-audit.mjs" node tests/admin-axe-audit.mjs
|
||
else
|
||
printf '\n ⚠ SKIPPED (RUN_AXE not set):未启用管理台 axe 42 格矩阵。\n'
|
||
printf ' 原因:该套件需本机 Chromium/Edge + 联网装 axe-core,单跑约 45s;\n'
|
||
printf ' 默认快跑路径(约 14s)不含它。启用:RUN_AXE=1 bash tests/run-guards.sh\n'
|
||
add_result "admin-axe-audit.mjs" "SKIPPED (RUN_AXE not set)" "-" "0s" "SKIPPED (RUN_AXE not set)"
|
||
fi
|
||
|
||
# ── 汇总表 ───────────────────────────────────────────────────
|
||
TOTAL_DUR=$((SECONDS - START))
|
||
printf '\n'
|
||
printf '══════════════════════════ 护栏汇总 ══════════════════════════\n'
|
||
printf ' %-34s %-18s %-8s %-6s %s\n' "套件" "状态" "退出码" "耗时" "指标"
|
||
printf ' ────────────────────────────────────────────────────────────────────────────\n'
|
||
npass=0; nfail=0; nskip=0
|
||
for r in "${RESULTS[@]}"; do
|
||
IFS='|' read -r name status rc dur metric <<< "$r"
|
||
printf ' %-34s %-18s %-8s %-6s %s\n' "$name" "$status" "$rc" "$dur" "$metric"
|
||
case "$status" in
|
||
PASS) npass=$((npass + 1)) ;;
|
||
FAIL) nfail=$((nfail + 1)) ;;
|
||
*) nskip=$((nskip + 1)) ;;
|
||
esac
|
||
done
|
||
printf ' ────────────────────────────────────────────────────────────────────────────\n'
|
||
printf ' 合计:%d 套件 PASS %d / FAIL %d / SKIPPED %d | 总耗时 %ss | 整体退出码 %d\n' \
|
||
"${#RESULTS[@]}" "$npass" "$nfail" "$nskip" "$TOTAL_DUR" "$OVERALL"
|
||
printf '══════════════════════════════════════════════════════════════\n'
|
||
exit "$OVERALL"
|