Verwenden von Azure Functions zum Erstellen benutzerdefinierter Branchrichtlinien

Azure DevOps Services | Azure DevOps Server 2020 | Azure DevOps Server 2019 | TFS 2018

Der Pull request (PR)-Workflow bietet Entwicklern die Möglichkeit, Feedback zu ihrem Code von Peers sowie von automatisierten Tools zu erhalten. Tools und Dienste von Drittanbietern können mithilfe der PR-Status-API an dem PR-Workflow teilnehmen. Dieser Artikel führt Sie durch den Prozess des Erstellens einer benutzerdefinierten Verzweigungsrichtlinie mithilfe von Azure Functions, um PRs in einem Azure DevOps Services Git-Repository zu überprüfen. Mit Azure Functions müssen Sie sich keine Gedanken über die Bereitstellung und Wartung von Servern machen, insbesondere, wenn Ihre Arbeitsauslastung wächst. Azure Functions bieten eine voll verwaltete Computeplattform mit hoher Zuverlässigkeit und Sicherheit.

Weitere Informationen zum PR-Status finden Sie unter Anpassen und Erweitern von Pullanforderungsworkflows mit Pullanforderungsstatus.

Voraussetzungen

Eine Organisation in Azure DevOps mit einem Git-Repo. Wenn Sie keine Organisation haben, registrieren Sie sich, um Code in kostenlosen privaten Git-Repositorys hochzuladen und freizugeben.

Erstellen einer grundlegenden Azure-Funktion zum Überwachen von Azure Repos Ereignissen

Folgen Sie der Erstellung Ihrer ersten Azure-Funktionsdokumentation , um eine einfache Funktion zu erstellen. Ändern Sie den Code im Beispiel so, dass es wie folgt aussieht:

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

Konfigurieren eines Diensthakens für PR-Ereignisse

Diensthaken sind ein Azure DevOps Services Feature, das externe Dienste benachrichtigen kann, wenn bestimmte Ereignisse auftreten. In diesem Beispiel möchten Sie einen Dienst-Hook für PR-Ereignisse einrichten, ihre Azure-Funktion wird benachrichtigt, wenn sich eine Pullanforderung ändert. Um Anforderungen zu empfangen POST , wenn Pullanforderungen geändert werden, müssen Sie den Dienst-Hook mit der Azure-Funktions-URL bereitstellen.

Für dieses Beispiel müssen Sie 2 Dienst-Hooks konfigurieren. Die erste wird für das erstellte Pull-Anforderungsereignis verwendet, und der zweite wird für das aktualisierte Pull-Anforderungsereignis verwendet.

  1. Rufen Sie die Funktions-URL aus dem Azure-Portal ab, indem Sie in der Azure-Funktionsansicht auf die URL der Get-Funktion klicken und die URL kopieren.

    Get function url

    Copy function url

  2. Navigieren Sie zu Ihrem Projekt in Azure DevOps, z. B.https://dev.azure.com/<your organization>/<your project name>

  3. Zeigen Sie im Navigationsmenü auf das Zahnrad , und wählen Sie "Service Hooks" aus.

    Choose Service hooks from the admin menu

  4. Wenn dies Ihr erster Dienst-Hook ist, wählen Sie +Abonnement erstellen aus.

    Select Create a new subscription from the toolbar

    Wenn Sie bereits andere Dienst-Hooks konfiguriert haben, wählen Sie das grüne Plus aus (+) , um ein neues Dienst-Hook-Abonnement zu erstellen.

    Select the green plus to create a new service hook subscription.

  5. Wählen Sie im Dialogfeld "Neues Dienst-Hooks-Abonnement" Web-Hooks aus der Liste der Dienste aus, und wählen Sie dann "Weiter" aus.

    Select web hooks from the list of services

  6. Wählen Sie pull-Anforderung aus der Liste der Ereignistrigger aus, und wählen Sie dann "Weiter" aus.

    Select pull request created from the list of event triggers

  7. Geben Sie auf der Seite "Aktion" die URL ein, die Sie in Schritt 1 im URL-Feld kopiert haben. Wählen Sie "Testen " aus, um ein Testereignis an Ihren Server zu senden.

    Enter the URL and select Test to test the service hook

    Im Azure-Funktionsprotokollfenster wird ein eingehender Wert angezeigt, der ein 200 OKeingehendes POST Ereignis zurückgibt, das angibt, dass Ihre Funktion das Dienst-Hook-Ereignis empfangen hat.

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

    Wählen Sie im Fenster "Benachrichtigung testen" die Registerkarte "Antwort" aus, um die Details der Antwort von Ihrem Server anzuzeigen. Sie sollten die Antwort von Ihrem Server sehen.

    Select the response tab to see the results of the test

  8. Schließen Sie das Fenster "Benachrichtigung testen", und wählen Sie " Fertig stellen" aus, um den Dienst-Hook zu erstellen.

Führen Sie die Schritte 2-8 erneut durch, aber dieses Mal konfigurieren Sie das aktualisierte Pull-Anforderungsereignis .

Wichtig

Führen Sie die vorherigen Schritte zweimal durch und erstellen Sie Dienst-Hooks für die aktualisierten Ereignisse der Pullanforderung und pull-Anforderung.

Erstellen Sie eine Pullanforderung, um zu überprüfen, ob Ihre Azure-Funktion Benachrichtigungen empfängt.

Poststatus für PRs

Nachdem Ihr Server Dienst-Hook-Ereignisse empfangen kann, wenn neue PRs erstellt werden, aktualisieren Sie sie, um den Status zurück auf die PR zu posten. Sie können die JSON-Nutzlast verwenden, die vom Dienst-Hook gepostet wurde, um zu bestimmen, welcher Status für Ihre PR festgelegt werden soll.

Aktualisieren Sie den Code Ihrer Azure-Funktion so, dass es wie im folgenden Beispiel aussieht.

Aktualisieren Sie den Code mit dem Namen Ihrer Organisation, dem Projektnamen, dem Repositorynamen und dem PAT-Token. Um über die Berechtigung zum Ändern des PR-Status zu verfügen, erfordert das PAT vso.code_status Bereich, den Sie erteilen können, indem Sie den Codebereich (Status) auf der Seite "Erstellen eines persönlichen Zugriffstokens" auswählen.

Wichtig

In diesem Beispielcode wird der PAT im Code gespeichert, um das Beispiel zu vereinfachen. Es wird empfohlen, geheime Schlüssel in KeyVault zu speichern und von dort abzurufen.

In diesem Beispiel wird der PR-Titel überprüft, um festzustellen, ob der Benutzer angegeben hat, ob es sich bei der PR um eine Arbeit handelt, indem WIP zum Titel hinzugefügt wird. Wenn ja, ändert der Beispielcode den Status, der wieder in die PR gepostet wurde. Ersetzen Sie den Code in Ihrer Azure-Funktion durch den folgenden Code, um die Aktualisierung des Status zu implementieren, der zurück in die PR gepostet wurde.

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

Erstellen eines neuen PR zum Testen des Statusservers

Nachdem Ihr Server jetzt ausgeführt wird und Dienst-Hook-Benachrichtigungen lauscht, erstellen Sie eine Pullanforderung, um sie auszutesten.

  1. Beginnen Sie in der Dateiansicht. Bearbeiten Sie die readme.md Datei in Ihrem Repository (oder eine andere Datei, wenn Sie keine readme.md haben).

    Select Edit from the context menu

  2. Nehmen Sie eine Bearbeitung vor, und übernehmen Sie die Änderungen am Repository.

    Edit the file and select Commit from the toolbar

  3. Stellen Sie sicher, dass Sie die Änderungen an einer neuen Verzweigung übernehmen, damit Sie im nächsten Schritt eine PR erstellen können.

    Enter a new branch name and select Commit

  4. Wählen Sie den Link " Pullanforderung erstellen" aus.

    Select Create a pull request from the suggestion bar

  5. Fügen Sie WIP im Titel hinzu, um die Funktionalität der App zu testen. Wählen Sie "Erstellen" aus, um die PR zu erstellen.

    Add WIP to the default PR title

  6. Nachdem die PR erstellt wurde, wird der Statusabschnitt mit dem Statuseintrag "Arbeit in Bearbeitung " angezeigt, der eine Verknüpfung mit der in der Nutzlast angegebenen URL enthält.

    Status section with Work in progress entry.

  7. Aktualisieren Sie den PR-Titel, und entfernen Sie den WIP-Text , und beachten Sie, dass sich der Status von "Arbeit" in"Bereit zur Überprüfung" ändert.

Nächste Schritte