chore: add release verification gate

This commit is contained in:
Your Name 2026-05-27 13:26:17 +08:00
parent 58f6f5f249
commit 4463bf1fc7
5 changed files with 88 additions and 3 deletions

View File

@ -81,6 +81,7 @@ pnpm check:artifacts
pnpm check:quality
pnpm clean:artifacts
pnpm audit:security
pnpm verify:release
pnpm exec tsc --noEmit --pretty false
pnpm test
pnpm db:migrate
@ -91,10 +92,17 @@ pnpm db:generate
```bash
pnpm check:quality
pnpm audit:security
AUTH_TRUST_HOST=true PORT=3001 pnpm dev
SMOKE_BASE_URL=http://127.0.0.1:3001 pnpm smoke:routes
```
发布或创建 PR 前可使用一条命令串起质量、安全和路由烟测:
```bash
SMOKE_BASE_URL=http://127.0.0.1:3001 pnpm verify:release
```
## 目录结构
```text

View File

@ -19,6 +19,7 @@ git diff --check
pnpm check:artifacts
pnpm clean:artifacts
pnpm audit:security
SMOKE_BASE_URL=http://127.0.0.1:3001 pnpm verify:release
```
等价的一键质量门禁:
@ -27,6 +28,14 @@ pnpm audit:security
pnpm check:quality
```
发布或创建 PR 前的一键验收:
```bash
SMOKE_BASE_URL=http://127.0.0.1:3001 pnpm verify:release
```
`pnpm verify:release` 会串起 `pnpm check:quality`、`pnpm audit:security` 和 `pnpm smoke:routes`。它要求显式设置 `SMOKE_BASE_URL`,避免误把路由烟测跑到错误环境。
其中 `pnpm check:artifacts` 会检查 `.DS_Store`、swap 文件、`.env.local`、`.claude/`、migration backup 和 `tsconfig.tsbuildinfo` 等本地污染物是否被 Git 追踪。
`pnpm clean:artifacts` 会清理当前工作区常见本地垃圾文件,例如 `.DS_Store`、swap 文件和 `tsconfig.tsbuildinfo`。它会跳过 `.git`、`node_modules`、`.next`、`.pnpm-store`、`.venv` 等重目录,适合在提交或打包前手动执行。

View File

@ -6,10 +6,8 @@
```bash
pnpm install --frozen-lockfile
pnpm check:quality
pnpm audit:security
AUTH_TRUST_HOST=true PORT=3001 pnpm dev
SMOKE_BASE_URL=http://127.0.0.1:3001 pnpm smoke:routes
SMOKE_BASE_URL=http://127.0.0.1:3001 pnpm verify:release
```
预期:
@ -20,6 +18,8 @@ SMOKE_BASE_URL=http://127.0.0.1:3001 pnpm smoke:routes
- `/classic` 和 `/chat/test-id` 重定向到 `/`。
- `/settings` 未登录重定向到 `/login?callbackUrl=%2Fsettings`。
`pnpm verify:release` 会依次运行 `pnpm check:quality`、`pnpm audit:security` 和 `pnpm smoke:routes`。必须显式提供 `SMOKE_BASE_URL`,确保路由烟测指向本地、预发或生产中的明确目标。
## 发布边界
- 旧 Chat UI 已移除;`/api/chat` 保留为 Canvas/Agent 内部生成 API。

View File

@ -30,6 +30,7 @@
"audit:security:json": "pnpm audit --registry=https://registry.npmjs.org --audit-level high --json",
"clean:artifacts": "node scripts/clean-local-artifacts.mjs",
"smoke:routes": "node scripts/smoke-routes.mjs",
"verify:release": "node scripts/verify-release.mjs",
"format": "pnpm exec ultracite fix",
"db:generate": "drizzle-kit generate",
"db:migrate": "npx tsx lib/db/migrate.ts",

View File

@ -0,0 +1,67 @@
#!/usr/bin/env node
import { spawn } from "node:child_process";
const baseUrl = process.env.SMOKE_BASE_URL || "";
const skipSmoke = process.argv.includes("--skip-smoke");
const steps = [
{
name: "quality gate",
command: "pnpm",
args: ["check:quality"],
},
{
name: "dependency audit",
command: "pnpm",
args: ["audit:security"],
},
];
if (baseUrl) {
steps.push({
name: "route smoke",
command: "pnpm",
args: ["smoke:routes"],
});
} else if (!skipSmoke) {
console.error(
[
"Release verification requires SMOKE_BASE_URL so route smoke tests run against an explicit target.",
"Example:",
" SMOKE_BASE_URL=http://127.0.0.1:3001 pnpm verify:release",
"Use `pnpm verify:release -- --skip-smoke` only for documentation or CI dry-runs where no app is running.",
].join("\n")
);
process.exit(1);
}
for (const [index, step] of steps.entries()) {
console.log(`\n[release:${index + 1}/${steps.length}] ${step.name}`);
await run(step.command, step.args);
}
if (skipSmoke && !baseUrl) {
console.warn("\n[release] route smoke skipped by explicit --skip-smoke flag.");
}
console.log("\nRelease verification passed.");
function run(command, args) {
return new Promise((resolve, reject) => {
const child = spawn(command, args, {
env: process.env,
shell: false,
stdio: "inherit",
});
child.on("error", reject);
child.on("exit", (code) => {
if (code === 0) {
resolve();
return;
}
reject(new Error(`${command} ${args.join(" ")} failed with exit code ${code}`));
});
});
}