github.com/shashidharatd/test-infra@v0.0.0-20171006011030-71304e1ca560/mungegithub/mungers/block_paths.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  	"os"
    22  	"regexp"
    23  
    24  	"k8s.io/kubernetes/pkg/util/sets"
    25  	"k8s.io/kubernetes/pkg/util/yaml"
    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  	githubapi "github.com/google/go-github/github"
    32  )
    33  
    34  const (
    35  	blockedPathsLabel = "do-not-merge/blocked-paths"
    36  	blockPathFormat   = `Adding label:%s because PR changes docs prohibited to auto merge
    37  See http://kubernetes.io/editdocs/ for information about editing docs`
    38  )
    39  
    40  var (
    41  	_                       = fmt.Print
    42  	blockPathBody           = fmt.Sprintf(blockPathFormat, blockedPathsLabel)
    43  	deprecatedBlockPathBody = fmt.Sprintf(blockPathFormat, doNotMergeLabel)
    44  )
    45  
    46  type configBlockPath struct {
    47  	BlockRegexp      []string `json:"blockRegexp,omitempty" yaml:"blockRegexp,omitempty"`
    48  	DoNotBlockRegexp []string `json:"doNotBlockRegexp,omitempty" yaml:"doNotBlockRegexp,omitempty"`
    49  }
    50  
    51  // BlockPath will add a label to block auto merge if a PR touches certain paths
    52  type BlockPath struct {
    53  	path             string
    54  	blockRegexp      []*regexp.Regexp
    55  	doNotBlockRegexp []*regexp.Regexp
    56  }
    57  
    58  func init() {
    59  	b := &BlockPath{}
    60  	RegisterMungerOrDie(b)
    61  	RegisterStaleIssueComments(b)
    62  }
    63  
    64  // Name is the name usable in --pr-mungers
    65  func (b *BlockPath) Name() string { return "block-path" }
    66  
    67  // RequiredFeatures is a slice of 'features' that must be provided
    68  func (b *BlockPath) RequiredFeatures() []string { return []string{} }
    69  
    70  // Initialize will initialize the munger
    71  func (b *BlockPath) Initialize(config *github.Config, features *features.Features) error {
    72  	return b.loadConfig()
    73  }
    74  
    75  func (b *BlockPath) loadConfig() error {
    76  	if len(b.path) == 0 {
    77  		return fmt.Errorf("'block-path-config' option is required with the block-path munger")
    78  	}
    79  	file, err := os.Open(b.path)
    80  	if err != nil {
    81  		return fmt.Errorf("Failed to load block-path config: %v", err)
    82  	}
    83  	defer file.Close()
    84  
    85  	c := &configBlockPath{}
    86  	if err := yaml.NewYAMLToJSONDecoder(file).Decode(c); err != nil {
    87  		return fmt.Errorf("Failed to decode the block-path config: %v", err)
    88  	}
    89  
    90  	blockRegexp := []*regexp.Regexp{}
    91  	for _, str := range c.BlockRegexp {
    92  		reg, err := regexp.Compile(str)
    93  		if err != nil {
    94  			return err
    95  		}
    96  		blockRegexp = append(blockRegexp, reg)
    97  	}
    98  
    99  	doNotBlockRegexp := []*regexp.Regexp{}
   100  	for _, str := range c.DoNotBlockRegexp {
   101  		reg, err := regexp.Compile(str)
   102  		if err != nil {
   103  			return err
   104  		}
   105  		doNotBlockRegexp = append(doNotBlockRegexp, reg)
   106  	}
   107  
   108  	b.blockRegexp = blockRegexp
   109  	b.doNotBlockRegexp = doNotBlockRegexp
   110  	return nil
   111  }
   112  
   113  // EachLoop is called at the start of every munge loop
   114  func (b *BlockPath) EachLoop() error { return nil }
   115  
   116  // RegisterOptions registers options for this munger; returns any that require a restart when changed.
   117  func (b *BlockPath) RegisterOptions(opts *options.Options) sets.String {
   118  	opts.RegisterString(&b.path, "block-path-config", "", "file containing the pathnames to block or not block")
   119  	opts.RegisterUpdateCallback(func(changed sets.String) error {
   120  		if changed.Has("block-path-config") {
   121  			if err := b.loadConfig(); err != nil {
   122  				glog.Errorf("error reloading block-path-config: %v", err)
   123  			}
   124  		}
   125  		return nil
   126  	})
   127  	return nil
   128  }
   129  
   130  func matchesAny(path string, regs []*regexp.Regexp) bool {
   131  	for _, reg := range regs {
   132  		if reg.MatchString(path) {
   133  			return true
   134  		}
   135  	}
   136  	return false
   137  }
   138  
   139  // Munge is the workhorse the will actually make updates to the PR
   140  func (b *BlockPath) Munge(obj *github.MungeObject) {
   141  	if !obj.IsPR() {
   142  		return
   143  	}
   144  
   145  	if obj.HasLabel(blockedPathsLabel) {
   146  		return
   147  	}
   148  
   149  	files, ok := obj.ListFiles()
   150  	if !ok {
   151  		return
   152  	}
   153  
   154  	for _, f := range files {
   155  		if matchesAny(*f.Filename, b.blockRegexp) {
   156  			if matchesAny(*f.Filename, b.doNotBlockRegexp) {
   157  				continue
   158  			}
   159  			obj.WriteComment(blockPathBody)
   160  			obj.AddLabels([]string{blockedPathsLabel})
   161  			return
   162  		}
   163  	}
   164  }
   165  
   166  func (b *BlockPath) isStaleIssueComment(obj *github.MungeObject, comment *githubapi.IssueComment) bool {
   167  	if !obj.IsRobot(comment.User) {
   168  		return false
   169  	}
   170  	if *comment.Body != blockPathBody && *comment.Body != deprecatedBlockPathBody {
   171  		return false
   172  	}
   173  	stale := !obj.HasLabel(blockedPathsLabel)
   174  	if stale {
   175  		glog.V(6).Infof("Found stale BlockPath comment")
   176  	}
   177  	return stale
   178  }
   179  
   180  // StaleIssueComments returns a slice of stale issue comments.
   181  func (b *BlockPath) StaleIssueComments(obj *github.MungeObject, comments []*githubapi.IssueComment) []*githubapi.IssueComment {
   182  	return forEachCommentTest(obj, comments, b.isStaleIssueComment)
   183  }