1
0
mirror of synced 2025-12-22 03:16:52 -05:00

fix: retry translation batch PR creation if we hit a 502 (#23633)

* Fix error on Pull Request creation

Fix https://github.com/github/docs-engineering/issues/1293. This
solution does not really handle the error, it just prevents the script
from stopping, since sometimes we get a timeout error from the load
balancer even though the request was successfully handled by rails.

* Add a nudge to the localization folks when a batch is created

Ever since the translation batches are created and merged automatically
it is difficult to see when they started or finished correctly. Also,
sometimes, docubot automatically closes some pull requests, and at the
time of this writing I don't know the reason.

This change intends to make batches not go unnoticed. We can revert it
later if this becomes too noisy and the process is stable.

* fix: remove ping to docs-localization team from batch PR body

Discussion: https://github.com/github/docs-internal/pull/23633/files#r771896816

* refactor: move report-reset-files into its own step

* refactor: move git push into its own step

* spike: move PR creation into its own script

* feat: annotate createTranslationBatchPullRequest fn

* enhancement: embed nwo#pr format in logger

* fix: import fs directly

* fix: import @actions/github directly

* docs: update annotations in fn block

* build: update step name to be self-documenting

* Retry on 502

* Rename method to reflect its behaviour

* Update codeowners

* Fix comment

* Update .github/actions-scripts/create-translation-batch-pr.js

Co-authored-by: Peter Bengtsson <peterbe@github.com>

* Update .github/actions-scripts/create-translation-batch-pr.js

Co-authored-by: Peter Bengtsson <peterbe@github.com>

* Update .github/actions-scripts/create-translation-batch-pr.js

Co-authored-by: Peter Bengtsson <peterbe@github.com>

* Appease linter and fix script

* Move options to main

* Rename var

* Refactor options

* Check for response

* First try to find and then to create the PR

* Update .github/actions-scripts/create-translation-batch-pr.js

Co-authored-by: Peter Bengtsson <peterbe@github.com>

* Update .github/actions-scripts/create-translation-batch-pr.js

Co-authored-by: Peter Bengtsson <peterbe@github.com>

* Update .github/actions-scripts/create-translation-batch-pr.js

Co-authored-by: Francis <15894826+francisfuzz@users.noreply.github.com>

* Remove unreachable conditional

* Lint

* fix: remove duplicate annotation

Co-authored-by: docubot <67483024+docubot@users.noreply.github.com>
Co-authored-by: Francis <15894826+francisfuzz@users.noreply.github.com>
Co-authored-by: Peter Bengtsson <peterbe@github.com>
This commit is contained in:
Marcelo Jacobus
2022-01-11 20:06:18 -03:00
committed by GitHub
parent 8e25d1e40d
commit cf2603ac01
3 changed files with 156 additions and 12 deletions

View File

@@ -0,0 +1,142 @@
#!/usr/bin/env node
import fs from 'fs'
import github from '@actions/github'
const OPTIONS = Object.fromEntries(
['BASE', 'BODY_FILE', 'GITHUB_TOKEN', 'HEAD', 'LANGUAGE', 'TITLE', 'GITHUB_REPOSITORY'].map(
(envVarName) => {
const envVarValue = process.env[envVarName]
if (!envVarValue) {
throw new Error(`You must supply a ${envVarName} environment variable`)
}
return [envVarName, envVarValue]
}
)
)
if (!process.env.GITHUB_REPOSITORY) {
throw new Error('GITHUB_REPOSITORY environment variable not set')
}
const RETRY_STATUSES = [
422, // Retry the operation if the PR already exists
502, // Retry the operation if the API responds with a `502 Bad Gateway` error.
]
const RETRY_ATTEMPTS = 3
const {
// One of the default environment variables provided by Actions.
GITHUB_REPOSITORY,
// These are passed in from the step in the workflow file.
TITLE,
BASE,
HEAD,
LANGUAGE,
BODY_FILE,
GITHUB_TOKEN,
} = OPTIONS
const [OWNER, REPO] = GITHUB_REPOSITORY.split('/')
const octokit = github.getOctokit(GITHUB_TOKEN)
/**
* @param {object} config Configuration options for finding the PR.
* @returns {Promise<number | undefined>} The PR number.
*/
async function findPullRequestNumber(config) {
// Get a list of PRs and see if one already exists.
const { data: listOfPullRequests } = await octokit.rest.pulls.list({
owner: config.owner,
repo: config.repo,
head: `${config.owner}:${config.head}`,
})
return listOfPullRequests[0]?.number
}
/**
* When this file was first created, we only introduced support for creating a pull request for some translation batch.
* However, some of our first workflow runs failed during the pull request creation due to a timeout error.
* There have been cases where, despite the timeout error, the pull request gets created _anyway_.
* To accommodate this reality, we created this function to look for an existing pull request before a new one is created.
* Although the "find" check is redundant in the first "cycle", it's designed this way to recursively call the function again via its retry mechanism should that be necessary.
*
* @param {object} config Configuration options for creating the pull request.
* @returns {Promise<number>} The PR number.
*/
async function findOrCreatePullRequest(config) {
const found = await findPullRequestNumber(config)
if (found) {
return found
}
try {
const { data: pullRequest } = await octokit.rest.pulls.create({
owner: config.owner,
repo: config.repo,
base: config.base,
head: config.head,
title: config.title,
body: config.body,
draft: false,
})
return pullRequest.number
} catch (error) {
if (!error.response || !config.retryCount) {
throw error
}
if (!config.retryStatuses.includes(error.response.status)) {
throw error
}
console.error(`Error creating pull request: ${error.message}`)
console.warn(`Retrying in 5 seconds...`)
await new Promise((resolve) => setTimeout(resolve, 5000))
config.retryCount -= 1
return findOrCreatePullRequest(config)
}
}
/**
* @param {object} config Configuration options for labeling the PR
* @returns {Promise<undefined>}
*/
async function labelPullRequest(config) {
await octokit.rest.issues.update({
owner: config.owner,
repo: config.repo,
issue_number: config.issue_number,
labels: config.labels,
})
}
async function main() {
const options = {
title: TITLE,
base: BASE,
head: HEAD,
body: fs.readFileSync(BODY_FILE, 'utf8'),
labels: ['translation-batch', `translation-batch-${LANGUAGE}`],
owner: OWNER,
repo: REPO,
retryStatuses: RETRY_STATUSES,
retryCount: RETRY_ATTEMPTS,
}
options.issue_number = await findOrCreatePullRequest(options)
const pr = `${GITHUB_REPOSITORY}#${options.issue_number}`
console.log(`Created PR ${pr}`)
// metadata parameters aren't currently available in `github.rest.pulls.create`,
// but they are in `github.rest.issues.update`.
await labelPullRequest(options)
console.log(`Updated ${pr} with these labels: ${options.labels.join(', ')}`)
}
main()