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 }