Tworzenie niestandardowych zasad rozgałęziania za pomocą usługi Azure Functions

Azure DevOps Services | Azure DevOps Server 2022 — Azure DevOps Server 2019

Przepływ pracy żądania ściągnięcia zapewnia deweloperom możliwość uzyskania opinii na temat kodu od osób równorzędnych, a także z narzędzi automatycznych. Narzędzia i usługi innych firm mogą uczestniczyć w przepływie pracy żądania ściągnięcia przy użyciu interfejsu API stanu żądania ściągnięcia. W tym artykule opisano proces tworzenia niestandardowych zasad gałęzi przy użyciu usługi Azure Functions w celu weryfikowania żądań ściągnięcia w repozytorium Git usługi Azure DevOps Services. Dzięki usłudze Azure Functions nie musisz martwić się o aprowizowanie i konserwowanie serwerów, zwłaszcza w przypadku wzrostu obciążenia. Usługa Azure Functions zapewnia w pełni zarządzaną platformę obliczeniową o wysokiej niezawodności i zabezpieczeniach.

Aby uzyskać więcej informacji na temat stanu żądania ściągnięcia, zobacz Dostosowywanie i rozszerzanie przepływów pracy żądań ściągnięcia ze stanem żądania ściągnięcia.

Wymagania wstępne

Organizacja w usłudze Azure DevOps z repozytorium Git. Jeśli nie masz organizacji, zarejestruj się, aby przekazać i udostępnić kod w bezpłatnych nieograniczonych prywatnych repozytoriach Git.

Tworzenie podstawowej funkcji platformy Azure do nasłuchiwania zdarzeń usługi Azure Repos

Postępuj zgodnie z dokumentacją tworzenia pierwszej funkcji platformy Azure, aby utworzyć prostą funkcję. Zmodyfikuj kod w przykładzie, aby wyglądał następująco:

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);
    }
}

Konfigurowanie elementu zaczepienia usługi dla zdarzeń żądania ściągnięcia

Punkty zaczepienia usług to funkcja usług Azure DevOps Services, która może powiadamiać usługi zewnętrzne o wystąpieniu niektórych zdarzeń. W tym przykładzie chcesz skonfigurować punkt zaczepienia usługi dla zdarzeń żądania ściągnięcia, funkcja platformy Azure zostanie powiadomiona po zmianie żądania ściągnięcia. Aby otrzymywać POST żądania po zmianie żądań ściągnięcia, należy podać punkt zaczepienia usługi przy użyciu adresu URL funkcji platformy Azure.

W tym przykładzie należy skonfigurować 2 punkty zaczepienia usługi. Pierwszy będzie dotyczyć zdarzenia utworzonego żądania ściągnięcia, a drugi będzie dotyczyć zdarzenia zaktualizowanego żądania ściągnięcia.

  1. Pobierz adres URL funkcji z witryny Azure Portal, klikając pozycję Pobierz adres URL funkcji w widoku funkcji platformy Azure i skopiuj adres URL.

    Uzyskiwanie adresu URL funkcji

    Adres URL funkcji kopiowania

  2. Przejdź do projektu w usłudze Azure DevOps, np. https://dev.azure.com/<your organization>/<your project name>

  3. Z menu nawigacji umieść kursor nad narzędziem zębatym i wybierz pozycję Punkty zaczepienia usługi.

    Wybierz pozycję Punkty zaczepienia usługi z menu administratora

  4. Jeśli jest to twój pierwszy punkt zaczepienia usługi, wybierz pozycję + Utwórz subskrypcję.

    Wybierz pozycję Utwórz nową subskrypcję na pasku narzędzi

    Jeśli masz już skonfigurowane inne punkty zaczepienia usługi, wybierz zielony plus (+) , aby utworzyć nową subskrypcję punktów zaczepienia usługi.

    Wybierz zielony znak plus, aby utworzyć nową subskrypcję elementu Service Hook.

  5. W oknie dialogowym New Service Hooks Subscription (Nowe punkty zaczepienia usługi) wybierz pozycję Web Hooks (Punkty zaczepienia usługi) z listy usług, a następnie wybierz pozycję Next ( Dalej).

    Wybieranie elementów webhook z listy usług

  6. Wybierz pozycję Żądanie ściągnięcia utworzone z listy wyzwalaczy zdarzeń, a następnie wybierz pozycję Dalej.

    Wybierz żądanie ściągnięcia utworzone z listy wyzwalaczy zdarzeń

  7. Na stronie Akcja wprowadź adres URL skopiowany w kroku 1 w polu Adres URL . Wybierz pozycję Testuj , aby wysłać zdarzenie testowe do serwera.

    Wprowadź adres URL i wybierz pozycję Testuj, aby przetestować punkt zaczepienia usługi

    W oknie dziennika funkcji platformy Azure zobaczysz przychodzące POST , które zwróciło wartość , wskazującą 200 OK, że funkcja odebrała zdarzenie punkt zaczepienia usługi.

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

    W oknie Powiadomienie testowe wybierz kartę Odpowiedź, aby wyświetlić szczegóły odpowiedzi z serwera. Powinna zostać wyświetlona odpowiedź z serwera.

    Wybierz kartę odpowiedź, aby wyświetlić wyniki testu

  8. Zamknij okno Powiadomienie testowe i wybierz pozycję Zakończ , aby utworzyć punkt zaczepienia usługi.

Ponownie wykonaj kroki 2–8, ale tym razem skonfiguruj zaktualizowane zdarzenie żądania ściągnięcia.

Ważne

Pamiętaj, aby wykonać poprzednie kroki dwa razy i utworzyć punkty zaczepienia usługi dla utworzonego żądania ściągnięcia i zaktualizowania żądania ściągnięcia.

Utwórz żądanie ściągnięcia, aby sprawdzić, czy funkcja platformy Azure odbiera powiadomienia.

Publikowanie stanu na żądania ściągnięcia

Teraz, gdy serwer może odbierać zdarzenia punktu zaczepienia usługi po utworzeniu nowych żądań ściągnięcia, zaktualizuj go, aby przywrócić stan żądania ściągnięcia do żądania ściągnięcia. Możesz użyć ładunku JSON opublikowanego przez element zaczepienia usługi, aby określić, jaki stan ma być ustawiony na żądanie ściągnięcia.

Zaktualizuj kod funkcji platformy Azure, aby wyglądał jak w poniższym przykładzie.

Pamiętaj, aby zaktualizować kod przy użyciu nazwy organizacji, nazwy projektu, nazwy repozytorium i tokenu PAT. Aby mieć uprawnienia do zmiany stanu żądania ściągnięcia, pat wymaga vso.code_status zakresu, który można udzielić, wybierając zakres Kod (stan) na stronie Tworzenie osobistego tokenu dostępu.

Ważne

Ten przykładowy kod przechowuje pat w kodzie, aby uprościć przykład. Zaleca się przechowywanie wpisów tajnych w usłudze KeyVault i pobieranie ich stamtąd.

Ten przykład sprawdza tytuł żądania ściągnięcia, aby sprawdzić, czy użytkownik wskazał, czy żądanie ściągnięcia jest w toku, dodając element WIP do tytułu. Jeśli tak, przykładowy kod zmienia stan opublikowany z powrotem na żądanie ściągnięcia. Zastąp kod w funkcji platformy Azure następującym kodem, aby zaimplementować aktualizowanie stanu opublikowanego z powrotem do żądania ściągnięcia.

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"
            }
        });
}

Tworzenie nowego żądania ściągnięcia w celu przetestowania serwera stanu

Teraz, gdy serwer jest uruchomiony i nasłuchuje powiadomień o podpięcie usługi, utwórz żądanie ściągnięcia, aby go przetestować.

  1. Uruchom w widoku plików. Edytuj plik readme.md w repozytorium (lub inny plik, jeśli nie masz readme.md).

    Wybierz pozycję Edytuj z menu kontekstowego

  2. Wprowadź edycję i zatwierdź zmiany w repozytorium.

    Edytuj plik i wybierz pozycję Zatwierdź na pasku narzędzi

  3. Pamiętaj, aby zatwierdzić zmiany w nowej gałęzi, aby można było utworzyć żądanie ściągnięcia w następnym kroku.

    Wprowadź nową nazwę gałęzi i wybierz pozycję Zatwierdź

  4. Wybierz link Utwórz żądanie ściągnięcia.

    Wybierz pozycję Utwórz żądanie ściągnięcia na pasku sugestii

  5. Dodaj funkcję WIP w tytule, aby przetestować funkcjonalność aplikacji. Wybierz pozycję Utwórz , aby utworzyć żądanie ściągnięcia.

    Dodawanie funkcji WIP do domyślnego tytułu żądania ściągnięcia

  6. Po utworzeniu żądania ściągnięcia zostanie wyświetlona sekcja stanu z wpisem Praca w toku , który łączy się z adresem URL określonym w ładunku.

    Sekcja Stanu z wpisem Praca w toku.

  7. Zaktualizuj tytuł żądania ściągnięcia i usuń tekst funkcji WIP i zwróć uwagę, że stan zmienia się z Pracy w toku na Gotowe do przeglądu.

Następne kroki

  • W tym artykule przedstawiono podstawy tworzenia bezserwerowej funkcji platformy Azure, która nasłuchuje zdarzeń żądania ściągnięcia za pośrednictwem punktów zaczepienia usługi i może publikować komunikaty o stanie przy użyciu interfejsu API stanu. Aby uzyskać więcej informacji na temat interfejsu API stanu żądania ściągnięcia, zobacz dokumentację interfejsu API REST.
  • Konfigurowanie zasad gałęzi dla usługi zewnętrznej.