Commit Graph

6 Commits

Author SHA1 Message Date
姚章浩 b7f7e4f121 fix(deploy): 上线前校验 .env 键值形状(两行粘成一行的坏值不再能上线)
build / build (push) Successful in 1m1s
事故复盘(2026-10-14,测试实例 p1 整体不可用约 6 分钟):
- 我用 `rstrip("\n") + 新行` 往 `.env` 追加 `VERSION_HISTORY_LINK_ORIGIN`,而 `.env` 最后一行
  当时没有结尾换行 ⇒ 两行**粘成一行**:
  `VERSION_HISTORY_GITEA_BASE_URL=http://192.168.50.223:3000VERSION_HISTORY_LINK_ORIGIN=https://gitea.zheromiku.top`
- 配置校验按设计拒绝这个非法 origin ⇒ `cmd/api`/`cmd/worker` 启动即退出 ⇒ 容器 crash-loop
  (fail-closed 是对的,但代价是平台起不来)。

修复:
- `scripts/deploy-api-web.sh` 新增 **1a 步护栏**:上线前解析 `.env`,只要某行的值里又出现 `KEY=`
  (两行粘连)或键名不合法 ⇒ 打印 `ENV_VALUE_LOOKS_MERGED` 并**拒绝部署**(不动线上);
- `internal/appconfig` 在校验失败时补线索:值里含 `=` ⇒ 提示"`.env` 里两行可能粘成了一行";
- 回归用例 `TestLoadRejectsMergedVersionHistoryEnvValue`(粘行必拒 + 线索文案 + 合法值仍放行)。

验证:护栏对"粘行样例/键名非法"退出码 1、对"正常样例/当前真实 .env"退出码 0;Go 用例通过;
make verify 15/15。线上已修 `.env` 并重启 api+worker(healthy),重新同步后页面链接已全部走公网 origin。
2026-10-14 11:17:35 +08:00
姚章浩 90db243a68 fix(web): 旧 index.html 的哈希资源回退到「上一版构建」,页面不再空白/裸奔
build / build (push) Successful in 56s
现象(主人反馈:页面**偶尔**变成"只有文字没样式"):旧入口 index.html 引用的是上一版的
`index-CddbQIqQ.js` / `index-DLg1Y9tN.css`,而新镜像里只剩当次构建的两个文件
⇒ JS 404 白屏、CSS 404 只有文字没样式。触发条件是"短时间内多次重建 web + CDN/浏览器还拿着旧入口"。

修复(三层,各管一段):

1. **入口不缓存**(上一笔已做):`index.html` → `no-cache, must-revalidate`,CDN/浏览器每次回源校验;
2. **旧哈希资源仍可取**(本笔):`/assets/` 未命中时先回退到只读挂载的
   `deploy/legacy-assets/assets`(**上一版构建**的资源),再 404 ⇒ 还持有旧入口的客户端
   不会因为一个文件 404 而白屏/裸奔;该目录由 `scripts/deploy-api-web.sh` 在**每次重建前**
   从"正在运行的那个镜像"里落盘(`LEGACY_ASSETS=` 会打在部署日志里),并加进 .gitignore;
3. **未命中不再回退成 HTML**:避免浏览器把 index.html 当 JS 执行报一堆语法错误。

验证:旧入口的两个引用 `assets/index-CddbQIqQ.js`、`assets/index-DLg1Y9tN.css` 现在都是 200
(旧客户端可用);当前入口的 `assets/index-Dbnd-DW-.js`、`assets/index-Bgkt4A1i.css` 也是 200;
不存在的文件名仍明确 404;`nginx -t` 通过;本机 8088 与公网入口 kg.zheromiku.top 一致;make verify 15/15。
2026-10-14 10:58:01 +08:00
姚章浩 4d43a9386a fix(version-history): tag 之前的提交不再被编号 + 部署脚本注入构建身份
build / build (push) Successful in 1m5s
真实数据发现(本机 p1 实例第一次同步后核对):
- **缺陷**:tag `v0.1.0` 之前的 20 个提交,提交内 CHANGELOG 顶部**也已经**是 0.1.0(旧分支遗留条目),
  而它们的祖先集合被那个**更晚才打**的 tag 全覆盖 ⇒ 按提交数算得 0 ⇒ 被错标成「0.1.0 开发版」。
  它们的正确判定是「历史提交」:那个 tag 是后来才打的,当时还没有发布基线。
- **修复**:基线 tag 必须是该提交的**祖先(或就是该提交)**,否则不给号;新增原因码
  `release_tag_created_later`(页面文案:'该版本是在更晚的提交上发布的 —— 这个提交当时还没有 tag')。
  回归用例两条:`TestDeriveVersionsDoesNotNumberCommitsThatPredateTheTag`、
  `TestDeriveVersionsSideBranchWithoutTagStaysHistorical`(不含 tag 的侧分支同样不给号)。
- **部署脚本注入构建身份**:`scripts/deploy-api-web.sh` 原先不传 `-ldflags -X`,产物 `CommitSHA=unknown`
  ⇒ 页面只能显示"当前运行版本未知"(契约上诚实,但没用)。现在按 `scripts/version.py --identity`
  (唯一来源,与 Makefile/CI 同规则)注入 VERSION/RELEASE_VERSION/BUILD_KIND/COMMITS_SINCE_RELEASE/
  COMMIT_SHA/BUILD_TIME;取不到身份**拒绝继续**(宁可不上线,也不上线一个认不出自己的产物),
  并在收尾核对注入的 SHA 真的进了产物。
- compose.yaml:把 VERSION_HISTORY_* 五项接进应用环境(api/worker 共享锚点),留空即未接线。

验证:真实仓库复验(114 条对账一致)⇒ 正式 1 / 开发 4 / 历史 109(原来错的 19 条现在都是
`release_tag_created_later`);p1 实例重新同步后当前快照分布一致,带号的记录只剩 1 正式 + 4 开发
(含真实存在的「同号不同 SHA」:950c1b4 与 3a1d32d 都是 0.1.1,各占一行);
make verify 15/15 success;前端 18 用例 + typecheck 通过。
2026-10-14 10:00:27 +08:00
姚章浩 fa244c447c fix(deploy): 重建 api 会静默关掉生产门禁 —— 加前置保护 + 门禁状态回显
界面验收第三遍(PDF)踩到:授权发布被回绝 "production release requests are disabled"。
根因:scripts/deploy-api-web.sh 的 COMPOSE_FILES 默认只叠 compose.yaml,重建 api 时没叠
compose.release-gates.yaml → PRODUCTION_RELEASE_REQUESTS_ENABLED / PRODUCTION_BACKUP_RESTORE_VERIFIED
变 false,生产授权接口被静默关闭(页面只在点击那一刻提示一句,极易误判成功能坏了)。

- 前置保护:正在运行的 api 已开闸、而本次 COMPOSE_FILES 不含门禁文件时直接拒绝
  (REFUSED_WOULD_CLOSE_PRODUCTION_GATES,并打印保留门禁的正确命令;确要关闸用 ALLOW_GATE_OFF=1)
- 结尾新增 === 7b 打印重建后的两道门禁状态,避免"以为开着"
- 已用同一段判定逻辑覆盖三种情形(已开闸+不叠门禁→拒绝 / 已开闸+叠门禁→放行 / 显式 ALLOW_GATE_OFF→放行)

验收第三遍同时确认:PDF→MinerU 解析链路是通的(入库 completed、验收"内容处理:通过"),
发布首轮 completed/verified(in_scope=8 foreign=8 降级保留正常),回滚 rolled_back、
目标库 enable_status=disabled。证据补进 docs/USER_FLOWS_ACCEPTANCE.md。
2026-10-10 11:10:51 +08:00
姚章浩 6440e3f679 feat(release): 发布目标知识库可选(源库 → 目标库映射)
- 迁移 000044:production_kb_bindings + release_item_executions.target_knowledge_base_id
- 系统设置新增「生产知识库绑定(对内/对外)」:从生产 WeKnora 实际列表选库,
  保存时真校验(不存在拒收;生产端不可达只提示并如实回报),仅管理员可改
- 执行计划按绑定解析目标库并冻结进 release_item_executions;空目标直接拒收;
  已发布过的条目保留原目标(重试/回滚回到同一处)
- DocumentBundle.RemapKnowledgeBase:knowledges/chunks/knowledge_tags/wiki_pages/
  wiki_folders 的 knowledge_base_id 与 ES 文档字段改写为目标库,重算表/文档摘要并重新封装
- 契约比对改为「声明的映射对」(KnowledgeBaseMapping),其余字段继续 fail-closed
- 契约检查器 / 目标身份 / 验证器全部走映射后的目标库;对象通道无需改写
- Compose 拆分:数据面接线 compose.release-sim.yaml + 两道门禁 compose.release-gates.yaml
- 测试:RemapKnowledgeBase 全通道/无操作/fail-closed、映射契约、绑定接口、RBAC、真实 PG 集成
2026-10-08 15:41:13 +08:00
姚章浩 e1c5f6895a chore(deploy): 部署与备份脚本、示例配置与忽略规则
build / build (push) Failing after 21s
- 新增 scripts/deploy-api-web.sh(API/Web 构建发布)与 scripts/wiki_cleanup_backup.sh(wiki 清理前备份)
- 更新 .env.example、compose.yaml 与 README 说明;忽略 /spool/ingestion 运行期产物
2026-09-30 10:54:07 +08:00