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

Support required statuses for Bulldozer to update PRs #307

Merged
merged 15 commits into from
May 20, 2022

Conversation

wrslatz
Copy link
Contributor

@wrslatz wrslatz commented May 2, 2022

Summary

Closes #259

  1. Add required_statuses to Bulldozer update config
    • Refactored ShouldUpdatePR to evaluate.go to be alongside ShouldMergePR and reuse some status checks code
    • No tests exist anymore in update_test.go since they all covered ShouldUpdatePR and were refactored to evaluate_test.go
  2. Enable update processing when status or check run events are handled
  3. Additional changes
    • Improve logging to differentiate between should merge and should update logs
    • Adjusted config tests to better test provided and default config values
    • Refactor TestShouldUpdatePR to table-based tests so it is easier to understand the test cases

@wrslatz
Copy link
Contributor Author

wrslatz commented May 3, 2022

So it looks like status changes not being fulfilled is working correctly as implemented 🎉

However, status changes themselves are not triggering an update, from the local testing I've done. Looks like I need to update the check run check_run.go) and status update (status.go) handling to consider updates as well as merges like we do for pushes or other pull events.

I'm not sure what implications this might have. Open to any thoughts or feedback.

@bluekeyes
Copy link
Member

However, status changes themselves are not triggering an update, from the local testing I've done. Looks like I need to update the check run (check_run.go) and status update (status.go) handling to consider updates as well as merges like we do for pushes or other pull events.

Yes, this sounds correct. Because statuses are pretty frequent events, it's important to filter them if we're not already doing so. At minimum, we should exit the handler immediately for non-successful statuses. Offhand, I'm not sure if there are any other optimizations to make here.

@wrslatz
Copy link
Contributor Author

wrslatz commented May 11, 2022

Yes, this sounds correct. Because statuses are pretty frequent events, it's important to filter them if we're not already doing so. At minimum, we should exit the handler immediately for non-successful statuses.

It looks like both already handle this logic

if checkState != "success" {
logger.Debug().Msgf("Doing nothing since context state for %q was %q", event.GetContext(), event.GetState())
return nil
}

if event.GetAction() != "completed" {
logger.Debug().Msgf("Doing nothing since check_run action was %q instead of 'completed'", event.GetAction())
return nil
}

Offhand, I'm not sure if there are any other optimizations to make here.

It looks like both of these handlers act on every open PR when processing

for _, pr := range prs {
pullCtx := pull.NewGithubContext(client, pr)
logger := logger.With().Int(githubapp.LogKeyPRNum, pr.GetNumber()).Logger()
config, err := h.FetchConfigForPR(ctx, client, pr)
if err != nil {
return err
}
if err := h.ProcessPullRequest(logger.WithContext(ctx), pullCtx, client, config, pr); err != nil {
logger.Error().Err(errors.WithStack(err)).Msg("Error processing pull request")
}
}

for _, pr := range prs {
logger := logger.With().Int(githubapp.LogKeyPRNum, pr.GetNumber()).Logger()
ctx := logger.WithContext(ctx)
// The PR included in the CheckRun response is very slim on information.
// It does not contain the owner information or label information we
// need to process the pull request.
fullPR, _, err := client.PullRequests.Get(ctx, repo.GetOwner().GetLogin(), repo.GetName(), pr.GetNumber())
if err != nil {
return errors.Wrapf(err, "failed to fetch PR number %q for CheckRun", pr.GetNumber())
}
pullCtx := pull.NewGithubContext(client, fullPR)
config, err := h.FetchConfigForPR(ctx, client, fullPR)
if err != nil {
return err
}
if err := h.ProcessPullRequest(ctx, pullCtx, client, config, fullPR); err != nil {
logger.Error().Err(errors.WithStack(err)).Msg("Error processing pull request")
}
}
return nil
}

Whereas we'd want the update logic to only act on the PR to which the check or status update corresponds?

@wrslatz
Copy link
Contributor Author

wrslatz commented May 11, 2022

Actually, I think I'm misunderstanding how that looping to try to merge works. Perhaps we should try updates in the same places. I'll make an attempt at that now

@bluekeyes
Copy link
Member

Yeah, both checks and statuses are attached to commits, which could be the HEAD of multiple open PRs (but in practice are usually only part of one.) The loops iterate over all of the open PRs associated with the updated commit, meaning you'll want to check if updates are necessary inside the loop, either before or after checking if merges are necessary.

@wrslatz
Copy link
Contributor Author

wrslatz commented May 18, 2022

I've finished most of the code changes here. I'm rewriting the TestShouldUpdatePR to use a table with named props instead of a matrix so it's easier to understand what exactly is being tested.

Do you think we need the full matrix of all combinations of test scenarios? It seems like a lot of tests, and I'm not sure how valuable all of the permutations are vs having tests covering the major scenarios. I can push what I have so far to help with considering, if needed.

@wrslatz wrslatz marked this pull request as ready for review May 19, 2022 19:17
@wrslatz wrslatz marked this pull request as draft May 19, 2022 21:47
Copy link
Member

@bluekeyes bluekeyes left a comment

Choose a reason for hiding this comment

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

Thanks for implementing this and especially for updating the tests - I really appreciate you leaving those better than you found them!

Overall this looks good, just a minor behavior question and some style suggestions.

@@ -102,34 +102,34 @@ func ShouldMergePR(ctx context.Context, pullCtx pull.Context, mergeConfig MergeC
if mergeConfig.Ignore.Enabled() {
ignored, reason, err := IsPRIgnored(ctx, pullCtx, mergeConfig.Ignore)
if err != nil {
return false, errors.Wrap(err, "failed to determine if pull request is ignored")
return false, errors.Wrap(err, "failed to determine if pull request is ignored for merge")
Copy link
Member

Choose a reason for hiding this comment

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

Thanks for clarifying/deduplicating these messages!

bulldozer/evaluate.go Show resolved Hide resolved
func TestShouldUpdatePR(t *testing.T) {
ctx := context.Background()
tests := []struct {
name string
Copy link
Member

Choose a reason for hiding this comment

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

Thanks for updating these tests! The new structure is much easier to read and follow.

When I write tests like this, I tend to use a map where the test name is the key. Do you have any thoughts on that style vs. including the name as a property?

Copy link
Contributor Author

@wrslatz wrslatz May 19, 2022

Choose a reason for hiding this comment

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

I have no preference either way 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Refactored to map in 96baa96. I do kind of like how this format looks. Helps compact the cases down a bit with one less line per case.

expectingUpdate: true,
},
// Test error handling
// TestShouldUpdatePR TODO: add error scenario coverage
Copy link
Member

Choose a reason for hiding this comment

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

If this is a new TODO, it's probably fine to ignore for now, since I don't think we covered the error cases in the old tests either.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, I'm fine leaving it out. I mainly added it to consider implementing error tests in a future update.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 75c6e43

Comment on lines 86 to 88
var err error
didUpdatePR, err = h.UpdatePullRequest(logger.WithContext(ctx), pullCtx, client, config, pr, base)
if err != nil {
logger.Error().Err(errors.WithStack(err)).Msg("Error updating pull request")
}
Copy link
Member

Choose a reason for hiding this comment

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

I think it will be a little simpler (and match the pull request handler) to invert this condition: instead of checking !didUpdatePR outside of this branch, check didUpdatePR here and continue.

Suggested change
var err error
didUpdatePR, err = h.UpdatePullRequest(logger.WithContext(ctx), pullCtx, client, config, pr, base)
if err != nil {
logger.Error().Err(errors.WithStack(err)).Msg("Error updating pull request")
}
didUpdatePR, err := h.UpdatePullRequest(logger.WithContext(ctx), pullCtx, client, config, pr, base)
if err != nil {
logger.Error().Err(errors.WithStack(err)).Msg("Error updating pull request")
}
if didUpdatePR {
continue
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good suggestion, happy to make this change

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed for both check_run and status events in 92aad8f

@wrslatz wrslatz marked this pull request as ready for review May 19, 2022 22:58
@bluekeyes bluekeyes merged commit 1ec9ef5 into palantir:develop May 20, 2022
@bluekeyes
Copy link
Member

Thanks again. I'll try to make a release with this early next week, but feel free to tag me here if that doesn't happen. For now, you can use the snapshot tag of the Docker image if you'd like to test it out.

@wrslatz
Copy link
Contributor Author

wrslatz commented May 20, 2022

Thanks again. I'll try to make a release with this early next week, but feel free to tag me here if that doesn't happen. For now, you can use the snapshot tag of the Docker image if you'd like to test it out.

Thank you!

@wrslatz wrslatz deleted the required-statuses-update branch June 6, 2022 14:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Option to only update when certain required status are passing
2 participants