github.com/abayer/test-infra@v0.0.5/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.PluginClient, 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  }