github.com/shashidharatd/test-infra@v0.0.0-20171006011030-71304e1ca560/mungegithub/mungers/cherrypick-auto-approve.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 "regexp" 22 "strconv" 23 "strings" 24 25 "k8s.io/kubernetes/pkg/util/sets" 26 "k8s.io/test-infra/mungegithub/features" 27 "k8s.io/test-infra/mungegithub/github" 28 "k8s.io/test-infra/mungegithub/options" 29 30 "github.com/golang/glog" 31 ) 32 33 const ( 34 cherrypickAutoApprove = "cherrypick-auto-approve" 35 ) 36 37 var ( 38 cpRe = regexp.MustCompile(`Cherry pick of #([[:digit:]]+) on release-([[:digit:]]+\.[[:digit:]]+).`) 39 ) 40 41 // CherrypickAutoApprove will add 'cherrypick-approved' to PRs which are 42 // for 'cherrypick-approved' parents. This only works if the PR (against 43 // the 'release-*' branch was done using the script. 44 type CherrypickAutoApprove struct { 45 config *github.Config 46 } 47 48 func init() { 49 RegisterMungerOrDie(&CherrypickAutoApprove{}) 50 } 51 52 // Name is the name usable in --pr-mungers 53 func (c *CherrypickAutoApprove) Name() string { return cherrypickAutoApprove } 54 55 // RequiredFeatures is a slice of 'features' that must be provided 56 func (c *CherrypickAutoApprove) RequiredFeatures() []string { return []string{} } 57 58 // Initialize will initialize the munger 59 func (c *CherrypickAutoApprove) Initialize(config *github.Config, features *features.Features) error { 60 c.config = config 61 return nil 62 } 63 64 // EachLoop is called at the start of every munge loop 65 func (c *CherrypickAutoApprove) EachLoop() error { return nil } 66 67 // RegisterOptions registers options for this munger; returns any that require a restart when changed. 68 func (c *CherrypickAutoApprove) RegisterOptions(opts *options.Options) sets.String { return nil } 69 70 func getCherrypickParentPRs(obj *github.MungeObject, config *github.Config) []*github.MungeObject { 71 out := []*github.MungeObject{} 72 if obj.Issue.Body == nil { 73 glog.Errorf("Found a nil body in %d", *obj.Issue.Number) 74 return nil 75 } 76 body := *obj.Issue.Body 77 78 // foundOne tracks if we found any valid lines. PR without any valid lines 79 // shouldn't get autolabeled. 80 81 lines := strings.Split(body, "\n") 82 for _, line := range lines { 83 matches := cpRe.FindStringSubmatch(line) 84 if len(matches) != 3 { 85 glog.V(6).Infof("%d: line:%v len(matches)=%d", *obj.Issue.Number, line, len(matches)) 86 continue 87 } 88 parentPRNum, err := strconv.Atoi(matches[1]) 89 if err != nil { 90 glog.Errorf("%d: Unable to convert %q to parent PR number", *obj.Issue.Number, matches[1]) 91 return nil 92 } 93 parentPR, err := config.GetObject(parentPRNum) 94 if err != nil { 95 glog.Errorf("Unable to get object for %d", parentPRNum) 96 return nil 97 } 98 out = append(out, parentPR) 99 } 100 return out 101 } 102 103 // Munge is the workhorse the will actually make updates to the PR 104 func (c *CherrypickAutoApprove) Munge(obj *github.MungeObject) { 105 if !obj.IsPR() { 106 return 107 } 108 forBranch, ok := obj.IsForBranch("master") 109 if !ok || forBranch { 110 return 111 } 112 milestone, ok := obj.ReleaseMilestone() 113 if !ok { 114 return 115 } 116 if obj.HasLabel(cpApprovedLabel) && milestone != "" { 117 return 118 } 119 120 parents := getCherrypickParentPRs(obj, c.config) 121 if len(parents) == 0 { 122 return 123 } 124 125 major := 0 126 minor := 0 127 branch, ok := obj.Branch() 128 if !ok { 129 return 130 } 131 if l, err := fmt.Sscanf(branch, "release-%d.%d", &major, &minor); err != nil || l != 2 { 132 return 133 } 134 branchImpliedMilestone := fmt.Sprintf("v%d.%d", major, minor) 135 136 if milestone != "" && milestone != branchImpliedMilestone { 137 glog.Errorf("Found PR %d on branch %q but have milestone %q", *obj.Issue.Number, branch, milestone) 138 return 139 } 140 141 for _, parent := range parents { 142 if !parent.HasLabel(cpApprovedLabel) { 143 return 144 } 145 146 // If the parent was for milestone v1.2 but this PR has 147 // comments saying it was 'on branch release-1.1' we should 148 // not auto approve 149 parentMilestone, ok := parent.ReleaseMilestone() 150 if parentMilestone != branchImpliedMilestone || !ok { 151 branch, _ := obj.Branch() 152 glog.Errorf("%d: parentReleaseMilestone=%q but branch is %q", *obj.Issue.Number, parentMilestone, branch) 153 return 154 } 155 } 156 if milestone == "" { 157 obj.SetMilestone(branchImpliedMilestone) 158 } 159 if !obj.HasLabel(cpApprovedLabel) { 160 obj.AddLabel(cpApprovedLabel) 161 } 162 }