Compartilhar via


Usar Azure Functions para criar políticas de branch personalizadas

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

O fluxo de trabalho de solicitação de pull permite que os desenvolvedores recebam feedback sobre o seu código, vindos de colegas e ferramentas automatizadas. Ferramentas e serviços que não são da Microsoft também podem participar do fluxo de trabalho de PR usando a API de Status de PR. Este artigo orienta você na criação de uma política de branch personalizada usando o Azure Functions para validar PRs em um repositório Git do Azure DevOps. O Azure Functions elimina a necessidade de provisionar e manter servidores, mesmo à medida que sua carga de trabalho aumenta. Eles fornecem uma plataforma de computação totalmente gerenciada com alta confiabilidade e segurança.

Para obter mais informações sobre status de PR, consulte Personalizar e estender fluxos de trabalho de solicitação de pull com status de solicitação de pull.

Pré-requisitos

Categoria Requisitos
Organização Uma organização no Azure DevOps com um repositório Git.
Função do Azure Uma função do Azure, que implementa uma solução controlada por eventos sem servidor que se integra ao Azure DevOps para criar políticas de branch personalizadas e automatizar a validação de PR.
Hooks de serviço Configure ganchos de serviço para eventos de PR para notificar sua função do Azure quando uma solicitação pull for alterada.
Autenticação Token do Microsoft Entra ID com o escopo código (status) para ter permissão para alterar o status de PR. Para obter mais informações, consulte Autenticação do Microsoft Entra.

Criar uma função básica do Azure para ouvir eventos do Azure Repos

Crie sua primeira função do Azure. Em seguida, modifique o código no exemplo para que fique como o código abaixo.

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

Configurar um gancho de serviço para eventos de PR

Os ganchos de serviço são um recurso do Azure DevOps que pode alertar os serviços externos quando determinados eventos ocorrem. Para este exemplo, configure um gancho de serviço para eventos de PR. Sua função do Azure é notificada quando uma solicitação pull for alterada. Para receber solicitações POST quando as solicitações pull forem alteradas, você precisará fornecer o gancho de serviço com a URL da função do Azure.

Para este exemplo, configure dois ganchos de serviço. O primeiro é para o evento Pull request criada e o segundo é para o evento Pull request atualizada .

  1. Obtenha a URL da função no portal do Azure clicando em Obter URL da função na visualização da função no Azure e copie a URL.

    Obter url de função

    Copiar url da função

  2. Navegue até seu projeto no Azure DevOps, por exemplo, https://dev.azure.com/<your organization>/<your project name>

  3. No menu de navegação, passe o mouse sobre a engrenagem e selecione Ganchos de Serviço.

    Escolher ganchos de serviço no menu de administração

  4. Se for seu primeiro gancho de serviço, selecione + Criar assinatura.

    Selecione Criar uma nova assinatura na barra de ferramentas

    Se você já tiver outros ganchos de serviço configurados, selecione o sinal verde de mais (+) para criar uma nova assinatura de gancho de serviço.

    Selecione o sinal verde de mais para criar uma nova assinatura de gancho de serviço.

  5. Na caixa de diálogo Nova Assinatura de Ganchos de Serviço, selecione Web Hooks na lista de serviços e, em seguida, selecione Avançar.

    Selecione os webhooks na lista de serviços

  6. Selecione Solicitação de pull criada na lista de gatilhos de evento e selecione Avançar.

    Selecione a solicitação de pull criada na lista de gatilhos de evento

  7. Na página Ação, insira a URL copiada na etapa 1 na caixa de URL . Selecione Teste para enviar um evento de teste para o seu servidor.

    Insira a URL e selecione Testar para testar o gancho de serviço

    Na janela log de funções do Azure, você vê um POST de entrada que retornou um 200 OK, indicando que sua função recebeu o evento de gancho de serviço.

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

    Na janela Notificação de Teste, selecione a guia Resposta para ver os detalhes da resposta do servidor. Você deverá ver a resposta do servidor.

    Selecione a guia resposta para ver os resultados do teste

  8. Feche a janela Notificação de Teste e selecione Concluir para criar o gancho de serviço.

Percorra as etapas 2 a 8 novamente, mas desta vez configure o evento de Solicitação de pull atualizado .

Importante

Siga as etapas anteriores duas vezes e crie ganchos de serviço tanto para os eventos de solicitação de pull criados e os de solicitação de pull atualizados .

Crie uma solicitação de pull para verificar se sua função do Azure está recebendo notificações.

Postar status em PRs

Agora que o servidor pode receber eventos de gancho de serviço quando novas PRs forem criadas, atualize-o para fazer o postback do status para a PR. Você pode usar a carga JSON postada pelo gancho de serviço para determinar qual status definir em sua PR.

Atualize o código da função do Azure, semelhante ao exemplo a seguir.

Atualize o código com o nome da sua organização, o nome do projeto, o nome do repositório e o token de ID do Microsoft Entra. Para ter permissão para alterar o status de PR, o token requer o âmbito vso.code_status, que você pode obter por meio da autenticação do Microsoft Entra.

Importante

Esse código de exemplo armazena o token no código, simplificando o exemplo. É recomendável armazenar segredos no Azure Key Vault e recuperá-los de lá usando a identidade gerenciada para segurança aprimorada.

Este exemplo inspeciona o título da PR para ver se o usuário indicou se a PR é um trabalho em andamento adicionando WIP ao título. Nesse caso, o código de exemplo altera o status enviado à PR. Substitua o código em sua função do Azure pelo código a seguir, que atualiza o status enviado à 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"
            }
        });
}

Crie uma nova PR e teste o servidor de status

Agora que o servidor está em execução e monitorando notificações de gancho de serviço, crie uma solicitação de pull para testá-lo.

  1. Comece na visualização de arquivos. Edite o arquivo readme.md em seu repositório (ou em qualquer outro arquivo se você não tiver um readme.md).

    Selecione Editar no menu de contexto

  2. Faça uma edição e confirme as alterações no repositório.

    Editar o arquivo e selecionar Confirmar na barra de ferramentas

  3. Assegure-se de confirmar as alterações em uma nova ramificação para que você possa criar uma PR na próxima etapa.

    Insira um novo nome de branch e selecione Confirmar

  4. Selecione o link Criar uma solicitação de pull .

    Selecione Criar uma solicitação de pull na barra de sugestões

  5. Adicione wip no título para testar a funcionalidade do aplicativo. Selecione Criar para criar a PR.

    Adicionar WIP ao título de PR padrão

  6. Depois que a PR é criada, a seção de status é exibida, com a entrada Trabalho em andamento vinculada à URL especificada no payload.

    Seção Status com a entrada Trabalho em andamento.

  7. Atualize o título da PR e remova o texto WIP e observe que o status muda de Trabalho em andamento para Pronto para revisão.

Próximas etapas