Compartilhar via


Criar um servidor de status de pull request com Node.js

Azure DevOps Services | Servidor Azure DevOps | Azure DevOps Server 2022 | Azure DevOps Server 2020

O fluxo de trabalho de PR (solicitação de pull) oferece aos desenvolvedores a oportunidade de obter comentários sobre seu código de colegas e de ferramentas automatizadas. Ferramentas e serviços que não são da Microsoft podem participar do fluxo de trabalho de PR usando a API de Status de PR. Este artigo orienta você pelo processo de criação de um servidor de status para validar PRs em um repositório Git do Azure DevOps Services. 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.
Ferramentas - Visual Studio Code ou outro editor de código de sua escolha.
- Node.js. O download contém um instalador, que pode ser executado para instalar o runtime Node.js no computador local. Ao instalar o Node.js, assegure-se de conservar a parte do gerenciador de pacotes npm da instalação, que é selecionada por padrão.
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 um servidor Web básico usando o Express

As etapas nesta seção usam Express, que é uma estrutura da Web leve para Node.js que fornece muitos métodos de utilitário HTTP que simplificam a criação de um servidor Web. Essa estrutura fornece as funções básicas necessárias para ouvir eventos de PR.

  1. Na linha de comando, crie uma nova pasta de projeto para o servidor Web.

    mkdir pr-server
    cd pr-server
    
  2. Use o comando npm init para criar um novo arquivo package.json para o projeto.

    npm init
    

    Selecione Insira para aceitar os padrões para todas as opções, exceto o ponto de entrada. Alterar para app.js

    entry point: (index.js) app.js
    
  3. Instale o Express no diretório pr-server usando o comando a seguir. Isso instala o Express e o salva na lista de dependências.

    npm install express
    
  4. Crie um aplicativo Express como base para o servidor de status de PR. As etapas a seguir baseiam-se no exemplo 'Hello world' do Express .

    a. Abra a pasta do projeto no Visual Studio Code executando o comando a seguir da pr-server pasta.

    code .
    

    b. Crie um novo arquivo (Ctrl + N) e cole o código de exemplo a seguir para criar um servidor Express básico.

    const express = require('express')
    const app = express()
    
    app.get('/', function (req, res) {
    res.send('Hello World!')
    })
    
    app.listen(3000, function () {
    console.log('Example app listening on port 3000!')
    })
    

    c. Salve o arquivo como app.js.

  5. Execute o servidor Web básico usando o seguinte comando:

    node app.js
    

    Verifique se o servidor está em execução navegando até http://localhost:3000/.

Monitorar solicitações HTTP POST

O servidor Web receberá POST solicitações do Azure DevOps Services, portanto, você precisa lidar com essas solicitações em seu servidor.

  1. No final do arquivo app.js, adicione o código a seguir e salve o arquivo.

    app.post('/', function (req, res) {
        res.send('Received the POST')
    })
    
  2. Execute novamente seu servidor Web usando o seguinte comando:

    node app.js
    

Configurar um gancho de serviço para eventos de PR

Os ganchos de serviço são um recurso do Azure DevOps Services que pode alertar os serviços externos quando determinados eventos ocorrem. Para este exemplo, configure dois ganchos de serviço para eventos de PR, para que o servidor de status possa ser notificado. O primeiro é para o evento Pull request criada e o segundo é para o evento Pull request atualizada .

Para receber as notificações de gancho de serviço, exponha uma porta para a Internet pública. O utilitário ngrok é útil para fazer isso em um ambiente de desenvolvimento.

  1. Baixe e descompacte a versão ngrok apropriada para sua plataforma.

  2. Utilize o ngrok para começar a escutar na mesma porta que o seu servidor de exemplo: a porta 3000. Execute o comando a seguir em uma nova janela de comando.

    ngrok http 3000
    

    O Ngrok cria uma URL pública que encaminha para localhost:3000. Anote a URL, pois você precisa dela na próxima etapa. Ele se parece com o seguinte exemplo:

    http://c3c1bffa.ngrok.io
    
  3. Navegue até seu projeto no Azure DevOps, por exemplo, https://dev.azure.com/<your account>/<your project name>

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

    Captura de tela mostrando Escolher ganchos de serviço no menu de administração.

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

    Captura de tela mostra a opção Criar uma nova assinatura selecionada na barra de ferramentas.

    Se você já tiver outras integrações de serviço configuradas, selecione o botão "+ (+)" para criar uma nova assinatura de integração de serviço.

    Captura de tela mostrando o sinal verde de mais selecionado para criar uma assinatura de gancho de serviço.

  6. 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.

    A captura de tela mostra os webhooks selecionados na lista de serviços.

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

    Captura de tela mostrando a pull request criada selecionada na lista de gatilhos de evento.

  8. Na página Ação, insira a URL do ngrok na caixa URL . Selecione Teste para enviar um evento de teste para o seu servidor.

    Captura de tela mostrando a URL informada e o teste selecionado para testar o gancho de serviço.

    Na janela do console do ngrok, um POST de entrada retorna 200 OK, indicando que o servidor 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 um comprimento de conteúdo de 17 que corresponde ao comprimento da cadeia de caracteres do manipulador POST (por exemplo, "Recebeu o POST").

    Captura de tela mostra a aba de resposta selecionada para os resultados do teste.

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

Percorra as etapas 3 a 9 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 .

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.

  1. As solicitações de gancho de serviço incluem uma carga JSON que descreve o evento. Para ajudar a analisar o JSON retornado pelo gancho de serviço, instale o pacote body-parser .

    npm install body-parser
    
  2. Atualize app.js para usar o body-parser para analisar application/json.

    var bodyParser = require('body-parser')
    
    app.use(bodyParser.json())
    
  3. Para simplificar a criação de chamadas à API REST para o Azure Repos, instale o pacote azure-devops-node-api.

    npm install azure-devops-node-api 
    
  4. Atualize app.js para usar o pacote azure-devops-node-api, configurar os detalhes de uma conexão com sua conta e obter uma instância da API do Git.

    const vsts = require("azure-devops-node-api")
    
    const collectionURL = process.env.COLLECTIONURL    
    const token = process.env.TOKEN
    
    var authHandler = vsts.getBearerHandler(token)
    var connection = new vsts.WebApi(collectionURL, authHandler)
    var vstsGit = connection.getGitApi()
    
  5. Crie uma variável de ambiente para a URL da coleção, substituindo <your account> pelo nome da sua organização do Azure DevOps.

    setx COLLECTIONURL "https://dev.azure.com/<your account>"
    
  6. Obtenha um token de ID do Microsoft Entra para seu aplicativo usar. Os tokens de ID do Microsoft Entra são o método de autenticação recomendado para APIs REST do Azure DevOps. Você pode obter esses tokens pelas seguintes maneiras:

    • Opção 1: CLI do Azure (para desenvolvimento/teste)
      az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --query "accessToken" --output tsv
      
    • Opção 2: Principal de Serviço (para produção)
      1. Registre um aplicativo no Microsoft Entra ID
      2. Criar um segredo do cliente para o aplicativo
      3. Conceder permissões apropriadas ao aplicativo no Azure DevOps
      4. Usar as credenciais da entidade de serviço para programaticamente obter tokens

    Para obter mais informações, consulte Autenticação do Microsoft Entra.

  7. Crie uma variável de ambiente para o token de ID do Microsoft Entra.

    setx TOKEN "your-entra-id-token-here"
    

Para aplicativos de produção, você deve obter tokens de ID do Microsoft Entra de forma programática, em vez de usar tokens estáticos. Veja como implementar isso usando a MSAL (Biblioteca de Autenticação da Microsoft) para Node.js:

  1. Instale o pacote MSAL para Node.js:

    npm install @azure/msal-node
    
  2. Criar um módulo de provedor de token (tokenProvider.js):

    const { ConfidentialClientApplication } = require('@azure/msal-node');
    
    const clientConfig = {
        auth: {
            clientId: process.env.CLIENT_ID,
            clientSecret: process.env.CLIENT_SECRET,
            authority: `https://login.microsoftonline.com/${process.env.TENANT_ID}`
        }
    };
    
    const cca = new ConfidentialClientApplication(clientConfig);
    
    async function getAccessToken() {
        const clientCredentialRequest = {
            scopes: ['499b84ac-1321-427f-aa17-267ca6975798/.default']
        };
    
        try {
            const response = await cca.acquireTokenByClientCredential(clientCredentialRequest);
            return response.accessToken;
        } catch (error) {
            console.error('Error acquiring token:', error);
            throw error;
        }
    }
    
    module.exports = { getAccessToken };
    
  3. Atualize seu app.js para que utilize o provedor de tokens:

    const { getAccessToken } = require('./tokenProvider');
    
    // Instead of using a static token, get a fresh token
    app.post("/", async function (req, res) {
        try {
            const token = await getAccessToken();
            var authHandler = vsts.getBearerHandler(token);
            var connection = new vsts.WebApi(collectionURL, authHandler);
    
            // ... rest of your POST handler code
        } catch (error) {
            console.error('Authentication error:', error);
            res.status(500).send('Authentication failed');
        }
    });
    
  4. Atualize a função post() para ler os detalhes de PR do conteúdo do gancho de serviço. Você precisa desses valores para o status de postback.

    var repoId = req.body.resource.repository.id
    var pullRequestId = req.body.resource.pullRequestId
    var title = req.body.resource.title
    
  5. Compile o objeto status para postar na PR.

    State é uma enumeração do tipo GitStatusState. Use succeeded para indicar que a PR passou na verificação de status e está pronta para mesclagem.

    description é um valor de cadeia de caracteres que é exibido para o usuário na seção Status e no feed de atividades na exibição de detalhes da PR.

    O targetUrl é uma URL usada para criar um link para o texto de descrição na seção Status e no feed de atividades, que é onde os usuários podem ir para obter mais informações sobre o status, por exemplo, um relatório de build ou uma execução de teste. Se nenhuma URL for especificada, a descrição será exibida como texto sem nenhum link.

    O contexto name e genre são usados para categorizar o status e distingui-lo do status de postagem de outros serviços.

        var prStatus = {
            "state": "succeeded",
            "description": "Ready for review",
            "targetUrl": "https://visualstudio.microsoft.com",
            "context": {
                "name": "wip-checker",
                "genre": "continuous-integration"
            }
        }
    
  6. Em vez de postar o status succeeded de imediato, inspecione o título da PR para ver se o usuário indicou se ela é um trabalho em andamento adicionando WIP ao título. Nesse caso, altere o status postado de volta para a PR.

        if (title.includes("WIP")) {
            prStatus.state = "pending"
            prStatus.description = "Work in progress"
        }
    
  7. Por fim, poste o status usando o método createPullRequestStatus(). Ele requer o objeto de status, a ID do repositório e a ID da solicitação de pull. Imprima a resposta no console do node para que você possa ver o resultado da postagem.

    vstsGit.createPullRequestStatus(prStatus, repoId, pullRequestId).then( result => {
        console.log(result)
    })
    
  8. O método resultante deve ser semelhante a este:

    app.post("/", async function (req, res) {
        try {
            // Get the details about the PR from the service hook payload
            var repoId = req.body.resource.repository.id
            var pullRequestId = req.body.resource.pullRequestId
            var title = req.body.resource.title
    
            // Build the status object that we want to post.
            // Assume that the PR is ready for review...
            var prStatus = {
                "state": "succeeded",
                "description": "Ready for review",
                "targetUrl": "https://visualstudio.microsoft.com",
                "context": {
                    "name": "wip-checker",
                    "genre": "continuous-integration"
                }
            }
    
            // Check the title to see if there is "WIP" in the title.
            if (title.includes("WIP")) {
                // If so, change the status to pending and change the description.
                prStatus.state = "pending"
                prStatus.description = "Work in progress"
            }
    
            // Get the Git API instance and post the status to the PR
            const gitApi = await vstsGit
            const result = await gitApi.createPullRequestStatus(prStatus, repoId, pullRequestId)
            console.log(result)
    
            res.send("Received the POST")
        } catch (error) {
            console.error('Error processing PR status:', error)
            res.status(500).send('Error processing request')
        }
    })
    
  9. Salve app.js e reinicie seu aplicativo Node.js.

    node app.js
    

Criar uma PR para testar 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).

    Captura de tela mostra o botão Editar selecionado no menu de contexto.

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

    Captura de tela mostra a edição do arquivo e o botão Confirmar selecionado 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.

    Captura de tela mostrando o nome do novo branch inserido e o botão Confirmar selecionado.

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

    A captura de tela mostra a opção

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

    Captura de tela mostrando WIP adicionado 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 conteúdo.

    Captura de tela mostra a seção de 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.