背景:run #37 在 runner 上失败于 Lint,日志报 @lingxi/* 下发类型无法解析—— workspace 包 types 指向 dist/,而 dist/ 被 gitignore,全新检出未构建导致 类型感知规则失效;另 postgres 健康检查 -U lingxi 与 POSTGRES_USER=postgres 不一致。 改动: - ci.yml:Lint 前新增 packages 构建步骤;健康检查改为 pg_isready -U postgres; 恢复 push main 自动触发(保留 workflow_dispatch)。 - ci-local.mjs:同步补充 packages 构建步骤,避免全新检出本地 CI 同样失败。 影响面:仅 CI 配置与本地 CI 脚本,不改业务代码;push main 后自动跑完整门禁。 回滚方式:revert 本提交;如需临时停用远端 CI,注释 on.push。 验证证据:runner 冒烟 run #38 全绿(ci-smoke.yml,71s);本提交推送后由远端 ci.yml 完整链路验证。
64 lines
2.5 KiB
JavaScript
64 lines
2.5 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* 本地 CI:按 .gitea/workflows/ci.yml 的门禁顺序执行(推送前强制;远端 Gitea Actions 为第二道防线)。
|
||
* 用法: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: '构建 workspace 包(Lint 类型解析前置)',
|
||
commands: [['exec', 'turbo', 'run', 'build', '--filter=./packages/*']],
|
||
},
|
||
{ 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。`);
|