Node.js を使用して pull request 状態サーバーを作成する

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

pull request (PR) ワークフローを使用すると、開発者は、自動化されたツールからだけでなくピアからもコードに関するフィードバックを得ることができます。 サードパーティのツールとサービスは、PR Status API を使用して PR ワークフローに参加できます。 この記事では、Azure DevOps Services Git リポジトリで PR を検証するためのステータス サーバーを作成するプロセスについて説明します。 PR 状態の詳細については、「pull request の状態を使用して pull request ワークフローをカスタマイズして拡張する」を参照してください。

前提条件

  • Git リポジトリを使用する Azure DevOps の組織。 組織がない場合は、サインアップして、無制限の無料プライベート Git リポジトリでコードをアップロードして共有します。
  • 任意の VS Code またはその他のコード エディターをインストールします。 このガイドの手順では VS Code を使用しますが、他のコード エディターの場合も手順は似ています。

Node.js をインストールする

Node.js をインストールするには、お使いのプラットフォームに適した LTS リリースをダウンロードします。 ダウンロードにはインストーラーが含まれています。これを実行すると、Node.js ランタイムをローカル コンピューターにインストールできます。 Node.js をインストールするときは、インストールの npm パッケージ マネージャー 部分を必ず保持してください。これは既定で選択されています。

Express を使用して基本的な Web サーバーを作成する

このセクションの手順では、Express を使用します。これは、Web サーバーの作成を簡素化する多数の HTTP ユーティリティ メソッドを提供する、Node.js 用の軽量 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. PR ステータス サーバー用に構築する単純な Express アプリを作成します。 次の手順は、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 イベント用に 2 つのサービス フックを設定することをお勧めします。 1 つ目は [Pull request が作成されました] イベント用で、2 つ目は [Pull request の更新完了] イベント用です。

サービス フック通知を受信するには、パブリック インターネットにポートを公開する必要があります。 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 フック] を選択し、[次へ] を選択します。

    サービスの一覧から [Web フック] を選択する

  7. イベント トリガーの一覧から [Pull request が作成されました] を選択し、[次へ] を選択します。

    イベント トリガーの一覧から [Pull request が作成されました] を選択する

  8. [アクション] ページで、ngrok の URL を [URL] ボックスに入力します。 [テスト] を選択して、テスト イベントをサーバーに送信します。

    URL を入力し、[テスト] を選択してサービスフックをテストする

    ngrok コンソール ウィンドウに、200 OK を返した受信 POST が表示されます。これは、サーバーがサービス フック イベントを受信したことを示します。

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

    [テスト通知] ウィンドウで、[応答] タブを選択して、サーバーからの応答の詳細を表示します。 POST ハンドラーからの文字列の長さに一致する 17 のコンテンツ長が表示されます ("POST を受信しました")。

    [応答] タブを選択してテストの結果を表示する

  9. [テスト通知] ウィンドウを閉じ、[終了] を選択してサービス フックを作成します。

手順 3 から 9 を再度実行しますが、今回は [Pull request の更新完了] イベントを構成します。

重要

必ず前述の手順を 2 回実行し、[Pull request が作成されました] および [Pull request の更新完了] の両方のイベントのサービス フックを作成してください。

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. azure-devops-node-api パッケージを使用するように app.js を更新し、アカウントに対する接続の詳細設定を行い、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 に投稿する status オブジェクトをビルドします。

    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. 単に succeeded の状態をやみくもに投稿するのではなく、タイトルに WIP を追加することで、PR タイトルを調べて、PR が進行中の作業であることをユーザーが示しているかどうかを確認します。 その場合は、PR にポストバックされた状態を変更します。

        if (title.includes("WIP")) {
            prStatus.state = "pending"
            prStatus.description = "Work in progress"
        }
    
  11. 最後に、createPullRequestStatus() メソッドを使用してこの状態を投稿します。 状態オブジェクト、リポジトリ ID、pull request 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 を作成する

サーバーが実行され、サービス フック通知をリッスンしている状態になったので、pull request を作成してテストします。

  1. ファイル ビューから開始します。 リポジトリ内の readme.md ファイル (または readme.md がない場合は他のファイル) を編集します。

    コンテキスト メニューから [編集] を選択する

  2. 編集を行い、リポジトリに変更をコミットします。

    ファイルを編集し、ツール バーから [コミット] を選択する

  3. 次の手順で PR を作成できるように、必ず新しいブランチに変更をコミットしてください。

    新しいブランチ名を入力し、[コミット] を選択する

  4. [Pull request の作成] リンクを選択します。

    提案バーから [Pull request の作成] を選択する

  5. タイトルに WIP を追加して、アプリの機能をテストします。 [作成] を選択して、PR を作成します。

    既定の PR タイトルに WIP を追加する

  6. PR が作成されると、ペイロードで指定された URL にリンクする [進行中の作業] エントリを含む [状態] セクションが表示されます。

    [進行中の作業] エントリを含む [状態] セクション。

  7. PR タイトルを更新し、WIP テキストを削除すると、状態が [進行中の作業] から [Ready for review](レビュー準備完了) に変わることに注意してください。

次の手順