github.com/abayer/test-infra@v0.0.5/mungegithub/mungers/path_label.go (about) 1 /* 2 Copyright 2015 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 "bufio" 21 "fmt" 22 "os" 23 "regexp" 24 "strings" 25 26 "k8s.io/apimachinery/pkg/util/sets" 27 "k8s.io/test-infra/mungegithub/features" 28 "k8s.io/test-infra/mungegithub/github" 29 "k8s.io/test-infra/mungegithub/options" 30 31 "github.com/golang/glog" 32 ) 33 34 var ( 35 _ = fmt.Print 36 ) 37 38 const ( 39 jenkinsBotName = "k8s-bot" 40 ) 41 42 type labelMap struct { 43 regexp *regexp.Regexp 44 label string 45 } 46 47 // PathLabelMunger will add labels to PRs based on what files it modified. 48 // The mapping of files to labels if provided in a file in --path-label-config 49 type PathLabelMunger struct { 50 pathLabelFile string 51 labelMap []labelMap 52 allLabels sets.String 53 } 54 55 func init() { 56 RegisterMungerOrDie(&PathLabelMunger{}) 57 } 58 59 // Name is the name usable in --pr-mungers 60 func (p *PathLabelMunger) Name() string { return "path-label" } 61 62 // RequiredFeatures is a slice of 'features' that must be provided 63 func (p *PathLabelMunger) RequiredFeatures() []string { return []string{} } 64 65 // Initialize will initialize the munger 66 func (p *PathLabelMunger) Initialize(config *github.Config, features *features.Features) error { 67 allLabels := sets.NewString() 68 out := []labelMap{} 69 file := p.pathLabelFile 70 if len(file) == 0 { 71 glog.Infof("No 'path-label-config' option supplied, applying no labels.") 72 return nil 73 } 74 fp, err := os.Open(file) 75 if err != nil { 76 return err 77 } 78 defer fp.Close() 79 scanner := bufio.NewScanner(fp) 80 for scanner.Scan() { 81 line := scanner.Text() 82 if strings.HasPrefix(line, "#") { 83 continue 84 } 85 if line == "" { 86 continue 87 } 88 fields := strings.Fields(line) 89 if len(fields) != 2 { 90 glog.Errorf("Invalid line in path based label munger config %s: %q", file, line) 91 continue 92 } 93 r, err := regexp.Compile(fields[0]) 94 if err != nil { 95 glog.Errorf("Invalid regexp in label munger config %s: %q", file, fields[0]) 96 continue 97 } 98 99 label := fields[1] 100 lm := labelMap{ 101 regexp: r, 102 label: label, 103 } 104 out = append(out, lm) 105 allLabels.Insert(label) 106 } 107 p.allLabels = allLabels 108 p.labelMap = out 109 return scanner.Err() 110 } 111 112 // EachLoop is called at the start of every munge loop 113 func (p *PathLabelMunger) EachLoop() error { return nil } 114 115 // RegisterOptions registers options for this munger; returns any that require a restart when changed. 116 func (p *PathLabelMunger) RegisterOptions(opts *options.Options) sets.String { 117 opts.RegisterString(&p.pathLabelFile, "path-label-config", "", "file containing the pathname to label mappings") 118 opts.RegisterUpdateCallback(func(changed sets.String) error { 119 if changed.Has("path-label-config") { 120 return p.Initialize(nil, nil) // Initialize doesn't use config or features. 121 } 122 return nil 123 }) 124 return nil 125 } 126 127 // Munge is the workhorse the will actually make updates to the PR 128 func (p *PathLabelMunger) Munge(obj *github.MungeObject) { 129 if !obj.IsPR() { 130 return 131 } 132 133 files, ok := obj.ListFiles() 134 if !ok { 135 return 136 } 137 138 needsLabels := sets.NewString() 139 for _, f := range files { 140 for _, lm := range p.labelMap { 141 if lm.regexp.MatchString(*f.Filename) { 142 needsLabels.Insert(lm.label) 143 } 144 } 145 } 146 147 SyncLabels(p.allLabels, needsLabels, obj) 148 } 149 150 // SyncLabels properly syncs a set of labels. 'allLabels' must be a superset of 151 // 'desiredLabels'; to disable removing labels, set them to be the same set. 152 // Multiple mungers must somehow coordinate on which labels the bot ought to 153 // apply, otherwise the bot will fight with itself. 154 // 155 // TODO: fix error handling. 156 func SyncLabels(allLabels, desiredLabels sets.String, obj *github.MungeObject) error { 157 hasLabels := obj.LabelSet().Intersection(allLabels) 158 159 missingLabels := desiredLabels.Difference(hasLabels) 160 if missingLabels.Len() != 0 { 161 obj.AddLabels(missingLabels.List()) 162 } 163 164 extraLabels := hasLabels.Difference(desiredLabels) 165 for _, label := range extraLabels.List() { 166 creator, ok := obj.LabelCreator(label) 167 if ok && obj.IsRobot(creator) { 168 obj.RemoveLabel(label) 169 } 170 } 171 return nil 172 }