Azure Functions를 사용하여 사용자 지정 분기 정책 만들기

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

PR(끌어오기 요청) 워크플로는 개발자에게 피어 및 자동화된 도구에서 코드에 대한 피드백을 얻을 수 있는 기회를 제공합니다. 타사 도구 및 서비스는 PR 상태 API를 사용하여 PR 워크플로에 참여할 수 있습니다. 이 문서에서는 Azure Functions를 사용하여 Azure DevOps Services Git 리포지토리에서 PR의 유효성을 검사하는 사용자 지정 분기 정책을 만드는 프로세스를 안내합니다. Azure Functions를 사용하면 특히 워크로드가 증가하는 경우 서버 프로비전 및 기본 관련에 대해 걱정할 필요가 없습니다. Azure Functions는 높은 안정성과 보안을 갖춘 완전 관리형 컴퓨팅 플랫폼을 제공합니다.

PR 상태 대한 자세한 내용은 끌어오기 요청 상태 사용하여 끌어오기 요청 워크플로 사용자 지정 및 확장을 참조하세요.

필수 조건

Git 리포지토리를 사용하는 Azure DevOps의 조직입니다. 조직이 없는 경우 등록하여 무제한 프라이빗 Git 리포지토리에서 코드를 업로드하고 공유합니다.

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 Services 기능입니다. 이 샘플에서는 PR 이벤트에 대한 서비스 후크를 설정하려고 합니다. 끌어오기 요청이 변경되면 Azure 함수에 알림이 표시됩니다. 끌어오기 요청이 변경될 때 요청을 받으 POST 려면 서비스 후크에 Azure 함수 URL을 제공해야 합니다.

이 샘플의 경우 2개의 서비스 후크를 구성해야 합니다. 첫 번째는 끌어오기 요청 생성 이벤트에 대한 것이고, 두 번째는 끌어오기 요청 업데이트 이벤트에 대한 것입니다.

  1. Azure 함수 보기에서 함수 URL 가져오기를 클릭하고 URL을 복사하여 Azure Portal에서 함수 URL 을 가져옵니다.

    함수 URL 가져오기

    함수 URL 복사

  2. 예를 들어 Azure DevOps에서 프로젝트로 이동합니다. https://dev.azure.com/<your organization>/<your project name>

  3. 탐색 메뉴에서 기어를 마우스로 가리키고 서비스 후크를 선택합니다.

    관리 메뉴에서 서비스 후크 선택

  4. 첫 번째 서비스 후크인 경우 + 구독 만들기를 선택합니다.

    도구 모음에서 새 구독 만들기 선택

    다른 서비스 후크를 이미 구성한 경우 녹색 더 (+) 하기 를 선택하여 새 서비스 후크 구독을 만듭니다.

    녹색 플러스를 선택하여 새 서비스 후크 구독을 만듭니다.

  5. 새 서비스 후크 구독 대화 상자의 서비스 목록에서 웹 후크를 선택한 다음, 다음을 선택합니다.

    서비스 목록에서 웹 후크 선택

  6. 이벤트 트리거 목록에서 만든 끌어오기 요청을 선택한 다음, 다음을 선택합니다.

    이벤트 트리거 목록에서 만든 끌어오기 요청 선택

  7. 작업 페이지에서 URL 상자에 1단계에서 복사한 URL입력합니다. 테스트를 선택하여 서버에 테스트 이벤트를 보냅니다.

    URL을 입력하고 테스트를 선택하여 서비스 후크를 테스트합니다.

    Azure 함수 로그 창에 함수가 200 OK서비스 후크 이벤트를 수신했음을 나타내는 들어오는 POST 항목이 표시됩니다.

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

    테스트 알림 창에서 응답 탭을 선택하여 서버의 응답 세부 정보를 확인합니다. 서버의 응답이 표시됩니다.

    응답 탭을 선택하여 테스트 결과를 확인합니다.

  8. 테스트 알림 창을 닫고 마침을 선택하여 서비스 후크를 만듭니다.

2-8단계를 다시 진행하지만 이번에는 끌어오기 요청 업데이트 이벤트를 구성합니다.

Important

이전 단계를 두 번 진행하여 끌어오기 요청 생성끌어오기 요청 업데이트 이벤트 모두에 대한 서비스 후크를 만들어야 합니다.

끌어오기 요청을 만들어 Azure 함수가 알림을 받고 있는지 확인합니다.

PR에 상태 게시

이제 서버가 새 PR을 만들 때 서비스 후크 이벤트를 수신할 수 있으므로 PR에 상태 게시하도록 업데이트합니다. 서비스 후크에서 게시한 JSON 페이로드를 사용하여 PR에 설정할 상태 확인할 수 있습니다.

다음 예제와 같이 Azure 함수의 코드를 업데이트합니다.

조직 이름, 프로젝트 이름, 리포지토리 이름 및 PAT 토큰으로 코드를 업데이트해야 합니다. PR 상태 변경할 수 있는 권한을 갖기 위해 PAT에는 개인 액세스 토큰 만들기 페이지에서 코드(상태) 범위를 선택하여 부여할 수 있는 vso.code_상태 범위가 필요합니다.

Important

이 샘플 코드는 샘플을 간소화하기 위해 PAT를 코드에 저장합니다. KeyVault에 비밀을 저장하고 여기에서 검색하는 것이 좋습니다.

이 샘플은 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 KeyVault and retrieve them from there.
*/
private static string pat = "[PAT 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("Basic", Convert.ToBase64String(
                ASCIIEncoding.ASCII.GetBytes(
                string.Format("{0}:{1}", "", pat))));

        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 만들기

서버가 실행 중이고 서비스 후크 알림을 수신 대기했으므로 끌어오기 요청을 만들어 테스트합니다.

  1. 파일 보기에서 시작합니다. 리포지토리에서 readme.md 파일을 편집합니다(또는 readme.md 없는 경우 다른 파일).

    상황에 맞는 메뉴에서 편집 선택

  2. 편집하고 변경 내용을 리포지토리에 커밋합니다.

    파일 편집 및 도구 모음에서 커밋 선택

  3. 다음 단계에서 PR을 만들 수 있도록 변경 내용을 새 분기에 커밋해야 합니다.

    새 분기 이름을 입력하고 커밋을 선택합니다.

  4. 끌어오기 요청 만들기 링크를 선택합니다.

    제안 표시줄에서 끌어오기 요청 만들기 선택

  5. 타이틀에 WIP를 추가하여 앱의 기능을 테스트합니다. 만들기를 선택하여 PR을 만듭니다.

    기본 PR 제목에 WIP 추가

  6. PR이 만들어지면 페이로드에 지정된 URL에 연결되는 진행 중인 작업 항목과 함께 상태 섹션이 표시됩니다.

    작업 진행 중인 항목이 있는 상태 섹션입니다.

  7. PR 제목을 업데이트하고 WIP 텍스트를 제거하고 상태 진행 중인 작업에서 검토 준비로 변경됩니다.

다음 단계

  • 이 문서에서는 서비스 후크를 통해 PR 이벤트를 수신 대기하고 상태 API를 사용하여 상태 메시지를 게시할 수 있는 서버리스 Azure 함수를 만드는 방법의 기본 사항을 알아보았습니다. 끌어오기 요청 상태 API에 대한 자세한 내용은 REST API 설명서를 참조하세요.
  • 외부 서비스에 대한 분기 정책을 구성합니다.