Files
Stev_Wang 087f27936e fix(ci): 修复本地 CI 端到端冒烟的 demo 会话与 A0 遗留用例
背景:
- .gitea/workflows/ci.yml 在 job env 中设置 VITE_PUBLIC_DEMO_SESSION=true,
  e2e 用 vite preview + 演示会话登录;scripts/ci-local.mjs 没有同步这组 env,
  本地 CI 第 10 步必失败;
- e2e/smoke.spec.ts 是 A0 用例:还在找「请输入账号或邮箱」占位符、断言登录后落在工作台;
  A1 接入真实登录后占位符为「请输入账号或手机号」,且登录默认落地 /system/users。

改动:
- ci-local:构建与 e2e 步骤注入 VITE_PUBLIC_DEMO_SESSION / VITE_PUBLIC_API_BASE_URL
  (与 ci.yml 对齐),只作用于这两个步骤,避免影响单测对真实登录流程的断言;
- turbo.json:声明 globalEnv,保证构建产物随这两个 VITE 变量变化,不复用非 demo 构建缓存;
- smoke.spec:登录占位符改为「请输入账号或手机号」;登录后按 A1 现状断言落地 /system/users,
  再从顶部「工作台」进入首页并校验模块导航;
- README:单独执行 e2e 时补充 demo 会话环境变量,并注明 ci:local 已内置。

影响面:
- 仅本地 CI 与 e2e 冒烟;业务代码与 CI 工作流行为不变(ci.yml 本就在 job env 注入这些变量);
- 本地构建缓存会因 globalEnv 变更失效一次。

回滚方式:
- 回滚本提交即恢复原 ci-local 步骤、冒烟用例与 README。

验证证据:
- pnpm ci:local 全部门禁通过(用时 218.8s);
- e2e smoke 4 项通过(登录 / 403 / 模块切换 / 404;A1 账号周期 7 项按预期 skip)。
2026-11-03 16:25:55 +08:00

60 lines
2.3 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* 本地 CI:在无 Gitea Actions runner 时,按 .gitea/workflows/ci.yml 的门禁顺序执行。
* 用法:pnpm ci:local(推荐通过 ./.tools/pnpm ci:local 锁定 pnpm 10)
*/
import { spawnSync } from 'node:child_process';
const pnpm = process.env.npm_execpath ? [process.execPath, process.env.npm_execpath] : ['pnpm'];
/**
* 端到端冒烟走 vite preview + demo 会话,需与 .gitea/workflows/ci.yml 的 job env 对齐;
* 只加在构建与 e2e 步骤,避免影响单测里对真实登录流程的断言。
*/
const e2eEnv = {
VITE_PUBLIC_API_BASE_URL: process.env.VITE_PUBLIC_API_BASE_URL ?? 'http://127.0.0.1:3000/api/v1',
VITE_PUBLIC_DEMO_SESSION: process.env.VITE_PUBLIC_DEMO_SESSION ?? 'true',
};
/** 每一步可包含多条 pnpm 命令,任一失败立即退出;env 为该步骤追加的环境变量。 */
const steps = [
{ label: '检查本地依赖(PG / Redis)', commands: [['check:dev']] },
{ label: '格式化检查', commands: [['format:check']] },
{ label: 'Lint', commands: [['lint']] },
{ label: '类型检查', commands: [['typecheck']] },
{ label: '单元测试', commands: [['test']] },
{ label: '构建', commands: [['build']], env: e2eEnv },
{
label: '数据库迁移与种子',
commands: [['exec', 'prisma', 'migrate', 'deploy'], ['db:seed']],
},
{ label: '集成测试', commands: [['test:int']] },
{ label: 'OpenAPI 导出与契约检查', commands: [['openapi:export'], ['openapi:diff']] },
{ label: '端到端测试', commands: [['test:e2e']], env: e2eEnv },
];
function runPnpm(args, env = {}) {
const result = spawnSync(pnpm[0], [...pnpm.slice(1), ...args], {
env: { ...process.env, ...env },
stdio: 'inherit',
});
if (result.error) {
console.error(`\n执行 pnpm ${args.join(' ')} 失败:${result.error.message}`);
process.exit(1);
}
if (result.status !== 0) {
console.error(`\n门禁失败:pnpm ${args.join(' ')}(exit ${result.status ?? 1})`);
process.exit(result.status ?? 1);
}
}
const startedAt = Date.now();
for (const [index, step] of steps.entries()) {
console.log(`\n[${index + 1}/${steps.length}] ${step.label}`);
for (const args of step.commands) {
runPnpm(args, step.env);
}
}
const seconds = ((Date.now() - startedAt) / 1000).toFixed(1);
console.log(`\n本地 CI 全部门禁通过,用时 ${seconds}s。`);