github.com/shashidharatd/test-infra@v0.0.0-20171006011030-71304e1ca560/mungegithub/mungers/cherrypick-clear-after-merge.go (about) 1 /* 2 Copyright 2016 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 mungers 18 19 import ( 20 "fmt" 21 "strings" 22 23 "k8s.io/kubernetes/pkg/util/sets" 24 "k8s.io/test-infra/mungegithub/features" 25 "k8s.io/test-infra/mungegithub/github" 26 "k8s.io/test-infra/mungegithub/options" 27 28 "github.com/golang/glog" 29 ) 30 31 const ( 32 clearAfterMergeName = "cherrypick-clear-after-merge" 33 ) 34 35 // ClearPickAfterMerge will remove the the cherrypick-candidate label from 36 // any PR that does not have a 'release' milestone set. 37 type ClearPickAfterMerge struct { 38 features *features.Features 39 } 40 41 func init() { 42 RegisterMungerOrDie(&ClearPickAfterMerge{}) 43 } 44 45 // Name is the name usable in --pr-mungers 46 func (c *ClearPickAfterMerge) Name() string { return clearAfterMergeName } 47 48 // RequiredFeatures is a slice of 'features' that must be provided 49 func (c *ClearPickAfterMerge) RequiredFeatures() []string { return []string{features.RepoFeatureName} } 50 51 // Initialize will initialize the munger 52 func (c *ClearPickAfterMerge) Initialize(config *github.Config, features *features.Features) error { 53 c.features = features 54 return nil 55 } 56 57 // EachLoop is called at the start of every munge loop 58 func (c *ClearPickAfterMerge) EachLoop() error { return nil } 59 60 // RegisterOptions registers options for this munger; returns any that require a restart when changed. 61 func (c *ClearPickAfterMerge) RegisterOptions(opts *options.Options) sets.String { return nil } 62 63 func handleFound(obj *github.MungeObject, branch string) error { 64 msg := fmt.Sprintf("Commit found in the %q branch appears to be this PR. Removing the %q label. If this is an error find help to get your PR picked.", branch, cpCandidateLabel) 65 obj.WriteComment(msg) 66 obj.RemoveLabel(cpCandidateLabel) 67 return nil 68 } 69 70 // foundLog will return if the given `logString` exists on the branch in question. 71 // it will also return the actual logs for further processing 72 func (c *ClearPickAfterMerge) foundLog(branch string, logString string) (bool, string) { 73 args := []string{"merge-base", "origin/master", "origin/" + branch} 74 out, err := c.features.Repos.GitCommand(args) 75 base := string(out) 76 if err != nil { 77 glog.Errorf("Unable to find the fork point for branch %s. %s:%v", branch, base, err) 78 return false, "" 79 } 80 lines := strings.Split(base, "\n") 81 if len(lines) < 1 { 82 glog.Errorf("Found 0 lines splitting the results of git merge-base") 83 } 84 base = lines[0] 85 86 // if release-1.2 branched from master at abcdef123 this should result in: 87 // abcdef123..origin/release-1.2 88 logRefs := fmt.Sprintf("%s..origin/%s", base, branch) 89 90 args = []string{"log", "--pretty=tformat:%H%n%s%n%b", "-F", "--grep", logString, logRefs} 91 out, err = c.features.Repos.GitCommand(args) 92 logs := string(out) 93 if err != nil { 94 glog.Errorf("Error grepping logs out=%q: %v", logs, err) 95 return false, "" 96 } 97 glog.V(10).Infof("args:%v", args) 98 99 glog.V(10).Infof("Searching for %q in %q", logString, logs) 100 if !strings.Contains(logs, logString) { 101 return false, "" 102 } 103 return true, logs 104 } 105 106 // Can we find a commit in the changelog that looks like it was done using git cherry-pick -m1 -x ? 107 func (c *ClearPickAfterMerge) foundByPickDashX(obj *github.MungeObject, branch string) bool { 108 sha, ok := obj.MergeCommit() 109 if !ok { 110 return false 111 } 112 if sha == nil { 113 glog.Errorf("Unable to get SHA of merged PR %d", *obj.Issue.Number) 114 return false 115 } 116 117 cherrypickMsg := fmt.Sprintf("(cherry picked from commit %s)", *sha) 118 found, logs := c.foundLog(branch, cherrypickMsg) 119 if !found { 120 return false 121 } 122 123 // double check for the 'non -x' message 124 logMsg := fmt.Sprintf("Merge pull request #%d from ", *obj.Issue.Number) 125 if !strings.Contains(logs, logMsg) { 126 return false 127 } 128 glog.Infof("Found cherry-pick for %d using -x information in branch %q", *obj.Issue.Number, branch) 129 return true 130 } 131 132 // Can we find a commit in the changelog that looks like it was done using git cherry-pick -m1 ? 133 func (c *ClearPickAfterMerge) foundByPickWithoutDashX(obj *github.MungeObject, branch string) bool { 134 logMsg := fmt.Sprintf("Merge pull request #%d from ", *obj.Issue.Number) 135 136 found, _ := c.foundLog(branch, logMsg) 137 if found { 138 glog.Infof("Found cherry-pick for %d using log matching for `git cherry-pick` in branch %q", *obj.Issue.Number, branch) 139 } 140 return found 141 } 142 143 // Check that the commit messages for all commits in the PR are on the branch 144 func (c *ClearPickAfterMerge) foundByAllCommits(obj *github.MungeObject, branch string) bool { 145 commits, ok := obj.GetCommits() 146 if !ok { 147 glog.Infof("unable to get commits") 148 return false 149 } 150 for _, commit := range commits { 151 if commit.Commit == nil { 152 return false 153 } 154 if commit.Commit.Message == nil { 155 return false 156 } 157 found, _ := c.foundLog(branch, *commit.Commit.Message) 158 if !found { 159 return false 160 } 161 } 162 return true 163 } 164 165 // Can we find a commit in the changelog that looks like it was done using the hack/cherry_pick_pull.sh script ? 166 func (c *ClearPickAfterMerge) foundByScript(obj *github.MungeObject, branch string) bool { 167 logMsg := fmt.Sprintf("Automated cherry pick of #%d", *obj.Issue.Number) 168 169 found, _ := c.foundLog(branch, logMsg) 170 if found { 171 glog.Infof("Found cherry-pick for %d using log matching for `hack/cherry_pick_pull.sh` in branch %q", *obj.Issue.Number, branch) 172 } 173 return found 174 } 175 176 // Munge is the workhorse the will actually make updates to the PR 177 func (c *ClearPickAfterMerge) Munge(obj *github.MungeObject) { 178 if !obj.IsPR() { 179 return 180 } 181 if !obj.HasLabel(cpCandidateLabel) { 182 return 183 } 184 185 if merged, ok := obj.IsMerged(); !ok || !merged { 186 return 187 } 188 189 releaseMilestone, ok := obj.ReleaseMilestone() 190 if !ok || releaseMilestone == "" || len(releaseMilestone) != 4 { 191 glog.Errorf("Found invalid milestone: %q", releaseMilestone) 192 return 193 } 194 rel := releaseMilestone[1:] 195 branch := "release-" + rel 196 197 if c.foundByPickDashX(obj, branch) { 198 handleFound(obj, branch) 199 return 200 } 201 202 if c.foundByAllCommits(obj, branch) { 203 handleFound(obj, branch) 204 return 205 } 206 207 if c.foundByPickWithoutDashX(obj, branch) { 208 handleFound(obj, branch) 209 return 210 } 211 212 if c.foundByScript(obj, branch) { 213 handleFound(obj, branch) 214 return 215 } 216 217 return 218 }