Hindsight 核心版本发布完全指南:从 release.sh 切割、Changelog 生成到博客 PR 的完整工作流
【免费下载链接】hindsightHindsight: Agent Memory That Learns项目地址: https://gitcode.com/GitHub_Trending/hindsight2/hindsight
Hindsight(Agent Memory That Learns)仓库中封装了一套面向 AI 助手的核心版本发布技能(位于 .claude/skills/hs-release/SKILL.md),它定义了一次core release的完整操作流程:在干净的main上执行scripts/release.sh切割版本并直推main触发 CI 发布,随后以独立 PR 补上 Changelog 与发布博客。本文以该技能文档为主体,结合仓库中 scripts/release.sh、scripts/generate-clients.sh、scripts/generate-docs-skill.sh 及 hindsight-dev/hindsight_dev/generate_changelog.py 的源码实现,还原每一步背后的脚本逻辑与工程细节,让读者不仅能照做,更能理解为什么这样做。
先厘清:什么是“核心发布”,什么不是
在进入流程前,技能文档首先划定了发布边界——核心(core)发布只针对产品本体:API(hindsight-api系列)、各语言客户端(Python / TypeScript / Go / Rust)、CLI(hindsight-cli)、控制平面(hindsight-control-plane)以及 Helm Chart。
集成(integrations)是独立版本化的。仓库中hindsight-integrations/下每一个集成(如litellm、pydantic-ai、claude-code、openai-agents等)都有自己的版本号和发布通道,必须使用 scripts/release-integration.sh 单独发布,而不是本技能。从源码看,集成发布的 tag 命名也完全不同:核心发布打v<version>(如v0.9.0),而集成发布打integrations/<integration>/v<version>(如integrations/litellm/v0.2.0),且集成脚本内置了约 50 个合法集成名的白名单校验(VALID_INTEGRATIONS数组,见 scripts/release-integration.sh),未知集成名会直接拒绝执行。
另一个必须提前建立的认知是:核心发布不可逆、且面向外部。release.sh会打 tag 并直接推送main,推送动作触发ReleaseGitHub Actions 工作流,进而把包发布到 PyPI / npm / Helm。这意味着发布前必须确认两件事:
- 版本号正确无误;
- 计划包含的修复已经合并进
main。
Step 0 — 发布前预检(Pre-flight)
1. 决定发布基准(base)
发布永远从最新的origin/main切割,绝不允许从功能分支发布。预检命令如下:
git fetch origin --tags git log v<prev>..origin/main --oneline第一条同步远端 tag;第二条用来确认用户口中"那几个修复"确实已经落在main上(v<prev>是上一个发布 tag)。只有确认修复已合入,才能继续。
2. 找到main所在的工作树
由于 git 的限制,main往往已经以兄弟 worktree的形式被检出(用git worktree list查看)。你不能在第二个 worktree 中再次检出main——必须在已经持有它的那个 worktree 里执行发布。
如果那个 worktree 里残留了临时杂物(比如.next-*的 tsconfig 路径、截图等),先暂存再快进:
git stash push -u git pull --ff-only origin main # 或 git merge --ff-only origin/main # ……执行发布…… git stash pop3. 一个著名坑:不要用管道链做 checkout
技能文档明确警告:永远不要把 checkout 塞进&&链并用| tail管道,例如:
git checkout main 2>&1 | tail && git reset --hard ...问题在于管道的退出码是tail的(恒为 0)。当 checkout 失败时,链不会被中断,随后的reset --hard会在错误的分支上执行——这是灾难性的。正确做法是:checkout 单独成命令,并且在 reset 之前用git branch --show-current确认当前分支确实是main。
Step 1 — 切割发布:release.sh做了什么
在干净(clean)的mainworktree 中执行:
./scripts/release.sh <version> # 例如 ./scripts/release.sh 0.8.1(不要带前导 v)阅读 scripts/release.sh 源码可以看到,脚本从入口就建立了四道防线:
- 版本格式校验:
VERSION必须匹配^[0-9]+\.[0-9]+\.[0-9]+$(语义化版本,见 scripts/release.sh); - 分支检查:
git branch --show-current非main时交互式确认(scripts/release.sh); - 工作区清洁检查:
git status -s有输出则直接报错退出(scripts/release.sh); - tag 冲突检查:
git rev-parse "v$VERSION"已存在则拒绝发布(scripts/release.sh)。
通过四道检查后,脚本依次完成以下动作:
(1)为所有组件统一 bump 版本。Python 包列表为hindsight-api、hindsight-api-slim、hindsight-all-slim、hindsight-dev、hindsight-all、hindsight-embed(scripts/release.sh),逐一改写pyproject.toml的version字段。此外还更新三处 Python__init__.py中的__version__(含hindsight-api-slim/hindsight_api/__init__.py、hindsight-embed/hindsight_embed/__init__.py、hindsight-clients/python/hindsight_client_api/__init__.py),以及 Rust CLI 的hindsight-cli/Cargo.toml、Helm Chart 的 helm/hindsight/Chart.yaml(同时更新version与appVersion)、控制平面的hindsight-control-plane/package.json、npm 包装包hindsight-all-npm/package.json、Python 与 TypeScript 客户端清单。
(2)re-pin 元包依赖。hindsight-api、hindsight-all、hindsight-all-slim是纯 shim 元包,必须精确锁定对应的 slim/api 版本(==$VERSION),否则pip install -U会留下旧版 slim,导致服务端上报过期的__version__。同理,hindsight-all还捆绑嵌入式 daemon,会同步把hindsight-embed锁到本版本;hindsight-dev则锁hindsight-api(scripts/release.sh)。
(3)刷新根package-lock.json。通过npm install --ignore-scripts --no-audit --no-fund让 workspace 版本与 bump 后的package.json一致。源码注释解释了原因:若不刷新,CI 里的npm ci会因 "Missing @vectorize-io/hindsight-client@ from lock file" 失败,连带 npm-publish 与 docs-deploy 两个 job 一起挂掉( scripts/release.sh)。
(4)更新文档版本。调用 scripts/update-docs-version.sh,其行为由 patch 号决定:
- patch 发布(如 0.8.1):把
hindsight-docs/docs/用rsync -av --delete同步到已有的versioned_docs/version-0.8/,并用 Node 把sidebars.ts编译成versioned_sidebars/version-0.8-sidebars.json; - minor/major 发布(如 0.9.0):执行
npx docusaurus docs:version 0.9创建全新的version-0.9快照并更新versions.json。
(5)重新生成 OpenAPI spec 与全部客户端 SDK。先由 scripts/generate-openapi.sh 执行uv run generate-openapi(来自hindsight-dev的命令)并npm run build构建文档站点;再由 scripts/generate-clients.sh 生成四种客户端:
- Rust:客户端在构建时由
build.rs(基于 progenitor)自动生成,脚本通过cargo build --release --locked触发再生成,--locked保证依赖解析可复现; - Python:用 Docker 固定版本
openapitools/openapi-generator-cli:v7.10.0(--platform linux/amd64保证 macOS 与 Linux CI 输出一致),并 patchrest.py把 aiohttp 初始化延迟到首次请求,规避 "no running event loop" 错误; - TypeScript:用
@hey-api/openapi-ts(npm run generate,版本锁在package.json),再 patchclient.gen.ts以兼容 Deno(client字段在 Deno 的RequestInit中是保留字); - Go:同样走 Docker openapi-generator,但保留手写的
hindsight_client.go、go.mod/go.sum等维护文件,并修复生成器的两个已知问题(union 类型MarshalJSON改为值接收者、api_files.go补充缺失的os导入)。
若客户端再生成失败,脚本会交互询问是否继续,选择取消则git checkout .回滚所有改动(scripts/release.sh)。
(6)提交、打 tag、推送。最后脚本git add -A,用--no-verify提交信息为Release v<version>(含组件清单与文档同步说明),创建 annotated tagv<version>,然后直接git push origin main与git push origin v<version>(scripts/release.sh)。
再次强调:这一步不是 PR,推送即触发 CI 构建发布制品。
发布后验证
推送完成后立即验证两条:
gh run list --limit 5 # 应能看到 Release v<version> 工作流正在运行 git ls-remote --tags origin v<version> # 应能返回该 tagStep 2 — Changelog + 博客 PR(独立进行)
这一步必须在 tag 已存在之后执行,并且是独立的 PR(仓库先例:v0.8.0 = PR #2053,v0.8.1 = PR #2080)。从新main拉分支:
git checkout -b docs-changelog-<version> origin/main只有当main被其他 worktree 占用、当前工作区又有不想打扰的改动时,才考虑单独 worktree:
git worktree add ../hindsight-changelog-<version> -b docs-changelog-<version> origin/main分支命名:为什么必须用docs-
分支名必须采用docs-(连字符)约定,例如docs-changelog-0.8.1。原因很具体:远端已存在一个字面名为docs的分支,任何docs/...形式的分支在 push 时都会被 git 以directory file conflict拒绝。这是本仓库最容易踩的命名坑。
生成 Changelog
uv run --directory hindsight-dev generate-changelog <version>该命令(实现见 hindsight-dev/hindsight_dev/generate_changelog.py)会:
- 拉取上一个 tag 到
v<version>之间的全部 commit; - 调用 LLM 总结,并把新条目prepend到 hindsight-docs/src/pages/changelog/index.md;
- 在条目末尾追加Database Migrations小节。
关于 Database Migrations 小节,有几点值得注意:
- 它由 git 确定性枚举(对
hindsight_api/alembic/versions/做--diff-filter=A找出新增迁移文件),不是 LLM 生成的,因此不要手工编辑;如果发现某个迁移缺失,应去检查对应 commit 是否真的在 tag 区间内新增了该文件(hindsight-dev/hindsight_dev/generate_changelog.py); - 源码中甚至维护了一张
TABLE_VOLUME分级表,把memory_units、memory_links、entities等表按"高/中/低数据量"标注,用于提示迁移锁范围与耗时(hindsight-dev/hindsight_dev/generate_changelog.py),并有测试保证新建表必须被分类; - 运行需要
OPENAI_API_KEY(仓库.env中已就绪); - 生成范围排除
hindsight-integrations/源码,但新集成的 commit 若顺带改了文档仍会出现——这符合既有先例,保留在 changelog 中即可。
手写发布博客
在 hindsight-docs/blog/ 下手写YYYY-MM-DD-version-X-Y-Z.md,可以镜像已有博客的格式(patch 发布通常很短,可参考 hindsight-docs/blog/2026-06-02-version-0-7-2.md,大版本可参考 hindsight-docs/blog/2026-08-06-hindsight-0-9-0.md)。写作规范有三条硬性要求:
- 讲用户影响,不讲内部机制:开头就要说明用户现在能做什么、要配置什么。配置项与环境变量名可以出现(面向开发者),但代码符号与内部实现不要写;
- 博客中不要列集成:核心博客只覆盖核心引擎 / API / 运维变化;每个集成有自己的 changelog。(集成出现在生成的
changelog/index.md中没问题,只是不要进博客正文。) - 有运维或数据完整性修复时,明确给出升级建议。
格式校验:
npx prettier --check <blog file>同步 docs skill
./scripts/generate-docs-skill.sh该脚本(scripts/generate-docs-skill.sh)把hindsight-docs/docs、src/pages(含 best-practices、faq、changelog)与docs-integrations转换为 AI Agent 可消费的 skills/hindsight-docs/ 技能目录:.mdx转.md、内联示例代码、把<LLMProvidersTable />等 JSX 组件渲染为 Markdown 表格、并把 Docusaurus 绝对路径链接重写为相对路径、最后做链接越界校验。
运行后会刷新 skills/hindsight-docs/references/changelog/index.md,同时把 skills/hindsight-docs/references/openapi.json 的版本号 +1。背景是:release.sh在OpenAPI bump 之前就再生成了 skill,所以发布 commit 里 skill 的openapi.json会滞后一个版本,这一步正是为了把它同步回来——预期会有一行version的 diff,保留它。
提交、推送、建 PR
git add -A git commit --no-verify -m "docs: changelog and blog post for v<version>" git push -u origin docs-changelog-<version> gh pr create --base main --title "docs: changelog and blog post for v<version>" --body "..."PR 中应包含四类文件:
- changelog 新条目(
hindsight-docs/src/pages/changelog/index.md); - 新增的博客文件;
- 再生成的 skill changelog 镜像;
- skill
openapi.json的版本同步 diff。
收尾(Cleanup)
如果 Step 2 中创建了临时 worktree,PR 建立后应移除(分支保留在 origin 上):
git worktree remove ../hindsight-changelog-<version>同时恢复 Step 0 中 pop 出来的 stash。
附:发布核对清单
一次完整的核心发布,按技能文档归纳为以下顺序(供 AI 助手与维护者自查):
git fetch origin --tags,确认目标修复已在main(git log v<prev>..origin/main --oneline)- 定位持有
main的 worktree(git worktree list),脏文件先git stash push -u - checkout 单独成命令,
git branch --show-current确认在main - 执行
./scripts/release.sh <version>(semver 格式,不带v) gh run list --limit 5看到Release v<version>在跑;git ls-remote --tags origin v<version>返回 tag- 从新
main建docs-changelog-<version>分支(勿用docs/...命名) uv run --directory hindsight-dev generate-changelog <version>(勿手改 Database Migrations 小节)- 手写发布博客并按
npx prettier --check校验 ./scripts/generate-docs-skill.sh同步 skill,保留 openapi.json 的一行版本 diff- 提交(
--no-verify)→ push →gh pr create,PR 含 changelog、博客、skill 镜像三部分 - 清理临时 worktree、恢复 stash
这套流程的精髓在于"切割与文档分离":release.sh负责把所有机械性工作(版本 bump、SDK 再生成、tag、推送)一次性原子完成,而 changelog 与博客走独立 PR,让发布文档可以在 tag 落定后从容补写、接受 review。对任何需要维护多语言 SDK + Helm Chart + 文档站点的开源项目来说,这个"一步切割 + 一步文档"的双阶段模式都值得直接借鉴。
【免费下载链接】hindsightHindsight: Agent Memory That Learns项目地址: https://gitcode.com/GitHub_Trending/hindsight2/hindsight
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考