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  }