Open Notebook 发布操作手册:版本镜像构建、清单验证、RC 栈验证与 GitHub 发布的完整命令参考
本文基于 Open Notebook 仓库中的发布运行手册 runbook.md 展开,系统讲解稳定版发布流程中"切版 → 推镜像 → 验证 → 发布 → 清理"各阶段(Phase 5–8)的可复制命令。读完本文,你将掌握如何用 CI 构建双架构版本镜像、如何用 docker manifest inspect 验证推送产物、如何用 rc-stack.sh 在本地起一套带真实开发数据副本的 RC 栈做人工走查,以及 GitHub Release 发布后如何自动推进 v1-latest 标签。该手册的"已知坑位"(Known Gotchas)维护在 .github/RELEASE_PROCESS.md,本文一并结合仓库内 Makefile、rc-stack.sh 等源码对其中的命令做逐条拆解。
一、runbook 在发布流程中的定位
Open Notebook 采用流程驱动的发布模型:工作从 ready 标签的 issue 进入 PR,PR 合入 main 后由维护者在分支积累足够且经过验证的变更时切版。完整的发布过程(changelog 审计、风险测试矩阵、镜像门禁、修复循环)定义在 .github/RELEASE_PROCESS.md,其设计依据见 ADR-005:
- Patch 版本:向后兼容的修复;
- Minor 版本:向后兼容的功能与改进;
- Major 版本:包含破坏性变更或需要用户配合的迁移时,配合里程碑提前规划;
- 变更标签约定:
in-dev-build表示变更已进入开发镜像,released表示已随版本发布。
runbook.md 明确自我定位为"命令参考":踩坑经验写在 RELEASE_PROCESS.md 的 Known Gotchas,本文对应文件只负责给出"确切可执行的命令"。它覆盖发布流水线的四个执行阶段:
| 阶段 | 内容 | 对应命令族 |
|---|---|---|
| Phase 5 | 通过 CI 构建并推送版本镜像 | gh workflow run / gh run watch |
| Phase 6 | 用开发数据副本起 RC 栈人工验证 | docker exec surreal export / make release-stack |
| Phase 7 | 发布 GitHub Release(需显式 GO 之后) | gh release create |
| Phase 8 | 清理本地环境 | make release-stack-down 及临时文件清理 |
二、Phase 5:通过 CI 构建版本镜像
runbook 给出的原始命令:
gh workflow run build-and-release.yml --ref main -f push_latest=false
gh run list --workflow=build-and-release.yml --limit 1 # grab the id
gh run watch <run-id> --exit-status # background it
逐条说明:
gh workflow run build-and-release.yml --ref main -f push_latest=false:从main分支手动触发 Build and Release 工作流,并通过-f push_latest=false声明式输入指定"只推版本标签、不动 latest"。之所以走 CI 而不是本地构建,.github/RELEASE_PROCESS.md 解释得很直接:CI 持有镜像仓库的推送凭据(Docker Hub 与 GitHub Container Registry 两端)。本地make docker-push同样可行,但要求你在两个 registry 上分别docker login。gh run list --workflow=... --limit 1:拿到本次触发的 run id,供下一条命令使用。gh run watch <run-id> --exit-status:阻塞式观察构建,--exit-status让进程退出码反映工作流结果,方便挂到后台或脚本中做断言。
从 Makefile 的本地等价目标可以看出 CI 推送的完整产物面(第 78–104 行 docker-push 目标):
- 常规镜像:
lfnovo/open_notebook:<ver>与ghcr.io/lfnovo/open-notebook:<ver>; - 单容器镜像(
--target single):<ver>-single双 registry 各一份; - 平台固定为
linux/amd64,linux/arm64(Makefile 中PLATFORMS变量)。
两个 registry、两种镜像变体、两个架构——这就是下一步清单验证要核对的 3 个 ref 的由来。
三、验证已推送的镜像清单(manifest)
runbook 的命令:
for ref in lfnovo/open_notebook:<ver> lfnovo/open_notebook:<ver>-single ghcr.io/lfnovo/open-notebook:<ver>; do
docker manifest inspect "$ref" | python3 -c "import json,sys; d=json.load(sys.stdin); print(sorted(set(m['platform']['architecture'] for m in d.get('manifests',[]) if m['platform']['architecture']!='unknown')))"
done
# expect ['amd64', 'arm64'] for each; repeat with v1-latest after publication
这条循环值得细读:
docker manifest inspect拉取 manifest list(多架构索引)的 JSON;- 内嵌的 Python 一行程序从
manifests数组中收集platform.architecture,过滤掉unknown——manifest list 中常有一个无平台信息的摘要项,不过滤会污染结果; - 对三个 ref 各执行一次,期望输出均为
['amd64', 'arm64']; - 注释明确要求:在正式发布(Phase 7)之后,还要对
v1-latest标签重复同样的检查,确保 latest 指针也指向双架构产物。
这一步对应 RELEASE_PROCESS.md 中的"bucket C 最终门禁"思想:main 上绿灯不等于镜像可用,必须验证推送产物本身。仓库内配套的自动化版本是 release-image-test.sh,它通过 make release-test TAG=<new> OLD_TAG=<previous> 触发,对真实容器跑 fresh install 与 upgrade 两个场景(详见该脚本注释与 docker-compose.release-test.yml),属于切版前的镜像门禁;而 runbook 这条 manifest 检查则是 CI 推送后的轻量核对。
四、Phase 6:用开发数据副本起 RC 栈人工验证
这是 runbook 中信息密度最高的段落,原文注释完整继承如下(# 开头的注释一并保留):
# 1. Find which SurrealDB instance dev actually uses — read SURREAL_URL in .env
# (multiple instances may run locally; the repo-compose one on :8000 may NOT
# be it), and note SURREAL_DATABASE.
# 2. Consistent export from the RUNNING instance (originals untouched):
docker exec <that-container> /surreal export --conn http://localhost:8000 \
--user root --pass root --ns open_notebook --db <that-db> /dev/stdout > /tmp/dev-dump.surql
# 3. Boot (rc-stack.sh docker-pulls the pushed tag by default, so a local
# build can't shadow the registry artifact):
make release-stack TAG=<ver> DUMP=/tmp/dev-dump.surql
# To exercise the opt-in heavy runtimes (Docling + Crawl4AI) on the pushed
# image with this data, append the flag (first boot installs them, ~minutes):
# bash scripts/release-test/rc-stack.sh up <ver> /tmp/dev-dump.surql --with-runtimes
# 4. Sanity: credentials decrypt (uses the dev encryption key from .env):
curl -s http://localhost:15055/api/credentials | python3 -c "import json,sys; c=json.load(sys.stdin); print(len(c), 'creds,', sum(1 for x in c if x.get('decryption_error')), 'decrypt errors')"
# 5. Opt-in gating is only meaningful on this fresh image (a dev venv may have
# the runtimes installed out-of-band): GET /api/capabilities should report
# both false until --with-runtimes installs them.
以及紧随其后的提醒:
提醒 owner:容器内指向宿主服务的凭据必须使用
http://host.docker.internal:<port>(Ollama、LM Studio 等)。
4.1 为什么先读 SURREAL_URL
runbook 第一条注释对应 RELEASE_PROCESS.md 的一个已知坑:本地可能同时跑着多个 SurrealDB 实例,开发 .env 里 SURREAL_URL 指向的那个才是真数据所在,repo-compose 起的 :8000 未必是它。所以导出前必须先确认目标实例,并记下 SURREAL_DATABASE。
4.2 一致的导出:不动原始数据
/surreal export 从运行中的实例导出,原始库只读不受影响,导出结果重定向到 /tmp/dev-dump.surql。这与 rc-stack.sh 头部注释给出的用法完全一致(脚本第 18–21 行)。
4.3 rc-stack.sh 的内部机制(源码佐证)
make release-stack 只是薄封装,Makefile 第 59–61 行将其转发为 bash scripts/release-test/rc-stack.sh up <TAG> [DUMP]。读 rc-stack.sh 可以确认 runbook 注释背后的一串关键设计:
- 默认
docker pull推送过的 tag(脚本第 60 行):这正是 runbook 注释"rc-stack.sh docker-pulls the pushed tag by default"的出处。本地make docker-build-local打的 tag 与 registry 产物同名(lfnovo/open_notebook:<ver>),若不先 pull,验证的就可能是自己的本地构建而非注册表产物——这是 v1.13.0 留下的教训(见 RELEASE_PROCESS.md Known Gotchas)。pull 失败仅告警不致命,因此纯本地 tag 也能用。 - 复用开发加密密钥(脚本第 39 行):从
.env读取OPEN_NOTEBOOK_ENCRYPTION_KEY注入 RC 栈,这样从开发库导出的加密凭据才能解密——对应 runbook 第 4 步的 sanity check:GET /api/credentials后统计decryption_error,期望为 0。 - 数据库名必须与 dump 来源一致(脚本第 40、46 行):默认取
.env的SURREAL_DATABASE。 - 导入前的两处 sed 处理(脚本第 65–70 行):SurrealDB 导入有两个实战坑,脚本都已处理——
OVERWRITE必须放在类型关键字之后(DEFINE FIELD OVERWRITE …,因为 RELATION 表会自动定义 in/out 字段);导出器可能把自己的一行日志(以 ESC 控制符开头)泄进 dump 文件,用sed -E $'/^\x1b/d'剔除。 API_URL必须显式设置:docker-compose.release-test.yml 第 14–18 行注释说明,不设置时前端/config会把浏览器指向host:5055——在开发机上那就是开发 API,会造成静默的数据串环境。脚本固定注入RC_API_URL="http://localhost:15055"。host.docker.internal映射(compose 第 32–37 行):通过extra_hosts: host-gateway让 Linux 下容器也能解析host.docker.internal,对应 runbook 末尾关于 Ollama / LM Studio 凭据的提醒,脚本启动后也会把这条 NOTE 打到终端。- 端口布局:UI
http://localhost:18502、nginx 代理http://localhost:18080、APIhttp://localhost:15055(compose 中三个端口均绑定127.0.0.1,不对外暴露)。 --with-runtimes标志:把OPEN_NOTEBOOK_ENABLE_DOCLING/OPEN_NOTEBOOK_ENABLE_CRAWL4AI置为true(脚本第 47 行),让推送出去的镜像在首次启动时真正执行可选重型引擎(Docling + Crawl4AI)的安装路径,首启需要数分钟。
4.4 为什么要在"干净镜像"上判断 opt-in 门控
runbook 第 5 条注释(GET /api/capabilities 在 --with-runtimes 安装前两者应为 false)对应 RELEASE_PROCESS.md 的 v1.13.0 教训:开发 venv 可能已经通过非 opt-in 途径装过 crawl4ai/docling,此时 capabilities 接口会误报"可用"、UI 会点亮引擎开关,但这不能代表精简默认镜像的真实状态。只有在 RC 栈(拉取的推送镜像)上验证,opt-in 门控的判定才有效。
五、Phase 7:发布 GitHub Release(显式 GO 之后)
runbook 原文命令:
gh release create v<ver> --title "v<ver> — <theme>" --notes-file <notes.md> --latest
# publication (non-prerelease) triggers the workflow that pushes v1-latest
gh run list --workflow=build-and-release.yml --limit 1 && gh run watch <id> --exit-status
机制拆解(结合 RELEASE_PROCESS.md 的 "Docker Image Publishing (reference)" 表):
| 命令/事件 | 行为 | 是否更新 latest |
|---|---|---|
CI Build and Release(push_latest=false) |
用 CI 凭据推送版本 tag | 否 |
| 非 pre-release 的 GitHub Release 发布 | 再次触发工作流,推送版本 + v1-latest |
是 |
make docker-push / docker-push-latest |
本地等价物(需 docker login) |
否 / 是 |
make tag |
创建并推送与 pyproject.toml 一致的 git tag |
— |
要点:
- 发布必须发生在 Phase 6 验证通过且 release owner 明确 GO 之后——按 ADR-005 的原则"自动化做准备,人扣扳机"。
- 发布的 release 必须不是 pre-release,因为正是"non-prerelease 发布"这一事件触发工作流去推
v1-latest(含v1-latest与v1-latest-single四个 tag,见 Makefile 第 106–136 行docker-push-latest的本地等价逻辑)。 - 发布后回到第三节的 manifest 检查,对
v1-latest再验一遍双架构。
RELEASE_PROCESS.md 还给出了 Release Notes 的结构约定(以 v1.11.0 为参考样板):一行结论 + 升级建议;Security / Features / Performance / Notable fixes 分节;对自托管者的行为变更必须显式提醒(任何可能要求升级时改配置的内容);致谢部分必须列出每位贡献者的 handle 及其交付物(通过 git log <last-tag>..<tag> 与 gh pr view 收集),不可省略。
六、给已交付的 issue 打 released 标签
runbook 原文命令:
# only actual closed ISSUES (changelog refs mix issues and PR numbers):
for n in <numbers>; do
STATE=$(gh api "repos/lfnovo/open-notebook/issues/$n" --jq 'if .pull_request then "pr" else .state end')
[ "$STATE" = "closed" ] && gh issue edit "$n" --add-label released
done
两个细节:
- changelog 引用混杂 issue 号与 PR 号(GitHub issues API 对 PR 同样返回),所以先用
if .pull_request过滤出纯 issue; - 只对 closed 状态的 issue 打标签,PR 与未关闭的 issue 一律跳过。
另一个流程细节(RELEASE_PROCESS.md Release Model 一节):released 标签曾在 v1.12.0 的标签体系整理中被误删,之后重建。原则是——如果文档引用的标签缺失,重建它,而不是跳过这一步。
七、Phase 8:清理
runbook 原文命令:
make release-stack-down
rm -f /tmp/dev-dump.surql; rm -rf /tmp/onrel-*
docker ps --format '{{.Names}}' | grep onrel # must be empty
git status --short # must be clean on main
对应源码行为:
make release-stack-down→ rc-stack.sh 的down分支:compose down -v移除数据卷并rm -rf /tmp/onrel-rc-data,保证数据目录一并清掉;rm -rf /tmp/onrel-*兜底清理release-image-test.sh各阶段用的mktemp目录(/tmp/onrel-fresh-XXXX、/tmp/onrel-upg-XXXX,见该脚本第 89、118 行);- 最后两条是必须为空/干净的断言:没有任何
onrel前缀容器残留(release-image-test.sh 特意给每个阶段分配独立端口,就是防止泄漏容器替错误的栈应答),且main分支git status --short无未提交变更——后者呼应 Known Gotchas 的第一条铁律(见下节)。
八、Known Gotchas 中与 runbook 直接相关的坑位
.github/RELEASE_PROCESS.md 的 Known Gotchas 是 runbook 各阶段的配套防线,摘出与上述命令强相关者:
- 版本 bump 绝不允许未提交地留在分支上:切版时编辑
pyproject.toml/CHANGELOG.md后切换分支,未暂存变更会被带走,下一次git add -A会把版本提升悄悄卷进无关的 fix PR——版本号会藏在fix(...)提交里发布。切版永远是最后一步、独立分支、立即提交(v1.14.0 教训)。这也解释了 Phase 8 为什么要求git status --short干净。 - cut 之后落的修复需要完整重新切版,而不是挪 tag:若已打 tag 但未发布(无 GitHub release、无
v1-latest)且 bucket C 发现阻断问题,tag 必须移到新提交且版本镜像必须重建——否则陈旧 tag 或陈旧 registry 镜像会被发布动作提升为v1-latest。 - 本地
docker-build-local的 tag 会遮蔽推送镜像:两者同名,Phase 6 可能验证的是自己的本地构建。rc-stack.sh up现已默认 pull;若用其他方式启动镜像,先 pull(v1.13.0 教训)。 - RC 栈在非默认端口时需要
API_URL,否则浏览器会打到host:5055——在开发机上那是开发 API(数据串环境)。rc-stack.sh已设好;自定义部署时自行记住。 - SurrealDB 导入坑:
OVERWRITE位置、导出器日志泄漏——rc-stack.sh的 sed 已处理(见 4.3)。 - 开发机端口可能属于其他项目:启动或杀进程前先查 3000/5055/8000 的占用者(
lsof -nP -iTCP:<port> -sTCP:LISTEN加进程 cwd);前端换端口跑冒烟(PORT=3001 npm run dev)完全没问题。 - 测试套件在加载了开发
.env时会打到真实开发库:bucket A 期间要快照各表记录数,前后有差异说明测试泄漏写操作(v1.12.0 曾捕获 48 条泄漏的Test凭据)。 - opt-in 运行时门控必须在干净镜像上判定(v1.13.0 教训,已并入 4.4)。
九、适用前提与限制
- 本文所有命令以当前仓库快照为准:镜像名为
lfnovo/open_notebook(Docker Hub)与ghcr.io/lfnovo/open-notebook,平台linux/amd64,linux/arm64,版本源头是 pyproject.toml(Makefile 第 7 行从中取VERSION); - runbook 引用的 Build and Release 工作流(
build-and-release.yml)在当前快照的.github/workflows/目录中未包含对应文件,从文件清单看该目录仅有docs-links.yml与test.yml——可以推断该工作流由发布方在远端仓库维护或按需启用,本地执行前请以远端实际可用的 workflow 名称为准; gh命令要求已登录并具备仓库写权限与 release 管理权限;make docker-push本地路线则要求双 registry 的docker login;- RC 栈全部端口仅绑定
127.0.0.1,面向本机验证;升级场景测试还要求历史版本镜像在 registry 上持续可用(ADR-005 Consequences 一节明确列出该依赖); - runbook 是"命令参考"性质,流程语义与判断标准(风险矩阵分桶、re-test 策略、沟通模板、复盘)以 .github/RELEASE_PROCESS.md 与 ADR-005 为准,两者配合使用才构成完整的发布操作闭环。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00