chore(git): enforce deployment branch and feature guards
This commit is contained in:
parent
675966a783
commit
231650dc00
1
.gitignore
vendored
1
.gitignore
vendored
@ -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/
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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。
|
||||
|
||||
@ -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]);
|
||||
|
||||
@ -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");
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user