Freigeben über


Verwenden von Azure Functions zum Erstellen benutzerdefinierter Branchrichtlinien

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

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 am PR-Workflow teilnehmen. Dieser Artikel führt Sie durch den Prozess zum Erstellen einer benutzerdefinierten Branchrichtlinie 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 Workload zunimmt. Azure Functions bietet eine vollständig verwaltete Computeplattform mit hoher Zuverlässigkeit und Sicherheit.

Weitere Informationen zu PR-Status finden Sie unter Anpassen und Erweitern von Pull Request-Workflows mit Pull Request-Status.

Voraussetzungen

Eine Organisation in Azure DevOps mit einem Git-Repository. Wenn Sie über keine Organisation verfügen, registrieren Sie sich, um Code in unbegrenzten kostenlosen privaten Git-Repositorys hochzuladen und freizugeben.

Erstellen einer einfachen Azure-Funktion zum Lauschen auf Azure Repos-Ereignisse

Befolgen Sie die Dokumentation zum Erstellen Ihrer ersten Azure-Funktion, um eine einfache Funktion zu erstellen. Ändern Sie den Code im Beispiel wie folgt:

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 Service Hooks für PR-Ereignisse

Service Hooks sind ein Feature von Azure DevOps Services, das externe Dienste benachrichtigen kann, wenn bestimmte Ereignisse auftreten. Für dieses Beispiel sollten Sie einen Service Hook für PR-Ereignisse einrichten, damit Ihre Azure-Funktion benachrichtigt wird, wenn sich ein Pull Request ändert. Um POST-Anforderungen empfangen zu können, wenn sich Pull Requests ändern, müssen Sie den Service Hook mit der Azure-Funktions-URL bereitstellen.

Für dieses Beispiel müssen Sie zwei Service Hooks konfigurieren. Der erste ist für das Pull Request erstellt-Ereignis und der zweite für das Pull Request aktualisiert-Ereignis.

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

    Get-Funktion

    Funktions-URL kopieren

  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ü mit dem Mauszeiger auf das Zahnrad, und wählen Sie Service Hooks aus.

    Service Hooks im Administratormenü auswählen

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

    „Neues Abonnement erstellen“ auswählen in der Symbolleiste

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

    Grünes Pluszeichen auswählen, um ein neues Service Hook-Abonnement zu erstellen.

  5. Wählen Sie im Dialogfeld „Neues Service Hook-Abonnement“ die Option Webhooks aus der Liste der Dienste aus, und wählen Sie dann Weiter aus.

    Webhooks aus der Liste der Dienste auswählen

  6. Wählen Sie in der Liste der Ereignistrigger Pull Request erstellt aus, und wählen Sie dann Weiter aus.

    In der Liste der Ereignistrigger „Pull Request erstellt“ auswählen

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

    Die URL eingeben und „Testen“ auswählen, um den Service Hook zu testen

    Im Azure-Funktionsprotokollfenster wird ein eingehender POST angezeigt, der ein 200 OK zurückgegeben hat, was anzeigt, dass Ihre Funktion das Service Hook-Ereignis empfangen hat.

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

    Wählen Sie im Fenster „Testbenachrichtigung“ die Registerkarte „Antwort“ aus, um die Details der Antwort von Ihrem Server anzuzeigen. Die Antwort von Ihrem Server sollte angezeigt werden.

    Registerkarte „Antwort“ auswählen, um die Ergebnisse des Tests anzuzeigen

  8. Schließen Sie das Fenster „Testbenachrichtigung“, und wählen Sie Fertig stellen aus, um den Service Hook zu erstellen.

Führen Sie die Schritte 2 bis 8 erneut aus, aber konfigurieren Sie dieses Mal das Pull Request aktualisiert-Ereignis.

Wichtig

Stellen Sie sicher, dass Sie die vorherigen Schritte zweimal ausführen, und Service Hooks sowohl für das Pull Request erstellt-Ereignis als auch für das Pull Request aktualisiert-Ereignis erstellen.

Erstellen Sie einen Pull Request, um zu überprüfen, ob Ihre Azure-Funktion Benachrichtigungen empfängt.

Bereitstellen des Status in PRs

Nachdem Ihr Server nun Service Hook-Ereignisse empfangen kann, wenn neue PRs erstellt werden, aktualisieren Sie ihn so, dass er den Status an den PR zurückpostet. Sie können die vom Service Hook bereitgestellte JSON-Nutzlast verwenden, um zu bestimmen, welcher Status für Ihren PR festgelegt werden soll.

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

Aktualisieren Sie den Code auf jeden Fall mit Ihrem Organisationsnamen, Projektnamen, Repositorynamen und dem PAT-Token. Um die Berechtigung zum Ändern des PR-Status zu erhalten, erfordert das PAT den vso.code_status-Umfang, den Sie gewähren können, indem Sie auf der Seite Persönliches Zugriffstoken erstellen den Umfang Code (Status) auswählen.

Wichtig

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

In diesem Beispiel wird der PR-Titel untersucht, um festzustellen, ob der Benutzer angezeigt hat, ob der PR in Bearbeitung ist, indem dem Titel WIP (Work in Progress) hinzugefügt wurde. Wenn ja, ändert der Beispielcode Den an den PR zurückgeposteten Status. Ersetzen Sie den Code in Ihrer Azure-Funktion durch den folgenden Code, um die Aktualisierung des an den PR zurückgeposteten Status zu implementieren.

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 nun ausgeführt wird und auf Service Hook-Benachrichtigungen lauscht, erstellen Sie einen Pull Request, um ihn zu testen.

  1. Beginnen Sie in der Dateiansicht. Bearbeiten Sie die „readme.md“-Datei in Ihrem Repository (oder einer anderen Datei, wenn Sie keine „readme.md“ haben).

    „Bearbeiten“ im Kontextmenü auswählen

  2. Nehmen Sie eine Bearbeitung vor, und committen Sie die Änderungen in das Repository.

    Bearbeiten der Datei und Auswählen von „Committen“ in der Symbolleiste

  3. Stellen Sie sicher, dass Sie die Änderungen in einen neuen Branch committen, damit Sie im nächsten Schritt einen PR erstellen können.

    Eingeben eines neuen Branchnamens und Auswählen von „Committen“

  4. Wählen Sie den Link Pull Request erstellen aus.

    Auswählen von „Pull Request erstellen“ auf der Vorschlagsleiste

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

    Hinzufügen von „WIP“ zum PR-Standardtitel

  6. Nachdem der PR erstellt wurde, wird der Abschnitt „Status“ mit dem Eintrag Work in progress (WIP, In Bearbeitung) angezeigt, der mit der in der Nutzlast angegebenen URL verknüpft ist.

    Abschnitt „Status“ mit Eintrag „Work in progress“ (WIP, In Bearbeitung).

  7. Aktualisieren Sie den PR-Titel, entfernen Sie den Text WIP, und beachten Sie, dass sich der Status von In Bearbeitung (Work in Progress, WIP) in Bereit für Prüfung ändert.

Nächste Schritte