github.com/abayer/test-infra@v0.0.5/mungegithub/mungers/sig-mention-handler.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package mungers 18 19 import ( 20 "fmt" 21 "regexp" 22 "strings" 23 24 "k8s.io/apimachinery/pkg/util/sets" 25 "k8s.io/test-infra/mungegithub/features" 26 "k8s.io/test-infra/mungegithub/github" 27 "k8s.io/test-infra/mungegithub/options" 28 29 "github.com/golang/glog" 30 githubapi "github.com/google/go-github/github" 31 ) 32 33 var ( 34 labelPrefixes = []string{"sig/", "committee/", "wg/"} 35 sigMentionRE = regexp.MustCompile(`@\S+\nThere are no sig labels on this issue.`) 36 ) 37 38 const needsSigLabel = "needs-sig" 39 40 type SigMentionHandler struct{} 41 42 func init() { 43 h := &SigMentionHandler{} 44 RegisterMungerOrDie(h) 45 RegisterStaleIssueComments(h) 46 } 47 48 // Name is the name usable in --pr-mungers 49 func (*SigMentionHandler) Name() string { return "sig-mention-handler" } 50 51 // RequiredFeatures is a slice of 'features' that must be provided 52 func (*SigMentionHandler) RequiredFeatures() []string { 53 return []string{} 54 } 55 56 // Initialize will initialize the munger 57 func (s *SigMentionHandler) Initialize(config *github.Config, features *features.Features) error { 58 return nil 59 } 60 61 // EachLoop is called at the start of every munge loop 62 func (*SigMentionHandler) EachLoop() error { return nil } 63 64 // RegisterOptions registers options for this munger; returns any that require a restart when changed. 65 func (*SigMentionHandler) RegisterOptions(opts *options.Options) sets.String { return nil } 66 67 func (*SigMentionHandler) HasSigLabel(obj *github.MungeObject) bool { 68 labels := obj.Issue.Labels 69 70 for i := range labels { 71 if labels[i].Name == nil { 72 continue 73 } 74 for j := range labelPrefixes { 75 if strings.HasPrefix(*labels[i].Name, labelPrefixes[j]) { 76 return true 77 } 78 } 79 } 80 81 return false 82 } 83 84 func (*SigMentionHandler) HasNeedsSigLabel(obj *github.MungeObject) bool { 85 labels := obj.Issue.Labels 86 87 for i := range labels { 88 if labels[i].Name != nil && strings.Compare(*labels[i].Name, needsSigLabel) == 0 { 89 return true 90 } 91 } 92 93 return false 94 } 95 96 // Munge is the workhorse notifying issue owner to add a @kubernetes/sig mention if there is none 97 // The algorithm: 98 // (1) return if it is a PR and/or the issue is closed 99 // (2) find if the issue has a sig label 100 // (3) find if the issue has a needs-sig label 101 // (4) if the issue has both the sig and needs-sig labels, remove the needs-sig label 102 // (5) if the issue has none of the labels, add the needs-sig label and comment 103 // (6) if the issue has only the sig label, do nothing 104 // (7) if the issue has only the needs-sig label, do nothing 105 func (s *SigMentionHandler) Munge(obj *github.MungeObject) { 106 if obj.Issue == nil || obj.IsPR() || obj.Issue.State == nil || *obj.Issue.State == "closed" { 107 return 108 } 109 110 hasSigLabel := s.HasSigLabel(obj) 111 hasNeedsSigLabel := s.HasNeedsSigLabel(obj) 112 113 if hasSigLabel && hasNeedsSigLabel { 114 if err := obj.RemoveLabel(needsSigLabel); err != nil { 115 glog.Errorf("failed to remove needs-sig label for issue #%d", *obj.Issue.Number) 116 } 117 } else if !hasSigLabel && !hasNeedsSigLabel { 118 if err := obj.AddLabel(needsSigLabel); err != nil { 119 glog.Errorf("failed to add needs-sig label for issue #%d", *obj.Issue.Number) 120 return 121 } 122 123 msg := fmt.Sprintf(`@%s 124 There are no sig labels on this issue. Please [add a sig label](https://go.k8s.io/bot-commands) by: 125 126 1. mentioning a sig: `+"`@kubernetes/sig-<group-name>-<group-suffix>`"+` 127 e.g., `+"`@kubernetes/sig-contributor-experience-<group-suffix>`"+` to notify the contributor experience sig, OR 128 129 2. specifying the label manually: `+"`/sig <label>`"+` 130 e.g., `+"`/sig scalability`"+` to apply the `+"`sig/scalability`"+` label 131 132 Note: Method 1 will trigger an email to the group. See the [group list](https://git.k8s.io/community/sig-list.md) and [label list](https://github.com/kubernetes/kubernetes/labels). 133 The `+"`<group-suffix>`"+` in the method 1 has to be replaced with one of these: _**bugs, feature-requests, pr-reviews, test-failures, proposals**_`, *obj.Issue.User.Login) 134 135 if err := obj.WriteComment(msg); err != nil { 136 glog.Errorf("failed to leave comment for %s that issue #%d needs sig label", *obj.Issue.User.Login, *obj.Issue.Number) 137 } 138 } 139 } 140 141 // isStaleIssueComment determines if a comment is stale based on if the issue has the needs-sig label 142 func (*SigMentionHandler) isStaleIssueComment(obj *github.MungeObject, comment *githubapi.IssueComment) bool { 143 if !obj.IsRobot(comment.User) { 144 return false 145 } 146 if !sigMentionRE.MatchString(*comment.Body) { 147 return false 148 } 149 stale := !obj.HasLabel(needsSigLabel) 150 if stale { 151 glog.V(6).Infof("Found stale SigMentionHandler comment") 152 } 153 return stale 154 } 155 156 // StaleIssueComments returns a slice of stale issue comments. 157 func (s *SigMentionHandler) StaleIssueComments(obj *github.MungeObject, comments []*githubapi.IssueComment) []*githubapi.IssueComment { 158 return forEachCommentTest(obj, comments, s.isStaleIssueComment) 159 }