From 231650dc002571cd61fd62fe6cd2b2ddbf18ea46 Mon Sep 17 00:00:00 2001 From: 134 Date: Thu, 13 Aug 2026 18:19:06 +0800 Subject: [PATCH] chore(git): enforce deployment branch and feature guards --- .gitignore | 1 + README.md | 2 +- docs/delivery-readiness-audit.md | 8 ++++---- docs/operations.md | 10 +++++---- docs/release-readiness.md | 5 +++-- scripts/check-branch-readiness.mjs | 33 ++++++++++++++++++++++++++++-- scripts/check-test-hygiene.mjs | 10 +++++++++ 7 files changed, 56 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index 2f6051ab0..b64f53e95 100644 --- a/.gitignore +++ b/.gitignore @@ -50,6 +50,7 @@ yarn-error.log* /playwright-report/ /blob-report/ /playwright/* +/output/ deploy.tar.gz libvips-8.14.5-linux-x64.tar.br .temp-packages/ diff --git a/README.md b/README.md index d86270d6b..35a70d008 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ pnpm db:generate 推荐提交前至少运行: ```bash -git fetch github main --prune +git fetch origin --prune pnpm check:branch pnpm check:quality pnpm audit:security diff --git a/docs/delivery-readiness-audit.md b/docs/delivery-readiness-audit.md index 9d71a78ed..ade51fafd 100644 --- a/docs/delivery-readiness-audit.md +++ b/docs/delivery-readiness-audit.md @@ -7,11 +7,11 @@ ## 当前分支状态 - 工作分支:`codex/admin-settings-ui-refactor` -- 基线分支:`github/main` -- 当前状态:已同步 `github/main`,`behind 0`;具体 ahead 数以 `pnpm check:branch` 当前输出为准 +- 基线分支:当前分支配置的 upstream +- 当前状态:ahead/behind 数以 `pnpm check:branch` 当前输出为准 - 本地工作区:干净 - Dry-run merge:`mergeConflictMarkers: 0` -- 当前 PR 体量:约 `198` 个文件变更;非 merge 提交数以当前 `git log --no-merges github/main..HEAD` 输出为准 +- 当前 PR 体量和非 merge 提交数以当前 upstream 对比输出为准 ## 主要交付范围 @@ -27,7 +27,7 @@ ## 已验证证据 ```bash -git fetch github main --prune +git fetch origin --prune pnpm check:branch pnpm build SMOKE_BASE_URL=http://127.0.0.1:3001 pnpm verify:release diff --git a/docs/operations.md b/docs/operations.md index 9f1ce622d..4c578c7f9 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -5,7 +5,7 @@ ## 合并前检查 ```bash -git fetch github main --prune +git fetch origin --prune pnpm check:branch pnpm lint pnpm check:logs @@ -46,17 +46,19 @@ SMOKE_BASE_URL=http://127.0.0.1:3001 pnpm verify:release ## 分支与 PR 就绪检查 ```bash -git fetch github main --prune +git fetch origin --prune pnpm check:branch ``` -`pnpm check:branch` 默认以 `github/main` 为基线,检查当前工作区是否干净、基线 ref 是否存在、当前分支相对 main 的 ahead/behind 状态、受影响文件数量,以及 dry-run merge tree 是否出现冲突标记。它会把 behind main 作为 warning,方便还没决定 rebase/merge 前先看风险;如果发布或合并策略要求必须最新,可使用: +`pnpm check:branch` 默认以当前分支的 upstream 为基线,检查当前工作区是否干净、基线 ref 是否存在、ahead/behind 状态、受影响文件数量,以及 dry-run merge tree 是否出现冲突标记。它会把 behind 作为 warning;如果发布或合并策略要求必须最新,可使用: ```bash REQUIRE_UP_TO_DATE=true pnpm check:branch ``` -如果远端名不同,可以用 `PR_BASE_REF=origin/main pnpm check:branch` 或 `pnpm check:branch origin/main` 覆盖基线。提交前临时验证未提交改动时,可使用 `ALLOW_DIRTY=true pnpm check:branch` 或 `pnpm check:branch --allow-dirty`。 +生产主机首次配置时应执行 `git config deployment.branch <生产分支>`。之后 `pnpm check:branch` 会拒绝从其他分支发布。也可以使用 `REQUIRED_BRANCH=<生产分支> pnpm check:branch`做单次校验。 + +如果需要显式覆盖比较基线,可以用 `PR_BASE_REF=origin/main pnpm check:branch` 或 `pnpm check:branch origin/main`。提交前临时验证未提交改动时,可使用 `ALLOW_DIRTY=true pnpm check:branch` 或 `pnpm check:branch --allow-dirty`。 ## CI diff --git a/docs/release-readiness.md b/docs/release-readiness.md index 2dca18e18..f2accdd13 100644 --- a/docs/release-readiness.md +++ b/docs/release-readiness.md @@ -6,7 +6,7 @@ ```bash pnpm install --frozen-lockfile -git fetch github main --prune +git fetch origin --prune pnpm check:branch AUTH_TRUST_HOST=true PORT=3001 pnpm dev SMOKE_BASE_URL=http://127.0.0.1:3001 pnpm verify:release @@ -15,7 +15,8 @@ SMOKE_BASE_URL=http://127.0.0.1:3001 pnpm verify:release 预期: - `pnpm check:quality` 通过 lint、日志脱敏门禁、公开交付面品牌治理、CI hygiene、环境变量示例卫生、secret hygiene、package metadata、测试门禁卫生、轻量 helper 回归测试、类型检查、diff hygiene 和 tracked 污染物检查。 -- `pnpm check:branch` 确认当前分支相对 `github/main` 没有 dry-run merge 冲突;behind main 会提示为 warning,发布前可用 `REQUIRE_UP_TO_DATE=true` 提升为失败。 +- `pnpm check:branch` 默认与当前分支的 upstream 比较,确认没有 dry-run merge 冲突;behind 会提示为 warning,发布前可用 `REQUIRE_UP_TO_DATE=true` 提升为失败。 +- 部署主机应设置 `git config deployment.branch <生产分支>`,或在检查时传入 `REQUIRED_BRANCH=<生产分支>`;分支不匹配时检查会直接失败。 - `pnpm audit:security` 输出已审计;如手动运行 Dependency Audit workflow,也应通过。若仍有 critical/high,必须在 [dependency-risk-register.md](dependency-risk-register.md) 中记录 remediation 或风险接受状态。 - `pnpm build` 完成生产构建,验证 Next.js route collection、静态/动态页面边界和构建期 TypeScript。 - `pnpm smoke:routes` 验证 `/` 返回 200。 diff --git a/scripts/check-branch-readiness.mjs b/scripts/check-branch-readiness.mjs index 8a21984fd..2e370a027 100644 --- a/scripts/check-branch-readiness.mjs +++ b/scripts/check-branch-readiness.mjs @@ -6,7 +6,14 @@ import { tmpdir } from "node:os"; import { join } from "node:path"; const args = process.argv.slice(2); -const baseRef = process.env.PR_BASE_REF || args.find((arg) => !arg.startsWith("--")) || "github/main"; +const currentBranch = git(["branch", "--show-current"]).trim() || "HEAD"; +const configuredDeploymentBranch = getOptionalGitConfig("deployment.branch"); +const requiredBranch = + String(process.env.REQUIRED_BRANCH || configuredDeploymentBranch || "").trim(); +const positionalBaseRef = args.find((arg) => !arg.startsWith("--")); +const upstreamRef = getOptionalUpstreamRef(); +const baseRef = + process.env.PR_BASE_REF || positionalBaseRef || upstreamRef || "origin/main"; const requireUpToDate = process.env.REQUIRE_UP_TO_DATE === "true" || args.includes("--require-up-to-date"); const allowDirty = process.env.ALLOW_DIRTY === "true" || args.includes("--allow-dirty"); @@ -14,11 +21,15 @@ const allowDirty = process.env.ALLOW_DIRTY === "true" || args.includes("--allow- const failures = []; const warnings = []; -const currentBranch = git(["branch", "--show-current"]).trim() || "HEAD"; const status = git(["status", "--porcelain"]); if (status.trim() && !allowDirty) { failures.push("working tree has uncommitted changes; commit, stash, or set ALLOW_DIRTY=true"); } +if (requiredBranch && currentBranch !== requiredBranch) { + failures.push( + `current branch is ${currentBranch}, but deployment requires ${requiredBranch}` + ); +} try { git(["rev-parse", "--verify", baseRef]); @@ -64,6 +75,8 @@ if (failures.length === 0) { { baseRef, branch: currentBranch, + requiredBranch: requiredBranch || null, + upstreamRef: upstreamRef || null, mergeBase, ahead, behind, @@ -95,6 +108,22 @@ function git(args) { }); } +function getOptionalGitConfig(key) { + try { + return git(["config", "--get", key]).trim(); + } catch { + return ""; + } +} + +function getOptionalUpstreamRef() { + try { + return git(["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{upstream}"]).trim(); + } catch { + return ""; + } +} + function runMergeTree(mergeBase, baseRef) { try { return git(["merge-tree", mergeBase, "HEAD", baseRef]); diff --git a/scripts/check-test-hygiene.mjs b/scripts/check-test-hygiene.mjs index 1bd884fe6..7a0da390f 100644 --- a/scripts/check-test-hygiene.mjs +++ b/scripts/check-test-hygiene.mjs @@ -31,6 +31,16 @@ expectIncludes( "/routes\\/canvas-chat-helpers\\.test\\.ts/", "canvas helper test target" ); +expectIncludes( + helpersConfig, + "/routes\\/agent-main-ai-template-entry\\.test\\.ts/", + "agent AI template regression target" +); +expectIncludes( + helpersConfig, + "/routes\\/canvas-psd-template-entry\\.test\\.ts/", + "canvas PSD template regression target" +); expectIncludes(helpersConfig, 'reporter: process.env.CI ? "github" : "list"', "fast non-html reporter"); expectIncludes(helpersConfig, "timeout: 60 * 1000", "bounded helper test timeout");