OpenViking 发版工程实践:多产物 tag 约定、GitHub Actions 发布流水线与补发策略
【免费下载链接】OpenVikingSelf-evolving Context Database for AI Agents. Unify Agent Memory, Knowledge RAG and Skills.项目地址: https://gitcode.com/GitHub_Trending/op/OpenViking
本文围绕 OpenViking 仓库的发版说明文档,系统拆解其“一次发版、多产物联动”的发布体系:主包、Python SDK、Docker 镜像、Rust CLI/npm 包、TOS 下载资产与 ClawHub 插件各自的 tag 命名空间与触发链路,并逐条对照仓库中的 GitHub Actions workflow 与包配置,说明构建、发布、验证与补发的完整操作路径,帮助维护者按规范完成一次正式发版并正确处理发布失败场景。
发版目标:一组相互关联的资产,而非单一产物
OpenViking 一次正式发版需要同时覆盖多个分发渠道,各产物面向不同使用入口:
openvikingPython 主包:面向本地运行时、服务端、CLI 及完整功能用户;- Python SDK
openviking-sdk:面向只通过 HTTP 调用已有 OpenViking 服务的轻量客户端用户; - Docker 镜像:面向容器化部署,同时发布到 GHCR 和 Docker Hub;
- TOS 发布资产:面向源码包、安装脚本和稳定下载路径;
- Rust CLI / npm 包:面向通过 npm 安装
ovCLI 的用户; - OpenClaw / ClawHub 插件:面向 OpenClaw 插件分发渠道;
- VikingBot:当前随
openviking[bot]extra 和官方 Docker 镜像分发,不再作为独立 PyPI 包维护。
从根目录 pyproject.toml 可以确认这一结构:包名为openviking、版本为dynamic,由setuptools_scm解析;[tool.setuptools.packages.find]的查找范围是[".", "bot"]、包含openviking*和vikingbot*包——即 VikingBot 的源码确实被打进主包一起发布。
发版的核心纪律是:正式主版本发版时,Python 主包、Docker 镜像和 TOS 资产必须使用同一个主版本 tag;SDK、CLI、ClawHub 插件则使用各自独立的 tag 或 version 命名空间。这一约定在仓库的多个 workflow 里通过 tag 前缀过滤得到强制落地(后文逐一展开)。
版本与 tag 约定
| 产物 | 推荐 tag / version | 说明 |
|---|---|---|
openviking主包 | vX.Y.Z | 主 release tag,例如v0.3.26 |
openviking-sdk | python-sdk@X.Y.Z | SDK 专用 tag,例如python-sdk@0.1.3 |
| Rust CLI / npm CLI | cli@X.Y.Z | CLI 专用 tag,例如cli@0.2.0 |
| ClawHub 插件 latest | YYYY.M.D或YYYY.M.D-N | 由 workflow 自动生成或手动指定 |
| ClawHub 插件 dev | YYYY.M.D-dev.N | dev channel 使用 |
tag 约定不只是文档规范,而是被setuptools_scm配置直接执行的:
- 主包:pyproject.toml 中
[tool.setuptools_scm]设置tag_regex = "^v(?P<version>[0-9]+(?:\\.[0-9]+)*)$",write_to = "openviking/_version.py",git_describe_command使用--match v[0-9]*。即主包版本只从v前缀 tag 解析。 - SDK:sdk/python/pyproject.toml 的
[tool.setuptools_scm]设置root = "../.."(以根仓库为版本源)、tag_regex = "^python-sdk@(?P<version>...)$",git_describe_command匹配python-sdk@*。由于setuptools_scm的 tag 匹配互斥,主包 tagv0.3.26不会干扰 SDK 版本解析,反之亦然。
这种“同一仓库、多 tag 命名空间、正则隔离”的设计,是理解后续所有 workflow 触发条件的关键。
正式主包发版流程
主包正式发版走根目录 GitHub Release,由 release.yml(workflow 名03. Release)驱动。完整流程:
- 确认待发布改动已合入目标分支,且 PR / main 分支检查通过;
- 创建主包 tag,例如
v0.3.26; - 在 GitHub 上基于该 tag 发布 Release;
03. Releaseworkflow 在 Release published 事件时触发——注意其buildjob 带有条件github.event_name == 'workflow_dispatch' || startsWith(github.event.release.tag_name, 'v'),只有v前缀的主包 tag 才会走这条流水线,python-sdk@*、cli@*等组件 tag 会被直接跳过;- build job 通过
uses: ./.github/workflows/_build.yml复用15. _Build Distribution,构建 sdist 和多平台 wheel; publish-pypijob 将构建产物发布到 PyPI;- Docker 镜像由独立的 tag 推送构建(见下文),release workflow 中的
verify-dockerjob 负责轮询等待并校验镜像确实落库; - release-tos.yml(
20. Release TOS Upload)在同一 Release published 事件下上传源码 zip 和安装脚本到 TOS。
正式主发版的发布目标汇总:
| 渠道 | 目标 |
|---|---|
| PyPI | openviking |
| GHCR | ghcr.io/<owner>/<repo> |
| Docker Hub | <dockerhub-user>/openviking |
| TOS | 版本化 release 路径和可选latest稳定路径 |
几个从 workflow 源码中可以确认的实现细节:
- 发布采用 Trusted Publishing:
publish-pypijob 只申请id-token: write权限,使用pypa/gh-action-pypi-publish@release/v1并设置skip-existing: true——同版本重复触发不会报错,而是幂等跳过。 - 手动 dispatch 有权限门禁:
permission-checkjob 通过getCollaboratorPermissionLevel校验触发者至少具有admin/maintain/write权限,防止无权限者触发发布。 - 构建矩阵可配:默认
os_json为["ubuntu-24.04", "ubuntu-24.04-arm", "macos-14", "macos-15-intel", "windows-latest"](对应 Linux x86_64/aarch64、macOS arm64/x86_64、Windows x86_64),默认python_json为["3.10"]。 - Linux wheel 在 glibc 2.31 环境中构建:_build.yml 的
build-linuxjob 运行在ubuntu:20.04容器内,从源码编译指定 CPython,再用auditwheel repair修复二进制依赖,保证 wheel 在较老 glibc 环境可安装。构建前还会把 Rust CLIov二进制打进openviking/bin/,即 pip 装主包同时获得ov命令。 - 内置冒烟测试:构建完成后 workflow 会实际
pip install该 wheel,校验 RAGFS 绑定客户端、向量引擎过滤 ABI 符号以及 Web Studio 静态资源是否随包安装,任何一项缺失即构建失败。
verify-dockerjob 还揭示了一个值得注意的时序问题:tag push 与 Release published 几乎同时发生,release workflow 启动时镜像可能尚未构建完成,因此该 job 会每 30 秒轮询一次build-docker-image.yml在该 tag 上的运行记录(最多 60 次),并进一步用docker buildx imagetools inspect断言 GHCR 与 Docker Hub 上版本 tag 与latesttag 的 digest 一致,不一致则整个 release 判定失败。
主包手动构建、测试发布与补发
release.yml 同样支持workflow_dispatch手动触发,输入参数target可选:
none:只构建,不发布;testpypi:发布到 TestPyPI;pypi:发布到 PyPI;both:同时发布 TestPyPI 和 PyPI。
此外还有build_sdist、build_wheels、os_json、python_json四个参数,可用于只构建部分平台的 wheel 或缩减构建范围,适合发版前验证。
如果需要基于已有构建产物补发Python 包(例如构建成功但发布步骤失败),应使用 _publish.yml(16. _Publish Distribution):手动 dispatch 时必填build_run_id(从对应 Build 运行 URL 中获取)和目标渠道,workflow 通过actions/download-artifact的跨 run 能力(run-id+GITHUB_TOKEN)拉取原构建产物的python-package-distributions-*工件,再走相同的pypa/gh-action-pypi-publish流程。该流程适合发布失败后的补发,不建议作为正常主发版入口——正常发版一律走03. Release,以保证 PyPI、Docker、TOS 三者的口径一致。
Docker 镜像发布与补发
Docker 镜像由 build-docker-image.yml(Build and Push Docker Image)构建,触发条件有三类:
main分支 push:产出maintag 镜像;v*.*.*tag push:产出版本 tag 镜像,并同时打上latesttag(type=raw,value=latest,enable=${{ github.ref_type == 'tag' }});workflow_dispatch手动触发:version输入框必填,重建指定版本镜像。
从 workflow 源码看,其执行结构是:
- 按矩阵在
ubuntu-24.04(amd64)与ubuntu-24.04-arm(arm64)上分别 buildx 构建并推送,同时推 GHCR(ghcr.io/<owner>/<repo>,镜像名统一小写化)和 Docker Hub(docker.io/<DOCKERHUB_USERNAME>/openviking),各自输出push-by-digestdigest; create-manifestjob 汇总两个架构的 digest artifact,用docker buildx imagetools create为每个 tag 创建多架构 manifest;- 版本解析分三种来源:手动触发用输入值,tag 触发用 tag 名,main 分支则调用 build_support/versioning.py 的
resolve_openviking_version()动态解析;解析结果为空或0.0.0时直接以退出码 2 失败,避免打出无版本镜像。
需要特别理解的口径问题:正式主发版时,Docker 镜像实际就是由这条独立 workflow 在 tag push 时构建的,而03. Releaseworkflow 只做等待与校验(verify-docker-image轮询的正是build-docker-image.yml的运行记录)。文档中“正式主发版时 Docker 镜像由主 release workflow 自动构建并发布”的表述,从源码结构看更准确的描述是:release 流水线与镜像构建流水线由同一 tag 事件并行触发、release 流水线负责验证镜像发布结果。
使用建议与文档一致:正式版本优先走主 release 流程(tag + Release);只有镜像补发(例如某个 tag 的 manifest 损坏)或特殊验证时,才单独使用workflow_dispatch指定版本重建,且应保留已发布版本 tag 的可追溯性,避免与正式 release 产物口径混淆。
TOS 发布资产
release-tos.yml 由 Release published 事件(仅v前缀 tag)或手动 dispatch 触发,手动补发时必填tag(如v0.3.24),update_latest默认为 true。
workflow 会 checkout 到对应 tag,生成并上传以下资产到 TOS(通过 AWS CLI 以 S3 协议访问 TOS endpoint):
releases/<tag>/openviking-<tag>-source.zip:由git archive生成的源码包,带immutable缓存头,视为不可变资产;releases/<tag>/memory-plugin-marketplace.zip与memory-plugins.git:memory 插件的瘦身市场包与 dumb HTTP git 仓库,供 Claude Code / Codex 等客户端离线安装;- Claude Code memory plugin、Codex memory plugin 及 shared 安装脚本(各含
install.sh与tos-install.sh)的对应 TOS 版本副本; - 当
update_latest=true时,上述内容还会服务端拷贝/同步到releases/latest/与根路径稳定位置(带no-store缓存头)。
两个重要的容错设计:
- TOS secrets 未配置时不失败:workflow 先检查
TOS_ACCESS_KEY、TOS_SECRET_KEY、TOS_REGION、TOS_RELEASE_BUCKET、TOS_ENDPOINT五项 secrets,任一缺失则跳过上传,并在 step summary 中写明跳过原因——这保证 TOS 渠道的故障不会阻塞 PyPI/Docker 主发布。 - 上传清单可审计:所有成功上传的 key 会写入
$GITHUB_STEP_SUMMARY,run 页面可直接看到本次发版落库了哪些资产、稳定安装脚本是否被更新。
Python SDK 发版流程
Python SDK 位于 sdk/python,PyPI 包名为openviking-sdk,使用独立 tag 命名空间python-sdk@X.Y.Z。对应 python-sdk-release.yml(Python SDK Release):
- 合入 SDK 相关改动;
- 创建并推送 tag,例如
python-sdk@0.1.3; - workflow 的
build-sdkjob 由startsWith(github.event.release.tag_name, 'python-sdk@')条件门控——主包v*tag 触发的 Release 不会误发 SDK; - job 在
sdk/python目录内运行python -m setuptools_scm解析版本(其 tag 正则只匹配python-sdk@*); - 强校验 tag 与解析版本一致:
EXPECTED_TAG="python-sdk@${SDK_VERSION}",不相等直接exit 1。这一步确保“tag 写的是 0.1.3、但 git describe 解析出别的版本”这类错误在发布前被拦截; python -m build构建后,按事件类型决定目标:Release 触发固定发 PyPI,手动 dispatch 可选testpypi/pypi/both,最终同样用pypa/gh-action-pypi-publish+skip-existing幂等发布。
Rust CLI / npm 发版流程
Rust CLI 对应 rust-cli.yml(Rust CLI Build),由cli@*tag push 触发(另监听main与feat/rust-cli分支的 crates 路径变更做 CI 构建,但不发布)。
构建矩阵覆盖 5 个平台,并各自打包成一个 npm 平台包:
| 构建目标 | 平台包 |
|---|---|
| x86_64-unknown-linux-musl | @openviking/cli-linux-x64 |
| aarch64-unknown-linux-musl | @openviking/cli-linux-arm64 |
| x86_64-apple-darwin | @openviking/cli-darwin-x64 |
| aarch64-apple-darwin | @openviking/cli-darwin-arm64 |
| x86_64-pc-windows-msvc | @openviking/cli-win32-x64 |
源码中可以看到几个关键工程决策:
- Linux 产物采用 musl 静态构建:workflow 安装 Zig +
cargo-zigbuild完成 musl 交叉编译,使二进制不依赖运行环境的 glibc 版本(注释明确说明这是为了兼容 CentOS 7 / RHEL 8 等旧 glibc 发行版); - 版本注入:tag 中的版本号通过
sed注入 crates/ov_cli/Cargo.toml(源文件中版本占位为0.0.0),非 tag 构建则使用0.0.0-dev; - 平台包按需生成:每个平台的
package.json由 workflow 现场生成,写入os/cpu约束和bin文件,npm 用户只需安装 wrapper 包 npm/cli; - 幂等发布:
npm-publishjob 在 tag 事件下先npm view <pkg>@<version>检查,已存在的版本直接跳过,随后把 wrapper 包@openviking/cli的version与全部optionalDependencies对齐到该版本再发布。
OpenClaw / ClawHub 插件发布
OpenClaw 插件通过 clawhub-dev-release.yml(OpenViking OpenClaw plugin release)发布,打包对象为 examples/openclaw-plugin。触发方式:main分支上examples/openclaw-plugin/**路径变更自动触发,或手动 dispatch。
手动触发的输入参数:
version:可选;留空时由 workflow 按日期自动生成(YYYY.M.D/YYYY.M.D-N/YYYY.M.D-dev.N);channel:auto、dev或latest;package_ref:指定打包的 git ref,默认main;changelog:本次插件发布说明;publish_clawhub/publish_npm:分别控制是否发布到 ClawHub 和 npm(@openviking/openclaw-plugin)。
从 workflow 结构看,其guardjob 负责解析 channel 与 version:结合CLAWHUB_UPSTREAM_REPOSITORY、CLAWHUB_LATEST_REPOSITORIES、CLAWHUB_DEV_REPOSITORIES、CLAWHUB_RELEASE_TIMEZONE等仓库变量决定 latest 与 dev 的发布来源,并通过concurrency: openclaw-plugin-release(不取消进行中的运行)串行化发版,避免连续合并时日期版本号解析互相竞争。发布形态上,workflow 同时保留向 ClawHub 发布 legacy zip 工件(兼容旧版 OpenClaw 的/download哈希机制)和向 npm 发布打包 tarball 两条路径。
推荐做法:正式渠道使用latest或auto,开发验证使用dev;手动指定 version 时确保符合对应 channel 的格式要求(YYYY.M.D系列)。
VikingBot 发布说明
VikingBot 当前不再作为推荐的独立 PyPI 包发版路径维护,现行分发方式是随主包发布:
- Python 安装入口:
pip install "openviking[bot]"; - 源码开发入口:
uv pip install -e ".[bot]"; - 官方 Docker 镜像默认已包含 VikingBot,可通过
--without-bot或OPENVIKING_WITH_BOT=0关闭。
仓库状态与这一结论互相印证:bot/目录下没有pyproject.toml、setup.py或setup.cfg(已核实不存在),因此无法从本仓库将其作为独立 Python 包发布。同时,bot/.github/workflows/release.yml 位于bot子目录内,应视为历史 bot 子项目或拆分仓库的发布参考,不是根仓库当前可直接触发的 GitHub Actions workflow。如需恢复独立vikingbot包,需要先在bot/下补齐独立的 Python 包配置、版本策略和发布凭证策略。
发版前检查清单
发版前建议逐项确认:
- 待发布改动已合入目标分支;
- CI / PR 检查已通过;
- 版本号未在 PyPI、npm 或 Docker registry 中发布过;
- tag 命名符合对应产物约定(
v*/python-sdk@*/cli@*/YYYY.M.D*); - Python 包依赖、构建配置和 README 已同步更新;
- Docker Hub、PyPI/TestPyPI、TOS、npm、ClawHub 等发布所需 secrets 或 trusted publishing 配置可用;
- Release notes 已准备好,且说明破坏性变更、迁移步骤和重要修复。
发版后验证清单
发版后建议验证:
- PyPI / TestPyPI 上的包版本与 tag 一致;
pip install openviking==<version>或pip install openviking-sdk==<version>可成功安装;- Docker registry 中存在版本 tag 和预期的
latest/maintag; - 多架构 Docker manifest 可正常拉取(
03. Release的verify-dockerjob 会自动做 digest 级校验); - TOS 版本化路径和稳定路径可访问;
- npm 上存在对应 CLI 平台包(5 个)和 wrapper 包
@openviking/cli; - ClawHub 插件 channel 和 version 符合预期。
故障处理与补发原则
- PyPI 和 npm 已发布版本通常不可覆盖:如包内容有误,应发布新版本;两条流水线的
skip-existing/npm view检查正是为此设计的幂等防线。 - Docker 的
latest、main和手动指定 tag可通过独立 Docker workflow 重建,但应保留已发布版本 tag 的可追溯性。 - TOS 的版本化路径应视为不可变资产(上传时显式写入
immutable缓存头);稳定路径可通过手动 workflow 的update_latest覆盖。 - 构建成功但发布失败时,优先使用补发 workflow(
16. _Publish Distribution传入build_run_id)或手动 dispatch,避免重新创建不同内容的同名 tag。 - tag 命名错误时,优先删除错误 tag 并重新创建正确 tag,前提是该 tag 尚未触发不可逆发布(PyPI 上传属于不可逆操作)。
综上,OpenViking 的发布体系核心是“tag 命名空间隔离 + workflow 前缀门控 + 幂等发布 + 独立的构建/发布分层”:主包、SDK、CLI 各自用正则有界的 tag 解析版本,发布动作全部走skip-existing幂等通道,构建(15. _Build Distribution)与发布(16. _Publish Distribution)解耦,使任何一次发布失败都能在不重建产物的前提下安全补发。
【免费下载链接】OpenVikingSelf-evolving Context Database for AI Agents. Unify Agent Memory, Knowledge RAG and Skills.项目地址: https://gitcode.com/GitHub_Trending/op/OpenViking
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考