Serverless Framework CI/CD 自定义脚本指南:在依赖安装、测试与部署前后注入自定义逻辑
当 Serverless Framework 配置了 CI/CD 时,流水线会对仓库执行三类核心操作:执行 npm install 安装依赖、执行 npm test 运行测试、执行 sls deploy 部署服务。本篇指南讲解如何在每个步骤的前后注入自定义脚本,包括利用 package.json 内置的 npm 生命周期钩子(preinstall、postinstall、pretest、posttest),以及利用 serverless-plugin-scripts 插件在 deploy 生命周期的指定节点(部署前、部署收尾)执行自定义命令,并结开源仓库源码说明这些钩子点究竟在什么时机被触发。
CI/CD 管道中的三个标准操作
配置了 CI/CD 后,Serverless Framework 在每次流水线运行时会依次执行:
- 安装依赖:
npm install; - 运行测试:若存在测试则执行
npm test; - 部署服务:
sls deploy。
这套流水线的背景可以在 CI/CD 概览文档中看到:项目需提交到 Git 仓库、部署到 AWS、使用 Node 或 Python 运行时,连接仓库后服务即可从主干分支自动测试和部署,详见 CI/CD 概览。
如果标准流水线不能满足定制需求(例如在装依赖前做构建准备、在测试后上传覆盖率报告、在部署前后执行数据库迁移或通知脚本),可以按下面三种方式为每个步骤的前后挂上自定义脚本。
一、在 npm install 前后执行脚本:preinstall / postinstall
npm 对 scripts 字段中的生命周期钩子是自动触发的:preinstall 会在 npm install 之前执行,postinstall 在其之后执行。只需在项目的 package.json 中声明对应脚本即可,无需任何额外插件。
部署安装前(Before npm install)
{
"name": "demo-serverless",
"version": "1.0.0",
"scripts": {
"preinstall": "<your script>"
}
}
安装完成后(After npm install)
{
"name": "demo-serverless",
"version": "1.0.0",
"scripts": {
"postinstall": "<your script>"
}
}
将 <your script> 替换为实际要执行的命令,例如 node scripts/generate-config.js。这类钩子由 npm 本身在每次 npm install 时触发,CI/CD 环境只是天然受益者——本地执行 npm install 时同样生效,因此脚本应保持幂等、避免产生副作用。
二、在 npm test 前后执行脚本:pretest / posttest
同理,npm 的 pretest 和 posttest 钩子会分别在 npm test 执行前后自动运行:
测试前(Before npm test)
{
"name": "demo-serverless",
"version": "1.0.0",
"scripts": {
"pretest": "<your script>"
}
}
测试后(After npm test)
{
"name": "demo-serverless",
"version": "1.0.0",
"scripts": {
"posttest": "<your script>"
}
}
适合放在 pretest 的有:生成测试桩数据、启动本地模拟环境;适合放在 posttest 的有:收集测试覆盖率、生成测试报告文件等。
三、在 serverless deploy 前后执行脚本:serverless-plugin-scripts
sls deploy 的执行属于 Serverless Framework 自身的命令生命周期,而不是 npm 的生命周期,因此 package.json 里的钩子对它无效。要在部署前后插入自定义脚本,官方文档推荐的方式是安装 serverless-plugin-scripts 插件,并在 serverless.yml 中为指定的生命周期事件声明钩子。
部署开始前(Before serverless deploy)
在 serverless.yml 中添加:
plugins:
- serverless-plugin-scripts
custom:
scripts:
hooks:
'before:deploy:deploy': <your script>
部署完成后(After serverless deploy)
plugins:
- serverless-plugin-scripts
custom:
scripts:
hooks:
'deploy:finalize': <your script>
从源码看这两个钩子点的真实时机
上面两个钩子名并不是随意取的,它们对应 sls deploy 内部真实存在的生命周期事件。仓库中的源码可以印证其触发时序。
1. before:deploy:deploy:真正部署动作之前的入口
通用的 deploy 命令插件在 deploy.js 中注册了 before:deploy:deploy 钩子:它会校验 provider 是否存在,并在未显式指定打包路径时调用 pluginManager.spawn('package') 先完成打包。随后 AWS 提供方插件在 aws/deploy/index.js 的同名钩子里执行配置校验(aws:common:validate)与工件处理。也就是说,挂在这个事件上的自定义脚本,运行在“配置校验、打包”这一系列准备动作开始的位置,此时部署还尚未真正触碰云资源。
2. deploy:finalize:部署收尾阶段的钩子
aws/deploy/index.js 中定义了两段内部生命周期:
- 外层
deploy阶段,其事件为deploy:deploy与deploy:finalize(见 第 169-174 行),分别spawn到aws:deploy:deploy和aws:deploy:finalize; - 其中
aws:deploy:deploy的内部事件依次是createStack、checkForChanges、uploadArtifacts、validateTemplate、updateStack(见 第 76-83 行),即从拉取 CloudFormation 堆栈、检查变更、上传工件到模板校验、更新堆栈的完整流程; aws:deploy:finalize只有一个cleanup事件(第 84-86 行),对应 aws:deploy:finalize:cleanup 钩子 里的清理 S3 桶、清理临时目录等收尾动作。
因此 deploy:finalize 上的自定义脚本会在堆栈更新完成后的收尾清理阶段执行——此时云资源侧的部署已经完成,适合执行“部署成功”类通知等动作。
3. 前缀语义:before: / 无 after: 与事件名的组合规则
钩子调度逻辑位于 plugin-manager.js。其中 第 736 行附近 通过 event.replace(/^(?:after:|before:)/, '') 剥离前缀来解析基础事件名;第 889 行 收集 before:${lifecycleEventName} 的钩子列表;第 951 行 在事件主体执行前先 runHooks(\before:${lifecycleEventName}`)。这解释了为什么配置里写 'before:deploy:deploy'能精确命中“部署事件主体之前”这一时机,以及事件名本身(如deploy:finalize)与 before:/after:` 前缀是可以自由组合的。
更多可用的生命周期钩子
除上述六个最常见的挂点外,两侧还有更丰富的扩展空间:
- npm 侧:npm 本身提供了远多于四个的生命周期钩子(如
prepare、prepack、postpublish等),完整列表以 npm 官方文档为准。由于这些钩子在 CI/CD 环境执行npm install/npm test时由 npm 自动触发,任何与 install/test 流程相关的钩子都可以间接被流水线利用。 - Serverless Framework 侧:由于
deploy内部拆分为createStack、checkForChanges、uploadArtifacts、validateTemplate、updateStack、cleanup等多个生命周期事件(源码见 aws/deploy/index.js),serverless-plugin-scripts的钩子配置可以精确挂在其中任意事件的before:/after:时点,而不限于before:deploy:deploy和deploy:finalize两个,更多可用钩子请参考该插件的官方文档。
选型建议小结
| 需求 | 推荐挂点 | 载体 |
|---|---|---|
| 安装依赖前后 | preinstall / postinstall |
package.json scripts |
| 运行测试前后 | pretest / posttest |
package.json scripts |
| 部署开始前的准备动作 | before:deploy:deploy |
serverless.yml + serverless-plugin-scripts |
| 部署完成后的收尾动作 | deploy:finalize |
serverless.yml + serverless-plugin-scripts |
npm 类钩子实现最简单,且不依赖任何插件,但只在 npm 触发的命令中生效、且在本地与 CI 中行为一致;serverless-plugin-scripts 类钩子则能覆盖 sls deploy 生命周期的任意节点,适合与云资源操作强相关的场景。两者组合使用即可覆盖整条 CI/CD 流水线的定制需求。
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