New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: add cron job to pick up hanging prs #1217
Conversation
const prs = await github.paginate(await github.pulls.list, { | ||
owner, | ||
repo, | ||
state: 'open', | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can probably reduce the # of queries, by using the search API: https://docs.github.com/en/free-pro-team@latest/rest/reference/search#search-issues-and-pull-requests
// check to see if the owner has branch protection rules | ||
let branchProtection: string[] | undefined; | ||
try { | ||
branchProtection = ( | ||
await github.repos.getBranchProtection({ | ||
owner, | ||
repo, | ||
branch: 'master', | ||
}) | ||
).data.required_status_checks.contexts; | ||
logger.info( | ||
`checking branch protection for ${owner}/${repo}: ${branchProtection}` | ||
); | ||
} catch (err) { | ||
err.message = `Error in getting branch protection\n\n${err.message}`; | ||
await github.issues.createComment({ | ||
owner, | ||
repo, | ||
issue_number: pr.number, | ||
body: | ||
"Your PR doesn't have any required checks. Please add required checks to your master branch and then re-add the label. Learn more about enabling these checks here: https://help.github.com/en/github/administering-a-repository/enabling-required-status-checks.", | ||
}); | ||
logger.error(err); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this logic share the same code as the webhook handling?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 I agree, I think the logic could be simplified if we:
- check to see if the PR is already tracked in datastore (if it is tracked do nothing.).
- if the PR is not tracked, but an automerge label exists, make a synthetic call to
app.on('pull_request.labeled', async context
; I think you could do this by looking at the fields you use from context, and then making sure you populate these in a fake context object you pass.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about faking an webhook - we could probably extract that common logic into a function to execute from both paths.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I simplified the logic here!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a couple notes. I agree with @chingor13, I think you could be clever and make it so that finding a labeled PR that isn't tracked hits the same code path as adding a label.
label.name === MERGE_ON_GREEN_LABEL_SECURE | ||
); | ||
|
||
if (labelFound) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should only do this if there's not already an entry in datastore
for the given PR right?
// check to see if the owner has branch protection rules | ||
let branchProtection: string[] | undefined; | ||
try { | ||
branchProtection = ( | ||
await github.repos.getBranchProtection({ | ||
owner, | ||
repo, | ||
branch: 'master', | ||
}) | ||
).data.required_status_checks.contexts; | ||
logger.info( | ||
`checking branch protection for ${owner}/${repo}: ${branchProtection}` | ||
); | ||
} catch (err) { | ||
err.message = `Error in getting branch protection\n\n${err.message}`; | ||
await github.issues.createComment({ | ||
owner, | ||
repo, | ||
issue_number: pr.number, | ||
body: | ||
"Your PR doesn't have any required checks. Please add required checks to your master branch and then re-add the label. Learn more about enabling these checks here: https://help.github.com/en/github/administering-a-repository/enabling-required-status-checks.", | ||
}); | ||
logger.error(err); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 I agree, I think the logic could be simplified if we:
- check to see if the PR is already tracked in datastore (if it is tracked do nothing.).
- if the PR is not tracked, but an automerge label exists, make a synthetic call to
app.on('pull_request.labeled', async context
; I think you could do this by looking at the fields you use from context, and then making sure you populate these in a fake context object you pass.
@chingor13 and @bcoe, made some adjustments, thanks for the review! |
* @param context the context of the webhook payload | ||
* @returns void | ||
*/ | ||
handler.pickUpPR = async function pickUpPR( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: can we call this scanForMissingPullRequests
or something like that?
handler.addPR = async function addPR( | ||
wp: WatchPR, | ||
url: string, | ||
github: Context['github'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other bots are using this type:
type ProbotOctokitType = InstanceType<typeof ProbotOctokit>; |
label: string; | ||
author: string; | ||
url: string; | ||
reactionId: number; | ||
installationId: number; | ||
reactionId?: number | undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: is undefined
implicitly possible with the ?
await handler.pickUpPR(owner, repo, context.github); | ||
return; | ||
} | ||
|
||
const start = Date.now(); | ||
await handler.checkPRMergeability(watchedPRs, app, context); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it worth trying to do these both async and awaiting them at the end? (it's going to complete all of pickUpPR
before trying to do the merges.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I'm expecting this to run on a separate cron job - we won't try merging on the same run as we try picking up the PRs.
await github.search.issuesAndPullRequests, | ||
{ | ||
q: | ||
'is:open is:pr user:googleapis user:GoogleCloudPlatform label:"automerge"', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use the constant?
await github.search.issuesAndPullRequests, | ||
{ | ||
q: | ||
'is:open is:pr user:googleapis user:GoogleCloudPlatform label:"automerge: exact"', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use the constant?
repo, | ||
state: 'continue', | ||
url: issue.html_url, | ||
label: 'automerge', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use the constant?
repo, | ||
state: 'continue', | ||
url: issue.html_url, | ||
label: 'automerge: exact', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use the constant?
) { | ||
// Github does not support searching the labels with 'OR'. | ||
// The searching for issues is considered to be an "AND" instead of an "OR" . | ||
const issuesAutomergeLabel = await github.paginate( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These 2 searches can probably be started at the same time then awaited before we want to start executing on them.
@chingor13, really appreciate the in-depth review: it's super helpful to make sure we stay consistent within and outside the package. Thank you! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking good to me, thanks for DRY(don't repeat yourself)ing things up 👍
@@ -960,6 +1226,7 @@ describe('merge-on-green', () => { | |||
|
|||
//This function is supposed to respond with an error | |||
it('does not add a PR if branch protection errors and comments on PR', async () => { | |||
addPRStub.restore(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
one thought here, I think we could use Sinon's sandbox functionality and just always restart the sandbox in a beforeEach
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we are, line 852.
We need to restore sometimes in order to provide specific functionality for some specific tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you need different setup scenarios, you can split describe blocks with separate beforeEach implementations.
@@ -960,6 +1226,7 @@ describe('merge-on-green', () => { | |||
|
|||
//This function is supposed to respond with an error | |||
it('does not add a PR if branch protection errors and comments on PR', async () => { | |||
addPRStub.restore(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you need different setup scenarios, you can split describe blocks with separate beforeEach implementations.
This addresses #1159
Following Jeff's suggestion, this PR adds functionality for a cron job that will run once an hour.
I believe that, if we format the cron job correctly, we will be able to trigger the gcf-utils mechanism of rotating through all of the repositories under which MOG is installed.
Taking a lead from the SRS cron job, I think we can just check if the
cron_org
exists in the payload to trigger the rotation through all of the repos, which is different from the current MOG cron job in Cloud Scheduler.I am cautious about this approach - we may trigger an abuse mechanism or hit our rate limit faster since we'd be checking all PRs in all our repos. I feel like we should try this solution, but if it doesn't work, we may want to write another bot that just picks up PRs.