Azure DevOps Services |Azure DevOps Server |Azure DevOps Server 2022 |Azure DevOps Server 2020
pull request (PR) ワークフローを使用すると、開発者はピアや自動化されたツールからコードに関するフィードバックを受け取ることができます。 Microsoft 以外のツールとサービスは、PR Status API を使用して PR ワークフローに参加することもできます。 この記事では、 Azure Functions を使用してカスタム ブランチ ポリシーを作成し、Azure DevOps Git リポジトリ内の PR を検証する方法について説明します。 Azure Functions を使用すると、ワークロードが増加しても、サーバーをプロビジョニングして保守する必要がなくなります。 高い信頼性とセキュリティを備えたフル マネージド コンピューティング プラットフォームを提供します。
PR ステータスの詳細については、「プル要求の状態を使用して pull request ワークフローをカスタマイズおよび拡張する」を参照してください。
[前提条件]
| カテゴリ | 要求事項 |
|---|---|
| 組織 | Git リポジトリを持つ Azure DevOps 内の組織。 |
| Azure 関数 | Azure 関数。Azure DevOps と統合してカスタム ブランチ ポリシーを作成し、PR 検証を自動化する、サーバーレスのイベント ドリブン ソリューションを実装します。 |
| サービス フック | プル要求が変更されたときに Azure 関数に通知するように PR イベントのサービス フックを構成します。 |
| 認証 | Microsoft Entra ID トークンの「コード (状態)」スコープでPR状態を変更する権限を持ちます。 詳細については、Microsoft Entra 認証に関するページを参照してください。 |
Azure Repos イベントをリッスンするための基本的な Azure 関数を作成する
最初の Azure 関数を作成します。 次に、サンプルのコードを次のコードのように変更します。
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using Newtonsoft.Json;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
try
{
log.Info("Service Hook Received.");
// Get request body
dynamic data = await req.Content.ReadAsAsync<object>();
log.Info("Data Received: " + data.ToString());
// Get the pull request object from the service hooks payload
dynamic jObject = JsonConvert.DeserializeObject(data.ToString());
// Get the pull request id
int pullRequestId;
if (!Int32.TryParse(jObject.resource.pullRequestId.ToString(), out pullRequestId))
{
log.Info("Failed to parse the pull request id from the service hooks payload.");
};
// Get the pull request title
string pullRequestTitle = jObject.resource.title;
log.Info("Service Hook Received for PR: " + pullRequestId + " " + pullRequestTitle);
return req.CreateResponse(HttpStatusCode.OK);
}
catch (Exception ex)
{
log.Info(ex.ToString());
return req.CreateResponse(HttpStatusCode.InternalServerError);
}
}
PR イベントのサービス フックを構成する
サービス フックは、特定のイベントが発生したときに外部サービスにアラートを送信できる Azure DevOps 機能です。 このサンプルでは、PR イベントのサービス フックを設定します。プル要求が変更されると、Azure 関数に通知されます。 プル要求が変更されたときに POST 要求を受信するには、サービス フックに Azure 関数 URL を指定します。
このサンプルでは、2 つのサービス フックを構成します。 1 つ目はイベントで作成された Pull request 用で、2 つ目は更新された Pull request イベント用です。
Azure 関数ビューで [関数 URL の取得] をクリックして Azure portal から 関数 URL を取得 し、URL をコピーします。
Azure DevOps でプロジェクトを参照します (例:
https://dev.azure.com/<your organization>/<your project name>ナビゲーション メニューから、の歯車 上にマウス ポインターを合わせ、サービス フック 選択します。
最初のサービス フックの場合は、[ + サブスクリプションの作成] を選択します。
他のサービス フックが既に構成されている場合は、緑色のプラス
(+)を選択して、新しいサービス フック サブスクリプションを作成します。
[新しいサービス フック サブスクリプション] ダイアログで、サービス一覧から [Web フック] を選択し、[次へ] を選択します。
イベント トリガーの一覧から [Pull request created] を選択し、[次へ] を選択します。
[アクション] ページで、手順 1 でコピーした URL を [URL ] ボックスに入力します。 テスト を選択して、テスト イベントをサーバーに送信します。
Azure 関数ログ ウィンドウには、関数がサービス フック イベントを受信したことを示す、
POSTを返した受信200 OKが表示されます。HTTP Requests ------------- POST / 200 OK[テスト通知] ウィンドウで、[応答] タブを選択して、サーバーからの応答の詳細を表示します。 サーバーからの応答が表示されます。
[テスト通知] ウィンドウを閉じ、[完了] 選択してサービス フックを作成します。
手順 2 から 8 をもう一度実行しますが、今回は Pull request 更新 イベントを構成します。
Von Bedeutung
前の手順を 2 回実行し、作成された Pull request と、更新された pull request イベント 両方のサービス フックを作成してください。
プル要求を作成して、Azure 関数が通知を受信することを確認します。
PR に状態をポストする
新しい PR が作成されたときにサーバーがサービス フック イベントを受信できるようになったので、それを更新して状態を PR にポストバックします。 サービス フックによって投稿された JSON ペイロードを使用して、PR のステータスを設定するための判断を下せます。
次の例のように、Azure 関数のコードを更新します。
組織名、プロジェクト名、リポジトリ名、Microsoft Entra ID トークンでコードを更新してください。 PR 状態を変更するアクセス許可を持つには、トークン にはvso.code_status スコープが必要です。このスコープは、Microsoft Entra 認証を通じて取得できます。
Von Bedeutung
このサンプル コードでは、トークンをコードに格納し、サンプルを簡略化します。 セキュリティを強化するために、シークレットを Azure Key Vault に格納し、マネージド ID を使用してそこから取得することをお勧めします。
このサンプルでは、PR タイトルを調べて、 タイトルに WIP を追加して、PR が進行中の作業であるかどうかをユーザーが示したかどうかを確認します。 その場合、サンプル コードは PR にポストバックされた状態を変更します。 Azure 関数のコードを、PR にポストバックされた状態を更新する次のコードに置き換えます。
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using Newtonsoft.Json;
private static string organizationName = "[Organization Name]"; // Organization name
private static string projectName = "[Project Name]"; // Project name
private static string repositoryName = "[Repo Name]"; // Repository name
/*
This is here just to simplify the sample, it is recommended to store
secrets in Azure Key Vault and retrieve them using managed identity.
*/
private static string accessToken = "[MICROSOFT_ENTRA_TOKEN]";
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
try
{
log.Info("Service Hook Received.");
// Get request body
dynamic data = await req.Content.ReadAsAsync<object>();
log.Info("Data Received: " + data.ToString());
// Get the pull request object from the service hooks payload
dynamic jObject = JsonConvert.DeserializeObject(data.ToString());
// Get the pull request id
int pullRequestId;
if (!Int32.TryParse(jObject.resource.pullRequestId.ToString(), out pullRequestId))
{
log.Info("Failed to parse the pull request id from the service hooks payload.");
};
// Get the pull request title
string pullRequestTitle = jObject.resource.title;
log.Info("Service Hook Received for PR: " + pullRequestId + " " + pullRequestTitle);
PostStatusOnPullRequest(pullRequestId, ComputeStatus(pullRequestTitle));
return req.CreateResponse(HttpStatusCode.OK);
}
catch (Exception ex)
{
log.Info(ex.ToString());
return req.CreateResponse(HttpStatusCode.InternalServerError);
}
}
private static void PostStatusOnPullRequest(int pullRequestId, string status)
{
string Url = string.Format(
@"https://dev.azure.com/{0}/{1}/_apis/git/repositories/{2}/pullrequests/{3}/statuses?api-version=4.1",
organizationName,
projectName,
repositoryName,
pullRequestId);
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var method = new HttpMethod("POST");
var request = new HttpRequestMessage(method, Url)
{
Content = new StringContent(status, Encoding.UTF8, "application/json")
};
using (HttpResponseMessage response = client.SendAsync(request).Result)
{
response.EnsureSuccessStatusCode();
}
}
}
private static string ComputeStatus(string pullRequestTitle)
{
string state = "succeeded";
string description = "Ready for review";
if (pullRequestTitle.ToLower().Contains("wip"))
{
state = "pending";
description = "Work in progress";
}
return JsonConvert.SerializeObject(
new
{
State = state,
Description = description,
TargetUrl = "https://visualstudio.microsoft.com",
Context = new
{
Name = "PullRequest-WIT-App",
Genre = "pr-azure-function-ci"
}
});
}
新しい PR を作成し、ステータス サーバーをテストする
サーバーが実行され、サービスフック通知をリッスンできる状態になったら、「プルリクエスト」を作成してテストします。
ファイル ビューから開始します。 リポジトリ内の readme.md ファイル (または readme.md がない場合は他のファイル) を編集します。
編集を行い、変更をリポジトリにコミットします。
次の手順で PR を作成できるように、必ず新しいブランチに変更をコミットしてください。
[プルリクエストの作成] リンクを選択します。
タイトル WIP を追加して、アプリの機能をテストします。 [作成] を選択して、PR を作成します。
PR が作成されると、状態セクションが表示され、[ 進行中の作業] エントリがペイロードで指定された URL にリンクされます。
PR タイトルを更新し、WIP テキストを削除すると、状態が "進行中の作業" から "レビュー準備完了" に変わることに注意してください。