Serverless Framework 发布流程详解:双 package.json 版本提升、金丝雀通道与完整发布管线
本文基于仓库中的 RELEASE_PROCESS.md,结合 release-framework.yml 工作流与 packages/sf-core 下的发布脚本源码,完整拆解 Serverless Framework 的发布工程:从版本提升与 PR 规范,到 CI/CD 测试矩阵、金丝雀(Canary)灰度发布、S3/CloudFront/MongoDB 三层产物与元数据分发,以及最终的 npm 可信发布。读完本文,你将能够独立执行一次完整的版本发布,理解每个发布脚本的输入输出,并掌握 frameworkVersion: canary 灰度通道的底层机制。
一、发布架构总览:npm 不是用户安装通道
Serverless Framework 的发布流程包含更新两个独立的 package.json 文件、通过 CI/CD 管线运行测试、并执行多个部署任务。整个过程由 GitHub Actions 自动化,包括:打 Git 标签、构建产物、将产物(tarball)发布到 S3、更新存储在 MongoDB 中的发布元数据、以及发布 npm 包。
一个关键设计决策是:npm 发布只是流程的一部分,而非用户的安装依赖。用户安装 CLI 不经过 npm:
curl -o- -L https://install.serverless.com | bash
这条命令拉取的产物直接托管在 S3(域名 install.serverless.com)并经由 CloudFront 分发,因此即使 npm 发布环节出现延迟,新版本对用户也是即时可用的。从发布脚本结构看,整套分发由四类载体组成:
| 载体 | 内容 | 位置 |
|---|---|---|
| S3 生产桶 | install.serverless.com,存放 serverless-{version}.tgz 归档与 releases.json、versions.json |
CloudFront 分发 ID E3OEL4OJF1G5FG |
| S3 金丝雀桶 | install.serverless-dev.com,存放 canary-{git-sha}.tgz、canary.tgz 与金丝雀版 releases.json |
CloudFront 分发 ID E1USPSJN28WQ8U |
| MongoDB | releases 集合(每个版本的安装记录)+ release-metadata 集合(supportedVersions 白名单) |
通过 RELEASES_MONGO_URI 访问 |
| npm | 安装器包(@serverless scope),供 npm install 场景使用 |
可信发布(OIDC) |
二、发布管线:触发条件与五个 Job
发布工作流定义在 release-framework.yml,触发条件为:
push到main分支,且变更路径命中packages/sf-core/**、packages/serverless/**、packages/engine/**或packages/mcp/**;- 或手动触发
workflow_dispatch。
工作流全局 permissions 声明了 id-token: write 与 contents: write(前者用于 npm 可信发布,后者用于推送 Git tag),默认 working-directory 为 ./packages/sf-core。管线共五个 Job:
| Job | 前置依赖 / 条件 | 职责 |
|---|---|---|
test-engine |
无 | 在 packages/engine 下运行引擎包单元测试(npm test) |
test-matrix |
无 | 以矩阵策略在 ubuntu-latest、latest-arm-linux、gh-windows-latest 三类平台运行集成测试(fail-fast: false,单平台失败不中断其他平台) |
release-canary |
needs: [test-matrix, test-engine] |
构建并发布金丝雀版本(Git-SHA 版本号);通过 diff packages/sf-core/package.json 检测版本提升,若发现新版本则以 sf-core@{version} 打 tag 并推送 |
release-stable |
needs: release-canary,且仅当输出 new_version 非空时执行 |
构建并上传生产 tarball、更新 MongoDB 与 S3 元数据、以 sf-core-installer@{version} 打 tag |
release-npm |
needs: release-stable |
在稳定版完成后将安装器包发布到 npm |
一个值得注意的工程细节:release-canary 的版本检测依赖 github.event.before(push 事件的前一提交),而在 workflow_dispatch 手动触发时该值为空,因此手动运行永远无法产生 Git tag,也无法触发稳定版发布——手动触发只用于验证构建与金丝雀上传本身。
所有 Job 统一使用 Node.js 24.x(actions/setup-node + check-latest 与 npm 缓存),通过 corepack enable npm 后在仓库根目录执行 npm ci 安装 workspace 依赖。
测试阶段的构建产物
test-matrix 在跑集成测试前会先执行三步构建,这些步骤在后续发布 Job 中会原样复现:
- Minify 开发模式 shim:
packages/serverless/lib/plugins/aws/dev/shim.js经 esbuild 打包压缩为shim.min.js(--bundle --platform=node --minify); - 构建 MCP 入口:在
packages/serverless下运行npm run build:mcp:entry; - 构建框架主包:
npm run build。
随后依次运行 npm run test:unit(sf-core)、packages/serverless 下的 npm run test:unit,最后以 npm run test 跑集成测试。测试还假定 serverless@3 已全局安装(用于兼容性对比),并通过 TEST_STAGE: mr-${{ github.actor }} 等环境变量区分测试阶段。
三、发布工作流五步详解
3.1 第一步:版本提升与 PR 创建
需要更新的文件(两个都要改,同一 PR 提交):
操作步骤:同时更新两个文件中的 version 字段,然后创建标题为 chore: release x.x.x 的 PR(x.x.x 为新版本号)。版本号的选择规则遵循 VERSIONING.md:严格语义化版本;包含任意 feat: 提交的发布取 MINOR,仅含 fix:/chore: 的发布取 PATCH,任何不向后兼容变更取 MAJOR。
原文档警告(务必理解):管线的版本检测只读
packages/sf-core/package.json,而npm publish发布的是packages/sf-core-installer/package.json中记载的版本,两者之间没有任何交叉校验。因此:
- 只提升 installer 文件 → 管线检测不到新版本,发布完全不会发生;
- 只提升 sf-core 文件 → tag 与 S3/MongoDB 元数据都按新版本走,但 npm 发布会推出一个过期的 installer 版本。
两个文件必须在同一个 PR 中同时提升。
3.2 第二节:CI/CD 管线执行
PR 合并进 main 后触发 release-framework.yml。管线首先执行 test-engine 验证引擎包功能,再执行 test-matrix 在 Linux(x64)、Linux(ARM)、Windows 三类平台上执行集成测试,确保框架在所有受支持环境上行为一致。所有测试通过后,条件式发布 Job 才会继续。
3.3 第三步:金丝雀发布与版本打标(release-canary)
该 Job 设置环境变量 IS_CANARY: true,步骤如下:
- 检出代码(
fetch-depth: 50,为后续git diff保留历史); - 环境准备:Node.js 24.x +
npm ci; - 构建:esbuild 压缩 dev shim →
build:mcp:entry→npm run build; - AWS 配置:通过
aws-actions/configure-aws-credentials假定角色arn:aws:iam::377024778620:role/GithubActionsPublicServerlessRepoAccessRole(us-east-1),全程无长期凭证; - 金丝雀发布:执行
bash prepareReleaseTars.sh,其行为详见 第五节; - CloudFront 失效:对金丝雀分发
aws cloudfront create-invalidation --distribution-id E1USPSJN28WQ8U --paths "/*",使新文件立即可用; - 版本检测与打标:
NEW_VERSION=`git diff -U0 ${{ github.event.before }} package.json | grep '"version": "' | tail -n 1 | grep -oE "[0-9]+\.[0-9]+\.[0-9]+"` || :
if [ -n "$NEW_VERSION" ] && [ $NEW_VERSION != "0.0.0" ];
then
git tag sf-core@$NEW_VERSION
git push --tags
echo "new_version=${NEW_VERSION}" >> $GITHUB_OUTPUT
fi
即:对比当前提交与 push 前提交在 package.json 上的差异,若发现新的语义版本号(且不是 0.0.0),则以 sf-core@x.x.x 打 tag 推送,并把版本写入 Job 输出 new_version 供下游使用。该步骤带 continue-on-error: true——未检测到版本提升(日常功能提交)时静默跳过。
产出:一个供早期验证的金丝雀版本;若检测到新版本,仓库被打上 sf-core@{version} 标签,流程继续。
3.4 第四步:生产发布(release-stable)
条件:仅当 release-canary 输出了 new_version 时执行(if: needs.release-canary.outputs.new_version)。步骤:
- 检出代码、Node.js 24.x、
npm ci; - 三步构建(同 3.3 节);
- AWS 配置:假定生产环境部署角色
arn:aws:iam::802587217904:role/GithubActionsPublicServerlessRepoAccessRole(与金丝雀 Job 的测试角色不同账号,实现环境隔离); - 发布 tarball:运行
bash prepareReleaseTars.sh(不设置IS_CANARY),并注入RELEASES_MONGO_URI机密。该脚本完整执行:- 使用
package.json中的版本(而非 Git SHA); - 更新生产
releases.json(由 updateReleasesJson.cjs 完成); - 运行 prepareDistributionTarballs.js 拷贝最终归档所需的附加文件——原文档特别强调这一步至关重要:如果源码中文件位置发生变化,发布会因缺少文件而损坏;
- 通过 pack-framework-dist.sh 打包(直接
tar归档,package/路径前缀,不使用npm pack); - 上传 tarball 到生产 S3 桶
install.serverless.com; - 通过两个脚本更新发布元数据:
publish:release(MongoDB,使用RELEASES_MONGO_URI机密):向releases集合插入新记录,并把版本追加进release-metadata集合的supportedVersions;publish:release-metadata(S3):把版本追加进生产桶中的versions.json;
- 以
sf-core-installer@{version}打 tag 并推送。注意:该 tag 是在prepareReleaseTars.sh脚本内部创建的,而不是工作流文件中的显式步骤——它与release-canary创建的sf-core@{version}tag 是两个独立标签,分别标识框架核心与安装器两个包的发布点;
- 使用
- CloudFront 失效:对生产分发
E3OEL4OJF1G5FG分别失效/releases.json与/versions.json两个具体路径,确保版本信息即时可见(无需全量失效/*,缩小缓存抖动范围)。
产出:新版本完整构建并部署至生产 S3,S3 与 MongoDB 元数据均已更新,CloudFront 失效保证用户立即可装。
3.5 第五步:npm 包发布(release-npm)
依赖 release-stable 成功后执行,工作目录切换到 ./packages/sf-core-installer:
- 检出代码,Node.js 24.x 并配置
registry-url: https://registry.npmjs.org、缓存 scope@serverless; - 准备:把仓库根目录
README.md拷贝为安装器包的 README(cp ../../README.md ./README.md),再npm ci; - 发布:
npm publish。认证方式为 npm 可信发布(Trusted Publishing):依托工作流顶层的id-token: write权限完成 OIDC 身份断言,全程不使用 npm token 机密。
产出:安装器包进入 npm registry。再次强调:由于用户可通过 curl 安装脚本直接从 S3/CloudFront 获取最新版本,npm 发布并不是用户获取新版本的必要条件。
四、金丝雀(Canary)发布通道
金丝雀发布是该部署策略的核心环节:在全面推广前,让小范围用户先行验证新版本,尽早发现问题、保障多数用户的稳定性。
4.1 实现方式
- 金丝雀与生产托管在不同域名:
install.serverless-dev.com(金丝雀)对install.serverless.com(生产),对应两个不同的 S3 桶与 CloudFront 分发; - 金丝雀版本以 Git 短 SHA(
git rev-parse --short HEAD)作为版本标识,而非语义版本号; - 由
release-canaryJob 在处理每个通过集成测试的main分支提交时自动产出。
技术实现上:工作流为金丝雀 Job 注入 IS_CANARY=true;prepareReleaseTars.sh 在金丝雀模式下改用金丝雀 S3 桶、以 Git SHA 为版本号、上传 canary-{git-sha}.tgz 与 canary.tgz 两个工件,并跳过 MongoDB 元数据更新与 Git 打标(这两步只在稳定发布中执行)。
4.2 用户如何使用金丝雀版本
在 serverless.yml 中通过 frameworkVersion 字段选择:
使用最新金丝雀版本:
frameworkVersion: canary
系统会下载 canary.tgz(每次金丝雀构建都会覆盖该固定 key,因此始终指向最新一次 main 构建)。
使用指定金丝雀版本:
frameworkVersion: canary-{git-sha}
将 {git-sha} 替换为目标提交的短 SHA。
一旦指定了金丝雀版本,系统的行为是:
- 显示黄色通知,提示当前处于金丝雀发布通道;
- 从金丝雀域名
install.serverless-dev.com下载框架; - 后续所有操作均使用该金丝雀版本。
4.3 完整金丝雀发布流程
- 变更推送到
main,触发集成工作流; - 集成测试在 Linux / Windows / ARM 多平台运行;
- 测试通过后,
release-canary以IS_CANARY=true执行; - 金丝雀准备过程(源码级细节见 第五节):
- 用
git rev-parse --short HEAD取得 Git SHA; - 从金丝雀 S3 桶下载当前
releases.json; - 将
releases.json的version字段更新为 Git SHA; - 准备分发 tarball:将必要文件拷贝进
framework-dist目录; - 把
framework-dist/package.json的版本改写为 Git SHA; - 经
scripts/pack-framework-dist.sh打成tar归档(package/前缀); - 上传至 S3,同时写为
canary-{git-sha}.tgz与canary.tgz; - 上传更新后的
releases.json至金丝雀桶; - 创建 CloudFront 失效,保证新文件立即可用。
- 用
仓库中的 packages/sf-core/scripts/releases.json 是 releases.json 的种子文件,除 version 外还维护 supportedOperatingSystems 清单(Windows x64 .exe、Linux x64/arm64、macOS x64/arm64 共五个安装器平台的 fileSuffix),供安装脚本选择对应平台的二进制启动器。
4.4 从金丝雀晋级为正式发布
- 当某个金丝雀版本经充分验证后,通过一个包含版本提升的 PR 合并到
main,即完成"晋级"; - 工作流检测到
packages/sf-core/package.json的版本变化,执行release-stable; - 生产发布使用
package.json中的语义版本(而非 Git SHA),上传生产 S3,经publish:release(MongoDB)与publish:release-metadata(S3versions.json)更新元数据,打sf-core-installer@{version}tag,失效生产 CloudFront,最后发布 npm 包; - 至此版本对所有用户可见,而不再局限于显式选择金丝雀通道的用户。
五、产物构建与元数据的源码级实现
本节把文档中的每个发布动作落到具体脚本,给出可复核的实现证据。
5.1 prepareReleaseTars.sh:金丝雀/稳定发布的总入口
脚本逻辑非常线性:
- 从
packages/sf-core/package.json读取version;读取环境变量IS_CANARY(默认false);生产桶名为install.serverless.com; - 金丝雀模式下改桶为
install.serverless-dev.com,并把版本替换为git rev-parse --short HEAD; - 从 S3 拉取当前
releases.json,依次执行:- updateReleasesJson.cjs:再次按
IS_CANARY决定版本来源(语义版本或 Git SHA),写入releasesJson.version后格式化回写; - prepareDistributionTarballs.js:把最终归档所需文件从 workspace 各包拷贝进
packages/framework-dist,并把framework-dist/package.json的版本改写为当前发布版本;
- updateReleasesJson.cjs:再次按
- 进入
framework-dist执行 pack-framework-dist.sh 生成serverlessinc-framework-alpha-${version}.tgz; - 打包后校验:解包 tarball 到临时目录,运行
verify-mcp-entry-packaging.js断言预构建的 MCP Lambda 入口确实随包分发——脚本注释明确说明动机:"路径漂移会产出一个让所有 MCP 部署都报MCP_ENTRY_BUNDLE_MISSING的 CLI,而没有任何 PR CI 会跑这条发布工作流",因此该断言在上传前必须通过(脚本未用set -e,故显式|| exit 1); - 上传归档:
- 金丝雀:
s3://…/archives/canary-${version}.tgz与s3://…/archives/canary.tgz(双 key:一个按 SHA 归档、一个滚动指向最新); - 稳定:
s3://…/archives/serverless-${version}.tgz;
- 金丝雀:
- 上传更新后的
releases.json至桶根路径; - 仅稳定模式(
IS_CANARY=false)下执行:npm run -w=release-scripts publish:release ${version}(MongoDB);npm run -w=release-scripts publish:release-metadata ${version}(S3);git tag sf-core-installer@${version}并推送。
5.2 prepareDistributionTarballs.js:文件清单就是发布契约
该脚本集中定义了"最终归档里必须有哪些文件",典型条目包括:
serverless/lib/plugins/aws/package/lib/*.json(AWS 包定义 JSON);invoke-local/runtime-wrappers与custom-resources/resources整目录;- 构建产物
dev/shim.min.js、local-lambda/runtime-wrappers/node.js; - 预构建 MCP Lambda 入口
serverless/lib/plugins/aws/mcp/entry/dist/entry.mjs——目标路径不写死,而是查询运行时权威函数entryPathFrom(...)取得,避免"拷贝逻辑"与"运行时解析"漂移; runners/cfn/aws/statuses.json与base.json(CfN 运行器元数据);@aws-cdk/aws-service-spec/db.json.gz(serverless diff依赖的 AWS 资源规格库,须位于包根、与package.json同级);- Python 插件的
unzip_requirements.py、供 MCP docs 工具使用的docs/目录; - ajv/ajv-formats/fast-deep-equal 的运行时文件(供运行时生成的独立校验器
require); - esbuild 完整包(去除宿主
bin/)+ 五个平台(darwin-arm64/x64、linux-arm64/x64、win32-x64)的二进制,且每个 tarball 下载后按 workspacepackage-lock.json记录的 SRI integrity 哈希校验,通过后才落盘——与npm install相同的信任锚点。
正因为发布归档依赖这些精确的文件位置,原文档才强调:任何文件移动都会直接导致发布损坏。
5.3 pack-framework-dist.sh:刻意绕开 npm pack
脚本注释说明其被发布流程与本地 test:build 共用。要点:
- 清理
dist/test-*.js等不应进入发布归档的临时测试文件; - 归档成员为
package.json dist lib docs db.json.gz,统一加上package/路径前缀,并归一化属主为 0/0(避免把构建者身份泄漏进归档); - 显式兼容 GNU tar(
--transform)与 BSD tar/macOS(-s ',^,package/,')两套语法; - 文档特别指出:这里不使用
npm pack,而是直接tar归档——因为框架分发不是标准 npm 包结构,npm pack的文件筛选语义不适用。
5.4 元数据双写:MongoDB 与 S3 的分工
publishReleaseToMongo.js(由 publish:release 调用):
- 强制要求
RELEASES_MONGO_URI环境变量,缺失即抛错; - 向
releases集合插入记录,字段为version、installable: true、releaseDate(ISO 时间戳)、s3Key、s3Bucket(install.serverless.com)、downloadUrl(https://install.serverless.com/archives/serverless-{version}.tgz); - 再向
release-metadata集合(metadataVersion: '1'的唯一文档)$push到supportedVersions——该字段同时携带blockedVersions能力,从数据结构上支持"版本黑名单/白名单"治理。
publishReleaseMetadataToS3.js(由 publish:release-metadata 调用):
- 从生产桶读取根路径
versions.json,向supportedVersions数组追加新版本号后写回。
MongoDB 面向内部服务(校验、Dashboard 等),S3 上的 versions.json 则贴近用户安装链路,二者配合 CloudFront 对 /versions.json、/releases.json 的精确失效,构成"版本信息秒级可见"的保证。
六、Go 二进制安装器的独立发布管线
文档中单独指出:基于 Go 的二进制安装器(curl 安装脚本与平台启动器二进制)拥有自己独立的发布管线 release-binary-installer.yml,且仅通过 workflow_dispatch 手动触发。该管线使用 Azure Artifact Signing 对 Windows 二进制做 Authenticode 签名,采用 GitHub OIDC 身份断言换取签名权限,不存储任何长期凭证。所需 secrets/variables 与产物验证方法见 binary-installer/README.md 的 "Code Signing" 一节。
从仓库结构看,packages/sf-core/scripts/releases.json 中的 supportedOperatingSystems.fileSuffix(如 win-x64.exe、linux-arm64、macos-arm64)正是安装脚本按平台选择启动器时消费的数据,与 releases.json 的版本号共同驱动 curl | bash 的完整安装行为。
七、版本号如何确定:与 VERSIONING.md 的衔接
发布 PR 中的版本选择不是随意行为,VERSIONING.md 给出了明确判据:
- PATCH:仅包含向后兼容的缺陷修复;"缺陷"指把功能恢复到上一个版本的行为;对已长期存在且用户已依赖的非预期行为做修改,不算缺陷修复而算破坏性变更——存疑时一律按破坏性变更处理;
- MINOR:以向后兼容方式新增功能;新增 CLI 选项、暴露新属性等都算新功能;
- MAJOR:任何不向后兼容变更,包括改变现有基础设施行为的 CloudFormation 输出、阻止在既有栈上部署的变更、CLI 命令/选项的移除或修改、CLI 结构化输出(如
--json)的结构变更、this.serverless对象上成员被移除等; - 实践规则:由于 PR 标题遵循 conventional commits 且以 squash-merge 方式合并,含任意
feat:提交的发布为 MINOR,仅含fix:/chore:的为 PATCH; - Node.js 支持策略:跟随主流云厂商的运行时版本,云厂商宣布退役旧运行时后随之移除支持。
八、小结:一次完整发布的检查清单
结合全文,执行一次 Serverless Framework 正式发布的可操作清单为:
- 按 VERSIONING.md 判定 PATCH/MINOR/MAJOR,在同一个 PR 中同时提升 packages/sf-core/package.json 与 packages/sf-core-installer/package.json,PR 标题写
chore: release x.x.x; - 合并后等待 release-framework.yml 完成
test-engine与三平台test-matrix; - 确认
release-canary打出sf-core@x.x.xtag、金丝雀桶install.serverless-dev.com出现对应产物; - 确认
release-stable完成生产上传、MongoDBreleases/release-metadata更新、sf-core-installer@x.x.xtag 推送; - 确认
release-npm以 OIDC 可信发布方式推出安装器包; - 验证:
curl安装脚本装出的 CLI 版本即为x.x.x(用户侧不依赖 npm)。
需要特别记住的三条纪律:两个 package.json 必须同 PR 同版本(无任何交叉校验兜底);手动 workflow_dispatch 无法产出稳定发布(github.event.before 为空导致版本检测失效);sf-core@ 与 sf-core-installer@ 是两个不同 Job 打的不同 tag,分别锚定框架核心与安装器。
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 StartedRust0627
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