sigs.k8s.io/prow@v0.0.0-20240503223140-c5e374dc7eb1/pkg/plugins/mergecommitblocker/mergecommitblocker.go (about) 1 /* 2 Copyright 2019 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 mergecommitblocker 18 19 import ( 20 "fmt" 21 "strings" 22 23 "github.com/sirupsen/logrus" 24 25 "sigs.k8s.io/prow/pkg/config" 26 "sigs.k8s.io/prow/pkg/git/v2" 27 "sigs.k8s.io/prow/pkg/github" 28 "sigs.k8s.io/prow/pkg/labels" 29 "sigs.k8s.io/prow/pkg/pluginhelp" 30 "sigs.k8s.io/prow/pkg/plugins" 31 ) 32 33 const ( 34 // PluginName defines this plugin's registered name. 35 PluginName = "mergecommitblocker" 36 ) 37 38 var ( 39 commentBody = fmt.Sprintf("Adding label `%s` because PR contains merge commits, which are not allowed in this repository.\nUse `git rebase` to reapply your commits on top of the target branch. Detailed instructions for doing so can be found [here](https://git.k8s.io/community/contributors/guide/github-workflow.md#4-keep-your-branch-in-sync).", labels.MergeCommits) 40 ) 41 42 // init registers out plugin as a pull request handler 43 func init() { 44 plugins.RegisterPullRequestHandler(PluginName, handlePullRequest, helpProvider) 45 } 46 47 // helpProvider provides information on the plugin 48 func helpProvider(config *plugins.Configuration, _ []config.OrgRepo) (*pluginhelp.PluginHelp, error) { 49 // Only the Description field is specified because this plugin is not triggered with commands and is not configurable. 50 return &pluginhelp.PluginHelp{ 51 Description: fmt.Sprintf("The merge commit blocker plugin adds the %s label to pull requests that contain merge commits", labels.MergeCommits), 52 }, nil 53 } 54 55 type githubClient interface { 56 AddLabel(org, repo string, number int, label string) error 57 RemoveLabel(owner, repo string, number int, label string) error 58 GetIssueLabels(org, repo string, number int) ([]github.Label, error) 59 CreateComment(org, repo string, number int, comment string) error 60 } 61 62 type pruneClient interface { 63 PruneComments(func(ic github.IssueComment) bool) 64 } 65 66 func handlePullRequest(pc plugins.Agent, pre github.PullRequestEvent) error { 67 if pre.Action != github.PullRequestActionOpened && 68 pre.Action != github.PullRequestActionReopened && 69 pre.Action != github.PullRequestActionSynchronize { 70 return nil 71 } 72 cp, err := pc.CommentPruner() 73 if err != nil { 74 return err 75 } 76 return handle(pc.GitHubClient, pc.GitClient, cp, pc.Logger, &pre) 77 } 78 79 func handle(ghc githubClient, gc git.ClientFactory, cp pruneClient, log *logrus.Entry, pre *github.PullRequestEvent) error { 80 var ( 81 org = pre.PullRequest.Base.Repo.Owner.Login 82 repo = pre.PullRequest.Base.Repo.Name 83 num = pre.PullRequest.Number 84 ) 85 86 // Clone the repo, checkout the PR. 87 r, err := gc.ClientFor(org, repo) 88 if err != nil { 89 return err 90 } 91 defer func() { 92 if err := r.Clean(); err != nil { 93 log.WithError(err).Error("Error cleaning up repo.") 94 } 95 }() 96 if err := r.CheckoutPullRequest(num); err != nil { 97 return err 98 } 99 // We are guaranteed to have both Base.SHA and Head.SHA 100 target, head := pre.PullRequest.Base.SHA, pre.PullRequest.Head.SHA 101 existMergeCommits, err := r.MergeCommitsExistBetween(target, head) 102 if err != nil { 103 return err 104 } 105 issueLabels, err := ghc.GetIssueLabels(org, repo, num) 106 if err != nil { 107 return err 108 } 109 hasLabel := github.HasLabel(labels.MergeCommits, issueLabels) 110 if hasLabel && !existMergeCommits { 111 log.Infof("Removing %q Label for %s/%s#%d", labels.MergeCommits, org, repo, num) 112 if err := ghc.RemoveLabel(org, repo, num, labels.MergeCommits); err != nil { 113 return err 114 } 115 cp.PruneComments(func(ic github.IssueComment) bool { 116 return strings.Contains(ic.Body, commentBody) 117 }) 118 } else if !hasLabel && existMergeCommits { 119 log.Infof("Adding %q Label for %s/%s#%d", labels.MergeCommits, org, repo, num) 120 if err := ghc.AddLabel(org, repo, num, labels.MergeCommits); err != nil { 121 return err 122 } 123 msg := plugins.FormatSimpleResponse(commentBody) 124 return ghc.CreateComment(org, repo, num, msg) 125 } 126 return nil 127 }