CI/CD 集成
info 提示 本章介绍 Nest Devtools 与 Nest 框架的集成。如果您正在寻找 Devtools 应用程序,请访问 Devtools 网站。CI/CD 集成在 Enterprise 计划中可用。
本地使用非常适合在构建应用程序时进行探索,但真正的回报在于 Devtools 成为交付管道的一部分。CI/CD 集成会在每次构建时发布应用程序图的快照,因此您可以获得架构演进的运行历史——更重要的是,每次拉取请求都会自动生成报告,精确显示结构上发生了什么变化。这就像希望重构没有破坏任何东西与知道它没有破坏之间的区别。
了解为什么团队依赖 CI/CD 集成在交付前捕获架构漂移:
VIDEO
发布图
首先,让我们将应用程序引导文件(main.ts)配置为使用从 @nestjs/devtools-integration 导出的 GraphPublisher 类(如果您尚未安装,请参阅 previous chapter ):
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 = { ... } // NOTE: this options object will vary depending on the CI/CD provider you're using
const graphPublisher = new GraphPublisher (app);
await graphPublisher .publish (publishOptions);
await app .close ();
} else {
await app .listen ( 3000 );
}
}
这里,GraphPublisher 将序列化后的图推送到集中注册表。PUBLISH_GRAPH 是一个自定义环境变量,用于区分 CI/CD 发布运行和常规应用程序引导。将 preview 设置为 true 会使应用程序以预览模式引导,这意味着控制器、增强器和提供者上的构造函数和生命周期钩子不会实际执行。这不是必需的 ,但它使 CI 运行快速而简单——例如,不需要数据库连接。
publishOptions 的形状取决于您使用的 CI/CD 提供商——我们将在下面介绍最流行的几种。如果您的提供商未列出,请不要担心:底层字段在任何地方都是相同的,因此您可以在几分钟内调整配置。
一旦您的图成功发布,您将在工作流日志中看到类似这样的输出:
每个发布的图都会在项目页面上显示为新条目:
报告
Devtools 会为每次构建生成报告,只要 注册表中已存在匹配的快照。因此,如果您针对 master 打开拉取请求,并且已经发布了 master 的图,Devtools 会检测差异并自动生成报告。否则,还没有可比较的内容。
在项目页面上查找您的报告(参见 organizations )。
这正是 Devtools 真正发挥作用的地方:捕获在代码审查中遗漏的更改。假设有人悄悄更改了深层嵌套提供者 的作用域——在差异中很容易错过,但在报告中不可能错过。从端点移除守卫,它会立即显示为受影响的更改。如果该路由没有被集成测试或端到端测试覆盖,您可能直到为时已晚才注意到——而 Devtools 会在审查时捕获它。
对于大型代码库 也是如此:将模块设为全局,您会立即看到图上有多少新边——这通常是一个强烈的信号,表明需要重新检查。
报告也是直接在拉取请求描述中链接的绝佳工件。与其要求审查者相信“这只涉及计费模块”,不如让他们查看更改的确切节点和边集——将架构声明变成他们可以在几秒钟内实际验证的内容。
构建预览
每个发布的图都可以重放——点击预览 以查看它在那个时间点的确切样子。当报告可用时,差异会直接在图上方高亮显示:
绿色节点表示新增元素
浅白色节点表示更新元素
红色节点表示删除元素
以下是它的样子:
能够回放和比较图使故障排除变得简单明了——不再需要猜测发生了什么变化以及何时发生。设置好一切,让每次拉取请求(甚至每次提交)都在注册表中获得自己的快照,您将始终有清晰的轨迹可循。将 Devtools 视为真正理解 Nest 如何组装您的应用程序的版本控制——并且可以向您展示 差异,而不仅仅是描述它。
集成:GitHub Actions
在 .github/workflows 中创建一个新的工作流文件——我们将其命名为 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 {{ '}}' }}
info 提示 为了更好的安全性,请从 GitHub Secrets 中获取 DEVTOOLS_API_KEY,而不是硬编码——了解更多 here 。
此工作流在针对 master 的每次拉取请求以及直接推送到 master 时运行。请随意根据项目需求进行调整——不可妥协的是提供 GraphPublisher 所依赖的环境变量。
不过,在此工作流运行之前,还有一个变量需要填写:DEVTOOLS_API_KEY。在 API keys page 上为您的项目生成专用 API 密钥。
最后,返回 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 应用 — 只需点击下方的"集成 GitHub 应用"。这是可选的,但非常值得。
安装应用后,您可以直接在拉取请求中看到预览/报告生成的状态:
集成:GitLab Pipelines
在项目根目录创建 .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'
info 提示 与 GitHub 一样,我们建议从 CI/CD 密钥中获取 DEVTOOLS_API_KEY,而不是直接提交。
此流水线在针对 master 的每次拉取请求以及直接推送到 master 时运行。请根据项目需求进行调整 — 只需确保 GraphPublisher 所需的环境变量始终存在。
此定义中有一个变量仍需要真实值:DEVTOOLS_API_KEY。在 API keys page 上为您的项目生成专用 API 密钥。
最后,返回 main.ts 并填写我们之前留空的 publishOptions 对象:
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 工具
不在 GitHub 或 GitLab 上?没问题 — 该集成实际上并不依赖这两个平台。GraphPublisher 只需要少量描述当前构建的值(谁触发的、哪个提交、哪个分支),因此它可以与您使用的任何提供者配合使用,包括 Bitbucket Pipelines 、CircleCI 等。
以下是完整的 publishOptions 结构以及每个字段的含义,以便您可以接入任何流水线:
const publishOptions = {
apiKey : process . env . DEVTOOLS_API_KEY , // This is your Nest Devtools API key
repository : '?' , // This is your repository name, for example, "my-api-repository"
owner : '?' , // This is your organization/team name, for example, "nestjs" OR in case of personal projects - your username
sha : '?' , // This represents the "current" commit SHA that triggered the workflow/pipeline
target : '?' , // This represents the "target" commit SHA (e.g., the last commit SHA of the "master" branch)
// New build will be compared to the "target" build to generate a report
// NOTE: Some CI/CD tools don't provide you with this information so instead, you can use the "targetBranch" property.
// NOTE: In this case, the "target" commit SHA will be automatically resolved to the last commit SHA of the "targetBranch" stored in the database.
// targetBranch: "master",
trigger : isPr ? 'pull' : 'push' , // Depending on whether the pipeline is triggered by a pull request or a regular push commit, you should set "pull" or "push"
branch : '?' , // This is the current branch name, for example, "develop" OR "feat/my-new-feature"
};
这些值中的大多数已经存在于您的 CI/CD 提供者的内置环境变量中 — 请参阅 CircleCI's environment variable reference 和 Bitbucket's 作为起点。
对于流水线触发器,我们建议以下设置:
push — 仅针对代表部署环境的分支,例如 master、main、staging 或 production。
pull request — 始终运行,或至少在目标分支 是部署环境时运行(见上文)。
这种组合使您的注册表为 master 的每个可部署状态保留快照,同时仍然在针对它的每个拉取请求上生成报告 — 这正是使报告有用的关键组合。