github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/plugins/cherrypickunapproved/cherrypick-unapproved.go (about) 1 /* 2 Copyright 2018 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 cherrypickunapproved adds the `do-not-merge/cherry-pick-not-approved` 18 // label to PRs against a release branch which do not have the 19 // `cherry-pick-approved` label. 20 package cherrypickunapproved 21 22 import ( 23 "fmt" 24 "regexp" 25 "strings" 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 = "cherry-pick-unapproved" 38 ) 39 40 func init() { 41 plugins.RegisterPullRequestHandler(PluginName, handlePullRequest, helpProvider) 42 } 43 44 func helpProvider(config *plugins.Configuration, enabledRepos []string) (*pluginhelp.PluginHelp, error) { 45 // Only the 'Config' and Description' fields are necessary because this 46 // plugin does not react to any commands. 47 pluginHelp := &pluginhelp.PluginHelp{ 48 Description: "Label PRs against a release branch which do not have the `cherry-pick-approved` label with the `do-not-merge/cherry-pick-not-approved` label.", 49 Config: map[string]string{ 50 "": fmt.Sprintf( 51 "The cherry-pick-unapproved plugin treats PRs against branch names satisfying the regular expression `%s` as cherry-pick PRs and adds the following comment:\n%s", 52 config.CherryPickUnapproved.BranchRegexp, 53 config.CherryPickUnapproved.Comment, 54 ), 55 }, 56 } 57 return pluginHelp, nil 58 } 59 60 type githubClient interface { 61 CreateComment(owner, repo string, number int, comment string) error 62 AddLabel(owner, repo string, number int, label string) error 63 RemoveLabel(owner, repo string, number int, label string) error 64 GetIssueLabels(org, repo string, number int) ([]github.Label, error) 65 } 66 67 type commentPruner interface { 68 PruneComments(shouldPrune func(github.IssueComment) bool) 69 } 70 71 func handlePullRequest(pc plugins.Agent, pr github.PullRequestEvent) error { 72 cp, err := pc.CommentPruner() 73 if err != nil { 74 return err 75 } 76 return handlePR( 77 pc.GitHubClient, pc.Logger, &pr, cp, 78 pc.PluginConfig.CherryPickUnapproved.BranchRe, pc.PluginConfig.CherryPickUnapproved.Comment, 79 ) 80 } 81 82 func handlePR(gc githubClient, log *logrus.Entry, pr *github.PullRequestEvent, cp commentPruner, branchRe *regexp.Regexp, commentBody string) error { 83 // Only consider the events that indicate opening of the PR and 84 // when the cpApproved and cpUnapproved labels are added or removed 85 cpLabelUpdated := (pr.Action == github.PullRequestActionLabeled || pr.Action == github.PullRequestActionUnlabeled) && 86 (pr.Label.Name == labels.CpApproved || pr.Label.Name == labels.CpUnapproved) 87 if pr.Action != github.PullRequestActionOpened && pr.Action != github.PullRequestActionReopened && !cpLabelUpdated { 88 return nil 89 } 90 91 var ( 92 org = pr.Repo.Owner.Login 93 repo = pr.Repo.Name 94 branch = pr.PullRequest.Base.Ref 95 ) 96 97 // if the branch doesn't match against the branch names allowed for cherry-picks, 98 // don't do anything 99 if !branchRe.MatchString(branch) { 100 return nil 101 } 102 103 issueLabels, err := gc.GetIssueLabels(org, repo, pr.Number) 104 if err != nil { 105 return err 106 } 107 hasCherryPickApprovedLabel := github.HasLabel(labels.CpApproved, issueLabels) 108 hasCherryPickUnapprovedLabel := github.HasLabel(labels.CpUnapproved, issueLabels) 109 110 // if it has the approved label, 111 // remove the unapproved label (if it exists) and 112 // remove any comments left by this plugin 113 if hasCherryPickApprovedLabel { 114 if hasCherryPickUnapprovedLabel { 115 if err := gc.RemoveLabel(org, repo, pr.Number, labels.CpUnapproved); err != nil { 116 log.WithError(err).Errorf("Github failed to remove the following label: %s", labels.CpUnapproved) 117 } 118 } 119 cp.PruneComments(func(comment github.IssueComment) bool { 120 return strings.Contains(comment.Body, commentBody) 121 }) 122 return nil 123 } 124 125 // if it already has the unapproved label, we are done here 126 if hasCherryPickUnapprovedLabel { 127 return nil 128 } 129 130 // only add the label and comment if none of the approved and unapproved labels are present 131 if err := gc.AddLabel(org, repo, pr.Number, labels.CpUnapproved); err != nil { 132 log.WithError(err).Errorf("Github failed to add the following label: %s", labels.CpUnapproved) 133 } 134 135 formattedComment := plugins.FormatSimpleResponse(pr.PullRequest.User.Login, commentBody) 136 if err := gc.CreateComment(org, repo, pr.Number, formattedComment); err != nil { 137 log.WithError(err).Errorf("Failed to comment %q", formattedComment) 138 } 139 140 return nil 141 }