github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/plugins/trigger/generic-comment.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 trigger
    18  
    19  import (
    20  	"fmt"
    21  	"regexp"
    22  
    23  	"k8s.io/test-infra/prow/github"
    24  	"k8s.io/test-infra/prow/labels"
    25  	"k8s.io/test-infra/prow/plugins"
    26  )
    27  
    28  var okToTestRe = regexp.MustCompile(`(?m)^/ok-to-test\s*$`)
    29  var testAllRe = regexp.MustCompile(`(?m)^/test all,?($|\s.*)`)
    30  var retestRe = regexp.MustCompile(`(?m)^/retest\s*$`)
    31  
    32  func handleGenericComment(c Client, trigger *plugins.Trigger, gc github.GenericCommentEvent) error {
    33  	org := gc.Repo.Owner.Login
    34  	repo := gc.Repo.Name
    35  	number := gc.Number
    36  	commentAuthor := gc.User.Login
    37  	// Only take action when a comment is first created,
    38  	// when it belongs to a PR,
    39  	// and the PR is open.
    40  	if gc.Action != github.GenericCommentActionCreated || !gc.IsPR || gc.IssueState != "open" {
    41  		return nil
    42  	}
    43  	// Skip bot comments.
    44  	botName, err := c.GitHubClient.BotName()
    45  	if err != nil {
    46  		return err
    47  	}
    48  	if commentAuthor == botName {
    49  		return nil
    50  	}
    51  
    52  	// Which jobs does the comment want us to run?
    53  	allowOkToTest := trigger == nil || !trigger.IgnoreOkToTest
    54  	isOkToTest := okToTestRe.MatchString(gc.Body) && allowOkToTest
    55  	testAll := isOkToTest || testAllRe.MatchString(gc.Body)
    56  	shouldRetestFailed := retestRe.MatchString(gc.Body)
    57  	requestedJobs := c.Config.MatchingPresubmits(gc.Repo.FullName, gc.Body, testAll)
    58  	if !shouldRetestFailed && len(requestedJobs) == 0 {
    59  		// If a trusted member has commented "/ok-to-test",
    60  		// eventually add ok-to-test and remove needs-ok-to-test.
    61  		l, err := c.GitHubClient.GetIssueLabels(org, repo, number)
    62  		if err != nil {
    63  			return err
    64  		}
    65  		if isOkToTest && !github.HasLabel(labels.OkToTest, l) {
    66  			trusted, err := TrustedUser(c.GitHubClient, trigger, commentAuthor, org, repo)
    67  			if err != nil {
    68  				return err
    69  			}
    70  			if trusted {
    71  				if err := c.GitHubClient.AddLabel(org, repo, number, labels.OkToTest); err != nil {
    72  					return err
    73  				}
    74  				if github.HasLabel(labels.NeedsOkToTest, l) {
    75  					if err := c.GitHubClient.RemoveLabel(org, repo, number, labels.NeedsOkToTest); err != nil {
    76  						return err
    77  					}
    78  				}
    79  			}
    80  		}
    81  		return nil
    82  	}
    83  
    84  	pr, err := c.GitHubClient.GetPullRequest(org, repo, number)
    85  	if err != nil {
    86  		return err
    87  	}
    88  
    89  	// Skip untrusted users comments.
    90  	trusted, err := TrustedUser(c.GitHubClient, trigger, commentAuthor, org, repo)
    91  	if err != nil {
    92  		return fmt.Errorf("error checking trust of %s: %v", commentAuthor, err)
    93  	}
    94  	var l []github.Label
    95  	if !trusted {
    96  		// Skip untrusted PRs.
    97  		l, trusted, err = TrustedPullRequest(c.GitHubClient, trigger, gc.IssueAuthor.Login, org, repo, number, nil)
    98  		if err != nil {
    99  			return err
   100  		}
   101  		if !trusted {
   102  			resp := fmt.Sprintf("Cannot trigger testing until a trusted user reviews the PR and leaves an `/ok-to-test` message.")
   103  			c.Logger.Infof("Commenting \"%s\".", resp)
   104  			return c.GitHubClient.CreateComment(org, repo, number, plugins.FormatResponseRaw(gc.Body, gc.HTMLURL, gc.User.Login, resp))
   105  		}
   106  	}
   107  
   108  	// At this point we can trust the PR, so we eventually update labels.
   109  	// Ensure we have labels before test, because TrustedPullRequest() won't be called
   110  	// when commentAuthor is trusted.
   111  	if l == nil {
   112  		l, err = c.GitHubClient.GetIssueLabels(org, repo, number)
   113  		if err != nil {
   114  			return err
   115  		}
   116  	}
   117  	if isOkToTest && !github.HasLabel(labels.OkToTest, l) {
   118  		if err := c.GitHubClient.AddLabel(org, repo, number, labels.OkToTest); err != nil {
   119  			return err
   120  		}
   121  	}
   122  	if (isOkToTest || github.HasLabel(labels.OkToTest, l)) && github.HasLabel(labels.NeedsOkToTest, l) {
   123  		if err := c.GitHubClient.RemoveLabel(org, repo, number, labels.NeedsOkToTest); err != nil {
   124  			return err
   125  		}
   126  	}
   127  
   128  	// Do we have to run some tests?
   129  	var forceRunContexts map[string]bool
   130  	if shouldRetestFailed {
   131  		combinedStatus, err := c.GitHubClient.GetCombinedStatus(org, repo, pr.Head.SHA)
   132  		if err != nil {
   133  			return err
   134  		}
   135  		skipContexts := make(map[string]bool)    // these succeeded or are running
   136  		forceRunContexts = make(map[string]bool) // these failed and should be re-run
   137  		for _, status := range combinedStatus.Statuses {
   138  			state := status.State
   139  			if state == github.StatusSuccess || state == github.StatusPending {
   140  				skipContexts[status.Context] = true
   141  			} else if state == github.StatusError || state == github.StatusFailure {
   142  				forceRunContexts[status.Context] = true
   143  			}
   144  		}
   145  		retests := c.Config.RetestPresubmits(gc.Repo.FullName, skipContexts, forceRunContexts)
   146  		requestedJobs = append(requestedJobs, retests...)
   147  	}
   148  
   149  	return RunOrSkipRequested(c, pr, requestedJobs, forceRunContexts, gc.Body, gc.GUID)
   150  }