CI/CD 集成
提示
本章介绍的是 Nest Devtools 与 Nest 框架的集成方式。如果你要访问 Devtools 应用本身,请前往 Devtools 网站。
CI/CD 集成功能仅对 Enterprise 方案用户开放。
你可以先看下面这个视频,了解 CI/CD 集成为什么有用、以及它是如何工作的:
发布图
首先,修改应用的启动文件(main.ts),让它使用 GraphPublisher 类(由 @nestjs/devtools-integration 导出,更多背景可参考上一章):
async function bootstrap() {
const shouldPublishGraph = process.env.PUBLISH_GRAPH === 'true';
const app = await NestFactory.create(AppModule, {
snapshot: true,
preview: shouldPublishGraph,
});
if (shouldPublishGraph) {
await app.init();
const publishOptions = { ... }; // 注意:这里的配置会随 CI/CD 平台不同而不同
const graphPublisher = new GraphPublisher(app);
await graphPublisher.publish(publishOptions);
await app.close();
} else {
await app.listen(process.env.PORT ?? 3000);
}
}可以看到,这里我们通过 GraphPublisher 把序列化图发布到中心化注册表中。
PUBLISH_GRAPH 是一个自定义环境变量,用于控制当前是否要发布图:在 CI/CD 工作流中启用,在普通应用启动时关闭。
这里还把 preview 设置成了 true。开启后,应用会以 preview 模式启动,这意味着应用中所有 controller、enhancer 和 provider 的构造函数及生命周期钩子都不会执行。注意,这个设置不是必须的,但这样会让 CI/CD 中的流程更简单,因为你通常就不必真的去连接数据库之类的外部依赖了。
publishOptions 对象的具体内容会因你使用的 CI/CD 平台不同而不同。后面的章节里会给出一些常见平台的示例。
图成功发布后,你会在工作流日志里看到类似下面的输出:

每次图被成功发布后,对应项目页面中都会出现一条新的记录:

报告
只有当中心化注册表中已经存在对应快照时,Devtools 才会为某次构建生成报告。
例如,如果你发起了一个面向 master 分支的 PR,而 master 的图此前已经发布过,那么系统就能自动比较差异并生成报告;否则不会生成报告。
查看报告时,请进入对应项目页面。

这对于发现那些在代码审查中很容易被忽略的变更尤其有帮助。
例如,某人修改了一个深层嵌套 provider 的作用域。这个变化在 review 时可能并不显眼,但借助 Devtools,我们可以立刻发现并确认它是否是有意为之。
又比如,如果某个端点意外移除了 guard,这在报告里会被标记为受影响项。如果该路由恰好又没有相关集成测试或 e2e 测试,那么我们很可能直到很晚才意识到它已经失去保护。
同理,如果你维护的是一个大型代码库,而某次变更把一个模块改成了全局模块,你就能在报告中看到图里新增了多少边。在大多数情况下,这往往是一个危险信号,说明我们可能做错了事情。
构建预览
对于每一个已发布的图,我们都可以通过点击 Preview 按钮回到过去,查看它之前的样子。
如果报告已经生成,那么图中的差异会被高亮显示:
- 绿色节点表示新增元素
- 浅白色节点表示更新元素
- 红色节点表示删除元素
如下图所示:

这种“回到过去”的能力,使你可以通过对比当前图与历史图来排查问题和追踪变化。具体粒度取决于你的流程配置:如果你愿意,甚至可以让每个 PR,乃至每次 commit,都在注册表中保存一份快照。
可以把 Devtools 理解为一种“理解 Nest 应用图结构、并能将其可视化的 Git”。
集成:GitHub Actions
首先,在项目的 .github/workflows 目录下新建一个 GitHub workflow 文件,例如 publish-graph.yml,并写入如下定义:
name: Devtools
on:
push:
branches:
- master
pull_request:
branches:
- '*'
jobs:
publish:
if: github.actor!= 'dependabot[bot]'
name: Publish graph
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '16'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Setup Environment (PR)
if: ${{ github.event_name == 'pull_request' }}
shell: bash
run: |
echo "COMMIT_SHA=${{ github.event.pull_request.head.sha }}" >>${GITHUB_ENV}
- name: Setup Environment (Push)
if: ${{ github.event_name == 'push' }}
shell: bash
run: |
echo "COMMIT_SHA=${GITHUB_SHA}" >> ${GITHUB_ENV}
- name: Publish
run: PUBLISH_GRAPH=true npm run start
env:
DEVTOOLS_API_KEY: CHANGE_THIS_TO_YOUR_API_KEY
REPOSITORY_NAME: ${{ github.event.repository.name }}
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
TARGET_SHA: ${{ github.event.pull_request.base.sha }}理想情况下,DEVTOOLS_API_KEY 应该从 GitHub Secrets 中读取,相关说明见官方文档。
这个 workflow 会在以下两种情况下运行:
- 针对
master分支的每一个 pull request - 直接推送到
master分支时
你当然可以根据自己项目的需求调整这些触发条件。关键是:必须为 GraphPublisher 提供它发布图所需的环境变量。
不过,在正式使用这个 workflow 之前,还需要先替换其中一个变量:DEVTOOLS_API_KEY。你可以在这个页面为项目生成专用 API key。
最后,再回到 main.ts,把前面留空的 publishOptions 对象补齐:
const publishOptions = {
apiKey: process.env.DEVTOOLS_API_KEY,
repository: process.env.REPOSITORY_NAME,
owner: process.env.GITHUB_REPOSITORY_OWNER,
sha: process.env.COMMIT_SHA,
target: process.env.TARGET_SHA,
trigger: process.env.GITHUB_BASE_REF ? 'pull' : 'push',
branch: process.env.BRANCH_NAME,
};为了获得更好的开发体验,建议为你的项目接入 GitHub application。只需点击 “Integrate GitHub app” 按钮即可。注意:这一步不是必须的。

启用该集成后,你就可以直接在 PR 中看到预览 / 报告生成状态:

集成:GitLab Pipelines
首先,在项目根目录下创建 GitLab CI 配置文件,例如 .gitlab-ci.yml,并写入如下内容:
image: node:16
stages:
- build
cache:
key:
files:
- package-lock.json
paths:
- node_modules/
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
when: always
- if: $CI_COMMIT_BRANCH == "master" && $CI_PIPELINE_SOURCE == "push"
when: always
- when: never
install_dependencies:
stage: build
script:
- npm ci
publish_graph:
stage: build
needs:
- install_dependencies
script: npm run start
variables:
PUBLISH_GRAPH: 'true'
DEVTOOLS_API_KEY: 'CHANGE_THIS_TO_YOUR_API_KEY'提示
理想情况下,DEVTOOLS_API_KEY 也应从 secrets 中读取。
这个 pipeline 会在以下情况下运行:
- 针对
master的每一个 merge request - 直接推送到
master分支时
同样,你可以按自己项目的需求进行调整。这里的关键仍然是:为 GraphPublisher 提供它发布图所需的环境变量。
在这套配置中,也要记得把 DEVTOOLS_API_KEY 替换成你自己的项目 API key。
随后,回到 main.ts,把 publishOptions 对象配置成适配 GitLab 环境变量的形式:
const publishOptions = {
apiKey: process.env.DEVTOOLS_API_KEY,
repository: process.env.CI_PROJECT_NAME,
owner: process.env.CI_PROJECT_ROOT_NAMESPACE,
sha: process.env.CI_COMMIT_SHA,
target: process.env.CI_MERGE_REQUEST_DIFF_BASE_SHA,
trigger: process.env.CI_MERGE_REQUEST_DIFF_BASE_SHA ? 'pull' : 'push',
branch:
process.env.CI_COMMIT_BRANCH ??
process.env.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME,
};其他 CI/CD 工具
Nest Devtools 的 CI/CD 集成并不限于上面提到的平台。你同样可以把它接入任何你偏好的 CI/CD 工具,例如 Bitbucket Pipelines、CircleCI 等。
理解下面这个 publishOptions 配置对象之后,你就会知道:为了给某个 commit / build / PR 发布图,到底需要哪些信息。
const publishOptions = {
apiKey: process.env.DEVTOOLS_API_KEY,
repository: process.env.CI_PROJECT_NAME,
owner: process.env.CI_PROJECT_ROOT_NAMESPACE,
sha: process.env.CI_COMMIT_SHA,
target: process.env.CI_MERGE_REQUEST_DIFF_BASE_SHA,
trigger: process.env.CI_MERGE_REQUEST_DIFF_BASE_SHA ? 'pull' : 'push',
branch:
process.env.CI_COMMIT_BRANCH ??
process.env.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME,
};其中大部分信息,都可以直接从各类 CI/CD 平台提供的内置环境变量中获取。比如可以参考 CircleCI 内置环境变量列表 和 Bitbucket variables。
至于“什么时候触发流水线来发布图”,我们推荐采用如下策略:
push事件:仅当当前分支代表某个部署环境时触发,例如master、main、staging、productionpull request事件:始终触发,或者仅在目标分支代表部署环境时触发