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(merge-on-green): second attempt to add cron to pick up hanging PRs #1269

Merged
merged 10 commits into from Jan 15, 2021

Conversation

sofisl
Copy link
Contributor

@sofisl sofisl commented Jan 9, 2021

Attempt to readdress #1159, fixing the issue that caused reverting in #1264.

I believe the issue was that reaction IDs were not being added to stored PRs, causing the main (merge-logic) to fail. This was because I was attempting to make reaction and reaction IDs optional. To fix, I've essentially copied the exact format in which PRs are currently being added, and made it into its own function (addPR).

I have also been doing some manual testing in the GCF function just to confirm this works. I was able to spot another potential bug, which is that we need to perform scans for missing PRs on different cron jobs with different installation IDs depending on the org, otherwise we won't have access to info like branch protection, etc.

I've also rescoped tests, which was a hanging comment left from #1217

@sofisl sofisl requested a review from a team January 9, 2021 08:15
@google-cla google-cla bot added the cla: yes This human has signed the Contributor License Agreement. label Jan 9, 2021
@sofisl sofisl requested review from bcoe and chingor13 January 9, 2021 08:15
@sofisl sofisl changed the title Add cron hanging prs fix(merge-on-green): second attempt to add cron to pick up hanging PRs Jan 9, 2021
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 really solid to me, a few things jumped out at me:

  • it feels a bit strange tome that we're checking branch protection in merge-on-green.ts, because I thought our, _"should we merge it?", logic lived elsewhere. I see this logic isn't new, so it might just be I'm not understanding a valuable optimization that's been added.
  • non blocker: It jumped out at me that we should perhaps have two sets of tests, those using nock, and those new ones you're adding using sinon (I like the movement towards sinon.)
  • I think we could cleanup tests quite a bit using sinon's sandbox API, and think this would be worth considering.

owner: string,
repo: string,
prNumber: number,
github: InstanceType<typeof ProbotOctokit>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, you could potentially do this once by going:

type GitHubType = InstanceType<typeof ProbotOctokit>

towards the top of the file.

) {
let branchProtection: string[] | undefined;
try {
branchProtection = await handler.checkForBranchProtection(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels a little strange to me that we're checking branchProtection before adding the PR, since this is something we check in merge-logic.ts right?

From a separation of functionality point of view, I'd be tempted to try to keep all the logic for if something should be merged in merge-logic.ts, whereas merge-on-green.ts just handles:

  • receiving Webhooks from GitHub
  • adding PRs to the DB (either based on webhook or your periodic cron job).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah! I think that was the thinking behind our original logic. The reason I changed where we check for branch protection is because it's a way of reducing API requests during the main merge-logic, especially since branch protection isn't really the kind of information that changes within the life of a PR (so it's not the kind of thing we need to check multiple times before merging a PR). The way we currently have it set up allows us to only check for branch protection once, and then we save it in our Datastore table and don't have to make that API call again. Another reason (though this one is weaker) is that it kind of makes sense for us to only add a PR to our queue of PRs to check if it has branch protection. In other words, instead of: (a) adding a PR to datastore, (b) checking if it has branch protection, (c) deleting the PR, we could save ourselves two steps and just never add the PR to Datastore if it doesn't have branch protection. Open to hearing your thoughts on this process!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My temptation would be to still have the logic for checking branch protection inside of the merge-logic.js class, and you could just call that method here (rather than having the logic in two places).

This doesn't need to be a blocker for this PR, but perhaps we could make a tracking ticket to do this?

q: `is:open is:pr user:${org} label:"${MERGE_ON_GREEN_LABEL}"`,
}),
github.paginate(github.search.issuesAndPullRequests, {
q: `is:open is:pr user:${org} label:"${MERGE_ON_GREEN_LABEL_SECURE}"`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love the search approach that you came up with for this.

// we cannot search in an org without the bot installation ID, so we need
// to divide up the cron jobs based on org
if (context.payload.org === 'googleapis') {
await handler.scanForMissingPullRequests(context.github, 'googleapis');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rather than using an if statement here, you could just do:

await handler.scanForMissingPullRequests(context.github, context.payload.org);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could... we're just going to end up with a lot of errors in the logs and additional API calls that we don't need to make. I'm not sure what is better here, less lines of code and more logs or vice versa!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what I"m getting at is that context.payload.org is going to be one of googleapis or GoogleCloudPlatform I believe?


Why do we return early for GoogleCloudPlatform but not googleapis, out of curiosity?

payload: {org: 'testOwner', cleanUp: true},
id: 'abc123',
it('adds a PR if branch protection when PR labeled', 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.

we can drop this commented out bit I believe. I'd be tempted to use Sinon's sandbox mode, and just always reset the sandbox in a before filter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops! Thanks for catching that! I was testing to make sure the sandboxing worked.

await probot.receive({
name: 'pull_request',
payload,
id: 'abc123',
});

scopes.forEach(s => s.done());
assert(addPRStub.called);
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.

I think a beforeEach that does the sandbox dance would simplify this.

logger.info('stub called? ' + addPRStub.called);
});
});
describe('stubbing all GCP functions', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't make the describe block describe "how you're going to write your tests" (_which is just an implementation detail), rather it should describe what you're testing.

If I'm following, we're moving away from nock based tests with these new tests towards using sinon more?

What I might do is go so far as splitting it across two test files:

  • integration.js <-- integration(ish) tests with all our nock tests.
  • unit.js <-- unit tests using sinon mianly for mocking.

This could be follow up work, not a blocker 👍

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 ended up dividing the functions into merge-logic vs. merge-on-green wrapper logic, which I think does a better job of describing the split (although, to your point, maybe it's worth adding more integration tests to have its own separate file)

@sofisl sofisl requested a review from bcoe January 12, 2021 23:31
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 mostly looking good to me 👍

I'm still confused by why we need to have this:

if (context.payload.org === 'googleapis') {

When we could just pass that string as input to scanForMissingPullRequests?


Otherwise I just had a future refactoring nit.

) {
let branchProtection: string[] | undefined;
try {
branchProtection = await handler.checkForBranchProtection(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My temptation would be to still have the logic for checking branch protection inside of the merge-logic.js class, and you could just call that method here (rather than having the logic in two places).

This doesn't need to be a blocker for this PR, but perhaps we could make a tracking ticket to do this?

// we cannot search in an org without the bot installation ID, so we need
// to divide up the cron jobs based on org
if (context.payload.org === 'googleapis') {
await handler.scanForMissingPullRequests(context.github, 'googleapis');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what I"m getting at is that context.payload.org is going to be one of googleapis or GoogleCloudPlatform I believe?


Why do we return early for GoogleCloudPlatform but not googleapis, out of curiosity?

});
});

describe('merge-on-green wrapper logic', () => {
suite('stubbing all GCP functions', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a good middle-ground, compared to breaking out the tests into multiple files, or completely restructuring tests.

@sofisl
Copy link
Contributor Author

sofisl commented Jan 13, 2021

Thanks Ben!

  1. For this comment: if (context.payload.org === 'googleapis')
    You were right, I was confused! I fixed it to just await handler.scanForMissingPullRequests(context.github, context.payload.org);

  2. Also, we were not supposed to return early for GoogleCloudPlatform vs. googleapis. The return statement should have been outside of the if-blocks. Another mistake, good catch!

  3. As for moving branch protection to merge-logic vs. keeping it where it is, my only concern would be that it's not really in our merge logic: we're not checking it every time to see if it's there in order to merge, we only check it once to see if it should be added to Datastore.
    Perhaps a future ticket might be to create a github-utils file that contains functions that call to the API, and another one for functions that call to GCP? I'm not sure if this solution would address your concern.

@sofisl
Copy link
Contributor Author

sofisl commented Jan 15, 2021

I've tested pre-merging, it is working!

@sofisl sofisl merged commit cdf0257 into master Jan 15, 2021
@JustinBeckwith JustinBeckwith deleted the addCronHangingPrs branch August 9, 2021 18:03
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

2 participants