// MungePullRequest is the workhorse the will actually make updates to the PR func (LGTMAfterCommitMunger) MungePullRequest(config *github_util.Config, pr *github.PullRequest, issue *github.Issue, commits []github.RepositoryCommit, events []github.IssueEvent) { if !github_util.HasLabel(issue.Labels, "lgtm") { return } lastModified := github_util.LastModifiedTime(commits) lgtmTime := github_util.LabelTime("lgtm", events) if lastModified == nil || lgtmTime == nil { glog.Errorf("PR %d unable to determine lastModified or lgtmTime", *pr.Number) return } if lastModified.After(*lgtmTime) { lgtmRemovedBody := "PR changed after LGTM, removing LGTM." if err := config.WriteComment(*pr.Number, lgtmRemovedBody); err != nil { return } config.RemoveLabel(*pr.Number, "lgtm") } }
func TestValidateLGTMAfterPush(t *testing.T) { tests := []struct { issueEvents []github.IssueEvent shouldPass bool lastModified time.Time }{ { issueEvents: []github.IssueEvent{ { Event: stringPtr("labeled"), Label: &github.Label{ Name: stringPtr("lgtm"), }, CreatedAt: timePtr(time.Unix(10, 0)), }, }, lastModified: time.Unix(9, 0), shouldPass: true, }, { issueEvents: []github.IssueEvent{ { Event: stringPtr("labeled"), Label: &github.Label{ Name: stringPtr("lgtm"), }, CreatedAt: timePtr(time.Unix(10, 0)), }, }, lastModified: time.Unix(11, 0), shouldPass: false, }, { issueEvents: []github.IssueEvent{ { Event: stringPtr("labeled"), Label: &github.Label{ Name: stringPtr("lgtm"), }, CreatedAt: timePtr(time.Unix(12, 0)), }, { Event: stringPtr("labeled"), Label: &github.Label{ Name: stringPtr("lgtm"), }, CreatedAt: timePtr(time.Unix(11, 0)), }, { Event: stringPtr("labeled"), Label: &github.Label{ Name: stringPtr("lgtm"), }, CreatedAt: timePtr(time.Unix(10, 0)), }, }, lastModified: time.Unix(11, 0), shouldPass: true, }, { issueEvents: []github.IssueEvent{ { Event: stringPtr("labeled"), Label: &github.Label{ Name: stringPtr("lgtm"), }, CreatedAt: timePtr(time.Unix(10, 0)), }, { Event: stringPtr("labeled"), Label: &github.Label{ Name: stringPtr("lgtm"), }, CreatedAt: timePtr(time.Unix(11, 0)), }, { Event: stringPtr("labeled"), Label: &github.Label{ Name: stringPtr("lgtm"), }, CreatedAt: timePtr(time.Unix(12, 0)), }, }, lastModified: time.Unix(11, 0), shouldPass: true, }, } for _, test := range tests { config := &github_util.Config{} client, server, mux := github_test.InitTest() config.Org = "o" config.Project = "r" config.SetClient(client) mux.HandleFunc(fmt.Sprintf("/repos/o/r/issues/1/events"), func(w http.ResponseWriter, r *http.Request) { if r.Method != "GET" { t.Errorf("Unexpected method: %s", r.Method) } w.WriteHeader(http.StatusOK) data, err := json.Marshal(test.issueEvents) if err != nil { t.Errorf("Unexpected error: %v", err) } w.Write(data) commits, err := config.GetFilledCommits(1) if err != nil { t.Errorf("Unexpected error getting filled commits: %v", err) } events, err := config.GetAllEventsForPR(1) if err != nil { t.Errorf("Unexpected error getting events commits: %v", err) } lastModifiedTime := github_util.LastModifiedTime(commits) lgtmTime := github_util.LabelTime("lgtm", events) if lastModifiedTime == nil || lgtmTime == nil { t.Errorf("unexpected lastModifiedTime or lgtmTime == nil") } ok := !lastModifiedTime.After(*lgtmTime) if ok != test.shouldPass { t.Errorf("expected: %v, saw: %v", test.shouldPass, ok) } }) server.Close() } }
// MungePullRequest is the workhorse the will actually make updates to the PR func (sq *SubmitQueue) MungePullRequest(config *github_util.Config, pr *github_api.PullRequest, issue *github_api.Issue, commits []github_api.RepositoryCommit, events []github_api.IssueEvent) { e2e := sq.e2e userSet := sq.userWhitelist if !github_util.HasLabels(issue.Labels, []string{"cla: yes"}) { sq.SetPRStatus(pr, noCLA) return } if mergeable, err := config.IsPRMergeable(pr); err != nil { glog.V(2).Infof("Skipping %d - unable to determine mergeability", *pr.Number) sq.SetPRStatus(pr, undeterminedMergability) return } else if !mergeable { glog.V(4).Infof("Skipping %d - not mergable", *pr.Number) sq.SetPRStatus(pr, unmergeable) return } // Validate the status information for this PR contexts := sq.RequiredStatusContexts if len(sq.E2EStatusContext) > 0 && (len(sq.DontRequireE2ELabel) == 0 || !github_util.HasLabel(issue.Labels, sq.DontRequireE2ELabel)) { contexts = append(contexts, sq.E2EStatusContext) } if ok := config.IsStatusSuccess(pr, contexts); !ok { glog.Errorf("PR# %d Github CI status is not success", *pr.Number) sq.SetPRStatus(pr, ciFailure) return } if !github_util.HasLabel(issue.Labels, sq.WhitelistOverride) && !userSet.Has(*pr.User.Login) { glog.V(4).Infof("Dropping %d since %s isn't in whitelist and %s isn't present", *pr.Number, *pr.User.Login, sq.WhitelistOverride) if !github_util.HasLabel(issue.Labels, needsOKToMergeLabel) { config.AddLabels(*pr.Number, []string{needsOKToMergeLabel}) body := "The author of this PR is not in the whitelist for merge, can one of the admins add the 'ok-to-merge' label?" config.WriteComment(*pr.Number, body) } sq.SetPRStatus(pr, needsok) return } // Tidy up the issue list. if github_util.HasLabel(issue.Labels, needsOKToMergeLabel) { config.RemoveLabel(*pr.Number, needsOKToMergeLabel) } if !github_util.HasLabels(issue.Labels, []string{"lgtm"}) { sq.SetPRStatus(pr, noLGTM) return } lastModifiedTime := github_util.LastModifiedTime(commits) lgtmTime := github_util.LabelTime("lgtm", events) if lastModifiedTime == nil || lgtmTime == nil { glog.Errorf("PR %d was unable to determine when LGTM was added or when last modified", *pr.Number) sq.SetPRStatus(pr, unknown) return } if lastModifiedTime.After(*lgtmTime) { glog.V(4).Infof("PR %d changed after LGTM. Will not merge", *pr.Number) sq.SetPRStatus(pr, lgtmEarly) return } if !e2e.Stable() { sq.SetPRStatus(pr, e2eFailure) return } // if there is a 'e2e-not-required' label, just merge it. if len(sq.DontRequireE2ELabel) > 0 && github_util.HasLabel(issue.Labels, sq.DontRequireE2ELabel) { config.MergePR(pr, "submit-queue") sq.SetPRStatus(pr, merged) return } sq.SetPRStatus(pr, githube2e) sq.Lock() sq.githubE2ERequest <- true sq.needsGithubE2E[*pr.Number] = pr sq.Unlock() return }