Files
patbond-doc/docs/development/releases.md
T
lixi 5cc6361534
CI / docs-build (push) Successful in 1m22s
docs: v0.4.0 发布记录 + 功能清单 M3.5 节 + 发布 E2E 回归报告
- releases.md 新增 v0.4.0(M3.5 体验补齐):三仓 tag、门禁 8 项证据、
  首次经 PR 流程发布的操作记录与流程验证结论、期间安全事件索引
- checklist 变更登记:**回归清单由两份改为四份**(M1/M2/M3/M3.5),
  原则「每个引入对外端点的迭代都应有对应 E2E 脚本并在此后每次发布回归」
- feature-checklist 新增第 13 节(M3.5 共 13 条)
- 08 号报告:E2E 四份 42/42 场景 234 断言零失败、契约偏差 0;
  另实证零迁移在存量库升级路径(Flyway: No migration necessary)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-14 11:19:13 +08:00

139 lines
11 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.
# 发布记录(常设)
> **定位**:跨迭代常设文档——每次 `dev → main` 发布在此追加一条记录:版本号、三仓 tag 与哈希、门禁证据、已知遗留。
> **维护约定**:按[发布 checklist](iterations/iteration-3/08-git-workflow-plan.md)(§3.3) 执行,完成后在此登记。最新版本在最上。
> 发布分支为 `main`(ADR-011 原写 master,ADR-021 更正);日常开发直推 `dev`。
---
## v0.4.0 — M3.5 体验补齐(2026-09-14
**首次经 PR 流程发布**v0.3.0 为直推 main,分支保护启用前)。
### 三仓 tag
| 仓库 | tag | 提交 | 内容 |
| --- | --- | --- | --- |
| patbond-api | `v0.4.0` | `3cd8005` | 用户资料读写 + 宠物头像 + 社区统计;379 测试;契约 v1.4.0 快照 |
| patbond-flutter | `v0.4.0` | `fbcd734` | 中文本地化 + 日期录入收口 + 资料页/编辑页 + 头像接线;597 测试;四份 E2E 脚本 |
| patbond-doc | `v0.4.0` | 本记录所在提交 | 契约 v1.4.0 + M3.5 报告 01~08 + 安全事件复盘 + 服务器暴露面清单 |
### 版本内容(相对 v0.3.0 的增量)
- **用户资料**`GET /me``nickname`/`avatarUrl`;新增 `PATCH /me`(三态:键缺省=不改 / 显式 null=清空 / 给值=设置)
- **头像**:用户与宠物头像上传(复用 M3 的 MinIO 两步上传),`purpose` 白名单 +`user_avatar`/+`pet_avatar`
- **社区统计**`GET /me/community-stats`(获赞总数 + 作品数,读侧实时聚合,无新冗余列)
- **客户端体验**:中文本地化(此前从未配置 `flutter_localizations`,Flutter 静默回退英文)、日期录入收口共享层(7 处调用点 + 手输 + 「今天」快捷)、资料页 demo 退役、花费卡显示实际月份
- **契约**openapi.yaml **v1.4.0**32 路径/45 操作/75 schema),**对 v1.3.0 纯增量**(无字段删改/类型变更/必填收紧,已写入 `info.description`
- **零 Flyway 迁移**ADR-022):所需列 V1/V3/V5 早已存在,`purpose` 白名单为配置项
- **决策**ADR-022
### 发布门禁证据
| 门禁项 | 结果 | 证据 |
| --- | --- | --- |
| 全量测试 | ✅ | api **379** / flutter **597**,双绿 |
| **E2E 回归(四份,同环境串行)** | ✅ | M1 7/7 + M2 11/11 + M3 14/14 + **M3.5 10/10** = **42/42 场景、234 断言零失败** — [08 号报告](iterations/iteration-3.5/08-release-e2e-regression.md) |
| 契约偏差 | ✅ **0** | 逐场景对照 v1.4.0;契约计数机械复核 32/45/75 |
| 契约向后兼容 | ✅ | v1.3.0→v1.4.0 纯增量,v1.3.0 客户端无需改动 |
| 契约一致性矩阵 | ✅ | 173→**181 格**,11 格「未声明字段即漂移」的红全部转绿,mutation 三处注毒自证 |
| **零迁移在存量库上实证** | ✅ | compose 起于既有 `pgdata` volumeFlyway 日志 `Current version: 5 / No migration necessary.` |
| CIpush + **pull_request** | ✅ | api 5m31s / 6m42sflutter 3m46s / 3m44sdoc 2m58s |
| 凭证防泄漏 | ✅ | `check-secrets.sh --all` exit 0 |
### 发布操作记录
1. **首次走 PR 流程**(分支保护生效后):两仓各建 `dev → main` PR(标题 `v0.4.0 M3.5 体验补齐`),等 `pull_request` 状态检查转绿后以 **Fast-forward only** 合并 → **main == dev、零合并提交、历史保持线性**
2. **流程本身也被验证了**v0.3.0 是直推 main,「PR + 状态检查」这套机制从未真实走过。本次确认:`pull_request` 触发器会跑、分支保护确实要求该检查通过、PR 会自动跟随 dev 新提交重新检查(发布途中 E2E 脚本提交推入 dev,PR 随即重跑)。
3. **CI 从安全事件恢复后的首次完整验证**:见下方「期间事件」。
### 期间事件:Gitea gitconfig 注入(已闭环)
本迭代期间发生一次安全事件,CI 全面中断约一天。完整复盘见 [07 号报告](iterations/iteration-3.5/07-security-incident-20260911.md),纪律固化见新建的[服务器暴露面清单](server-exposure.md)。要点:攻击**未达成代码执行**、三仓代码经核对**未被篡改**、**无系统层入侵**;处置后外部验证四项通过。
### checklist 变更(下次发布适用)
- **回归清单由两份改为四份**:M1/M2/M3/M3.5。原则是「每个引入对外端点的迭代都应有对应的 E2E 脚本,并在此后每次发布回归」。
- 发布前若 dev 仍有提交推入,PR 检查会重跑——**以最终 HEAD 的检查结果为准**,不要用旧的绿色状态放行。
### 已知遗留
**真机验证**M2 两项 + M3 四项 + M3.5 新增(头像上传弱网、头像缓存)——步骤全部备齐在[真机验证清单](device-verification.md),待设备到位。
**功能遗留**:「我的收藏与草稿」列表页(后端就绪,缺 2 页面)、月份网格选择器、`SegmentedButton` 主题债、`widthPx/heightPx` 恒 null、`eventVersion` 口径未定型、uploading 超时清理任务、429 限流、话题/关注列表/作者主页(ADR-018 剪出)。
**跨迭代技术债**access token 黑名单、`/internal` 改 mTLS。
**待评估**:文档站公开可访问是否加访问控制(见服务器暴露面清单)。
## v0.3.0 — M3 社区(2026-09-10
**首次正式发布**,发布流程首次演练。
### 三仓 tag
| 仓库 | tag | 提交 | 内容 |
| --- | --- | --- | --- |
| patbond-api | `v0.3.0` | `8089c06` | 五模块(common/auth:8081/user:8082/pet:8083/community:8084),334 测试 |
| patbond-flutter | `v0.3.0` | `0e87413` | 502 测试 + 三份 E2E 烟囱脚本(M1/M2/M3) |
| patbond-doc | `v0.3.0` | 本记录所在提交 | 契约 v1.3.0 + 三迭代全部报告(20+30+30 份) |
### 版本内容
- **M1 认证纵切**:JWT RS256、refresh 轮换、多设备会话、登录锁定
- **M2 宠物健康档案**:宠物 CRUD + 三角色权限 + 体重/疫苗/健康事件/提醒 + 档案聚合(18 操作)
- **M3 社区**:图片媒体上传闭环(自托管 MinIO,ADR-016)+ 帖子草稿/发布/删除 + 公共 Feed 游标分页 + 单层评论 + 点赞收藏幂等 + 关注(13 路径/19 操作)
- **契约**:openapi.yaml **v1.3.0 冻结**,31 路径/43 操作/72 schema;契约一致性测试矩阵 173 格、43/43 操作零漂移
- **部署形态**:docker compose 六容器(postgres:18 + MinIO + auth + user + pet + community),应用容器无状态(ADR-007)
- **数据库**:Flyway V1~V5(identity/media、platform 埋点、pet_health、字典种子、community)
- **决策**:ADR-001~021
### 发布门禁证据
| 门禁项 | 结果 | 证据 |
| --- | --- | --- |
| 三仓 CI 绿 | ✅ | Gitea commit status API 直查 success |
| 全量测试 | ✅ | api 334 / flutter 502,`mvnw clean test``flutter test` 双绿 |
| E2E 回归(M2+M3 同环境) | ✅ | **M2 11/11 + M3 14/14**,各连跑 3 轮零 flake,契约偏差 0 — [30 号报告](iterations/iteration-3/30-release-e2e-regression.md) |
| M3 验收标准逐条取证 | ✅ | 四条全过 — [28 号报告](iterations/iteration-3/28-e2e-smoke-report.md) |
| 契约向后兼容 | ✅ | v1.2.0→v1.3.0 结构化比对:paths/schemas **removed 与 changed 均为 NONE**(严格增量) |
| 共享代码面回归分析 | ✅ | M3 全区间 `patbond-pet/src/main/` 0 文件变更;`patbond-common` 仅 ErrorCode +9 行纯新增 |
| 凭证防泄漏 | ✅ | `check-secrets.sh --all` 三仓 exit 0(9 规则两层检查,ADR-021) |
### 发布操作记录(首次一次性项)
1. **命名统一**:ADR-011 的 `master` 更正为 `main`(ADR-021);api 本地孤儿 master 已删。
2. **api main 重建**(方案 A,用户拍板):远端 main 原为建仓自动生成的单提交 `ff876bc "Add README"`,与 dev **无共同祖先**,无法 ff 也不宜缝合孤儿历史。操作:Gitea 默认分支临时切 dev → 删除远端 main → `git push origin dev:refs/heads/main` 重建 → 默认分支切回 main。结果:main 41 提交、与 dev 同点位、零 force push。原孤儿提交保留本地备份 ref `refs/backup/old-main-ff876bc`
3. **flutter main 快进**:main 本就是 dev 祖先,用 `git push origin dev:main` 完成——**较 checklist 第 4 步的 `checkout main && merge --ff-only` 改进**:不切换工作区(当时有 E2E 脚本正在该工作区运行),且非快进推送会被 git 自动拒绝,等于内建 ff-only 保护。
4. **分支保护启用**(checklist 第 6 步,Gitea 平台):api 与 flutter 的 `main` 均已启用,经 Gitea API `GET /repos/{owner}/{repo}/branches/main` 核实:
| 仓库 | protected | 推送 | 状态检查上下文 | 所需批准 |
| --- | --- | --- | --- | --- |
| patbond-api | `true` | 禁用直接推送 | `CI / backend-test (push)` | 0 |
| patbond-flutter | `true` | 禁用直接推送 | `CI / flutter-gates (push)` | 0 |
| patbond-doc | 未启用 | —— | —— | —— |
两仓 `dev` 均保持 `protected=false`(直推流,ADR-021 分层策略);doc 仓 main 即日常分支、不参与发布分支语义,按规划不设保护。**状态检查上下文显式填写而非留空**:留空时 Gitea 语义为「所有上报的检查都通过」,若某次工作流未触发则空集为真反而放行;写死检查名消除该歧义。
### 已知遗留(不阻塞发布)
**真机验证四项挂起**(步骤已备齐在[真机验证清单](device-verification.md)):媒体上传弱网、乐观更新手感、Feed 图片加载、社区事件落库;另有 M2 两项(Android 事件落库、SessionTracker 30min)。桌面/脚本不可替代——`platform=linux` 埋点整批 400 属契约内行为。
**功能遗留**:完整草稿列表与自动保存、大图下滑关闭手势、`widthPx/heightPx` 恒 null(单图帖回落 4:3)、`eventVersion` 口径未定型、uploading 超时清理任务、429 限流(连带客户端 Retry-After 分支)、话题/关注列表/作者主页(ADR-018 剪出)。
**跨迭代技术债**:access token 黑名单(退出后已签发 access 在剩余 ≤15 分钟内仍有效)、`/internal` 改 mTLS。
### 发布后生效的纪律
- **`main` 已禁止直接推送**——影响 `main` 的一切变更(含发布本身与 hotfix)一律走 PR,CI 状态检查通过方可合并(ADR-021 强制情形之二正式生效)。`dev` 保持直推流不变。
- **⚠️ 下次发布的姿势与本次不同**:本次首发用 `git push origin dev:main` 直推(当时 main 尚未保护);保护启用后该命令会被拒绝。**此后发布流程为**:
1. 完成 checklist 第 1~2 步(三仓 CI 绿 + E2E 双份回归 PASS);
2. 在 Gitea 上创建 PR:`dev``main`(标题写版本号,正文贴门禁证据链接);
3. 等 PR 的 CI 状态检查转绿(即上表的 `status_check_contexts`);
4. 在 Gitea 上合并 PR——因 dev 与 main 无分叉,合并应为快进;
5. 继续 checklist 第 5、7 步(打 tag、写本页记录)。
故 checklist 第 4 步的本地 `merge --ff-only && push` 写法**仅适用于首次发布**(保护启用前),后续版本以上述 PR 流程替代;第 3、6 步为一次性项,不再重复。
- 若某次 PR 显示 `dev``main` 有分叉(无法快进),说明 main 被绕过 dev 改动过,**先查明原因再合并**,不要用合并提交掩盖。