github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/plugins/docs-no-retest/docs-no-retest.go (about) 1 /* 2 Copyright 2017 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 docsnoretest contains a Prow plugin which manages a label indicating 18 // whether a given pull requests only changes documentation. In such cases it 19 // would not need to be retested. 20 package docsnoretest 21 22 import ( 23 "fmt" 24 "path" 25 "regexp" 26 27 "k8s.io/test-infra/prow/github" 28 "k8s.io/test-infra/prow/pluginhelp" 29 "k8s.io/test-infra/prow/plugins" 30 ) 31 32 const ( 33 pluginName = "docs-no-retest" 34 labelSkipRetest = "retest-not-required-docs-only" 35 ) 36 37 // TODO: Make the regex configurable. 38 var ( 39 docFilesRegex = regexp.MustCompile(`^.*\.(md|png|svg|dia)$`) 40 ownersFilesRegex = regexp.MustCompile(`^OWNERS$`) 41 licenseFilesRegex = regexp.MustCompile(`^LICENSE$`) 42 securityContactsRegex = regexp.MustCompile(`^SECURITY_CONTACTS$`) 43 ownersAliasesRegex = regexp.MustCompile(`^OWNERS_ALIASES$`) 44 ) 45 46 func init() { 47 plugins.RegisterPullRequestHandler(pluginName, handlePullRequest, helpProvider) 48 } 49 50 func helpProvider(config *plugins.Configuration, enabledRepos []string) (*pluginhelp.PluginHelp, error) { 51 // The {WhoCanUse, Usage, Examples, Config} fields are omitted because this plugin cannot be 52 // manually triggered and is not configurable. 53 return &pluginhelp.PluginHelp{ 54 Description: `The docs-no-retest plugin applies the '` + labelSkipRetest + `' label to pull requests that only touch documentation type files and thus do not need to be retested against the latest master commit before merging. 55 <br>Files extensions '.md', '.png', '.svg', and '.dia' are considered documentation.`, 56 }, 57 nil 58 } 59 60 func handlePullRequest(pc plugins.Agent, pe github.PullRequestEvent) error { 61 return handlePR(pc.GitHubClient, pe) 62 } 63 64 // Strict subset of *github.Client methods. 65 type githubClient interface { 66 AddLabel(owner, repo string, number int, label string) error 67 RemoveLabel(owner, repo string, number int, label string) error 68 GetIssueLabels(org, repo string, number int) ([]github.Label, error) 69 GetPullRequestChanges(org, repo string, number int) ([]github.PullRequestChange, error) 70 } 71 72 func handlePR(gc githubClient, pe github.PullRequestEvent) error { 73 var ( 74 owner = pe.PullRequest.Base.Repo.Owner.Login 75 repo = pe.PullRequest.Base.Repo.Name 76 num = pe.PullRequest.Number 77 ) 78 79 if pe.Action != github.PullRequestActionOpened && 80 pe.Action != github.PullRequestActionReopened && 81 pe.Action != github.PullRequestActionSynchronize { 82 return nil 83 } 84 85 changes, err := gc.GetPullRequestChanges(owner, repo, num) 86 if err != nil { 87 return fmt.Errorf("cannot get pull request changes for docs-no-retest plugin: %v", err) 88 } 89 90 docsOnly := true 91 for _, change := range changes { 92 _, basename := path.Split(change.Filename) 93 if docFilesRegex.MatchString(basename) { 94 continue 95 } 96 if ownersFilesRegex.MatchString(basename) { 97 continue 98 } 99 if licenseFilesRegex.MatchString(basename) { 100 continue 101 } 102 if securityContactsRegex.MatchString(basename) { 103 continue 104 } 105 if ownersAliasesRegex.MatchString(basename) { 106 continue 107 } 108 docsOnly = false 109 break 110 } 111 112 labels, err := gc.GetIssueLabels(owner, repo, num) 113 if err != nil { 114 return fmt.Errorf("cannot get labels for docs-no-retest plugin: %v", err) 115 } 116 117 hasTargetLabel := false 118 for _, label := range labels { 119 if label.Name == labelSkipRetest { 120 hasTargetLabel = true 121 break 122 } 123 } 124 125 if docsOnly && !hasTargetLabel { 126 if err := gc.AddLabel(owner, repo, num, labelSkipRetest); err != nil { 127 return fmt.Errorf("error adding label to %s/%s#%d: %v", owner, repo, num, err) 128 } 129 } else if !docsOnly && hasTargetLabel { 130 if err := gc.RemoveLabel(owner, repo, num, labelSkipRetest); err != nil { 131 return fmt.Errorf("error removing label from %s/%s#%d: %v", owner, repo, num, err) 132 } 133 } 134 135 return nil 136 }