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  }