使用 Node.js 创建拉取请求状态服务器

Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019

拉取请求 (PR) 工作流为开发人员提供了从同行以及自动化工具获取代码反馈的机会。 第三方工具和服务可以使用 PR 状态 API 参与 PR 工作流。 本文将指导你完成创建状态服务器以验证 Azure DevOps Services Git 存储库中的 PR 的过程。 有关 PR 状态的详细信息,请参阅使用拉取请求状态自定义并扩展拉取请求工作流

先决条件

  • Azure DevOps 中具有 Git 存储库的组织。 如果没有组织,请注册以在无限制的免费专用 Git 存储库中上传并共享代码。
  • 安装 VS Code 或所选的其他代码编辑器。 本指南中的说明使用 VS Code,但其他代码编辑器中的操作步骤类似。

安装 Node.js

要安装 Node.js,请下载适合平台的 LTS 版本。 下载包含一个安装程序,可通过运行它在本地计算机上安装 Node.js 运行时。 安装 Node.js 时,请务必保留安装的 npm 包管理器部分,该部分默认处于选中状态。

使用 Express 创建基本 Web 服务器

本部分的步骤使用 Express,它是 Node.js 的轻量级 Web 框架,提供了许多 HTTP 实用工具方法,可简化 Web 服务器的创建。 此框架提供侦听 PR 事件所需的基本函数。

  1. 在命令行中,为 Web 服务器创建新的项目文件夹。

    mkdir pr-server
    cd pr-server
    
  2. 使用 npm init 命令为项目创建新的 package.json 文件。

    npm init
    

    按 Enter 接受除入口点以外的所有选项的默认值。 请将其更改为 app.js

    entry point: (index.js) app.js
    
  3. 使用以下命令在 pr-server 目录中安装 Express。 这将安装 Express 并将其保存到依赖项列表中。

    npm install express
    
  4. 创建一个简单的 Express 应用,为 PR 状态服务器提供基础。 以下步骤基于 Express Hello World 示例。 通过从 pr-server 文件夹中运行以下命令,在 VS Code 中打开项目文件夹。

    code .
    
  5. 创建新的文件 (Ctrl + N) 并粘贴以下示例代码。

    const express = require('express')
    const app = express()
    
    app.get('/', function (req, res) {
    res.send('Hello World!')
    })
    
    app.listen(3000, function () {
    console.log('Example app listening on port 3000!')
    })
    
  6. 将文件另存为 app.js

  7. 使用以下命令运行基本 Web 服务器:

    node app.js
    

    通过浏览到 http://localhost:3000/ 验证服务器是否正在运行。

侦听 HTTP POST 请求

Web 服务器将接收来自 Azure DevOps Services 的 POST 请求,因此需要在服务器中处理这些请求。

  1. app.js 文件末尾添加以下代码,并保存该文件。

    app.post('/', function (req, res) {
        res.send('Received the POST')
    })
    
  2. 使用以下命令重新运行 Web 服务器:

    node app.js
    

为 PR 事件配置服务挂钩

服务挂钩是 Azure DevOps Services 的一项功能,可在发生某些事件时向外部服务发出警报。 对于此示例,需要为 PR 事件设置两个服务挂钩,以便可以通知状态服务器。 第一个用于“已创建拉取请求”事件,第二个用于“已更新拉取请求”事件。

若要接收服务挂钩通知,需要向公共 Internet 公开端口。 ngrok 实用工具非常适合用于在开发环境中执行此操作。

  1. 下载适合平台的 ngrok 版本并将其解压缩。

  2. 使用 ngrok 开始侦听与示例服务器相同的端口 - 端口 3000。 在新命令窗口中运行以下命令。

    ngrok http 3000
    

    ngrok 将创建一个转发到 localhost:3000 的公共 URL。 请记下此 URL,因为在下一步中将用到它。 它将如下所示:

    http://c3c1bffa.ngrok.io
    
  3. 浏览到 Azure DevOps 中的项目,例如 https://dev.azure.com/<your account>/<your project name>

  4. 在导航菜单中,将鼠标悬停在齿轮上,然后选择“服务挂钩”。

    从管理菜单中选择“服务挂钩”

  5. 如果这是第一个服务挂钩,请选择“+ 创建订阅”。

    从工具栏中选择“创建新订阅”

    如果已配置其他服务挂钩,请选择绿色加号 (+) 以创建新的服务挂钩订阅。

    选择绿色加号以创建新的服务挂钩订阅。

  6. 在“新建服务挂钩订阅”对话框中,从服务列表中选择“Web 挂钩”,然后选择“下一步”。

    从服务列表中选择 Webhook

  7. 从事件触发器列表中选择“已创建拉取请求”,然后选择“下一步”。

    从事件触发器列表中选择“已创建拉取请求”

  8. 在“操作”页的 URL 框中,输入来自 ngrok 的 URL。 选择“测试”,向服务器发送测试事件。

    输入 URL 并选择“测试”以测试服务挂钩

    在 ngrok 控制台窗口中,你将看到一个传入的 POST,它返回了 200 OK,指示服务器收到了服务挂钩事件。

    HTTP Requests
    -------------
    
    POST /                         200 OK
    

    在“测试通知”窗口中,选择“响应”选项卡以查看来自服务器的响应详细信息。 应看到的内容长度为 17,该长度与 POST 处理程序中的字符串长度匹配(即“已收到 POST”)。

    选择“响应”选项卡以查看测试结果

  9. 关闭“测试通知”窗口,然后选择“完成”以创建服务挂钩。

再次完成步骤 3-9,但这次是配置“已更新拉取请求”事件。

重要

请务必执行上述步骤两次,为“已创建拉取请求”事件和“已更新拉取请求”事件创建服务挂钩。

将状态发布到 PR

现在,服务器可以在创建新 PR 时接收服务挂钩事件,请将其更新,以便将状态回发 PR。

  1. 服务挂钩请求包括描述事件的 JSON 有效负载。 为了帮助分析由服务挂钩返回的 JSON,请安装 body-parser 包。

    npm install body-parser
    
  2. 更新 app.js 以使用 body-parser 分析 application/json

    var bodyParser = require('body-parser')
    
    app.use(bodyParser.json())
    
  3. 若要简化对 Azure Repos 的 REST API 调用,请安装 azure-devops-node-api 包。

    npm install azure-devops-node-api 
    
  4. 更新 app.js 以使用 azure-devops-node-api 包,设置与帐户的连接详细信息,并获取 Git API 的实例。

    const vsts = require("azure-devops-node-api")
    
    const collectionURL = process.env.COLLECTIONURL    
    const token = process.env.TOKEN
    
    var authHandler = vsts.getPersonalAccessTokenHandler(token)
    var connection = new vsts.WebApi(collectionURL, authHandler)
    
    var vstsGit = connection.getGitApi().then( 
        vstsGit => {                                    
            vstsGit.createPullRequestStatus(prStatus, repoId, pullRequestId).then( result => {
                console.log(result);
            },
            error => {
                console.log(error);
            })
        }, 
        error => { 
            console.log(error);
        } 
    );
    
  5. 为集合 URL 创建环境变量,将 <your account> 替换为 Azure DevOps 组织的名称。

    setx COLLECTIONURL "https://dev.azure.com/<your account>"
    
  6. 按照以下说明创建供应用使用的个人身份验证令牌 (PAT):使用个人访问令牌进行身份验证。 应为用于访问帐户的每个服务创建新的 PAT,并对其进行适当的命名。

  7. 为 PAT 创建环境变量。

    setx TOKEN "yourtokengoeshere"
    
  8. 更新 post() 函数,从服务挂钩有效负载中读取 PR 详细信息。 需要这些值才能回发状态。

    var repoId = req.body.resource.repository.id
    var pullRequestId = req.body.resource.pullRequestId
    var title = req.body.resource.title
    
  9. 生成要在 PR 上发布的状态对象。

    StateGitStatusState 类型的枚举。 使用 succeeded 指示 PR 已通过状态检查并准备好合并。

    description 是一个字符串值,将在 PR 详细信息视图中的“状态”部分和活动源中向用户显示。

    targetUrl 是一个 URL,用于为“状态”部分和活动源中的说明文本创建链接。 用户可以转到此链接获取有关状态的详细信息,例如生成报告或测试运行。 如果未指定 URL,说明将显示为没有链接的文本。

    上下文 namegenre 用于对状态进行分类,并将其与其他服务发布状态区分开来。

        var prStatus = {
            "state": "succeeded",
            "description": "Ready for review",
            "targetUrl": "https://visualstudio.microsoft.com",
            "context": {
                "name": "wip-checker",
                "genre": "continuous-integration"
            }
        }
    
  10. 检查 PR 标题,查看用户是否向标题添加了 WIP 来指示 PR 是否正在进行中,而不是盲目发布 succeeded 状态。 如果是,请更改回发 PR 的状态。

        if (title.includes("WIP")) {
            prStatus.state = "pending"
            prStatus.description = "Work in progress"
        }
    
  11. 最后,使用 createPullRequestStatus() 方法发布状态。 它需要状态对象、存储库 ID 和拉取请求 ID。 将响应输出到节点控制台,以便查看发布的结果。

    vstsGit.createPullRequestStatus(prStatus, repoId, pullRequestId).then( result => {
        console.log(result)
    })
    
  12. 生成的方法应如下所示:

    app.post("/", function (req, res) {
    
        // Get the details about the PR from the service hook payload
        var repoId = req.body.resource.repository.id
        var pullRequestId = req.body.resource.pullRequestId
        var title = req.body.resource.title
    
        // Build the status object that we want to post.
        // Assume that the PR is ready for review...
        var prStatus = {
            "state": "succeeded",
            "description": "Ready for review",
            "targetUrl": "https://visualstudio.microsoft.com",
            "context": {
                "name": "wip-checker",
                "genre": "continuous-integration"
            }
        }
    
        // Check the title to see if there is "WIP" in the title.
        if (title.includes("WIP")) {
    
            // If so, change the status to pending and change the description.
            prStatus.state = "pending"
            prStatus.description = "Work in progress"
        }
    
        // Post the status to the PR
        vstsGit.createPullRequestStatus(prStatus, repoId, pullRequestId).then( result => {
            console.log(result)
        })
    
        res.send("Received the POST")
    })
    
  13. 保存 app.js 并重启节点应用。

    node app.js
    

创建新的 PR 以测试状态服务器

现在服务器正在运行并侦听服务挂钩通知,请创建一个拉取请求来测试它。

  1. 从文件视图开始。 编辑存储库中的 readme.md 文件(如果没有 readme.md,则编辑任何其他文件)。

    从上下文菜单中选择“编辑”

  2. 进行编辑并将更改提交到存储库。

    编辑文件,然后在工具栏中选择“提交”

  3. 请务必将更改提交到新分支,以便在下一步中创建 PR。

    输入新的分支名称,然后选择“提交”

  4. 选择“创建拉取请求”链接。

    在建议栏中选择“创建拉取请求”

  5. 在标题中添加 WIP 以测试应用的功能。 选择“创建”以创建 PR。

    将 WIP 添加到默认 PR 标题

  6. 创建 PR 后,会看到状态部分,其中“正在进行”条目链接到有效负载中指定的 URL。

    包含“正在进行”条目的状态部分。

  7. 更新 PR 标题并删除 WIP 文本,注意状态将从“正在进行”更改为“可供评审”。

后续步骤

  • 本文介绍了如何通过服务挂钩创建侦听 PR 事件的服务以及如何使用状态 API 发布状态消息的基础知识。 有关拉取请求状态 API 的详细信息,请参阅 REST API 文档
  • 为外部服务配置分支策略