Commit Graph

8 Commits

Author SHA1 Message Date
姚章浩 4f0852a94d fix(ci): 契约门同时核进程退出码与负例归因
为什么:版本契约门只看 `--- PASS: X` 两行,rc1 取到却没用。同一进程里别的测试
失败时 go test 仍会打印这两个 PASS 行、退出码非 0——那种"绿"证明不了这一轮测试
成功,负构建因编译/网络错误非 0 也会被当成"断言还活着"。审计 F19 的原始触发即此。

改了什么:判据抽成 `judge_contract_positive` / `judge_contract_negative`,正向要求
rc=0 + 每个必需测试真 PASS + 无 FAIL 事件 + 未跳过;负例要求非 0 且失败在预期断言上,
命中编译/网络/工具链标记即判红。判据的反例自检 `contract_judgement_problems()` 同时在
`make verify` 的 contracts 步骤与 `--self-test` 里跑,判据被改坏在本地就红。
POLICY_REVISION 提到 ci-checks/4。

怎么验:`python3 scripts/ci_checks.py --self-test` rc=0(10 汇总分支 + 10 契约反例);
`--internal-contracts $(command -v go)` rc=0;反例探针(假 go 打印两个 PASS 后 exit 1)
现在 rc=1,修复前同一输入 rc=0。
2026-10-30 16:14:46 +08:00
姚章浩 8a3aa800a3 fix(scripts): 演练判据改用认领与决策行并校验预备份复用
v4 判据(都带作业身份,且时间戳必须晚于杀点):

- `scripts/takeover_log_contract.py` 是唯一判据:认领行 + 决策行都要有本作业 id、
  target/backup 一致;只有汇总行、别的作业的认领行、杀之前的旧日志三种一律红;
  `--self-test` 11 条(正例 2 / 负例 9)进 `make verify`(新增第 16 项必需检查);
- `prebackup` 不再强制 `skipped`:真实接管是复用后重跑整条链路,阶段记 succeeded。
  但**不取消备份校验**,改为证明复用同一份——快照 id、清单字节与 mtime、
  版本行首次登记时刻都不变,且证据里有 `backup_reused=true`;
- 杀之前把预备份清单 sha256/mtime 与版本行登记时刻拍进 evidence;
- summary.json 增加 takeover_decision / claim_at / backup_registered_at。

verify: 判据自检 11 条全过;用真实 Go 输出行形状的正例日志 rc=0;旧形状(只有
汇总行)rc=2 且理由点名"汇总";`--check` 前置体检 13 项全 pass(含新增
preflight_log_contract);make verify overall success。
2026-10-30 07:56:16 +08:00
姚章浩 013ce4a24e fix(version): 补全开发快照记录与同步交付闭环
build / build (push) Successful in 1m3s
收录 main、注释开发快照和完整运行 SHA;开发说明绑定原提交,补记 0.1.1–0.1.25。
进行中同步按仓库合并,完成后重新执行;启动入队,部署脚本读回版本证据。
开发快照独立于正式标签与 Release,既有冲突标签不移动。

已运行:整仓 make verify,Go 673 通过、前端 274 通过,92 个已声明可选跳过,必需跳过 0。
隔离 PostgreSQL 重复同步队列用例及 -race 通过;未更新现有 8088、未修改生产问答。
2026-10-18 18:11:49 +08:00
t 6de5d52363 feat(version): 开发号随提交自动递增 + 首个正式版本 0.1.0
build / build (push) Successful in 51s
按使用者要求定的版本规则(自动升的是**开发标识**,发布版本与 tag 仍由人批准):

- **开发号自动递增**:display_version(development) = `<正式版本的主.次>.<自 v<正式版本> 以来的提交数>`
  (正式 0.1.0 之后第 1 个提交 0.1.1、第 12 个 0.1.12;恰在发布提交上 = 正式号)。
  它**不写进 CHANGELOG**(发布记录只在发版时动),由 git 算出来 ⇒ 每个提交都有自己的号、数字只增。
- **浅克隆不猜号**:`--depth` 会把条数算少 ⇒ 浅仓库里拒绝算号,退回 `<版本>-dev+g<短SHA>`;
  检出步骤在仓库有 tag 时 `--unshallow` 补全历史。
- 接线三处齐全:`version.py --identity` → 门禁 `-X …CommitsSinceRelease=<n>`;Makefile 的
  `COMMITS_SINCE_RELEASE`;Dockerfile 同名 build-arg;Go 侧新增 `CommitsSinceRelease` 与
  DisplayVersion() 新规则;契约测试**交叉核对**"注入的提交数 == git 算出的"(防注入错号)。
- **首个正式版本 0.1.0**:CHANGELOG 的 `[Unreleased]` 提成 `## [0.1.0] - 2026-10-13`;
  头部补"版本怎么走"两行;RELEASE_RUNBOOK 补递增规则与"下一次发布把次版本加一"。
- 发版预检新增:打印**当前开发号**,并挡住"候选正式版本 <= 当前开发号"(会把开发号当正式号;建议次版本加一)。

验证:version.py --self-test(含 tag 后第 2 个提交 = 0.2.2、恰在 tag 上 = 0.2.0、无 tag 不猜号);
release_preflight --self-test(21 分支,含开发号两条);go test 本地三态 + 临时仓库 tag+2 提交实测
(Python 与 Go 都得出 0.2.2);反向夹具 85/0;make verify 15/15 success。
2026-10-13 16:45:56 +08:00
姚章浩 05144309c3 feat(version): 构建身份(release_version/build_kind/display_version),无基线也放行
解决"开发构建沿用已发布版本号,分不清开发版与正式版":

- 新增 `scripts/version.py --identity`:release_version(无基线即空)/ build_kind /
  commit_sha(完整 40 位)/ display_version / built_at。显示规则:发布 `X.Y.Z`、
  开发 `X.Y.Z-dev+g<短SHA>`、无基线 `dev+g<短SHA>`、什么都拿不到 `dev`。
- `build_kind=release` 只认**显式发布上下文**(`BUILD_KIND=release` / `--release`);
  声明发布构建却没有已发布版本 ⇒ 拒绝(rc=2)。普通 main 构建永远是 development,
  **不许只显示上一正式版本号、不许自称正式版**。
- Go 侧 `internal/version` 加 ReleaseVersion/BuildKind/DisplayVersion()/IsRelease()/Info(),
  原 Number/CommitSHA/BuildTime 语义不变(向后兼容);契约测试改成:发布构建必须等于
  CHANGELOG 顶部、开发构建必须是 dev 形状;`make verify` 的 contracts 项新增
  **负例**「自称正式版 ⇒ 必须红」(只有正例的契约会被静默掏空)。
- 构建接线:Makefile/Dockerfile/ci_checks `--internal-build` 统一走 identity;
  Makefile 的 `make version` 打印身份 JSON;Dockerfile 新增 RELEASE_VERSION/BUILD_KIND。
- `tests-go` 的"按设计跳过"声明补上 `TestDevBuildDoesNotClaimRelease`(它的强制点在 contracts)。

验证:go test 三态实测(未注入 SKIP、开发身份 PASS、自称正式版 FAIL);
--internal-build 产出并打印 dev+g<sha>;--internal-identity 契约项 rc=0;
make verify 15/15 success;反向夹具 80/0。
2026-10-13 14:59:36 +08:00
姚章浩 40d84994cb feat(ci): 版本记录检查(四节 + 正式 tag 一致性)+ 只读发版预检
check_changelog.py:正式版本条目必须四节齐备且非空(改了什么/兼容性与迁移影响/怎么验收/已知问题)、版本号三段无前导零、[Unreleased] 在最上面;新增 --tags 核对正式 tag(必须注释 tag)与它指到那个提交里的版本记录一致。只有 Unreleased 的新工程放行;没有正式 tag 不判红(开发版本不打 tag 是常态)。ci_checks.py:新增必需检查 version(门禁 15 项),策略版本 ci-checks/2。ci_checkout.sh:记录远端 tag 清单并取 tag 对象供核对。release_preflight.py:只读预检(版本建议/完整 SHA/CI 证据/CHANGELOG/远端 tag 与 Release/遗留项),写类 git 命令与 POST 在预检路径上被拒(--self-test 证明);批准后 --execute 才打注释 tag、推 tag、建 Release 并读回。CHANGELOG 0.1.0 补上原本缺的两节。
2026-10-12 17:57:43 +08:00
姚章浩 88da069e1f ci(checks): 必需与可选测试按声明区分,未声明就跳过的用例判红
test-scope.json 提供声明:package/test 至少给一个、必须给 reason 与 owner、package 级要写 expected_skips(跳过数涨了要重新声明)、review_by 过期即失效。文件缺失或解析不了 = 声明不出来 ⇒ 不许默认放行。
2026-10-12 11:15:18 +08:00
姚章浩 a3a1319a11 feat(ci): 唯一权威门禁 make verify(14 项必需检查,汇总不认"未运行")
一次性把本地与 CI 的门禁收成一条命令:make verify = python3 scripts/ci_checks.py。
两边跑的是同一份逻辑,不再各写一套步骤(两边各写一套的结局是迟早只改一边)。

14 项必需检查:identity(检出 == 事件完整 SHA,只在 CI 必需)、commits、deps-install、
format、static-go、static-web、build-go、build-web、tests-go、tests-web、contracts、
security-secrets、security-deps、manifests-locked。make list 打印清单。

三条判据写死在代码里:
- 失败即停,但剩下的记 not_run 并如实写进报告;aggregate() 只认 success,
  未运行/被跳过都不算通过(不适用要在清单里声明 required_in,不许运行时悄悄 skip)。
- 工具缺失、依赖没按锁装、输出解析不了 ⇒ 失败,不当成"检查通过"。
- 测试必须真跑:go test 加 -count=1 并盘点通过/跳过数;前端读 vitest 的 JSON 报告,
  空套件不算通过。跳过原因会打进报告(当前 72 个集成用例因缺 TEST_DATABASE_URL 跳过)。

新增四个自带反向自检的脚本(--self-test 故意弄坏必须红):
scripts/check_format.py(Go 判输出而非退出码 + 前端只能减的基线)、
scripts/check_secrets.py(只打印位置与类别,绝不打印值)、
scripts/check_deps_vuln.py(govulncheck 锁 v1.1.4 + npm audit 分级 + 到期例外)、
scripts/ci_checks.py(门禁 + 汇总)。

验证(本机实跑,全部为真实退出码):
make verify → 总体 success(档案 local),identity=skipped 已在清单里声明为 CI 必需;
tests-go 执行 31 个包、通过 549 / 失败 0 / 跳过 72;tests-web 106 个套件、191 个用例全过;
security-deps:govulncheck v1.1.4 0 命中、npm audit 生产与全量均 0。
2026-10-12 00:10:51 +08:00