{"meta":{"title":"Reentrega automática de entregas com falha para um webhook de repositório","intro":"Você pode escrever um script para lidar com falhas nas entregas de um webhook de repositório.","product":"Webhooks","breadcrumbs":[{"href":"/pt/webhooks","title":"Webhooks"},{"href":"/pt/webhooks/using-webhooks","title":"Usando webhooks"},{"href":"/pt/webhooks/using-webhooks/automatically-redelivering-failed-deliveries-for-a-repository-webhook","title":"Redistribuição automática para o repositório"}],"documentType":"article"},"body":"# Reentrega automática de entregas com falha para um webhook de repositório\n\nVocê pode escrever um script para lidar com falhas nas entregas de um webhook de repositório.\n\n## Sobre a reentrega automática de entregas com falha\n\nEste artigo descreve como escrever um script para localizar e reenviar entregas com falha para um webhook de repositório. Para obter mais informações sobre entregas com falha, confira [Como lidar com falhas de entrega de webhook](/pt/webhooks/using-webhooks/handling-failed-webhook-deliveries).\n\nEste exemplo mostra:\n\n* Um script que localizará e reenviará entregas com falha para um webhook de repositório\n* Quais credenciais seu script precisará e como armazenar as credenciais com segurança como GitHub Actions segredos\n* Um GitHub Actions fluxo de trabalho que pode acessar suas credenciais com segurança e executar o script periodicamente\n\nEste exemplo usa GitHub Actions, mas você também pode executar esse script em seu servidor que lida com entregas de webhook. Para obter mais informações, confira [Métodos alternativos](#alternative-methods).\n\n## Armazenamento de credenciais para o script\n\nO `GITHUB_TOKEN` interno não tem permissões suficientes para reentregar webhooks. Em vez de usar `GITHUB_TOKEN`, este exemplo usa um personal access token. Como alternativa, em vez de criar um personal access token, você pode criar um GitHub App e usar as credenciais do aplicativo para gerar um token de acesso de instalação durante o fluxo de trabalho do GitHub Actions. Para saber mais, confira [Fazer solicitações de API autenticadas com um aplicativo GitHub em um fluxo de trabalho GitHub Actions](/pt/apps/creating-github-apps/authenticating-with-a-github-app/making-authenticated-api-requests-with-a-github-app-in-a-github-actions-workflow).\n\n1. Crie um personal access token com o seguinte acesso. Para saber mais, confira [Gerenciar seus tokens de acesso pessoal](/pt/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens).\n   * Para um fine-grained personal access token, conceda o token:\n     * Acesso ao repositório onde seu webhook foi criado\n     * Acesso ao repositório em que esse fluxo de trabalho será executado\n     * Acesso para gravação à permissão de webhooks do repositório\n     * Permissão de acesso de gravação a variáveis do repositório\n   * Para um personal access token (classic), conceda ao token o escopo `repo`.\n2. Armazene-o personal access token como um GitHub Actions segredo no repositório em que você deseja que o fluxo de trabalho seja executado. Para saber mais, confira [Usar segredos em ações do GitHub](/pt/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets).\n\n## Adicionando um fluxo de trabalho que executará o script\n\nEsta seção demonstra como você pode usar um GitHub Actions fluxo de trabalho para acessar com segurança as credenciais armazenadas na seção anterior, definir variáveis de ambiente e executar periodicamente um script para localizar e resgatar entregas com falha.\n\nCopie esse GitHub Actions fluxo de trabalho em um arquivo YAML no `.github/workflows` diretório no repositório em que você deseja que o fluxo de trabalho seja executado. Substitua os espaços reservados na etapa `Run script` conforme descrito abaixo.\n\n```yaml copy annotate\n#\nname: Redeliver failed webhook deliveries\n\n# This workflow runs every 6 hours or when manually triggered.\non:\n  schedule:\n    - cron: '20 */6 * * *'\n  workflow_dispatch:\n\n# This workflow will use the built in `GITHUB_TOKEN` to check out the repository contents. This grants `GITHUB_TOKEN` permission to do that.\npermissions:\n  contents: read\n\n#\njobs:\n  redeliver-failed-deliveries:\n    name: Redeliver failed deliveries\n    runs-on: ubuntu-latest\n    steps:\n      # This workflow will run a script that is stored in the repository. This step checks out the repository contents so that the workflow can access the script.\n      - name: Check out repo content\n        uses: actions/checkout@v6\n\n      # This step sets up Node.js. The script that this workflow will run uses Node.js.\n      - name: Setup Node.js\n        uses: actions/setup-node@v7\n        with:\n          node-version: '18.x'\n\n      # This step installs the octokit library. The script that this workflow will run uses the octokit library.\n      - name: Install dependencies\n        run: npm install octokit\n\n      # This step sets some environment variables, then runs a script to find and redeliver failed webhook deliveries.\n      # - Replace `YOUR_SECRET_NAME` with the name of the secret where you stored your personal access token.\n      # - Replace `YOUR_REPO_OWNER` with the owner of the repository where the webhook was created.\n      # - Replace `YOUR_REPO_NAME` with the name of the repository where the webhook was created.\n      # - Replace `YOUR_HOOK_ID` with the ID of the webhook.\n      # - Replace `YOUR_LAST_REDELIVERY_VARIABLE_NAME` with the name that you want to use for a configuration variable that will be stored in the repository where this workflow is stored. The name can be any string that contains only alphanumeric characters and `_`, and does not start with `GITHUB_` or a number. For more information, see [AUTOTITLE](/actions/learn-github-actions/variables#defining-configuration-variables-for-multiple-workflows).\n      \n      - name: Run script\n        env:\n          TOKEN: ${{ secrets.YOUR_SECRET_NAME }}\n          REPO_OWNER: 'YOUR_REPO_OWNER'\n          REPO_NAME: 'YOUR_REPO_NAME'\n          HOOK_ID: 'YOUR_HOOK_ID'\n          LAST_REDELIVERY_VARIABLE_NAME: 'YOUR_LAST_REDELIVERY_VARIABLE_NAME'\n          \n          WORKFLOW_REPO_NAME: ${{ github.event.repository.name }}\n          WORKFLOW_REPO_OWNER: ${{ github.repository_owner }}\n        run: |\n          node .github/workflows/scripts/redeliver-failed-deliveries.js\n```\n\n## Adicionando o script\n\nEsta seção demonstra como você pode escrever um script para localizar e reenviar entregas com falha.\n\nCopie esse script em um arquivo chamado `.github/workflows/scripts/redeliver-failed-deliveries.js` no mesmo repositório em que você salvou o arquivo de GitHub Actions fluxo de trabalho acima.\n\n```javascript copy annotate\n// This script uses GitHub's Octokit SDK to make API requests. For more information, see [AUTOTITLE](/rest/guides/scripting-with-the-rest-api-and-javascript).\nconst { Octokit } = require(\"octokit\");\n\n//\nasync function checkAndRedeliverWebhooks() {\n  // Get the values of environment variables that were set by the GitHub Actions workflow.\n  const TOKEN = process.env.TOKEN;\n  const REPO_OWNER = process.env.REPO_OWNER;\n  const REPO_NAME = process.env.REPO_NAME;\n  const HOOK_ID = process.env.HOOK_ID;\n  const LAST_REDELIVERY_VARIABLE_NAME = process.env.LAST_REDELIVERY_VARIABLE_NAME;\n  \n  const WORKFLOW_REPO_NAME = process.env.WORKFLOW_REPO_NAME;\n  const WORKFLOW_REPO_OWNER = process.env.WORKFLOW_REPO_OWNER;\n\n  // Create an instance of `Octokit` using the token values that were set in the GitHub Actions workflow.\n  const octokit = new Octokit({ \n    auth: TOKEN,\n  });\n\n  try {\n    // Get the last time that this script ran from the configuration variable. If the variable is not defined, use the current time minus 24 hours.\n    const lastStoredRedeliveryTime = await getVariable({\n      variableName: LAST_REDELIVERY_VARIABLE_NAME,\n      repoOwner: WORKFLOW_REPO_OWNER,\n      repoName: WORKFLOW_REPO_NAME,\n      octokit,\n    });\n    const lastWebhookRedeliveryTime = lastStoredRedeliveryTime || (Date.now() - (24 * 60 * 60 * 1000)).toString();\n\n    // Record the time that this script started redelivering webhooks.\n    const newWebhookRedeliveryTime = Date.now().toString();\n\n    // Get the webhook deliveries that were delivered after `lastWebhookRedeliveryTime`.\n    const deliveries = await fetchWebhookDeliveriesSince({\n      lastWebhookRedeliveryTime,\n      repoOwner: REPO_OWNER,\n      repoName: REPO_NAME,\n      hookId: HOOK_ID,\n      octokit,\n    });\n\n    // Consolidate deliveries that have the same globally unique identifier (GUID). The GUID is constant across redeliveries of the same delivery.\n    let deliveriesByGuid = {};\n    for (const delivery of deliveries) {\n      deliveriesByGuid[delivery.guid]\n        ? deliveriesByGuid[delivery.guid].push(delivery)\n        : (deliveriesByGuid[delivery.guid] = [delivery]);\n    }\n\n    // For each GUID value, if no deliveries for that GUID have been successfully delivered within the time frame, get the delivery ID of one of the deliveries with that GUID.\n    //\n    // This will prevent duplicate redeliveries if a delivery has failed multiple times.\n    // This will also prevent redelivery of failed deliveries that have already been successfully redelivered.\n    let failedDeliveryIDs = [];\n    for (const guid in deliveriesByGuid) {\n      const deliveries = deliveriesByGuid[guid];\n      const anySucceeded = deliveries.some(\n        (delivery) => delivery.status === \"OK\"\n      );\n      if (!anySucceeded) {\n        failedDeliveryIDs.push(deliveries[0].id);\n      }\n    }\n\n    // Redeliver any failed deliveries.\n    for (const deliveryId of failedDeliveryIDs) {\n      await redeliverWebhook({\n        deliveryId,\n        repoOwner: REPO_OWNER,\n        repoName: REPO_NAME,\n        hookId: HOOK_ID,\n        octokit,\n      });\n    }\n\n    // Update the configuration variable (or create the variable if it doesn't already exist) to store the time that this script started.\n    // This value will be used next time this script runs.\n    await updateVariable({\n      variableName: LAST_REDELIVERY_VARIABLE_NAME,\n      value: newWebhookRedeliveryTime,\n      variableExists: Boolean(lastStoredRedeliveryTime),\n      repoOwner: WORKFLOW_REPO_OWNER,\n      repoName: WORKFLOW_REPO_NAME,\n      octokit,\n    });\n\n    // Log the number of redeliveries.\n    console.log(\n      `Redelivered ${\n        failedDeliveryIDs.length\n      } failed webhook deliveries out of ${\n        deliveries.length\n      } total deliveries since ${new Date(Number(lastWebhookRedeliveryTime))}.`\n    );\n  } catch (error) {\n    // If there was an error, log the error so that it appears in the workflow run log, then throw the error so that the workflow run registers as a failure.\n    if (error.response) {\n      console.error(\n        `Failed to check and redeliver webhooks: ${error.response.data.message}`\n      );\n    }\n    console.error(error);\n    throw(error);\n  }\n}\n\n// This function will fetch all of the webhook deliveries that were delivered since `lastWebhookRedeliveryTime`.\n// It uses the `octokit.paginate.iterator()` method to iterate through paginated results. For more information, see [AUTOTITLE](/rest/guides/scripting-with-the-rest-api-and-javascript#making-paginated-requests).\n//\n// If a page of results includes deliveries that occurred before `lastWebhookRedeliveryTime`,\n// it will store only the deliveries that occurred after `lastWebhookRedeliveryTime` and then stop.\n// Otherwise, it will store all of the deliveries from the page and request the next page.\nasync function fetchWebhookDeliveriesSince({\n  lastWebhookRedeliveryTime,\n  repoOwner,\n  repoName,\n  hookId,\n  octokit,\n}) {\n  const iterator = octokit.paginate.iterator(\n    \"GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries\",\n    {\n      owner: repoOwner,\n      repo: repoName,\n      hook_id: hookId,\n      per_page: 100,\n      headers: {\n        \"x-github-api-version\": \"2026-03-10\",\n      },\n    }\n  );\n\n  const deliveries = [];\n\n  for await (const { data } of iterator) {\n    const oldestDeliveryTimestamp = new Date(\n      data[data.length - 1].delivered_at\n    ).getTime();\n\n    if (oldestDeliveryTimestamp < lastWebhookRedeliveryTime) {\n      for (const delivery of data) {\n        if (\n          new Date(delivery.delivered_at).getTime() > lastWebhookRedeliveryTime\n        ) {\n          deliveries.push(delivery);\n        } else {\n          break;\n        }\n      }\n      break;\n    } else {\n      deliveries.push(...data);\n    }\n  }\n\n  return deliveries;\n}\n\n// This function will redeliver a failed webhook delivery.\nasync function redeliverWebhook({\n  deliveryId,\n  repoOwner,\n  repoName,\n  hookId,\n  octokit,\n}) {\n  await octokit.request(\n    \"POST /repos/{owner}/{repo}/hooks/{hook_id}/deliveries/{delivery_id}/attempts\",\n    {\n      owner: repoOwner,\n      repo: repoName,\n      hook_id: hookId,\n      delivery_id: deliveryId,\n    }\n  );\n}\n\n// This function gets the value of a configuration variable.\n// If the variable does not exist, the endpoint returns a 404 response and this function returns `undefined`.\nasync function getVariable({ variableName, repoOwner, repoName, octokit }) {\n  try {\n    const {\n      data: { value },\n    } = await octokit.request(\n      \"GET /repos/{owner}/{repo}/actions/variables/{name}\",\n      {\n        owner: repoOwner,\n        repo: repoName,\n        name: variableName,\n      }\n    );\n    return value;\n  } catch (error) {\n    if (error.status === 404) {\n      return undefined;\n    } else {\n      throw error;\n    }\n  }\n}\n\n// This function will update a configuration variable (or create the variable if it doesn't already exist). For more information, see [AUTOTITLE](/actions/learn-github-actions/variables#defining-configuration-variables-for-multiple-workflows).\nasync function updateVariable({\n  variableName,\n  value,\n  variableExists,\n  repoOwner,\n  repoName,\n  octokit,\n}) {\n  if (variableExists) {\n    await octokit.request(\n      \"PATCH /repos/{owner}/{repo}/actions/variables/{name}\",\n      {\n        owner: repoOwner,\n        repo: repoName,\n        name: variableName,\n        value: value,\n      }\n    );\n  } else {\n    await octokit.request(\"POST /repos/{owner}/{repo}/actions/variables\", {\n      owner: repoOwner,\n      repo: repoName,\n      name: variableName,\n      value: value,\n    });\n  }\n}\n\n// This will execute the `checkAndRedeliverWebhooks` function.\n(async () => {\n  await checkAndRedeliverWebhooks();\n})();\n\n```\n\n## Testando o script\n\nVocê pode acionar manualmente seu fluxo de trabalho para testar o script. Para saber mais, confira [Executar um fluxo de trabalho manualmente](/pt/actions/how-tos/manage-workflow-runs/manually-run-a-workflow) e [Usando registros de execução de fluxo de trabalho](/pt/actions/how-tos/monitor-workflows/use-workflow-run-logs).\n\n## Métodos alternativos\n\nEste exemplo usado GitHub Actions para armazenar credenciais com segurança e executar o script em um agendamento. No entanto, se preferir executar esse script no seu servidor que lida com entregas de webhooks, você poderá:\n\n* Armazene as credenciais de outra maneira segura, como um gerenciador de segredos como [Azure key vault](https://azure.microsoft.com/products/key-vault). Também será necessário atualizar o script para acessar as credenciais do novo local.\n* Execute o script em um cronograma no seu servidor, por exemplo, usando um trabalho cron ou um agendador de tarefas.\n* Atualize o script para armazenar o último tempo de execução em algum lugar que seu servidor possa acessar e atualizar. Se você optar por não armazenar o último tempo de execução como um GitHub Actions segredo, poderá remover as chamadas à API para acessar e atualizar a variável de configuração."}