Merge current main into versioned dependency workspace
Why: Include the latest Dev task-card closure and governance fix while preserving the versioned dependency migration. Evidence: SELF merge conflict inspection, clean staged diff, bash syntax check, and prior isolated E2E replay; full final-head verification pending. Impact: Keep existing Backend and Frontend source directories outside the Git index and retain their on-disk Git data. Preserve password setup, audit grants, scoped browser coverage, and owned process cleanup. Refs: #537, #538, mq-dev-20261014-537-01
This commit is contained in:
+42
-17
@@ -1,10 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
# e2e.sh —— U4 运行验收一条龙(真实浏览器:Playwright × RDM 审计工作区)。
|
||||
#
|
||||
# 动线(与 frontend/e2e/rdm-audit.spec.ts 的环境契约一一对应):
|
||||
# 1. 构建:RDM 二进制(RDM 仓 origin/main 临时 worktree,pins 物化)
|
||||
# + 基座 CLI(本 worktree modules/backend,建号用)
|
||||
# + frontend dist(本 worktree modules/frontend)
|
||||
# 动线(与 Frontend e2e/rdm-audit.playwright.ts 的环境契约一一对应):
|
||||
# 1. 构建:版本化候选从固定 Dev 组合取得 RDM、同源基座 CLI 与 Frontend dist;
|
||||
# 历史候选继续按原 gitlink 和 pins 路径物化。
|
||||
# 2. 种子:RDM -bootstrap-admin(域管理员)→ 基座 user create(登录账号)
|
||||
# → serve(auth on)→ API rdm.project.create×2 + rdm.ingest(主项目)
|
||||
# 3. 验收:vite preview(dist+/api 代理)→ npx playwright test
|
||||
@@ -17,6 +16,7 @@
|
||||
# RDM_REPO RDM 仓路径(默认 <本仓上级>/Agent-System-RDM)
|
||||
# E2E_API_PORT/E2E_UI_PORT 服务端口(默认 18791/4173)
|
||||
# E2E_OWNER/E2E_PASSWORD 登录账号(默认 e2e-auditor/E2eAudit#2026)
|
||||
# E2E_PASSWORD2 首次登录后的设密结果(默认 E2eAudit#2026b)
|
||||
# E2E_PROJECT_MAIN/E2E_PROJECT_ISOLATED 项目名(默认 审计演示项目/隔离检查项目)
|
||||
# E2E_OUT 工件目录(默认 .tmp/e2e/<ts>;.gitignore 已覆盖 .tmp)
|
||||
# 依赖:go、node/npm、curl;首次运行会 npx playwright install chromium。
|
||||
@@ -39,6 +39,7 @@ API_PORT=${E2E_API_PORT:-18791}
|
||||
UI_PORT=${E2E_UI_PORT:-4173}
|
||||
OWNER=${E2E_OWNER:-e2e-auditor}
|
||||
PASSWORD=${E2E_PASSWORD:-E2eAudit#2026}
|
||||
PASSWORD2=${E2E_PASSWORD2:-E2eAudit#2026b}
|
||||
PROJ_MAIN=${E2E_PROJECT_MAIN:-审计演示项目}
|
||||
PROJ_ISO=${E2E_PROJECT_ISOLATED:-隔离检查项目}
|
||||
API_BASE="http://127.0.0.1:$API_PORT"
|
||||
@@ -53,7 +54,8 @@ die() { printf '\033[31m[e2e][FAIL]\033[0m %s\n' "$*" >&2; exit 1; }
|
||||
command -v go >/dev/null || die "go not found"
|
||||
command -v node >/dev/null || die "node not found"
|
||||
command -v curl >/dev/null || die "curl not found"
|
||||
git -C "$RDM_REPO" rev-parse --git-dir >/dev/null 2>&1 || die "RDM repo not found at $RDM_REPO (set RDM_REPO)"
|
||||
[ "$(git -C "$RDM_REPO" rev-parse --is-inside-work-tree 2>/dev/null)" = true ] \
|
||||
|| die "RDM repo not found at $RDM_REPO (set RDM_REPO)"
|
||||
|
||||
if [ "$BUILD_ONLY" != 1 ]; then
|
||||
for p in "$API_PORT" "$UI_PORT"; do
|
||||
@@ -66,10 +68,11 @@ fi
|
||||
API_PID=""
|
||||
UI_PID=""
|
||||
cleanup() {
|
||||
if [ -n "${API_PID:-}" ]; then kill "$API_PID" 2>/dev/null || true; fi
|
||||
if [ -n "${UI_PID:-}" ]; then
|
||||
if [ -n "${API_PID:-}" ]; then kill "$API_PID" 2>/dev/null || true; wait "$API_PID" 2>/dev/null || true; fi
|
||||
# Preview is a single owned Vite process. Check its command before signaling
|
||||
# so a recycled PID cannot stop a process from another worktree.
|
||||
if [ -n "${UI_PID:-}" ] && ps -p "$UI_PID" -o args= 2>/dev/null | rg -Fq -- "vite preview --outDir $FRONTEND_DIST --port $UI_PORT"; then
|
||||
kill "$UI_PID" 2>/dev/null || true
|
||||
pkill -f "vite preview --port $UI_PORT" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
@@ -179,9 +182,23 @@ stop_serve() {
|
||||
[ -n "${API_PID:-}" ] && { kill "$API_PID" 2>/dev/null || true; wait "$API_PID" 2>/dev/null || true; API_PID=""; }
|
||||
}
|
||||
login() {
|
||||
curl -sf -c "$JAR" -X POST "$API_BASE/api/auth/login" \
|
||||
local i
|
||||
for i in $(seq 1 20); do
|
||||
if curl -sf -c "$JAR" -X POST "$API_BASE/api/auth/login" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "{\"owner\":\"$OWNER\",\"password\":\"$PASSWORD\"}" >/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
sleep 0.25
|
||||
done
|
||||
die "login failed"
|
||||
}
|
||||
finish_password_gate() {
|
||||
curl -sf -b "$JAR" -c "$JAR" -X POST "$API_BASE/api/auth/password" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "{\"owner\":\"$OWNER\",\"password\":\"$PASSWORD\"}" >/dev/null || die "login failed"
|
||||
-d "{\"next\":\"$PASSWORD2\"}" >/dev/null || die "password setup failed"
|
||||
PASSWORD=$PASSWORD2
|
||||
login
|
||||
}
|
||||
|
||||
cmd() { # cmd <id> <params-json> → payload(失败即死,种子步骤不容忍部分成功)
|
||||
@@ -196,11 +213,17 @@ cmd() { # cmd <id> <params-json> → payload(失败即死,种子步骤不容
|
||||
start_serve
|
||||
JAR="$OUT/cookies.txt"
|
||||
login
|
||||
finish_password_gate
|
||||
log "seed: create projects"
|
||||
PID_MAIN=$(cmd rdm.project.create "{\"name\":\"$PROJ_MAIN\"}" | python3 -c 'import json,sys; print(json.load(sys.stdin)["projectId"])')
|
||||
PID_ISO=$(cmd rdm.project.create "{\"name\":\"$PROJ_ISO\"}" | python3 -c 'import json,sys; print(json.load(sys.stdin)["projectId"])')
|
||||
log "seed: projects main=$PID_MAIN iso=$PID_ISO"
|
||||
|
||||
log "seed: grant explicit audit.read for the fixture owner"
|
||||
cmd rdm.perm.cap '{"action":"audit.read","allow":true}' >/dev/null
|
||||
cmd rdm.perm.set "{\"projectId\":\"$PID_MAIN\",\"member\":\"$OWNER\",\"action\":\"audit.read\",\"allow\":true}" >/dev/null
|
||||
cmd rdm.perm.set "{\"projectId\":\"$PID_ISO\",\"member\":\"$OWNER\",\"action\":\"audit.read\",\"allow\":true}" >/dev/null
|
||||
|
||||
log "seed: import requirement ledger into $PID_MAIN"
|
||||
cmd rdm.reqs.import "{\"projectId\":\"$PID_MAIN\",\"items\":$(
|
||||
python3 - <<'PY'
|
||||
@@ -253,11 +276,12 @@ if [ "$SEED_ONLY" = 1 ]; then
|
||||
fi
|
||||
|
||||
log "start vite preview at http://127.0.0.1:$UI_PORT"
|
||||
(cd "$FRONTEND_DIR" && \
|
||||
PLAYWRIGHT_API_TARGET="$API_BASE" npx vite preview --outDir "$FRONTEND_DIST" --port "$UI_PORT" --strictPort \
|
||||
>"$OUT/preview.log" 2>&1 &
|
||||
echo $! > "$OUT/preview.pid")
|
||||
UI_PID=$(cat "$OUT/preview.pid")
|
||||
(
|
||||
cd "$FRONTEND_DIR"
|
||||
export PLAYWRIGHT_API_TARGET="$API_BASE"
|
||||
exec ./node_modules/.bin/vite preview --outDir "$FRONTEND_DIST" --port "$UI_PORT" --strictPort
|
||||
) >"$OUT/preview.log" 2>&1 &
|
||||
UI_PID=$!
|
||||
for i in $(seq 1 40); do
|
||||
curl -sf -o /dev/null "http://127.0.0.1:$UI_PORT/" && break
|
||||
sleep 0.25
|
||||
@@ -269,10 +293,11 @@ log "playwright (install chromium if missing)"
|
||||
|
||||
log "run specs"
|
||||
cd "$FRONTEND_DIR"
|
||||
export PLAYWRIGHT_BASE_URL="http://127.0.0.1:$UI_PORT" E2E_OWNER="$OWNER" E2E_PASSWORD="$PASSWORD" \
|
||||
export PLAYWRIGHT_BASE_URL="http://127.0.0.1:$UI_PORT" E2E_OWNER="$OWNER" \
|
||||
E2E_PASSWORD="${E2E_PASSWORD:-E2eAudit#2026}" E2E_PASSWORD2="$PASSWORD2" \
|
||||
E2E_PROJECT_MAIN="$PROJ_MAIN" E2E_PROJECT_ISOLATED="$PROJ_ISO"
|
||||
set +e
|
||||
npx playwright test --reporter=list >"$OUT/playwright.log" 2>&1
|
||||
npx playwright test e2e/rdm-audit.playwright.ts --reporter=list >"$OUT/playwright.log" 2>&1
|
||||
PW_EXIT=$?
|
||||
set -e
|
||||
cat "$OUT/playwright.log"
|
||||
|
||||
+4
-2
@@ -64,7 +64,9 @@ cmd_precheck() {
|
||||
pb=$(card_of_branch "$pc"); [ -n "$pb" ] || continue
|
||||
pst=$(card_status "$pc"); case "$pst" in 已完成*) continue ;; esac
|
||||
card_pending "${pc##*/}" && continue
|
||||
if git log --merges --format='%s' "origin/${TARGET_BRANCH}" 2>/dev/null | grep -qF " from $pb into ${TARGET_BRANCH}"; then
|
||||
# Consume the complete producer output: grep -q exits on the first match,
|
||||
# making git log die with SIGPIPE (141 under pipefail) on long histories.
|
||||
if git log --merges --format='%s' "origin/${TARGET_BRANCH}" 2>/dev/null | grep -F " from $pb into ${TARGET_BRANCH}" >/dev/null; then
|
||||
open_cards="${open_cards} ${pc##*/}"
|
||||
fi
|
||||
done
|
||||
@@ -392,7 +394,7 @@ card_of_branch() { sed -n 's/^- 工作区:.*分支:\([A-Za-z0-9._/-]\+\).*/\
|
||||
card_status() { sed -n 's/^.*;状态:\([^;]*\).*/\1/p' "$1" | head -1; }
|
||||
card_merge() { # $1=branch → "sha|subject"(该分支合入 origin/<target> 的合并提交)
|
||||
git log --merges --format='%H|%s' "origin/${TARGET_BRANCH}" 2>/dev/null \
|
||||
| grep -F " from $1 into ${TARGET_BRANCH}" | head -1
|
||||
| grep -F " from $1 into ${TARGET_BRANCH}" | sed -n '1p'
|
||||
}
|
||||
card_pending() { # $1=卡片文件名——闭合是否已在某 origin/feature/card-close-* 分支待合入
|
||||
local cb
|
||||
|
||||
Reference in New Issue
Block a user