Skip to content

Commit f69c1b3

Browse files
committed
Merge remote-tracking branch 'upstream/main' into fix/skill-tag-overflow
# Conflicts: # .i18n/scan-config.json
2 parents 6c7fece + 001fb5a commit f69c1b3

168 files changed

Lines changed: 16341 additions & 5107 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# e2e visual baseline bootstrap: 维护者手动触发,在 GitHub Actions runner 生成初始 baseline PNG。
2+
# 不接 pull_request,避免普通 PR gate 拿写权限;baseline PNG 通过单独 PR review 后合入。
3+
4+
name: e2e-baseline-bootstrap
5+
6+
on:
7+
workflow_dispatch:
8+
inputs:
9+
branch_name:
10+
description: "Baseline init branch to create/update"
11+
required: false
12+
default: "chore/e2e-baseline-init"
13+
14+
permissions:
15+
contents: write
16+
pull-requests: write
17+
18+
concurrency:
19+
group: e2e-baseline-bootstrap
20+
cancel-in-progress: false
21+
22+
jobs:
23+
bootstrap:
24+
runs-on: ubuntu-22.04
25+
container:
26+
image: mcr.microsoft.com/playwright:v1.61.1-jammy
27+
env:
28+
E2E_TARGET: local
29+
PW_PREVIEW_PORT: "5175"
30+
BASELINE_BRANCH: ${{ inputs.branch_name }}
31+
steps:
32+
- uses: actions/checkout@v4
33+
with:
34+
fetch-depth: 0
35+
- name: Trust workspace for git
36+
shell: bash
37+
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
38+
- name: Prepare bootstrap branch
39+
shell: bash
40+
run: |
41+
set -euo pipefail
42+
git fetch origin "$BASELINE_BRANCH" || true
43+
if git show-ref --verify --quiet "refs/remotes/origin/$BASELINE_BRANCH"; then
44+
git switch -C "$BASELINE_BRANCH" "origin/$BASELINE_BRANCH"
45+
else
46+
git switch -C "$BASELINE_BRANCH"
47+
fi
48+
- uses: pnpm/action-setup@v4
49+
- uses: actions/setup-node@v4
50+
with:
51+
node-version: "22.16.0"
52+
cache: "pnpm"
53+
- name: Install dependencies
54+
run: pnpm install --frozen-lockfile --prefer-offline
55+
- name: Build e2e bundle
56+
working-directory: apps/web
57+
run: pnpm build:e2e
58+
- name: Generate visual baseline snapshots
59+
working-directory: apps/web
60+
shell: bash
61+
run: |
62+
set -euo pipefail
63+
E2E_LOG="e2e-kit/playwright-report/e2e-visual-bootstrap-update.log"
64+
mkdir -p "$(dirname "$E2E_LOG")"
65+
pnpm exec playwright test --config=e2e-kit/playwright.ci.config.ts \
66+
--grep "@visual" --update-snapshots 2>&1 | tee "$E2E_LOG"
67+
- name: Verify generated visual baselines
68+
working-directory: apps/web
69+
shell: bash
70+
run: |
71+
set -euo pipefail
72+
E2E_LOG="e2e-kit/playwright-report/e2e-visual-bootstrap-verify.log"
73+
mkdir -p "$(dirname "$E2E_LOG")"
74+
pnpm exec playwright test --config=e2e-kit/playwright.ci.config.ts \
75+
--grep "@visual" 2>&1 | tee "$E2E_LOG"
76+
if grep -R "http proxy error:" e2e-kit/playwright-report/*.log; then
77+
echo "❌ visual bootstrap 出现 Vite proxy error, 说明 mock 覆盖漏网, 拒绝提交 baseline"
78+
exit 1
79+
fi
80+
- name: Commit snapshots to bootstrap branch
81+
id: commit
82+
shell: bash
83+
run: |
84+
set -euo pipefail
85+
if ! command -v git >/dev/null 2>&1; then
86+
echo "❌ container image missing git binary"
87+
exit 1
88+
fi
89+
90+
git config user.name "github-actions[bot]"
91+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
92+
git add -f apps/web/e2e-kit/screenshots/
93+
94+
if git diff --cached --quiet; then
95+
echo "No baseline snapshot changes to commit."
96+
echo "committed=false" >> "$GITHUB_OUTPUT"
97+
exit 0
98+
fi
99+
100+
git commit -m "chore(e2e): bootstrap visual baselines"
101+
git push --set-upstream origin "$BASELINE_BRANCH"
102+
echo "committed=true" >> "$GITHUB_OUTPUT"
103+
- name: Open or reuse bootstrap PR
104+
if: steps.commit.outputs.committed == 'true'
105+
uses: actions/github-script@v7
106+
with:
107+
script: |
108+
const branch = process.env.BASELINE_BRANCH;
109+
const { owner, repo } = context.repo;
110+
const existing = await github.rest.pulls.list({
111+
owner,
112+
repo,
113+
head: `${owner}:${branch}`,
114+
base: 'main',
115+
state: 'open',
116+
});
117+
if (existing.data.length > 0) {
118+
core.notice(`Baseline bootstrap PR already exists: ${existing.data[0].html_url}`);
119+
return;
120+
}
121+
const { data: pr } = await github.rest.pulls.create({
122+
owner,
123+
repo,
124+
base: 'main',
125+
head: branch,
126+
title: 'chore(e2e): bootstrap visual baselines',
127+
body: 'Generate initial Playwright visual baseline PNGs from the GitHub Actions runner.\n\nThis PR only contains files under apps/web/e2e-kit/screenshots/.',
128+
});
129+
core.notice(`Created baseline bootstrap PR: ${pr.html_url}`);
130+
- name: Upload playwright report
131+
if: always()
132+
uses: actions/upload-artifact@v4
133+
with:
134+
name: playwright-report-visual-bootstrap
135+
path: |
136+
apps/web/e2e-kit/playwright-report/
137+
apps/web/e2e-kit/test-results/
138+
retention-days: 30
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
# e2e visual baseline update: 维护者手动触发,把指定 PR 的 @visual baseline 更新回 PR 分支。
2+
# 只支持同仓库分支 PR;fork PR 不回写 baseline,避免权限和信任边界混乱。
3+
4+
name: e2e-baseline-update
5+
6+
on:
7+
workflow_dispatch:
8+
inputs:
9+
pr_number:
10+
description: "PR number whose head branch should receive updated visual baselines"
11+
required: true
12+
13+
permissions:
14+
contents: write
15+
pull-requests: read
16+
17+
concurrency:
18+
group: e2e-baseline-update-${{ inputs.pr_number }}
19+
cancel-in-progress: false
20+
21+
jobs:
22+
update:
23+
runs-on: ubuntu-22.04
24+
container:
25+
image: mcr.microsoft.com/playwright:v1.61.1-jammy
26+
env:
27+
E2E_TARGET: local
28+
PW_PREVIEW_PORT: "5176"
29+
steps:
30+
- name: Resolve PR head
31+
id: pr
32+
uses: actions/github-script@v7
33+
env:
34+
PR_NUMBER: ${{ inputs.pr_number }}
35+
with:
36+
script: |
37+
const prNumber = Number(process.env.PR_NUMBER);
38+
const { owner, repo } = context.repo;
39+
const { data: pr } = await github.rest.pulls.get({ owner, repo, pull_number: prNumber });
40+
if (pr.head.repo.full_name !== `${owner}/${repo}`) {
41+
core.setFailed(`PR #${prNumber} is from ${pr.head.repo.full_name}; baseline update only pushes to same-repo branches.`);
42+
return;
43+
}
44+
core.setOutput('head_ref', pr.head.ref);
45+
core.setOutput('head_sha', pr.head.sha);
46+
core.setOutput('base_ref', pr.base.ref);
47+
- uses: actions/checkout@v4
48+
with:
49+
ref: ${{ steps.pr.outputs.head_ref }}
50+
fetch-depth: 0
51+
- name: Trust workspace for git
52+
shell: bash
53+
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
54+
- uses: pnpm/action-setup@v4
55+
- uses: actions/setup-node@v4
56+
with:
57+
node-version: "22.16.0"
58+
cache: "pnpm"
59+
- name: Install dependencies
60+
run: pnpm install --frozen-lockfile --prefer-offline
61+
- name: Build e2e bundle
62+
working-directory: apps/web
63+
run: pnpm build:e2e
64+
- name: Run @p0 business gate before baseline update
65+
working-directory: apps/web
66+
shell: bash
67+
run: |
68+
set +e
69+
E2E_LOG="e2e-kit/playwright-report/e2e-baseline-update-p0.log"
70+
SUMMARY="e2e-kit/playwright-report/e2e-baseline-update-summary.txt"
71+
mkdir -p "$(dirname "$E2E_LOG")"
72+
pnpm exec playwright test --config=e2e-kit/playwright.ci.config.ts --grep "@p0" 2>&1 | tee "$E2E_LOG"
73+
PW_EXIT=${PIPESTATUS[0]}
74+
set -e
75+
76+
JUNIT="e2e-kit/playwright-report/junit.xml"
77+
if [[ ! -f "$JUNIT" ]]; then
78+
echo "❌ junit.xml missing, refuse to update baseline"
79+
exit 1
80+
fi
81+
82+
FAILED_NAMES=$(awk '
83+
/<testcase / { match($0, /name="[^"]*"/); name = substr($0, RSTART+6, RLENGTH-7); has_fail=0; next }
84+
/<failure|<error/ { if (name && !has_fail) { print name; has_fail=1 } }
85+
/<\/testcase>/ { name="" }
86+
' "$JUNIT")
87+
TOTAL_FAIL=$(echo "$FAILED_NAMES" | grep -c . || true)
88+
VISUAL_FAIL=$(echo "$FAILED_NAMES" | grep "@visual" | grep -vc "@p0" || true)
89+
BUSINESS_FAIL=$((TOTAL_FAIL - VISUAL_FAIL))
90+
TOTAL_TESTS=$(grep -oE 'tests="[0-9]+"' "$JUNIT" | head -1 | grep -oE '[0-9]+' || echo 0)
91+
SKIPPED=$(grep -oE 'skipped="[0-9]+"' "$JUNIT" | head -1 | grep -oE '[0-9]+' || echo 0)
92+
ERRORS=$(grep -oE 'errors="[0-9]+"' "$JUNIT" | head -1 | grep -oE '[0-9]+' || echo 0)
93+
PROXY_ERRORS=$(grep -c "http proxy error:" "$E2E_LOG" || true)
94+
95+
{
96+
echo "PW_EXIT=$PW_EXIT"
97+
echo "TOTAL_TESTS=$TOTAL_TESTS"
98+
echo "SKIPPED=$SKIPPED"
99+
echo "ERRORS=$ERRORS"
100+
echo "BUSINESS_FAIL=$BUSINESS_FAIL"
101+
echo "VISUAL_FAIL=$VISUAL_FAIL"
102+
echo "PROXY_ERRORS=$PROXY_ERRORS"
103+
echo "---FAIL_NAMES---"
104+
echo "$FAILED_NAMES"
105+
} > "$SUMMARY"
106+
cat "$SUMMARY"
107+
108+
EXPECTED_P0=29
109+
if [[ "$TOTAL_TESTS" -eq 0 || "$TOTAL_TESTS" -lt "$EXPECTED_P0" || "$SKIPPED" -gt 0 || "$ERRORS" -gt 0 ]]; then
110+
echo "❌ @p0 did not really pass cleanly; refuse to update baseline"
111+
exit 1
112+
elif [[ "$PROXY_ERRORS" -gt 0 ]]; then
113+
echo "❌ Vite proxy error detected; refuse to update baseline"
114+
exit 1
115+
elif [[ "$BUSINESS_FAIL" -gt 0 || "$PW_EXIT" -ne 0 ]]; then
116+
echo "❌ @p0 business gate failed; refuse to update baseline"
117+
exit 1
118+
fi
119+
- name: Update visual baseline snapshots
120+
working-directory: apps/web
121+
shell: bash
122+
run: |
123+
set -euo pipefail
124+
E2E_LOG="e2e-kit/playwright-report/e2e-baseline-update-visual.log"
125+
mkdir -p "$(dirname "$E2E_LOG")"
126+
pnpm exec playwright test --config=e2e-kit/playwright.ci.config.ts \
127+
--grep "@visual" --update-snapshots 2>&1 | tee "$E2E_LOG"
128+
- name: Verify updated visual baselines
129+
working-directory: apps/web
130+
shell: bash
131+
run: |
132+
set -euo pipefail
133+
E2E_LOG="e2e-kit/playwright-report/e2e-baseline-update-verify.log"
134+
mkdir -p "$(dirname "$E2E_LOG")"
135+
pnpm exec playwright test --config=e2e-kit/playwright.ci.config.ts \
136+
--grep "@visual" 2>&1 | tee "$E2E_LOG"
137+
if grep -R "http proxy error:" e2e-kit/playwright-report/*.log; then
138+
echo "❌ visual update 出现 Vite proxy error, 拒绝提交 baseline"
139+
exit 1
140+
fi
141+
- name: Commit snapshots to PR branch
142+
shell: bash
143+
env:
144+
HEAD_REF: ${{ steps.pr.outputs.head_ref }}
145+
run: |
146+
set -euo pipefail
147+
if ! command -v git >/dev/null 2>&1; then
148+
echo "❌ container image missing git binary"
149+
exit 1
150+
fi
151+
152+
git config user.name "github-actions[bot]"
153+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
154+
git add -f apps/web/e2e-kit/screenshots/
155+
156+
if git diff --cached --quiet; then
157+
echo "No visual baseline changes to commit."
158+
exit 0
159+
fi
160+
161+
git commit -m "chore(e2e): update visual baselines"
162+
git push origin "HEAD:$HEAD_REF"
163+
- name: Upload playwright report
164+
if: always()
165+
uses: actions/upload-artifact@v4
166+
with:
167+
name: playwright-report-visual-update-${{ inputs.pr_number }}
168+
path: |
169+
apps/web/e2e-kit/playwright-report/
170+
apps/web/e2e-kit/test-results/
171+
retention-days: 30

.github/workflows/e2e-nightly.yml

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# e2e nightly: 全量跑 (排除 @visual, baseline 未 ready 前跳过视觉)
1+
# e2e nightly: 全量跑已标记的 @p* 与 @visual case
22
# 每天 UTC 03:00 (北京 11:00) 触发, 只在 default branch 生效.
33
# workflow_dispatch 也开着, 便于维护者手动触发 debug.
44

@@ -59,19 +59,20 @@ jobs:
5959
cache: "pnpm"
6060
- name: Install dependencies
6161
run: pnpm install --frozen-lockfile --prefer-offline
62-
- name: Run e2e (tagged @p* only, non-visual)
62+
- name: Run e2e (tagged @p* plus @visual)
6363
working-directory: apps/web
64-
# --grep "@p[0-9]": 只跑打了优先级 tag case (@p0/@p1/@p2), 排除:
64+
# --grep "@p[0-9]|@visual": 跑打了优先级 tag 的业务 case (@p0/@p1/@p2) 与独立视觉 case, 排除:
6565
# - tests/bind/ (未打 @p 系列, dev-server-only, 未验证过 preview 环境)
6666
# - tests/standalone-doc/ (同上, AC-N: pattern 未走 CaseId 约定)
67-
# --grep-invert "@visual": baseline 未 ready, 视觉 case 目前必红且不该 block nightly
68-
# 组合语义: grep AND grep-invert (playwright 官方支持)
6967
shell: bash
7068
run: |
69+
E2E_LOG="e2e-kit/playwright-report/e2e-nightly.log"
70+
mkdir -p "$(dirname "$E2E_LOG")"
71+
7172
set +e
7273
pnpm exec playwright test --config=e2e-kit/playwright.ci.config.ts \
73-
--grep "@p[0-9]" --grep-invert "@visual"
74-
PW_EXIT=$?
74+
--grep "@p[0-9]|@visual" 2>&1 | tee "$E2E_LOG"
75+
PW_EXIT=${PIPESTATUS[0]}
7576
set -e
7677
7778
JUNIT="e2e-kit/playwright-report/junit.xml"
@@ -80,17 +81,28 @@ jobs:
8081
exit 1
8182
fi
8283
TOTAL_TESTS=$(grep -oE 'tests="[0-9]+"' "$JUNIT" | head -1 | grep -oE '[0-9]+' || echo 0)
84+
SKIPPED=$(grep -oE 'skipped="[0-9]+"' "$JUNIT" | head -1 | grep -oE '[0-9]+' || echo 0)
85+
PROXY_ERRORS=$(grep -c "http proxy error:" "$E2E_LOG" || true)
8386
# nightly 也加 tests=0 guard: 防 grep drift 让 nightly 静默"全绿"
8487
EXPECTED_P0=29
88+
EXPECTED_VISUAL=2
89+
EXPECTED_TOTAL=$((EXPECTED_P0 + EXPECTED_VISUAL))
8590
if [[ "$TOTAL_TESTS" -eq 0 ]]; then
8691
echo "❌ nightly tests=0 (grep 漂了 / preview 挂了), fail"
8792
exit 1
88-
elif [[ "$TOTAL_TESTS" -lt "$EXPECTED_P0" ]]; then
89-
echo "❌ nightly tests=$TOTAL_TESTS < EXPECTED_P0=$EXPECTED_P0 (有 case 掉 @p tag), fail"
93+
elif [[ "$TOTAL_TESTS" -lt "$EXPECTED_TOTAL" ]]; then
94+
echo "❌ nightly tests=$TOTAL_TESTS < EXPECTED_TOTAL=$EXPECTED_TOTAL (有 case 掉 @p/@visual tag), fail"
95+
exit 1
96+
elif [[ "$SKIPPED" -gt 0 ]]; then
97+
echo "❌ nightly skipped=$SKIPPED (有 case 没真执行), fail"
98+
exit 1
99+
elif [[ "$PROXY_ERRORS" -gt 0 ]]; then
100+
echo "❌ nightly 出现 $PROXY_ERRORS 条 Vite proxy error, 说明 mock 覆盖漏网, fail"
101+
grep "http proxy error:" "$E2E_LOG" | sed -E 's/.*http proxy error: //g' | sort | uniq -c
90102
exit 1
91103
fi
92104
echo "✅ nightly ran $TOTAL_TESTS tests (playwright exit=$PW_EXIT)"
93-
exit $PW_EXIT
105+
exit "$PW_EXIT"
94106
- name: Upload playwright report
95107
if: always()
96108
uses: actions/upload-artifact@v4

0 commit comments

Comments
 (0)