From 4463bf1fc71ae870786a99dd5c8ee589fbcec924 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 27 May 2026 13:26:17 +0800 Subject: [PATCH] chore: add release verification gate --- README.md | 8 +++++ docs/operations.md | 9 +++++ docs/release-readiness.md | 6 ++-- package.json | 1 + scripts/verify-release.mjs | 67 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 scripts/verify-release.mjs diff --git a/README.md b/README.md index f97711358..5e8ac55db 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/docs/operations.md b/docs/operations.md index 5381fb168..c79fb30c4 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -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` 等重目录,适合在提交或打包前手动执行。 diff --git a/docs/release-readiness.md b/docs/release-readiness.md index 7baf289db..185d679fb 100644 --- a/docs/release-readiness.md +++ b/docs/release-readiness.md @@ -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。 diff --git a/package.json b/package.json index 57d5d99fa..b1667722d 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/scripts/verify-release.mjs b/scripts/verify-release.mjs new file mode 100644 index 000000000..b6715f09c --- /dev/null +++ b/scripts/verify-release.mjs @@ -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}`)); + }); + }); +}