// Munge is the workhorse the will actually make updates to the PR func (b *BlockPath) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } if obj.HasLabel(doNotMergeLabel) { return } files, err := obj.ListFiles() if err != nil { return } for _, f := range files { if matchesAny(*f.Filename, b.blockRegexp) { if matchesAny(*f.Filename, b.doNotBlockRegexp) { continue } obj.WriteComment(blockPathBody) obj.AddLabels([]string{doNotMergeLabel}) return } } }
// Munge is the workhorse the will actually make updates to the PR func (b *BlockPath) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } if obj.HasLabel(doNotMergeLabel) { return } commits, err := obj.GetCommits() if err != nil { return } for _, c := range commits { for _, f := range c.Files { if matchesAny(*f.Filename, b.blockRegexp) { if matchesAny(*f.Filename, b.doNotBlockRegexp) { continue } body := fmt.Sprintf(`Adding label:%s because PR changes docs prohibited to auto merge See http://kubernetes.io/editdocs/ for information about editing docs`, doNotMergeLabel) obj.WriteComment(body) obj.AddLabels([]string{doNotMergeLabel}) return } } } }
// Munge is the workhorse the will actually make updates to the PR func (p *PathLabelMunger) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } files, err := obj.ListFiles() if err != nil { return } needsLabels := sets.NewString() for _, f := range files { for _, lm := range p.labelMap { if lm.regexp.MatchString(*f.Filename) { needsLabels.Insert(lm.label) } } } // This is all labels on the issue that the path munger controls hasLabels := obj.LabelSet().Intersection(p.allLabels) missingLabels := needsLabels.Difference(hasLabels) if missingLabels.Len() != 0 { obj.AddLabels(needsLabels.List()) } extraLabels := hasLabels.Difference(needsLabels) for _, label := range extraLabels.List() { creator := obj.LabelCreator(label) if creator == botName { obj.RemoveLabel(label) } } }
// Munge is the workhorse the will actually make updates to the PR func (p *PathLabelMunger) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } commits, err := obj.GetCommits() if err != nil { return } needsLabels := sets.NewString() for _, c := range commits { for _, f := range c.Files { for _, lm := range p.labelMap { if lm.regexp.MatchString(*f.Filename) { if !obj.HasLabel(lm.label) { needsLabels.Insert(lm.label) } } } } } if needsLabels.Len() != 0 { obj.AddLabels(needsLabels.List()) } }
// Munge is the workhorse the will actually make updates to the PR func (p *PathLabelMunger) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } commits, err := obj.GetCommits() if err != nil { return } labelMap := *p.labelMap needsLabels := sets.NewString() for _, c := range commits { for _, f := range c.Files { for prefix, label := range labelMap { if strings.HasPrefix(*f.Filename, prefix) && !obj.HasLabel(label) { needsLabels.Insert(label) } } } } if needsLabels.Len() != 0 { obj.AddLabels(needsLabels.List()) } }
// Munge is the workhorse the will actually make updates to the PR func (s *SizeMunger) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } issue := obj.Issue s.getGeneratedFiles(obj) genFiles := *s.genFiles genPrefixes := *s.genPrefixes files, err := obj.ListFiles() if err != nil { return } adds := 0 dels := 0 for _, f := range files { skip := false for _, p := range genPrefixes { if strings.HasPrefix(*f.Filename, p) { skip = true break } } if skip { continue } if genFiles.Has(*f.Filename) { continue } if f.Additions != nil { adds += *f.Additions } if f.Deletions != nil { dels += *f.Deletions } } newSize := calculateSize(adds, dels) newLabel := labelSizePrefix + newSize existing := github.GetLabelsWithPrefix(issue.Labels, labelSizePrefix) needsUpdate := true for _, l := range existing { if l == newLabel { needsUpdate = false continue } obj.RemoveLabel(l) } if needsUpdate { obj.AddLabels([]string{newLabel}) body := fmt.Sprintf("Labelling this PR as %s", newLabel) obj.WriteComment(body) } }
// Munge is the workhorse the will actually make updates to the PR func (NeedsRebaseMunger) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } mergeable, err := obj.IsMergeable() if err != nil { glog.V(2).Infof("Skipping %d - problem determining mergeable", *obj.Issue.Number) return } if mergeable && obj.HasLabel(needsRebase) { obj.RemoveLabel(needsRebase) } if !mergeable && !obj.HasLabel(needsRebase) { obj.AddLabels([]string{needsRebase}) } }
// Munge is the workhorse the will actually make updates to the PR func (s *LabelMunger) Munge(obj *github.MungeObject) { //this munger only works on issues if obj.IsPR() { return } issue := obj.Issue if obj.HasLabel("kind/flake") { return } tLabels := github.GetLabelsWithPrefix(issue.Labels, "team/") if len(tLabels) != 0 { //already labeled return } cLabels := github.GetLabelsWithPrefix(issue.Labels, "component/") if len(cLabels) != 0 { //already labeled return } routingLabelsToApply, err := http.PostForm("http://issue-triager-service:5000", url.Values{"title": {*issue.Title}, "body": {*issue.Body}}) if err != nil { //handle the error glog.Error(err) return } defer routingLabelsToApply.Body.Close() response, err := ioutil.ReadAll(routingLabelsToApply.Body) if routingLabelsToApply.StatusCode != 200 { glog.Errorf("%d: %s", routingLabelsToApply.StatusCode, response) return } obj.AddLabels(strings.Split(string(response), ",")) }
// Munge is the workhorse the will actually make updates to the PR func (lm *LabelMunger) Munge(obj *github.MungeObject) { //this munger only works on issues if obj.IsPR() { return } if obj.HasLabel("kind/flake") { return } tLabels := github.GetLabelsWithPrefix(obj.Issue.Labels, "team/") cLabels := github.GetLabelsWithPrefix(obj.Issue.Labels, "component/") if len(tLabels) == 0 && len(cLabels) == 0 { obj.AddLabels(getRoutingLabels(lm.TriagerUrl, obj.Issue.Title, obj.Issue.Body)) } else { newLabels := needsUpdate(obj) if len(newLabels) != 0 { updateModel(lm.TriagerUrl, obj.Issue.Title, obj.Issue.Body, newLabels) } } }
// Munge is the workhorse the will actually make updates to the PR func (NeedsRebaseMunger) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } mergeable, err := obj.IsMergeable() if err != nil { glog.V(2).Infof("Skipping %d - problem determining mergeable", *obj.Issue.Number) return } if mergeable && obj.HasLabel(needsRebase) { obj.RemoveLabel(needsRebase) } if !mergeable && !obj.HasLabel(needsRebase) { obj.AddLabels([]string{needsRebase}) body := fmt.Sprintf("@%s PR needs rebase", *obj.Issue.User.Login) if err := obj.WriteComment(body); err != nil { return } } }
// Munge is the workhorse the will actually make updates to the PR func (s *SizeMunger) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } issue := obj.Issue pr, err := obj.GetPR() if err != nil { return } s.getGeneratedFiles(obj) genFiles := *s.genFiles genPrefixes := *s.genPrefixes if pr.Additions == nil { glog.Warningf("PR %d has nil Additions", *pr.Number) return } adds := *pr.Additions if pr.Deletions == nil { glog.Warningf("PR %d has nil Deletions", *pr.Number) return } dels := *pr.Deletions commits, err := obj.GetCommits() if err != nil { return } for _, c := range commits { for _, f := range c.Files { for _, p := range genPrefixes { if strings.HasPrefix(*f.Filename, p) { adds = adds - *f.Additions dels = dels - *f.Deletions continue } } if genFiles.Has(*f.Filename) { adds = adds - *f.Additions dels = dels - *f.Deletions continue } } } newSize := calculateSize(adds, dels) newLabel := labelSizePrefix + newSize existing := github.GetLabelsWithPrefix(issue.Labels, labelSizePrefix) needsUpdate := true for _, l := range existing { if l == newLabel { needsUpdate = false continue } obj.RemoveLabel(l) } if needsUpdate { obj.AddLabels([]string{newLabel}) body := fmt.Sprintf("Labelling this PR as %s", newLabel) obj.WriteComment(body) } }
// Munge is the workhorse the will actually make updates to the PR func (sq *SubmitQueue) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } e2e := sq.e2e userSet := sq.userWhitelist if !obj.HasLabels([]string{claYes}) && !obj.HasLabels([]string{claHuman}) { sq.SetMergeStatus(obj, noCLA, false) return } if mergeable, err := obj.IsMergeable(); err != nil { sq.SetMergeStatus(obj, undeterminedMergability, false) return } else if !mergeable { sq.SetMergeStatus(obj, unmergeable, false) return } // Validate the status information for this PR contexts := sq.requiredStatusContexts(obj) if ok := obj.IsStatusSuccess(contexts); !ok { sq.SetMergeStatus(obj, ciFailure, false) return } if !obj.HasLabel(sq.WhitelistOverride) && !userSet.Has(*obj.Issue.User.Login) { if !obj.HasLabel(needsOKToMergeLabel) { obj.AddLabels([]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?" obj.WriteComment(body) } sq.SetMergeStatus(obj, needsok, false) return } // Tidy up the issue list. if obj.HasLabel(needsOKToMergeLabel) { obj.RemoveLabel(needsOKToMergeLabel) } if !obj.HasLabels([]string{"lgtm"}) { sq.SetMergeStatus(obj, noLGTM, false) return } lastModifiedTime := obj.LastModifiedTime() lgtmTime := obj.LabelTime("lgtm") if lastModifiedTime == nil || lgtmTime == nil { glog.Errorf("PR %d was unable to determine when LGTM was added or when last modified", *obj.Issue.Number) sq.SetMergeStatus(obj, unknown, false) return } if lastModifiedTime.After(*lgtmTime) { sq.SetMergeStatus(obj, lgtmEarly, false) return } if !e2e.Stable() { sq.flushGithubE2EQueue(e2eFailure) sq.SetMergeStatus(obj, e2eFailure, false) return } // if there is a 'e2e-not-required' label, just merge it. if obj.HasLabel(e2eNotRequiredLabel) { obj.MergePR("submit-queue") sq.SetMergeStatus(obj, merged, true) return } added := false sq.Lock() if _, ok := sq.githubE2EQueue[*obj.Issue.Number]; !ok { sq.githubE2EQueue[*obj.Issue.Number] = obj sq.githubE2EWakeup <- true added = true } sq.Unlock() if added { sq.SetMergeStatus(obj, ghE2EQueued, true) } return }
// Munge is the workhorse the will actually make updates to the PR func (sq *SubmitQueue) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } userSet := sq.userWhitelist if !obj.HasLabels([]string{claYes}) && !obj.HasLabels([]string{claHuman}) { sq.SetMergeStatus(obj, noCLA, false) return } if mergeable, err := obj.IsMergeable(); err != nil { sq.SetMergeStatus(obj, undeterminedMergability, false) return } else if !mergeable { sq.SetMergeStatus(obj, unmergeable, false) return } // Validate the status information for this PR contexts := sq.requiredStatusContexts(obj) if ok := obj.IsStatusSuccess(contexts); !ok { sq.SetMergeStatus(obj, ciFailure, false) return } if !obj.HasLabel(sq.WhitelistOverride) && !userSet.Has(*obj.Issue.User.Login) { if !obj.HasLabel(needsOKToMergeLabel) { obj.AddLabels([]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?" obj.WriteComment(body) } sq.SetMergeStatus(obj, needsok, false) return } // Tidy up the issue list. if obj.HasLabel(needsOKToMergeLabel) { obj.RemoveLabel(needsOKToMergeLabel) } if !obj.HasLabels([]string{"lgtm"}) { sq.SetMergeStatus(obj, noLGTM, false) return } lastModifiedTime := obj.LastModifiedTime() lgtmTime := obj.LabelTime("lgtm") if lastModifiedTime == nil || lgtmTime == nil { glog.Errorf("PR %d was unable to determine when LGTM was added or when last modified", *obj.Issue.Number) sq.SetMergeStatus(obj, unknown, false) return } if lastModifiedTime.After(*lgtmTime) { sq.SetMergeStatus(obj, lgtmEarly, false) return } added := false sq.Lock() if _, ok := sq.githubE2EQueue[*obj.Issue.Number]; !ok { added = true } // Add this most-recent object in place of the existing object. It will // have more up2date information. Even though we explicitly refresh the // PR information before do anything with it, this allow things like the // queue order to change dynamically as labels are added/removed. sq.githubE2EQueue[*obj.Issue.Number] = obj sq.Unlock() if added { sq.SetMergeStatus(obj, ghE2EQueued, true) } return }
// validForMerge is the base logic about what PR can be automatically merged. // PRs must pass this logic to be placed on the queue and they must pass this // logic a second time to be retested/merged after they get to the top of // the queue. // // If you update the logic PLEASE PLEASE PLEASE update serveMergeInfo() as well. func (sq *SubmitQueue) validForMerge(obj *github.MungeObject) bool { // Can't merge an issue! if !obj.IsPR() { return false } // Can't merge something already merged. if m, err := obj.IsMerged(); err != nil { glog.Errorf("%d: unknown err: %v", *obj.Issue.Number, err) sq.SetMergeStatus(obj, unknown) return false } else if m { sq.SetMergeStatus(obj, merged) return false } userSet := sq.userWhitelist // Must pass CLA checks if !obj.HasLabel(claYesLabel) && !obj.HasLabel(claHumanLabel) { sq.SetMergeStatus(obj, noCLA) return false } // Obviously must be mergeable if mergeable, err := obj.IsMergeable(); err != nil { sq.SetMergeStatus(obj, undeterminedMergability) return false } else if !mergeable { sq.SetMergeStatus(obj, unmergeable) return false } // Validate the status information for this PR contexts := sq.requiredStatusContexts(obj) if ok := obj.IsStatusSuccess(contexts); !ok { sq.SetMergeStatus(obj, ciFailure) return false } // The user either must be on the whitelist or have ok-to-merge if !obj.HasLabel(okToMergeLabel) && !userSet.Has(*obj.Issue.User.Login) { if !obj.HasLabel(needsOKToMergeLabel) { obj.AddLabels([]string{needsOKToMergeLabel}) obj.WriteComment(notInWhitelistBody) } sq.SetMergeStatus(obj, needsok) return false } // Tidy up the issue list. if obj.HasLabel(needsOKToMergeLabel) { obj.RemoveLabel(needsOKToMergeLabel) } // Clearly if !obj.HasLabel(lgtmLabel) { sq.SetMergeStatus(obj, noLGTM) return false } // PR cannot change since LGTM was added lastModifiedTime := obj.LastModifiedTime() lgtmTime := obj.LabelTime(lgtmLabel) if lastModifiedTime == nil || lgtmTime == nil { glog.Errorf("PR %d was unable to determine when LGTM was added or when last modified", *obj.Issue.Number) sq.SetMergeStatus(obj, unknown) return false } if lastModifiedTime.After(*lgtmTime) { sq.SetMergeStatus(obj, lgtmEarly) return false } // PR cannot have the label which prevents merging. if obj.HasLabel(doNotMergeLabel) { sq.SetMergeStatus(obj, noMerge) return false } return true }