#!/usr/bin/env bash # # 一键验收:把「必须带六个环境变量、且必须 0 skip」这条纪律固化下来。 # # ./scripts/verify.sh # 全量验收(默认,约 8 分钟) # ./scripts/verify.sh --fast # 只做 gofmt / vet / build(约 20 秒) # ./scripts/verify.sh --race # 全量 + -race(更慢,需 mingw 在 PATH) # ./scripts/verify.sh --cover # 全量 + 覆盖率 # ./scripts/verify.sh --pkg ./internal/web/ --run 'TestMapPreview' # 定点跑 # # 退出码:0 = 全绿且 0 skip;非 0 = 有失败或有跳过。 set -u -o pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$ROOT" ABS="$ROOT" command -v pwd >/dev/null && ABS="$(pwd -W 2>/dev/null || pwd)" CONF="${ROND_TEST_CONF:-conf/app-test.ini}" ROND_SRC="${ROND_SRC:-$ABS/data/20260914_151649.rondbackup}" FOG_SRC="${FOG_SRC:-$ABS/data/Snapshot-20260914T154839+0800.fwss}" OUT="$ABS/storage/.verify" CLEAN_OUT="$OUT/acceptance-clean.rondbackup" LOG="$OUT/verify.log" FAST=0; RACE=0; COVER=0; PKGS="./..."; RUN="" while [ $# -gt 0 ]; do case "$1" in --fast) FAST=1 ;; --race) RACE=1 ;; --cover) COVER=1 ;; --pkg) PKGS="${2:?--pkg 后面要给包路径}"; shift ;; --run) RUN="${2:?--run 后面要给正则}"; shift ;; *) echo "未知参数:$1" >&2; exit 2 ;; esac shift done fail() { echo "✗ $*" >&2; exit 1; } ok() { echo "✓ $*"; } echo "== 仓库:$ROOT" if [ "$FAST" = 1 ]; then unformatted="$(gofmt -l internal/ main.go 2>&1)" [ -n "$unformatted" ] && fail "gofmt 不干净(注意 CRLF 会假警):$(echo "$unformatted" | tr '\n' ' ')" ok "gofmt" go vet ./... || fail "go vet" ok "go vet" go build ./... || fail "go build" ok "go build" exit 0 fi # --- 六个环境变量 --------------------------------------------------------------- [ -f "$CONF" ] || fail "找不到 $CONF" [ -f "$ROND_SRC" ] || fail "找不到备份包 ROND_SRC=$ROND_SRC" [ -f "$FOG_SRC" ] || fail "找不到迷雾快照 FOG_SRC=$FOG_SRC" if [ -n "${ROND_DSN:-}" ]; then DSN="$ROND_DSN" else DSN="$(grep -m1 -iE '^[[:space:]]*dsn[[:space:]]*=' "$CONF" | sed 's/^[^=]*=[[:space:]]*//' | tr -d '\r\n')" fi [ -n "$DSN" ] || fail "没能从 $CONF 取到 dsn(也不在环境里),别把口令写进脚本" # 指到维护库 postgres 会多出「users 不存在」+ 一堆 stats 假 skip,直接拦住 DBNAME="$(printf '%s' "$DSN" | sed -E 's#^[^/]*//[^/]*/([^/?]+).*#\1#')" case "$DBNAME" in test|*test*) : ;; *) fail "ROND_DSN 指向的库名是 '$DBNAME',不含 test —— 验收必须用应用测试库(rond_fog_test)" ;; esac export ROND_TEST_CONF="$CONF" ROND_SRC FOG_SRC ROND_DSN="$DSN" export ROND_CLEAN_OUT="$CLEAN_OUT" ROND_EXPORT="$CLEAN_OUT" mkdir -p "$OUT" echo "== 库:$DBNAME 备份:$(basename "$ROND_SRC") / $(basename "$FOG_SRC")" GOFLAGS_EXTRA="" [ "$RACE" = 1 ] && GOFLAGS_EXTRA="$GOFLAGS_EXTRA -race" [ "$COVER" = 1 ] && GOFLAGS_EXTRA="$GOFLAGS_EXTRA -cover" if [ "$RACE" = 1 ] && command -v gcc >/dev/null 2>&1; then :; else for d in "$HOME/scoop/apps/mingw/current/bin"; do [ -d "$d" ] && export PATH="$d:$PATH" done fi if [ "$RACE" = 1 ] && ! command -v gcc >/dev/null 2>&1; then fail "-race 需要 C 编译器:scoop install mingw,或把 mingw 的 bin 加进 PATH" fi # --- 第一段:先产出一份「纯净往返包」,第二段要读它 ------------------------------ if [ -z "$RUN" ] && [ "$PKGS" = "./..." ]; then echo "== 第一段:导出纯净往返包" if ! go test -count=1 -run '^TestMovementCleanExport$' -v ./internal/web/ 2>&1 | tee "$OUT/stage1.log" | grep -E '^(--- (PASS|FAIL|SKIP)|ok|FAIL)'; then fail "第一段导出失败,见 $OUT/stage1.log" fi grep -q '^--- PASS' "$OUT/stage1.log" || fail "TestMovementCleanExport 没跑起来(被 skip 了?)" ok "纯净往返包已生成:$(basename "$CLEAN_OUT")" fi # --- 第二段:全量(或定点)------------------------------------------------------ RUNARG="" [ -n "$RUN" ] && RUNARG="-run $RUN" echo "== 第二段:go test -count=1 -v $GOFLAGS_EXTRA $RUNARG $PKGS" # shellcheck disable=SC2086 go test -count=1 -v $GOFLAGS_EXTRA -timeout 2400s $RUNARG $PKGS >"$LOG" 2>&1 RC=$? PASS=$(grep -c '^--- PASS' "$LOG" || true) FAILN=$(grep -c '^--- FAIL' "$LOG" || true) SKIP=$(grep -c '^--- SKIP' "$LOG" || true) BADPKG=$(grep -c '^FAIL' "$LOG" || true) echo "== 结果:PASS=$PASS FAIL=$FAILN SKIP=$SKIP 非ok包=$BADPKG" echo "== 日志:$LOG" if [ "$SKIP" != 0 ]; then echo "-- 以下用例被跳过(=没跑。门控测试缺环境变量时 go test 依然回 ok,别当通过看)--" grep -n -B1 '^--- SKIP' "$LOG" | head -40 fi if [ "$FAILN" != 0 ] || [ "$BADPKG" != 0 ]; then echo "-- 失败明细(前 40 行)--" grep -n -B2 '^--- FAIL' "$LOG" | head -40 fi if [ "$RC" != 0 ] || [ "$FAILN" != 0 ] || [ "$BADPKG" != 0 ]; then fail "有失败" fi if [ "$SKIP" != 0 ]; then fail "有 $SKIP 个用例被跳过 ⇒ 这次不算验收过" fi ok "全绿且 0 skip(PASS=$PASS)"