Skip to content
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

Merged
merged 12 commits into from Dec 23, 2020
Merged

fix: add cron job to pick up hanging prs #1217

merged 12 commits into from Dec 23, 2020

Conversation

sofisl
Copy link
Contributor

@sofisl sofisl commented Dec 5, 2020

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.

@sofisl sofisl requested a review from a team December 5, 2020 03:59
@google-cla google-cla bot added the cla: yes This human has signed the Contributor License Agreement. label Dec 5, 2020
@sofisl sofisl requested review from bcoe and chingor13 December 5, 2020 03:59
Comment on lines 346 to 350
const prs = await github.paginate(await github.pulls.list, {
owner,
repo,
state: 'open',
});
Copy link
Contributor

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

Comment on lines 369 to 391
// 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);
Copy link
Contributor

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?

Copy link
Contributor

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:

  1. check to see if the PR is already tracked in datastore (if it is tracked do nothing.).
  2. 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.

Copy link
Contributor

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.

Copy link
Contributor Author

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!

Copy link
Contributor

@bcoe bcoe left a 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) {
Copy link
Contributor

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?

Comment on lines 369 to 391
// 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);
Copy link
Contributor

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:

  1. check to see if the PR is already tracked in datastore (if it is tracked do nothing.).
  2. 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.

@sofisl
Copy link
Contributor Author

sofisl commented Dec 14, 2020

@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(
Copy link
Contributor

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']
Copy link
Contributor

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;
Copy link
Contributor

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 ?

Comment on lines 537 to 542
await handler.pickUpPR(owner, repo, context.github);
return;
}

const start = Date.now();
await handler.checkPRMergeability(watchedPRs, app, context);
Copy link
Contributor

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

Copy link
Contributor Author

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"',
Copy link
Contributor

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"',
Copy link
Contributor

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',
Copy link
Contributor

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',
Copy link
Contributor

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(
Copy link
Contributor

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.

@sofisl
Copy link
Contributor Author

sofisl commented Dec 16, 2020

@chingor13, really appreciate the in-depth review: it's super helpful to make sure we stay consistent within and outside the package. Thank you!

Copy link
Contributor

@bcoe bcoe left a 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();
Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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();
Copy link
Contributor

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.

@sofisl sofisl merged commit 628e32c into googleapis:master Dec 23, 2020
sofisl added a commit that referenced this pull request Jan 6, 2021
sofisl added a commit that referenced this pull request Jan 6, 2021
* Revert "fix(merge-on-green): hanging cron pr (#1261)"

This reverts commit 985b284.

* Revert "fix: add cron job to pick up hanging prs (#1217)"

This reverts commit 628e32c.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes This human has signed the Contributor License Agreement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants