Sprint 4 T-B-24 · Lighthouse CI (G3 闸门真实化)
- apps/web/lighthouserc.cjs: desktop preset, 3 runs, P/A/B/S ≥ 90 断言 - apps/web/scripts/lhci-scores.cjs: 本地汇总脚本(median P/A/B/S + ✅/❌ 标记) - .github/workflows/web-ci.yml: 新增 lighthouse job,treosh/lighthouse-ci-action@v12 - apps/web/package.json: +lhci script - apps/web/.gitignore: .lighthouseci/ 加入忽略(避免本地 12 个 run 报告污染 repo) 本地验证(desktop preset, 3 runs × 4 URLs): perf=100 a11y=95-96 best=96 seo=91 (全部 ≥ 90 ✅) G3 闸门状态:typecheck ✅ / lint ✅ / vitest 69 ✅ / e2e 84/84 ✅ / build 87.85KB ✅ / Lighthouse P/A/B/S ✅ / 真实后端 ⏳(T-B-27)/ Poster CLI Docker ⏳(T-C-44)
This commit is contained in:
39
.github/workflows/web-ci.yml
vendored
39
.github/workflows/web-ci.yml
vendored
@@ -127,3 +127,42 @@ jobs:
|
||||
workingDir: apps/web
|
||||
buildScriptName: build
|
||||
exitZeroOnChanges: true
|
||||
|
||||
lighthouse:
|
||||
# G3 闸门真实化:P/A/B/S 均 ≥ 90
|
||||
runs-on: ubuntu-latest
|
||||
needs: ci
|
||||
timeout-minutes: 15
|
||||
if: github.event_name == 'push' || github.event_name == 'pull_request'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build
|
||||
run: pnpm --filter @gaokao/web build
|
||||
|
||||
- name: Run Lighthouse CI (G3 闸门:P/A/B/S ≥ 90)
|
||||
uses: treosh/lighthouse-ci-action@v12
|
||||
with:
|
||||
configPath: ./apps/web/lighthouserc.cjs
|
||||
uploadArtifacts: true
|
||||
temporaryPublicStorage: true
|
||||
runs: 3
|
||||
serverUrl: http://127.0.0.1:8080/
|
||||
# treosh action 自动启动 vite preview @ 8080
|
||||
url: |
|
||||
http://127.0.0.1:8080/
|
||||
http://127.0.0.1:8080/data-query
|
||||
http://127.0.0.1:8080/plans
|
||||
http://127.0.0.1:8080/about
|
||||
|
||||
1
apps/web/.gitignore
vendored
1
apps/web/.gitignore
vendored
@@ -48,3 +48,4 @@ playwright-report/
|
||||
test-results/
|
||||
coverage/
|
||||
.turbo/
|
||||
.lighthouseci/
|
||||
|
||||
42
apps/web/lighthouserc.cjs
Normal file
42
apps/web/lighthouserc.cjs
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* V10 · Sprint 4 · T-B-24 · Lighthouse CI 配置
|
||||
*
|
||||
* G3 闸门真实化:P/A/B/S 均 ≥ 90
|
||||
* - perf: 性能(首屏加载、TTI、TBT)
|
||||
* - a11y: 无障碍(WCAG 2.1 AA)
|
||||
* - best-practices: HTTPS / CSP / console errors
|
||||
* - seo: meta tags / viewport
|
||||
*
|
||||
* 注意:mobile 测试(默认 form factor = mobile)更严格
|
||||
*/
|
||||
module.exports = {
|
||||
ci: {
|
||||
collect: {
|
||||
// 静态服务器从 vite preview 启动(8081),见 CI workflow
|
||||
url: [
|
||||
'http://127.0.0.1:8080/',
|
||||
'http://127.0.0.1:8080/data-query',
|
||||
'http://127.0.0.1:8080/plans',
|
||||
'http://127.0.0.1:8080/about',
|
||||
],
|
||||
numberOfRuns: 3, // 取 P75(median)
|
||||
settings: {
|
||||
// P75 算分(默认 median = P50)
|
||||
preset: 'desktop',
|
||||
chromeFlags: '--no-sandbox --headless=new',
|
||||
},
|
||||
},
|
||||
assert: {
|
||||
// G3 闸门:每类 ≥ 90
|
||||
assertions: {
|
||||
'categories:performance': ['error', { minScore: 0.9 }],
|
||||
'categories:accessibility': ['error', { minScore: 0.9 }],
|
||||
'categories:best-practices': ['error', { minScore: 0.9 }],
|
||||
'categories:seo': ['error', { minScore: 0.9 }],
|
||||
},
|
||||
},
|
||||
upload: {
|
||||
target: 'temporary-public-storage', // 公开 storage 30 天;后续接 LHCI server
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -16,6 +16,7 @@
|
||||
"test:e2e": "playwright test",
|
||||
"test:storybook": "echo 'storybook is a Sprint 5 task'",
|
||||
"chromatic": "chromatic --project-token=__CHROMATIC_TOKEN__ --exit-zero-on-changes",
|
||||
"lhci": "lhci autorun --config=./lighthouserc.cjs",
|
||||
"codegen": "tsx scripts/codegen.ts",
|
||||
"codegen:check": "tsx scripts/codegen-check.ts",
|
||||
"clean": "rm -rf dist .vite .turbo coverage playwright-report test-results"
|
||||
@@ -45,6 +46,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.4",
|
||||
"@lhci/cli": "^0.14.0",
|
||||
"@playwright/test": "^1.49.0",
|
||||
"@tailwindcss/vite": "^4",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
|
||||
33
apps/web/scripts/lhci-scores.cjs
Normal file
33
apps/web/scripts/lhci-scores.cjs
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* V10 Sprint 4 · T-B-24 · lhci 分数汇总脚本
|
||||
* 读 .lighthouseci/lhr-*.json,对每个 URL 计算 P/A/B/S 的 median
|
||||
*/
|
||||
const fs = require('fs');
|
||||
const dir = '.lighthouseci';
|
||||
const files = fs.readdirSync(dir).filter((f) => f.startsWith('lhr-') && f.endsWith('.json'));
|
||||
const urlMap = {};
|
||||
for (const f of files) {
|
||||
const j = JSON.parse(fs.readFileSync(`${dir}/${f}`, 'utf8'));
|
||||
if (!j.categories || !j.categories.performance) continue;
|
||||
const url = j.requestedUrl.replace('http://127.0.0.1:8080', '') || '/';
|
||||
if (!urlMap[url]) urlMap[url] = [];
|
||||
urlMap[url].push({
|
||||
p: Math.round(j.categories.performance.score * 100),
|
||||
a: Math.round(j.categories.accessibility.score * 100),
|
||||
b: Math.round(j.categories['best-practices'].score * 100),
|
||||
s: Math.round(j.categories.seo.score * 100),
|
||||
});
|
||||
}
|
||||
console.log('URL perf a11y best seo');
|
||||
for (const k of Object.keys(urlMap)) {
|
||||
const runs = urlMap[k];
|
||||
const med = (x) => {
|
||||
const arr = runs.map((r) => r[x]).sort((a, b) => a - b);
|
||||
const m = Math.floor(arr.length / 2);
|
||||
return arr.length % 2 ? arr[m] : Math.round((arr[m - 1] + arr[m]) / 2);
|
||||
};
|
||||
const ok = (x) => med(x) >= 90 ? '✅' : '❌';
|
||||
console.log(
|
||||
`${k.padEnd(15)} ${String(med('p')).padStart(4)}${ok('p')} ${String(med('a')).padStart(4)}${ok('a')} ${String(med('b')).padStart(4)}${ok('b')} ${String(med('s')).padStart(4)}${ok('s')}`,
|
||||
);
|
||||
}
|
||||
1916
pnpm-lock.yaml
generated
1916
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user