github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/plugins/wip/wip-label.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 wip will label a PR a work-in-progress if the author provides 18 // a prefix to their pull request title to the same effect. The submit- 19 // queue will not merge pull requests with the work-in-progress label. 20 // The label will be removed when the title changes to no longer begin 21 // with the prefix. 22 package wip 23 24 import ( 25 "fmt" 26 "regexp" 27 28 "github.com/sirupsen/logrus" 29 30 "k8s.io/test-infra/prow/github" 31 "k8s.io/test-infra/prow/labels" 32 "k8s.io/test-infra/prow/pluginhelp" 33 "k8s.io/test-infra/prow/plugins" 34 ) 35 36 const ( 37 // PluginName defines this plugin's registered name. 38 PluginName = "wip" 39 ) 40 41 var ( 42 titleRegex = regexp.MustCompile(`(?i)^\W?WIP\W`) 43 ) 44 45 type event struct { 46 org string 47 repo string 48 number int 49 hasLabel bool 50 needsLabel bool 51 } 52 53 func init() { 54 plugins.RegisterPullRequestHandler(PluginName, handlePullRequest, helpProvider) 55 } 56 57 func helpProvider(config *plugins.Configuration, enabledRepos []string) (*pluginhelp.PluginHelp, error) { 58 // Only the Description field is specified because this plugin is not triggered with commands and is not configurable. 59 return &pluginhelp.PluginHelp{ 60 Description: "The wip (Work In Progress) plugin applies the '" + labels.WorkInProgress + "' Label to pull requests whose title starts with 'WIP' and removes it from pull requests when they remove the title prefix. The '" + labels.WorkInProgress + "' Label is typically used to block a pull request from merging while it is still in progress.", 61 }, 62 nil 63 } 64 65 // Strict subset of *github.Client methods. 66 type githubClient interface { 67 GetIssueLabels(org, repo string, number int) ([]github.Label, error) 68 AddLabel(owner, repo string, number int, label string) error 69 RemoveLabel(owner, repo string, number int, label string) error 70 } 71 72 func handlePullRequest(pc plugins.Agent, pe github.PullRequestEvent) error { 73 // These are the only actions indicating the PR title may have changed. 74 if pe.Action != github.PullRequestActionOpened && 75 pe.Action != github.PullRequestActionReopened && 76 pe.Action != github.PullRequestActionEdited { 77 return nil 78 } 79 80 var ( 81 org = pe.PullRequest.Base.Repo.Owner.Login 82 repo = pe.PullRequest.Base.Repo.Name 83 number = pe.PullRequest.Number 84 title = pe.PullRequest.Title 85 ) 86 87 currentLabels, err := pc.GitHubClient.GetIssueLabels(org, repo, number) 88 if err != nil { 89 return fmt.Errorf("could not get labels for PR %s/%s:%d in WIP plugin: %v", org, repo, number, err) 90 } 91 hasLabel := false 92 for _, l := range currentLabels { 93 if l.Name == labels.WorkInProgress { 94 hasLabel = true 95 } 96 } 97 98 needsLabel := titleRegex.MatchString(title) 99 100 e := &event{ 101 org: org, 102 repo: repo, 103 number: number, 104 hasLabel: hasLabel, 105 needsLabel: needsLabel, 106 } 107 return handle(pc.GitHubClient, pc.Logger, e) 108 } 109 110 // handle interacts with GitHub to drive the pull request to the 111 // proper state by adding and removing comments and labels. If a 112 // PR has a WIP prefix, it needs an explanatory comment and label. 113 // Otherwise, neither should be present. 114 func handle(gc githubClient, le *logrus.Entry, e *event) error { 115 if e.needsLabel && !e.hasLabel { 116 if err := gc.AddLabel(e.org, e.repo, e.number, labels.WorkInProgress); err != nil { 117 le.Warnf("error while adding Label %q: %v", labels.WorkInProgress, err) 118 return err 119 } 120 } else if !e.needsLabel && e.hasLabel { 121 if err := gc.RemoveLabel(e.org, e.repo, e.number, labels.WorkInProgress); err != nil { 122 le.Warnf("error while removing Label %q: %v", labels.WorkInProgress, err) 123 return err 124 } 125 } 126 return nil 127 }