github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/plugins/hold/hold.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 hold contains a plugin which will allow users to label their 18 // own pull requests as not ready or ready for merge. The submit queue 19 // will honor the label to ensure pull requests do not merge when it is 20 // applied. 21 package hold 22 23 import ( 24 "fmt" 25 "regexp" 26 27 "github.com/sirupsen/logrus" 28 29 "k8s.io/test-infra/prow/github" 30 "k8s.io/test-infra/prow/labels" 31 "k8s.io/test-infra/prow/pluginhelp" 32 "k8s.io/test-infra/prow/plugins" 33 ) 34 35 const ( 36 // PluginName defines this plugin's registered name. 37 PluginName = "hold" 38 ) 39 40 var ( 41 labelRe = regexp.MustCompile(`(?mi)^/hold\s*$`) 42 labelCancelRe = regexp.MustCompile(`(?mi)^/hold cancel\s*$`) 43 ) 44 45 type hasLabelFunc func(label string, issueLabels []github.Label) bool 46 47 func init() { 48 plugins.RegisterGenericCommentHandler(PluginName, handleGenericComment, helpProvider) 49 } 50 51 func helpProvider(config *plugins.Configuration, enabledRepos []string) (*pluginhelp.PluginHelp, error) { 52 // The Config field is omitted because this plugin is not configurable. 53 pluginHelp := &pluginhelp.PluginHelp{ 54 Description: "The hold plugin allows anyone to add or remove the '" + labels.Hold + "' Label from a pull request in order to temporarily prevent the PR from merging without withholding approval.", 55 } 56 pluginHelp.AddCommand(pluginhelp.Command{ 57 Usage: "/hold [cancel]", 58 Description: "Adds or removes the `" + labels.Hold + "` Label which is used to indicate that the PR should not be automatically merged.", 59 Featured: false, 60 WhoCanUse: "Anyone can use the /hold command to add or remove the '" + labels.Hold + "' Label.", 61 Examples: []string{"/hold", "/hold cancel"}, 62 }) 63 return pluginHelp, nil 64 } 65 66 type githubClient interface { 67 AddLabel(owner, repo string, number int, label string) error 68 RemoveLabel(owner, repo string, number int, label string) error 69 GetIssueLabels(org, repo string, number int) ([]github.Label, error) 70 } 71 72 func handleGenericComment(pc plugins.Agent, e github.GenericCommentEvent) error { 73 hasLabel := func(label string, labels []github.Label) bool { 74 return github.HasLabel(label, labels) 75 } 76 return handle(pc.GitHubClient, pc.Logger, &e, hasLabel) 77 } 78 79 // handle drives the pull request to the desired state. If any user adds 80 // a /hold directive, we want to add a label if one does not already exist. 81 // If they add /hold cancel, we want to remove the label if it exists. 82 func handle(gc githubClient, log *logrus.Entry, e *github.GenericCommentEvent, f hasLabelFunc) error { 83 if e.Action != github.GenericCommentActionCreated { 84 return nil 85 } 86 needsLabel := false 87 if labelRe.MatchString(e.Body) { 88 needsLabel = true 89 } else if labelCancelRe.MatchString(e.Body) { 90 needsLabel = false 91 } else { 92 return nil 93 } 94 95 org := e.Repo.Owner.Login 96 repo := e.Repo.Name 97 issueLabels, err := gc.GetIssueLabels(org, repo, e.Number) 98 if err != nil { 99 return fmt.Errorf("failed to get the labels on %s/%s#%d: %v", org, repo, e.Number, err) 100 } 101 102 hasLabel := f(labels.Hold, issueLabels) 103 if hasLabel && !needsLabel { 104 log.Infof("Removing %q Label for %s/%s#%d", labels.Hold, org, repo, e.Number) 105 return gc.RemoveLabel(org, repo, e.Number, labels.Hold) 106 } else if !hasLabel && needsLabel { 107 log.Infof("Adding %q Label for %s/%s#%d", labels.Hold, org, repo, e.Number) 108 return gc.AddLabel(org, repo, e.Number, labels.Hold) 109 } 110 return nil 111 }