首页
/ Serverless Framework 发布流程详解:双 package.json 版本提升、金丝雀通道与完整发布管线

Serverless Framework 发布流程详解:双 package.json 版本提升、金丝雀通道与完整发布管线

2026-09-07 16:57:14作者:咎岭娴Homer

本文基于仓库中的 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.jsonversions.json CloudFront 分发 ID E3OEL4OJF1G5FG
S3 金丝雀桶 install.serverless-dev.com,存放 canary-{git-sha}.tgzcanary.tgz 与金丝雀版 releases.json CloudFront 分发 ID E1USPSJN28WQ8U
MongoDB releases 集合(每个版本的安装记录)+ release-metadata 集合(supportedVersions 白名单) 通过 RELEASES_MONGO_URI 访问
npm 安装器包(@serverless scope),供 npm install 场景使用 可信发布(OIDC)

二、发布管线:触发条件与五个 Job

发布工作流定义在 release-framework.yml,触发条件为:

  • pushmain 分支,且变更路径命中 packages/sf-core/**packages/serverless/**packages/engine/**packages/mcp/**
  • 或手动触发 workflow_dispatch

工作流全局 permissions 声明了 id-token: writecontents: write(前者用于 npm 可信发布,后者用于推送 Git tag),默认 working-directory./packages/sf-core。管线共五个 Job:

Job 前置依赖 / 条件 职责
test-engine packages/engine 下运行引擎包单元测试(npm test
test-matrix 以矩阵策略在 ubuntu-latestlatest-arm-linuxgh-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 中会原样复现:

  1. Minify 开发模式 shimpackages/serverless/lib/plugins/aws/dev/shim.js 经 esbuild 打包压缩为 shim.min.js--bundle --platform=node --minify);
  2. 构建 MCP 入口:在 packages/serverless 下运行 npm run build:mcp:entry
  3. 构建框架主包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,步骤如下:

  1. 检出代码fetch-depth: 50,为后续 git diff 保留历史);
  2. 环境准备:Node.js 24.x + npm ci
  3. 构建:esbuild 压缩 dev shim → build:mcp:entrynpm run build
  4. AWS 配置:通过 aws-actions/configure-aws-credentials 假定角色 arn:aws:iam::377024778620:role/GithubActionsPublicServerlessRepoAccessRole(us-east-1),全程无长期凭证;
  5. 金丝雀发布:执行 bash prepareReleaseTars.sh,其行为详见 第五节
  6. CloudFront 失效:对金丝雀分发 aws cloudfront create-invalidation --distribution-id E1USPSJN28WQ8U --paths "/*",使新文件立即可用;
  7. 版本检测与打标
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)。步骤:

  1. 检出代码、Node.js 24.x、npm ci
  2. 三步构建(同 3.3 节);
  3. AWS 配置:假定生产环境部署角色 arn:aws:iam::802587217904:role/GithubActionsPublicServerlessRepoAccessRole(与金丝雀 Job 的测试角色不同账号,实现环境隔离);
  4. 发布 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 是两个独立标签,分别标识框架核心与安装器两个包的发布点;
  5. CloudFront 失效:对生产分发 E3OEL4OJF1G5FG 分别失效 /releases.json/versions.json 两个具体路径,确保版本信息即时可见(无需全量失效 /*,缩小缓存抖动范围)。

产出:新版本完整构建并部署至生产 S3,S3 与 MongoDB 元数据均已更新,CloudFront 失效保证用户立即可装。

3.5 第五步:npm 包发布(release-npm

依赖 release-stable 成功后执行,工作目录切换到 ./packages/sf-core-installer

  1. 检出代码,Node.js 24.x 并配置 registry-url: https://registry.npmjs.org、缓存 scope @serverless
  2. 准备:把仓库根目录 README.md 拷贝为安装器包的 README(cp ../../README.md ./README.md),再 npm ci
  3. 发布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 短 SHAgit rev-parse --short HEAD)作为版本标识,而非语义版本号;
  • release-canary Job 在处理每个通过集成测试的 main 分支提交时自动产出。

技术实现上:工作流为金丝雀 Job 注入 IS_CANARY=trueprepareReleaseTars.sh 在金丝雀模式下改用金丝雀 S3 桶、以 Git SHA 为版本号、上传 canary-{git-sha}.tgzcanary.tgz 两个工件,并跳过 MongoDB 元数据更新与 Git 打标(这两步只在稳定发布中执行)。

4.2 用户如何使用金丝雀版本

serverless.yml 中通过 frameworkVersion 字段选择:

使用最新金丝雀版本

frameworkVersion: canary

系统会下载 canary.tgz(每次金丝雀构建都会覆盖该固定 key,因此始终指向最新一次 main 构建)。

使用指定金丝雀版本

frameworkVersion: canary-{git-sha}

{git-sha} 替换为目标提交的短 SHA。

一旦指定了金丝雀版本,系统的行为是:

  1. 显示黄色通知,提示当前处于金丝雀发布通道;
  2. 从金丝雀域名 install.serverless-dev.com 下载框架;
  3. 后续所有操作均使用该金丝雀版本。

4.3 完整金丝雀发布流程

  1. 变更推送到 main,触发集成工作流;
  2. 集成测试在 Linux / Windows / ARM 多平台运行;
  3. 测试通过后,release-canaryIS_CANARY=true 执行;
  4. 金丝雀准备过程(源码级细节见 第五节):
    • git rev-parse --short HEAD 取得 Git SHA;
    • 从金丝雀 S3 桶下载当前 releases.json
    • releases.jsonversion 字段更新为 Git SHA;
    • 准备分发 tarball:将必要文件拷贝进 framework-dist 目录;
    • framework-dist/package.json 的版本改写为 Git SHA;
    • scripts/pack-framework-dist.sh 打成 tar 归档(package/ 前缀);
    • 上传至 S3,同时写为 canary-{git-sha}.tgzcanary.tgz
    • 上传更新后的 releases.json 至金丝雀桶;
    • 创建 CloudFront 失效,保证新文件立即可用。

仓库中的 packages/sf-core/scripts/releases.jsonreleases.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(S3 versions.json)更新元数据,打 sf-core-installer@{version} tag,失效生产 CloudFront,最后发布 npm 包;
  • 至此版本对所有用户可见,而不再局限于显式选择金丝雀通道的用户。

五、产物构建与元数据的源码级实现

本节把文档中的每个发布动作落到具体脚本,给出可复核的实现证据。

5.1 prepareReleaseTars.sh:金丝雀/稳定发布的总入口

脚本逻辑非常线性:

  1. packages/sf-core/package.json 读取 version;读取环境变量 IS_CANARY(默认 false);生产桶名为 install.serverless.com
  2. 金丝雀模式下改桶为 install.serverless-dev.com,并把版本替换为 git rev-parse --short HEAD
  3. 从 S3 拉取当前 releases.json,依次执行:
    • updateReleasesJson.cjs:再次按 IS_CANARY 决定版本来源(语义版本或 Git SHA),写入 releasesJson.version 后格式化回写;
    • prepareDistributionTarballs.js:把最终归档所需文件从 workspace 各包拷贝进 packages/framework-dist,并把 framework-dist/package.json 的版本改写为当前发布版本;
  4. 进入 framework-dist 执行 pack-framework-dist.sh 生成 serverlessinc-framework-alpha-${version}.tgz
  5. 打包后校验:解包 tarball 到临时目录,运行 verify-mcp-entry-packaging.js 断言预构建的 MCP Lambda 入口确实随包分发——脚本注释明确说明动机:"路径漂移会产出一个让所有 MCP 部署都报 MCP_ENTRY_BUNDLE_MISSING 的 CLI,而没有任何 PR CI 会跑这条发布工作流",因此该断言在上传前必须通过(脚本未用 set -e,故显式 || exit 1);
  6. 上传归档:
    • 金丝雀:s3://…/archives/canary-${version}.tgzs3://…/archives/canary.tgz(双 key:一个按 SHA 归档、一个滚动指向最新);
    • 稳定:s3://…/archives/serverless-${version}.tgz
  7. 上传更新后的 releases.json 至桶根路径;
  8. 仅稳定模式(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-wrapperscustom-resources/resources 整目录;
  • 构建产物 dev/shim.min.jslocal-lambda/runtime-wrappers/node.js
  • 预构建 MCP Lambda 入口 serverless/lib/plugins/aws/mcp/entry/dist/entry.mjs——目标路径不写死,而是查询运行时权威函数 entryPathFrom(...) 取得,避免"拷贝逻辑"与"运行时解析"漂移;
  • runners/cfn/aws/statuses.jsonbase.json(CfN 运行器元数据);
  • @aws-cdk/aws-service-spec/db.json.gzserverless 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 下载后按 workspace package-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 集合插入记录,字段为 versioninstallable: truereleaseDate(ISO 时间戳)、s3Keys3Bucketinstall.serverless.com)、downloadUrlhttps://install.serverless.com/archives/serverless-{version}.tgz);
  • 再向 release-metadata 集合(metadataVersion: '1' 的唯一文档)$pushsupportedVersions——该字段同时携带 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.exelinux-arm64macos-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 正式发布的可操作清单为:

  1. VERSIONING.md 判定 PATCH/MINOR/MAJOR,在同一个 PR 中同时提升 packages/sf-core/package.jsonpackages/sf-core-installer/package.json,PR 标题写 chore: release x.x.x
  2. 合并后等待 release-framework.yml 完成 test-engine 与三平台 test-matrix
  3. 确认 release-canary 打出 sf-core@x.x.x tag、金丝雀桶 install.serverless-dev.com 出现对应产物;
  4. 确认 release-stable 完成生产上传、MongoDB releases/release-metadata 更新、sf-core-installer@x.x.x tag 推送;
  5. 确认 release-npm 以 OIDC 可信发布方式推出安装器包;
  6. 验证:curl 安装脚本装出的 CLI 版本即为 x.x.x(用户侧不依赖 npm)。

需要特别记住的三条纪律:两个 package.json 必须同 PR 同版本(无任何交叉校验兜底);手动 workflow_dispatch 无法产出稳定发布github.event.before 为空导致版本检测失效);sf-core@sf-core-installer@ 是两个不同 Job 打的不同 tag,分别锚定框架核心与安装器。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388