github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/pkg/plugins/merge-method-comment/merge-method-comment.go (about) 1 /* 2 Copyright 2020 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 mergemethodcomment contains a Prow plugin which comments on PRs with 18 // 2 or more commits, informing the user: 19 // - How to request commits to be squashed if default merge method is merge, 20 // - How to request commits to be merged if the repo squashes commits by default, 21 // - That the commits will be merged/squashed if it is not possible to override 22 // the default merge method. 23 package mergemethodcomment 24 25 import ( 26 "fmt" 27 "strings" 28 29 "sigs.k8s.io/prow/pkg/config" 30 "sigs.k8s.io/prow/pkg/git/types" 31 "sigs.k8s.io/prow/pkg/github" 32 "sigs.k8s.io/prow/pkg/pluginhelp" 33 "sigs.k8s.io/prow/pkg/plugins" 34 ) 35 36 const pluginName = "merge-method-comment" 37 38 // Strict subset of github.Client methods. 39 type githubClient interface { 40 ListIssueComments(org, repo string, number int) ([]github.IssueComment, error) 41 CreateComment(org, repo string, number int, comment string) error 42 BotUserChecker() (func(candidate string) bool, error) 43 } 44 45 func init() { 46 plugins.RegisterPullRequestHandler(pluginName, handlePullRequest, helpProvider) 47 } 48 49 func helpProvider(config *plugins.Configuration, _ []config.OrgRepo) (*pluginhelp.PluginHelp, error) { 50 return &pluginhelp.PluginHelp{ 51 Description: "The merge-method-comment plugin adds a comment on how to request a different-from-default merge method to PRs with more than 1 commit", 52 }, 53 nil 54 } 55 56 func handlePullRequest(pc plugins.Agent, pe github.PullRequestEvent) error { 57 return handlePR(pc.GitHubClient, pc.Config.ProwConfig.Tide, pe) 58 } 59 60 func handlePR(gc githubClient, c config.Tide, pe github.PullRequestEvent) error { 61 if !isPRChanged(pe) { 62 return nil 63 } 64 65 commentNeeded, comment := needsComment(c, pe) 66 if !commentNeeded { 67 return nil 68 } 69 70 owner := pe.PullRequest.Base.Repo.Owner.Login 71 repo := pe.PullRequest.Base.Repo.Name 72 num := pe.PullRequest.Number 73 74 hasComment, err := issueHasComment(gc, owner, repo, num, comment) 75 if err != nil { 76 return err 77 } 78 if hasComment { 79 return nil 80 } 81 82 return gc.CreateComment(owner, repo, num, plugins.FormatSimpleResponse(comment)) 83 } 84 85 func needsComment(c config.Tide, pe github.PullRequestEvent) (bool, string) { 86 if pe.PullRequest.Commits <= 1 { 87 return false, "" 88 } 89 90 orgRepo := config.OrgRepo{ 91 Org: pe.PullRequest.Base.Repo.Owner.Login, 92 Repo: pe.PullRequest.Base.Repo.Name, 93 } 94 method := c.MergeMethod(orgRepo) 95 comment := fmt.Sprintf("This PR has multiple commits, and the default merge method is: %s.\n", method) 96 97 switch { 98 case method == types.MergeSquash && c.MergeLabel != "": 99 comment = fmt.Sprintf("%sYou can request commits to be merged using the label: %s", comment, c.MergeLabel) 100 case method == types.MergeSquash && c.MergeLabel == "": 101 comment = comment + "Commits will be squashed, as no merge labels are defined" 102 case method == types.MergeMerge && c.SquashLabel != "": 103 comment = fmt.Sprintf("%sYou can request commits to be squashed using the label: %s", comment, c.SquashLabel) 104 case method == types.MergeMerge && c.SquashLabel == "": 105 comment = comment + "Commits will be merged, as no squash labels are defined" 106 } 107 108 return true, comment 109 } 110 111 func issueHasComment(gc githubClient, org, repo string, number int, comment string) (bool, error) { 112 botNameChecker, err := gc.BotUserChecker() 113 if err != nil { 114 return false, err 115 } 116 117 comments, err := gc.ListIssueComments(org, repo, number) 118 if err != nil { 119 return false, fmt.Errorf("error listing issue comments: %w", err) 120 } 121 122 for _, c := range comments { 123 if botNameChecker(c.User.Login) && strings.Contains(c.Body, comment) { 124 return true, nil 125 } 126 } 127 return false, nil 128 } 129 130 // These are the only actions indicating the code diffs may have changed. 131 func isPRChanged(pe github.PullRequestEvent) bool { 132 switch pe.Action { 133 case github.PullRequestActionOpened: 134 return true 135 case github.PullRequestActionReopened: 136 return true 137 case github.PullRequestActionSynchronize: 138 return true 139 case github.PullRequestActionEdited: 140 return true 141 default: 142 return false 143 } 144 }