免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

caveman 公共包发布流水线:从注解标签到 npm/PyPI 可信发布的完整机制

caveman 公共包发布流水线:从注解标签到 npm/PyPI 可信发布的完整机制 caveman 公共包发布流水线从注解标签到 npm/PyPI 可信发布的完整机制【免费下载链接】caveman why use many token when few token do trick — Claude Code skill that cuts 65% of tokens by talking like caveman项目地址: https://gitcode.com/GitHub_Trending/caveman1/caveman本文基于 docs/PACKAGE_RELEASES.md 展开讲解 caveman 如何通过.github/workflows/release-packages.yml工作流把caveman-ai/sdk、caveman-ai/agent、caveman-ai/create-agent与 PyPIcaveman-sdk四个公共包安全发布到注册表。读完后你将理解caveman 的发布标签规范、构建与发布隔离的 OIDC 安全模型、标签与包元数据的版本一致性校验、npm tarball 与 Python wheel/sdist 的构建及冒烟验证流程以及发布后的验证与回滚纪律。发布范围与构件矩阵caveman 在 2026-08-11 以 Bootstrap 方式发布了四个经过审查的构件并在干净环境中匿名验证通过发布渠道包名初始版本npmcaveman-ai/sdk1.0.0npmcaveman-ai/agent0.1.0npmcaveman-ai/create-agent0.1.0PyPIcaveman-sdk1.0.0这些版本与当前仓库中的包元数据完全一致packages/sdk/typescript/package.json中为caveman-ai/sdk1.0.0packages/agent/package.json为caveman-ai/agent0.1.0packages/create-caveman-agent/package.json为0.1.0而 packages/sdk/python/pyproject.toml 中项目名为caveman-sdk、版本1.0.0Python 侧的运行时导入名是caveman_cloud包名与导入名不同这是发布时需要特别留意的点。验证结论包括全新安装与运行时 import 均通过注册表下载内容与受审构件的 SHA-256 哈希一致且发布构件中不包含任何注册表凭证。需要说明的是从工作流源码看.github/workflows/release-packages.yml 的触发标签模式实际还包含第五种pi-v*对应 packages/pi-extensionnpm 名caveman-ai/pi当前版本0.1.0仓库中也存在pi-v0.1.0标签。文档描述的四个构件是 Bootstrap 阶段的受审发布面工作流本身对 pi 扩展包的发布通道已预留。工作流架构构建与发布严格隔离整个工作流的安全设计核心是一条原则构建任务没有 OIDC 权限只有隔离的发布任务可以铸造注册表令牌。具体体现在工作流顶层与三个 job 的划分# .github/workflows/release-packages.yml permissions: contents: read # 工作流默认只有只读内容权限 concurrency: group: release-package-${{ github.ref }} cancel-in-progress: false # 同标签的发布任务不允许互相抢占 jobs: build: # 只负责解析包、构建、测试、打包 publish-npm: # environment: npmpermissions: id-token: write publish-pypi: # environment: pypipermissions: id-token: writebuildjob30 分钟超时完成所有重活校验标签、解析包、构建、测试、审计、打包最后把 tarball 或 wheel/sdist 上传为一次性 artifact保留期仅 1 天retention-days: 1。publish-npm/publish-pypijob各 10 分钟超时只依赖build的输出下载 artifact 后调用注册表发布接口。它们绑定受保护的 GitHub 环境npm/pypi并在environment的url字段中直接写明对应包的注册表页面方便在 Actions 界面跳转核对。只有这两个发布 job 声明了id-token: write即只有它们能通过 OIDC 向 npm/PyPI 换取发布令牌buildjob 即使被恶意代码劫持也无法触达注册表凭证。cancel-in-progress: false保证同一个标签一旦开始发布就不会被后续 push 取消避免半截发布。标签门禁注解、签名验证与 main 祖先关系标签是发布的第一道闸门。文档规定标签必须是注解标签annotated、必须通过 GitHub 签名验证、且必须指向main。工作流中有一个专门的步骤把这三点都硬性校验release-packages.ymlset -euo pipefail tag_ref$(gh api repos/$GITHUB_REPOSITORY/git/ref/tags/$GITHUB_REF_NAME --jq .object.sha) tag_type$(gh api repos/$GITHUB_REPOSITORY/git/ref/tags/$GITHUB_REF_NAME --jq .object.type) [[ $tag_type tag ]] || { echo release tag must be annotated 2; exit 1; } verified$(gh api repos/$GITHUB_REPOSITORY/git/tags/$tag_ref --jq .verification.verified) [[ $verified true ]] || { echo release tag signature is not GitHub-verified 2; exit 1; } target$(gh api repos/$GITHUB_REPOSITORY/git/tags/$tag_ref --jq .object.sha) git cat-file -e $target^{commit} git merge-base --is-ancestor $target origin/main这段脚本依次检查标签对象类型为tag注解标签而非轻量的commit指针GitHub 对该标签签名的验证结果为true标签指向的对象确实是一个 commit该 commit 是origin/main的祖先git merge-base --is-ancestor即发布内容必须来自main分支杜绝从任意旁支标签直接发布。标签命名遵循包前缀 版本号的约定触发模式为sdk-ts-v*、sdk-python-v*、agent-v*、create-agent-v*以及上文提到的pi-v*。版本一致性校验标签必须等于包元数据文档明确Workflow rejects a tag whose version differs from package metadata工作流拒绝版本与包元数据不一致的标签。工作流的Resolve package and require tag/version parity步骤release-packages.yml实现了这一点case $GITHUB_REF_NAME in sdk-ts-v*) packagepackages/sdk/typescript ecosystemnpm version${GITHUB_REF_NAME#sdk-ts-v} actual$(node -p require(./$package/package.json).version) ;; sdk-python-v*) packagepackages/sdk/python ecosystempypi version${GITHUB_REF_NAME#sdk-python-v} actual$(python -c import pathlib,tomllib; print(tomllib.loads(pathlib.Path(packages/sdk/python/pyproject.toml).read_text())[project][version])) ;; agent-v*) # → packages/agent create-agent-v*)# → packages/create-caveman-agent pi-v*) # → packages/pi-extension esac [[ $version ~ ^[0-9]\.[0-9]\.[0-9]([-][0-9A-Za-z.-])?$ ]] || { echo release version is not semver: $version 2; exit 1; } [[ $actual $version ]] || { echo tag version $version does not match package version $actual 2; exit 1; }这里有三层防护标签到包的映射按前缀把标签解析到具体的包目录并判定发布生态npm或pypiSemver 语法校验版本必须匹配标准 SemVer 正则允许build/-pre后缀防止agent-v1.0这类非法版本进入流水线精确相等比对从package.jsonnpm 包用node -p读取或pyproject.toml用tomllib解析project.version中读出实际版本与标签版本做字符串相等比较任何漂移比如忘了 bump 版本号就打标签都会直接exit 1。当前标签与构件的对应关系文档表格已用仓库实际元数据核对标签构件当前版本sdk-ts-v1.0.0npmcaveman-ai/sdk1.0.0sdk-python-v1.0.0PyPIcaveman-sdk1.0.0agent-v0.1.0npmcaveman-ai/agent0.1.0create-agent-v0.1.0npmcaveman-ai/create-agent0.1.0npm 构建流水线安装、审计、测试与按包定制的冒烟验证npm 生态的构建步骤Build and test npm packagerelease-packages.yml在对应包的 working directory 中执行npm ci --ignore-scripts --no-audit --no-fund npm audit --package-lock-only --omitdev --audit-levellow npm audit --package-lock-only --audit-levelhigh npm test npm pack --pack-destination $RUNNER_TEMP/package-dist mapfile -t tarballs (find $RUNNER_TEMP/package-dist -maxdepth 1 -type f -name *.tgz -print) [[ ${#tarballs[]} 1 ]] || { echo expected exactly one npm tarball 2; exit 1; }关键设计点可移植的提交锁文件使用npm ci --ignore-scripts基于已提交的 lockfile 安装保证锁文件里写什么就装什么且禁用 install 脚本以防安装期代码执行双层安全审计对运行时依赖图做low级别审计任何运行时漏洞即失败再对完整依赖图做high级别审计。这与 packages/agent/README.md 中描述的安全边界一致——发布 CI 拒绝任何运行时 advisory 以及完整图中的 high/critical advisory全量测试npm test运行该包的完整测试套件恰好一个 tarballnpm pack产物必须唯一防止意外打包了多余文件。随后工作流把 tarball 装进一个临时干净项目$RUNNER_TEMP/npm-artifact-smoke按包类型执行不同的真机冒烟case ${{ steps.package.outputs.package }} in packages/sdk/typescript) (cd $smoke node --input-typemodule -e const mawait import(caveman-ai/sdk); if(typeof m.Cave!function) process.exit(1)) ;; packages/agent) (cd $smoke node --input-typemodule -e const mawait import(caveman-ai/agent); if(typeof m.agent!function) process.exit(1)) $smoke/node_modules/.bin/caveman-agent --version ;; packages/pi-extension) # 发布的 tarball 必须真正携带 package.json pi.extensions 所命名的打包扩展 test -s $smoke/node_modules/caveman-ai/pi/dist/index.mjs ;; packages/create-caveman-agent) generated$smoke/generated-agent $smoke/node_modules/.bin/create-caveman-agent $generated --provider openai --no-install test -f $generated/src/agent.ts test -f $generated/src/run.ts node -e ... 断言生成项目依赖 caveman-ai/agent: ^0.1.0 ... $generated/package.json node -e ... 断言 run 脚本为 node --experimental-strip-types src/run.ts ... $generated/package.json grep -F entryPath: src/agent.ts $generated/src/run.ts grep -F rootDir $generated/src/run.ts ;; esac这些冒烟测试验证的是用户拿到 tarball 之后真正会发生什么caveman-ai/sdk干净项目 import 后必须能拿到Cave构造函数。对照 packages/sdk/typescript/src/index.ts 的导出Cave确实是 SDK 的核心入口类CaveOptions支持apiKey、baseURL、agent、retention、timeoutMs、signal等选项断言的是真实 API 面而非占位导出caveman-ai/agent除agent()导出外还实际执行caveman-agent --version验证bin可执行文件在 tarball 中可用caveman-ai/pi检查dist/index.mjs非空确保打包扩展真的随 tarball 分发——工作流注释直言这正是关键因为package.json的pi.extensions字段指向的构建产物若缺失插件市场安装后会是空壳caveman-ai/create-agent实际运行 initializer 生成一个项目然后逐项断言生成的src/agent.ts、src/run.ts存在、package.json依赖锁定在^0.1.0、run 脚本为node --experimental-strip-types src/run.ts、run.ts内含entryPath: src/agent.ts。这是对脚手架产物契约的端到端验证。Python 构建流水线wheel 与 sdist 双工件Python 包发布wheel plus sdist构建步骤release-packages.ymlpython -m pip install --disable-pip-version-check build1.3.0 pytest9.0.3 python -m pytest python -m build --sdist --wheel --outdir $RUNNER_TEMP/package-dist # 断言恰好 1 个 wheel 1 个 sdist # wheel 冒烟 python -m venv $RUNNER_TEMP/wheel-smoke $RUNNER_TEMP/wheel-smoke/bin/python -m pip install --no-deps ${wheels[0]} $RUNNER_TEMP/wheel-smoke/bin/python -c import caveman_cloud,importlib.metadata; assert importlib.metadata.version(caveman-sdk) ${{ steps.package.outputs.version }} # sdist 冒烟 python -m venv $RUNNER_TEMP/sdist-smoke $RUNNER_TEMP/sdist-smoke/bin/python -m pip install setuptools80.9.0 $RUNNER_TEMP/sdist-smoke/bin/python -m pip install --no-deps --no-build-isolation ${sdists[0]} $RUNNER_TEMP/sdist-smoke/bin/python -c import caveman_cloud,importlib.metadata; assert importlib.metadata.version(caveman-sdk) ${{ steps.package.outputs.version }}两个独立的 venv 分别验证 wheel 和 sdist 的安装路径wheel 直装sdist 用固定版本 setuptools 且--no-build-isolation源码构建。两者都要满足能import caveman_cloud且importlib.metadata读到的发行版名/版本与标签一致。注意这里再次体现了 PyPI 发行名caveman-sdk与 Python 导入名caveman_cloud的分离——对应源码目录 packages/sdk/python/caveman_cloud测试位于 packages/sdk/python/tests。OIDC 可信发布发布任务的令牌从哪来两个发布 job 都不带长期凭证。publish-npmrelease-packages.yml的流程permissions: contents: read id-token: write steps: - uses: actions/download-artifact... # 拉取 build 产出的 tarball - uses: actions/setup-node... with: node-version: 24.16.0 registry-url: https://registry.npmjs.org - name: Publish with npm trusted publishing run: | set -euo pipefail npm install --global npm11.5.1 npm publish ./dist/*.tgz --access publicpublish-pypi则使用pypa/gh-action-pypi-publishv1.14.0动作release-packages.yml。发布时 GitHub Actions 用id-token: write权限向注册表出示 OIDC 身份owner / repo / workflow / environment / tag注册表侧配置的可信发布者校验该身份后自动铸造一次性令牌——这正是文档所说only isolated publish jobs can mint registry tokens的实现方式构建机上的任何秘密都无法被用来发布因为 build job 根本拿不到 OIDC 权限。文档同时给出了可信发布者的精确身份字段注册表身份字段大小写敏感这是未来切到受信发布者发布时的核对清单npm确认 founder 拥有caveman-aiscope为每个包配置可信发布者ownerJuliusBrussee、repositorycaveman、workflowrelease-packages.yml、environmentnpm、actionnpm publish随后禁用长期发布令牌PyPI为项目caveman-sdk配置可信发布者ownerJuliusBrussee、repositorycaveman、workflowrelease-packages.yml、environmentpypiPython 导入名保持caveman_cloud不变。从源码结构看当前工作流中两个发布 job 已绑定npm/pypi两个 GitHub 环境含指向注册表页面的url字段环境侧的受保护规则限制部署到main上的签名发布标签、要求 founder 审批是文档剩余工作中需要补齐的配套设置。后续切换受信发布的四步计划文档列出了把未来发布迁移到 trusted publishers 的完整步骤值得作为发布工程参考用tools/publish-public.sh --apply镜像受审源码人工检查 diff 后再 commit 并 push 到公共仓库JuliusBrussee/caveman。脚本本身从不commit 或 push——推送动作必须由人完成。需要说明当前仓库中未见tools/publish-public.sh该脚本属于文档描述的外部发布仓库侧工具本仓库作为受审源码镜像不承载它在公共仓库创建受保护的 GitHub 环境npm与pypi限制部署到main上的签名发布标签并要求 founder 审批在 npm 侧确认caveman-aiscope 归属按上文身份字段配置各包的可信发布者并禁用长期发布令牌在 PyPI 侧为caveman-sdk配置可信发布者。发布后验证证据先行绝不覆盖文档对发布成功的定义非常严格——不要在注册表端点从干净环境解析回本项目之前切换任何公共安装命令。每个发布版本都要证明五件事确切版本号、包归属owner/repository、全新安装可用、import 可用、initializer 输出正确。对 Agent SDK具体做法是在生成项目中运行零 provider 调用的caveman-agent doctor再执行一次带凭证的陌生人stranger响应。这与 packages/agent/README.md 中的说明吻合npx caveman-agent doctor是零 provider 调用的就绪检查Engine/CLI/gateway 缺失只给 WARN 且退出码 0observe-only 仍可运行而 Node 版本、沙箱隔离、配置或锁漂移等问题才会使其失败。provider 费用明确属于发布工作流之外。失败处理路径同样是强制性的冒烟失败时对受影响的 npm 版本执行deprecate或yankPyPI 发布移除公共安装命令用新版本前向修复fix forward保留失败的构件与工作流日志作为证据绝不覆盖已发布的版本。相关文件索引docs/PACKAGE_RELEASES.md本文的主体文档记录注册表状态、剩余设置、标签规范与发布后证明要求.github/workflows/release-packages.yml发布工作流完整实现标签门禁、版本一致性、npm/Python 构建与冒烟、OIDC 发布packages/sdk/typescript/package.jsoncaveman-ai/sdk1.0.0元数据packages/sdk/python/pyproject.tomlcaveman-sdk1.0.0元数据导入名caveman_cloudpackages/agent/package.jsoncaveman-ai/agent0.1.0元数据packages/create-caveman-agent/package.jsoncaveman-ai/create-agent0.1.0元数据与bin定义packages/pi-extension/package.jsoncaveman-ai/pi0.1.0工作流已覆盖的第五个发布通道packages/agent/README.mdcaveman-agent doctor的就绪检查语义与发布后证明步骤对应【免费下载链接】caveman why use many token when few token do trick — Claude Code skill that cuts 65% of tokens by talking like caveman项目地址: https://gitcode.com/GitHub_Trending/caveman1/caveman创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表