Files
patbond-doc/docs/development/iterations/iteration-3/08-git-workflow-plan.md
T
lixi d2867826d3
CI / docs-build (push) Successful in 55s
docs: M3 开工分析 8 份报告入档 + ADR-016~021 拍板决策
- iteration-3 报告 01-08(PM 拆解/后端/Flutter/RC 首个 CERTIFIED/UI/埋点/证据基线/Git)
- ADR-016 自托管 MinIO 起步预留迁云(用户确认现无云存储)、ADR-017 patbond-community
  :8084 + media 归 user + 作者信息跨 schema 只读、ADR-018 范围裁剪(话题剪出/单层评论)、
  ADR-019 幂等按域(PUT/DELETE + request_hash)、ADR-020 聚合 feed_viewed/字典 v3/
  北极星不变/队列三项升第一波、ADR-021 Git 修订(main 更正/PR 情形触发/首发布重建 main/
  防泄漏 grep 先行)
- mkdocs 挂「第三迭代」导航,build --strict 通过

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-08 16:02:53 +08:00

140 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 08 M3 Git 与 CI 工作流核查规划
- 执行人:Git Workflow Master
- 日期:2026-09-08
- 范围:第三迭代(M3 社区)开工前的三仓状态核查、ADR-011 PR 条款实践复盘、发布分支启用规划、对象存储凭证防泄漏、CI 增量评估。**本报告只核查与规划,未改动任何代码、工作流或 mkdocs.yml,未执行 commit/push。**
---
## 1. 三仓当前状态核查(2026-09-08 实测)
| 仓库 | 分支 | 相对 origin | 工作区 | stash | 最新提交 CI 状态 |
| --- | --- | --- | --- | --- | --- |
| patbond-api | dev | 同步(fetch --prune 后确认) | 干净 | 无 | **success**`64c9b72`CI / backend-testrun 315m18s |
| patbond-flutter | dev | 同步 | 干净 | 无 | **success**`720865b`CI / flutter-gatesrun 372m12s |
| patbond-doc | main | 同步 | 干净 | 无 | **success**`e68b655`CI / docs-buildrun 3929s |
CI 状态经 Gitea commit status API 逐仓核实,非转述。**未提交内容清单:无**(本报告文件本身除外,按波次规则随下一波提交)。M2 收官时的「三仓 commit + push + CI 绿」闭环纪律保持完好。
### 1.1 历史遗留分支的新发现(比 M2 报告掌握的更严重一档)
M2 报告只记录了「api 本地孤儿 `master` 上游已删」。本次为发布分支规划做了祖先关系核查,发现:
- **patbond-api 的 `ff876bc`(本地孤儿 master、远端 `origin/main` 共同指向的初始 README 提交)不是 dev 的祖先**——`git merge-base --is-ancestor ff876bc dev` 判定失败,dev 的根提交是 `b1252b9`Initialize patbond microservice modules)。即 **api 远端默认分支 main 与 dev 是两条不相干历史**unrelated histories)。这直接影响第 3 节「dev→发布分支」怎么做第一次合并。
- patbond-flutter 的 `main``030b11f`**是** dev 祖先,未来 dev→main 可干净 fast-forward。
- M2 报告建议的「核实后删本地孤儿 master」当时的前提(`ff876bc` 已被 dev 包含)实测**不成立**,但结论不变:该提交仅是初始 README,远端 `origin/main` 仍保留它,本地 `git branch -D master` 无信息损失,可顺手做。
## 2. M2 工作流实践复盘:ADR-011「高风险走 PR」条款何去何从
### 2.1 实践事实(git log + Gitea Actions 全量核查)
- **PR 使用次数:0。** 三仓 M2 期间(09-07 至 09-08)无任何 merge commit,历史全程线性。
- **四类「高风险」全部直推了**Flyway V3/V4`49299fb`T2-01)、契约冻结 v1.2.0doc `511617b`)、事件字典白名单扩充(`64c9b72`)均直推 dev/main。
- **风险事件清点:零。** 具体证据:
1. M2 期间三仓 CI **零失败**——Actions 全量 run 列表中的 7 次 failure 全部集中在 09-04(M1 末 CI 搭建期),且全是流水线自身配置问题(外部 action 不可达、JDK 安装方式、format 未跑),无一是业务代码直推打红 dev;
2. **零 revert**`--grep` 回退/回滚/revert 无命中);
3. **迁移不可变规则守住了**V3/V4 文件推送后零修改(`git log --follow` 各只有一次提交);
4. **契约冻结守住了**`openapi.yaml` 在冻结提交 `511617b` 之后零改动;
5. 无 force push 痕迹(线性历史 + 各推送头全绿)。
### 2.2 为什么直推没出事——机制归因,而非运气
M2 的安全性不是来自 PR 的缺席碰巧无事,而是四道机制已经覆盖了 PR 想防的东西:
1. **Flyway 迁移**`./mvnw clean test` 经 Testcontainers 起真库执行完整迁移链,每次 push 都等于迁移演练——PR 合入前 CI 与 push 后 CI 跑的是同一条命令,对串行开发者而言只差「红了是否已在 dev 上」,而两人+AI 模式下红 dev 的传播面就是自己。
2. **契约破坏**:T2-09 契约一致性测试把破坏性变更变成红测试,比人工 PR review 更机械可靠。
3. **波次收尾 compose 实测 + E2E 烟囱**兜住了集成层。
4. **串行作业**:M2 全程实质单线程推进(AI 辅助不产生 git 并发),四类触发条件中真正指向并发风险的「两人并行期」从未发生。
### 2.3 结论建议(**待拍板 #1**):条款降级为「按情形触发」,不是纪律失效
判定:**这不是纪律失效,是条款的触发条件设计错了**——它按「改动类别」(迁移/契约/依赖)触发,而 M2 证明这些类别在串行+CI 全量门禁下并无 PR 才能拦住的残余风险。真正需要 PR 的是「情形」:
- **建议修订 ADR-011 备注**:Flyway 迁移、契约变更、依赖升级在串行开发期**直推 dev + CI 绿 + 波次实测**即为足够实践,不再列为 PR 推荐触发项;
- **PR 保留为强制的仅两种情形**:
1. **两人并行改同一仓库期间**(唯一真实的并发冲突风险源);
2. **首次 dev→发布分支合并之后**,凡影响已发布版本的破坏性变更(不可变迁移的例外处理、已冻结契约的破坏性修订、发布分支 hotfix)——发布后爆炸半径从「自己人」扩大到「装了 App 的用户」,性质不同。
- 三仓 ci.yml 的 `pull_request:` 触发器保留不动(零成本待命);M2 待拍板 #2 的分支保护同理**降级为「随首次发布对发布分支启用」**,dev 不开(见 3.3 checklist)。
这样条款从「写了但没人执行的推荐」变成「触发即无争议的强制」,规范与实践重新一致。
## 3. M3 发布分支启用规划
### 3.1 先解决命名与历史两个前置问题
**命名不一致(待拍板 #2**ADR-011 写的是「`master` 保留为发布分支」,但远端实况是:api 的 `origin/master` 已删除(默认分支为 main)、flutter/doc 默认分支均为 `main`,**三仓远端今天没有任何一个 master 分支**。建议:**统一以 `main` 为发布分支名**,修订 ADR-011 措辞(master→main),顺手删除 api 本地孤儿 master(§1.1,无信息损失)。反向方案(重建三仓 master)多一次全员改默认分支操作,无收益。
**api 的 main 与 dev 历史不相干(待拍板 #3)**`origin/main``ff876bc`)不是 dev 祖先,首次 dev→main 无法 fast-forward,普通 merge 需要 `--allow-unrelated-histories` 且会把一条孤儿历史永久缝进发布线。三个选项:
| 选项 | 操作 | 评价 |
| --- | --- | --- |
| A(推荐) | Gitea 仓库设置将默认分支临时切到 dev → 删除远端 main → 从 dev 重建 main → 默认分支按需切回 | 零 force push、历史干净,纯平台操作 |
| B | 一次性 `git push --force origin dev:main`,在 ADR 中记录为例外 | 结果等价,但破「不 force push 共享分支」戒律,留坏先例 |
| C | `merge --allow-unrelated-histories` | 永久保留无意义的孤儿历史缝合点,不推荐 |
flutter 无此问题(main 是 dev 祖先,直接 ff);doc 仓 main 即日常分支,不参与发布分支语义。
### 3.2 何时启用:建议 M3 末做第一次 dev→main 发布(**待拍板 #4**
理由:M2 收官已具备「E2E 烟囱脚本 + 全绿测试基线 + 冻结契约」的可发布形态,缺的只是发布动作本身;北极星指标出数(ADR-012,M3 末 A/B 前置目标全绿)需要一个稳定版本承载;再往后拖,发布流程的首次演练会和 M4 首实验挤在一起。M3 末做第一次,把流程走通比版本内容重要。
### 3.3 发布 checklist 草案(首次发布用,验证后固化进 git-workflow.md
1. **冻结**:发布波次收尾,三仓 commit + push + CI 绿(既有纪律);
2. **实测**compose 全栈起,跑 M2+M3 两份 E2E 烟囱脚本,全场景 PASS,证据入波次报告;
3. **前置一次性项**(仅首次):完成 §3.1 的命名统一与 api main 重建;
4. **合并**api/flutter 各执行 `git checkout main && git merge --ff-only dev && git push origin main`(此后每次发布 dev→main 都应 ff-only 可过,过不了说明 main 被绕过 dev 改动,先查明);
5. **打标**:两仓 `git tag -a v0.3.0 -m "M3 社区"`(版本号待拍板时一并定)并 push tag;doc 仓同点位打同名 tag,三仓互为对照;
6. **平台侧**Gitea 为 api/flutter 的 main 开启分支保护(禁直推、合并需 CI 状态检查通过)——dev 仍不开,保持直推流;
7. **记录**:发布说明入 doc 仓(版本、三仓 tag 哈希、E2E 证据链接、已知遗留);
8. **发布后**:影响 main 的 hotfix 一律走短命分支 + PR(§2.3 强制情形之二正式生效)。
## 4. 对象存储凭证防泄漏(M2 方案未实施,重新评估)
### 4.1 现状核查
- M2 报告 §5 的两层纯 shell 方案**零实施**:三仓均无 `scripts/hooks/``core.hooksPath` 均未设置,ci.yml 均无检查 step。
- M2 没出事的原因和 PR 条款同理:M2 引入的凭证(RS256 密钥对、DB 密码、internal token)全部由 `deploy/init-secrets.sh` 生成且不入库,`*.sample` 占位约定执行到位——但这套卫生依赖「凭证只在本机生成」这个前提。
### 4.2 M3 威胁面变化:这次不一样,建议先落第二层(**待拍板 #5**)
M3 的对象存储凭证(ADR-010 剪出项回归:COS/OSS/MinIO 的 AccessKey/SecretKey)与 M2 的密钥有本质区别:**它是云厂商控制台签发的长期凭证,泄漏即可被外部直接使用且常绑计费**,不是本机自生成的内部秘密。AI 辅助开发下,凭证从「配置文件」流向「示例代码/测试/报告」的路径变多,纯约定不够。重新评估结论:
- **第二层(CI 兜底 grep)从「可选」升为「M3 第一波、对象存储凭证进入任何开发机之前必须上线」**。各仓 ci.yml 加一个纯 shell step(零外部依赖,秒级),模式清单在 M2 方案基础上增补云凭证特征:`AKID[A-Za-z0-9]{13,}`(腾讯云)、`LTAI[A-Za-z0-9]{12,}`(阿里云)、`(access|secret)[-_]?key\s*[:=]` 后跟非占位值、40 位以上连续 base64/hex;文件名黑名单增补 `.env``credentials``*.csv`(控制台导出的密钥文件形态)。
- 第一层(共享 pre-commit 脚本)维持推荐;若继续搁置,第二层单独上线也成立(拦「已提交的」比拦「即将提交的」在两人团队更关键——push 即触发,无 `--no-verify` 逃逸)。
- 沿用 M2 结论:不引入 gitleaks 等外部工具;真泄漏的第一动作是**去云控制台轮换/禁用密钥**,历史清理其后——此条随实施写进 git-workflow.md。
- 实施时顺手核对三仓 .gitignore 对 `.env` 的覆盖(api 仓 compose 依赖 `.env`,规则应已有,实施时以 `git check-ignore` 取证)。
## 5. CI 增量评估
### 5.1 新模块接入:确认零成本(同仓模块方案下)
patbond-api 是 maven 聚合工程(根 pom `<modules>` 现有 common/user/auth/pet 四个)。若 M3 沿 ADR-009 模式在 api 仓内新建 `patbond-community` / `patbond-media` 模块:**根 pom 加一行 `<module>`ci.yml 零改动**`./mvnw -B clean test` 自动覆盖新模块(含其 Testcontainers 测试)。**确认零成本,无待拍板。**
仅当选择独立新仓(当前无此计划)才有增量:复制既有 ci.yml(三仓模板已统一:手动 checkout + apt/镜像装工具链)+ 仓库设置启用 Actions,runner 是实例级共享的,无需新注册,估计半小时内。
### 5.2 E2E 烟囱进 CI:技术可行但不建议进 push 门禁(**待拍板 #6,倾向不做**)
可行性核查(基于 ci-runner-setup.md 与 act_runner 现状):
- **docker.sock 已挂进 job 容器**Testcontainers 依赖,实测可用),job 内跑 `docker compose up` 起的是宿主 sibling 容器——技术上通。
- 但有四项实际成本:
1. **网络**compose 端口发布在宿主,job 容器内 `127.0.0.1:8081-8083` 不可达,E2E 脚本的 base URL 需改造为可注入,并让 job 容器走宿主网关 IP 或直接加入 compose 网络;
2. **工具链**job 镜像需补 docker CLI + compose 插件;
3. **时长**`mvnw package` + 三个镜像 build + 全栈起 + 11 场景,估计给流水线加 5–10 分钟(现 backend-test 5m18s,翻倍以上);
4. **跨仓**:脚本在 flutter 仓、compose 在 api 仓,任一仓的 push CI 跑它都要 clone 另一仓,触发归属含糊。
- **建议**:push 门禁维持现状(快、单仓、职责清晰);E2E 保持 M2 已验证的「波次收尾手动跑、证据入档」模式,并作为发布 checklist 第 2 步的强制项。若要自动化,做成独立的 `workflow_dispatch` 手动触发工作流(发布前一键跑),M3 内低优先,不占开工路径。
## 6. 待拍板事项汇总
| # | 事项 | 推荐 | 见 |
| --- | --- | --- | --- |
| 1 | ADR-011 PR 条款降级:类别触发(迁移/契约/依赖)取消,改为仅「两人并行同仓」与「首次发布后影响 main 的变更」两种情形强制 PR;分支保护随之改为只对发布分支启用 | 采纳修订 | 2.3 |
| 2 | 发布分支统一命名为 `main`(修订 ADR-011 的 master 措辞),顺手删 api 本地孤儿 master | 采纳 | 3.1 |
| 3 | api 远端 main 与 dev 历史不相干的一次性处理:Gitea 平台删除重建(选项 A) | 选项 A | 3.1 |
| 4 | M3 末执行第一次 dev→main 发布,采纳 §3.3 checklist(含版本号定名) | 采纳 | 3.2/3.3 |
| 5 | 防泄漏第二层(CI 兜底 grep + 云凭证模式增补)升为 M3 第一波必做、先于任何对象存储凭证落地;第一层 pre-commit 维持推荐 | 采纳 | 4.2 |
| 6 | E2E 烟囱不进 push 门禁;可选做 workflow_dispatch 手动工作流(低优先) | 不进门禁 | 5.2 |
采纳后需要落实的改动(本报告未执行):ADR-011 修订、git-workflow.md 增补(PR 情形条款、发布流程、泄漏应急)、三仓 ci.yml 加防泄漏 step、api main 重建操作、本报告挂入 mkdocs 导航。