Partilhar via


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

Serviços de DevOps do Azure | Azure DevOps Server | Azure DevOps Server 2022

O fluxo de trabalho de pull request (PR) oferece aos desenvolvedores a oportunidade de obter feedback sobre seu código de colegas e de ferramentas automatizadas. As ferramentas e os serviços que não sejam da Microsoft podem participar do fluxo de trabalho de RP usando a API de Status de RP . Este artigo orienta você pelo processo de criação de um servidor de status para validar PRs em um repositório Git dos Serviços de DevOps do Azure. Para obter mais informações sobre o status de RP, consulte Personalizar e estender fluxos de trabalho de solicitação pull com status de solicitação pull.

Pré-requisitos

Categoria Requerimentos
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 tu podes executar para instalar o tempo de execução Node.js na tua máquina local. Ao instalar o Node.js, certifique-se de manter o gerenciador de pacotes npm parte da instalação, que é selecionada por padrão.
Autenticação Token de ID do Microsoft Entra com o escopo Código (status) para ter permissão para alterar o status de RP. 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 o 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. Esta estrutura fornece-lhe as funções básicas necessárias para escutar eventos de relações públicas.

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

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

    npm init
    

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

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

    npm install express
    
  4. Crie um aplicativo Express para desenvolver para o servidor de status de RP. As etapas a seguir são baseadas no exemplo "Hello World" do Express .

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

    code .
    

    b) Crie um novo arquivo (Ctrl + N) e cole no 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/.

Ouvir solicitações HTTP POST

O servidor Web receberá solicitações de POST dos Serviços de DevOps do Azure, 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 o servidor Web usando o seguinte comando:

    node app.js
    

Configurar um gancho de serviço para eventos de RP

Os ganchos de serviço são um recurso dos Serviços de DevOps do Azure que pode alertar serviços externos quando determinados eventos ocorrem. Para este exemplo, configure dois ganchos de serviço para eventos de RP, para que o servidor de status possa ser notificado. A primeira é para o evento de criação da solicitação de pull request , e a segunda é para o evento de atualização da solicitação de pull request .

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

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

  2. Use ngrok para começar a ouvir na mesma porta que o seu servidor de exemplo - porta 3000. Execute o seguinte comando em uma nova janela de comando.

    ngrok http 3000
    

    O Ngrok cria uma URL pública que encaminha para localhost:3000. Anote o URL, pois vai precisar dele na próxima etapa. É semelhante ao 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 rato sobre o ícone de engrenagem e selecione Ganchos de Serviço.

    Captura de ecrã mostra Escolher integrações 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 já tiver outros hooks de serviço configurados, selecione o botão de adição (+) para criar uma nova subscrição de hook de serviço.

    Captura de tela mostra selecionado o plus para criar uma nova assinatura de gancho de serviço.

  6. Na caixa de diálogo Nova Subscrição de Ganchos de Serviço, selecione de Ganchos da Web na lista de serviços e, em seguida, selecione Seguinte.

    Captura de ecrã mostra os webhooks selecionados da lista de serviços.

  7. Selecione solicitação pull criada na lista de gatilhos de evento e, em seguida, selecione Avançar.

    Captura de tela mostra a solicitação pull selecionada criada a partir da lista de gatilhos de evento.

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

    A captura de ecrã mostra o URL inserido e o teste selecionado para testar a ligação ao serviço.

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

    Captura de tela mostra a guia 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.

Passe pelas etapas 3 a 9 novamente, mas desta vez configure a solicitação Pull atualizada evento.

Importante

Certifique-se de passar pelas etapas anteriores duas vezes e de criar ganchos de serviço para os eventos de o pull request criado e o pull request atualizado.

Postar status para RPs

Agora que seu servidor pode receber eventos de gancho de serviço quando novos PRs são criados, atualize-o para postar de volta o status para o PR.

  1. As solicitações de gancho de serviço incluem uma carga JSON descrevendo 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 o app.js para usar o body-parser para analisar o application/json.

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

    npm install azure-devops-node-api 
    
  4. Atualize o app.js para usar o pacote azure-devops-node-api, configure os detalhes de uma conexão com a sua conta e obtenha 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 sua URL de 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 Microsoft Entra ID são o método de autenticação recomendado para APIs REST do Azure DevOps. Você pode obter esses tokens através das 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. Registar uma aplicação no Microsoft Entra ID
      2. Criar um segredo do cliente para o aplicativo
      3. Conceder permissões apropriadas ao aplicativo no Azure DevOps
      4. Use as credenciais da entidade de serviço para obter tokens programaticamente

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

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

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

Para aplicações de produção, deve obter tokens do Microsoft Entra ID programaticamente em vez de usar tokens estáticos. Veja como implementar isso usando a Biblioteca de Autenticação da Microsoft (MSAL) para Node.js:

  1. Instale o pacote MSAL para o Node.js.

    npm install @azure/msal-node
    
  2. Crie 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 o seu app.js para utilizar o fornecedor de token.

    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 RP da carga útil do gancho de serviço. Você precisa desses valores para enviar novamente o status.

    var repoId = req.body.resource.repository.id
    var pullRequestId = req.body.resource.pullRequestId
    var title = req.body.resource.title
    
  5. Crie o objeto de status para postar no PR.

    State é um enum do tipo GitStatusState. Use succeeded para indicar que o PR passou na verificação de status e está pronto para integrar.

    O description é um valor de texto que é exibido para o usuário na seção de Status e no feed de atividades na visualização de detalhes de PR.

    O targetUrl é uma URL usada para criar um link para o texto da 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 compilação ou execução de teste. Se nenhum URL for especificado, a descrição aparecerá como texto sem link.

    Os contextos name e genre são usados para categorizar o status e distingui-lo de outros serviços de publicação de status.

        var prStatus = {
            "state": "succeeded",
            "description": "Ready for review",
            "targetUrl": "https://visualstudio.microsoft.com",
            "context": {
                "name": "wip-checker",
                "genre": "continuous-integration"
            }
        }
    
  6. Em vez de publicar o estado succeeded imediatamente, verifique o título do PR para ver se o utilizador indicou que o PR é um trabalho em andamento, adicionando WIP ao título. Em caso afirmativo, altere o status postado de volta para o PR.

        if (title.includes("WIP")) {
            prStatus.state = "pending"
            prStatus.description = "Work in progress"
        }
    
  7. Finalmente, poste o status usando o método createPullRequestStatus(). Ele requer o objeto de status, o ID de repositório e o ID de solicitação pull. Envie a resposta para o console do nó 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 ter esta aparência:

    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. Guarde app.js e reinicie a sua aplicação Node.

    node app.js
    

Criar um novo PR para testar o servidor de estado

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

  1. Comece na visualização de arquivos. Edite o arquivo readme.md em seu repositório (ou 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.

    A captura de tela mostra a edição do arquivo e a seleção do botão Confirmar na barra de ferramentas.

  3. Certifique-se de confirmar as alterações num novo ramo para criar um PR na próxima etapa.

    Captura de tela mostra o novo nome da ramificação inserido e o botão Confirmar selecionado.

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

    Captura de tela mostra a opção Criar uma solicitação pull selecionada na barra de sugestões.

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

    Captura de tela mostra WIP adicionado ao título PR padrão.

  6. Depois que o PR é criado, a seção de status é exibida com a entrada Work in progress que vincula à URL especificada na carga útil.

    Captura de tela mostra a seção de status com a entrada Trabalho em andamento.

  7. Atualize o título de RP e remova o texto do WIP e observe que o status muda de Work in progress para Ready for review.