github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/plugins/lifecycle/close.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 lifecycle
    18  
    19  import (
    20  	"fmt"
    21  	"regexp"
    22  
    23  	"github.com/sirupsen/logrus"
    24  
    25  	"k8s.io/test-infra/prow/github"
    26  	"k8s.io/test-infra/prow/plugins"
    27  )
    28  
    29  var closeRe = regexp.MustCompile(`(?mi)^/close\s*$`)
    30  
    31  type closeClient interface {
    32  	IsCollaborator(owner, repo, login string) (bool, error)
    33  	CreateComment(owner, repo string, number int, comment string) error
    34  	CloseIssue(owner, repo string, number int) error
    35  	ClosePR(owner, repo string, number int) error
    36  	GetIssueLabels(owner, repo string, number int) ([]github.Label, error)
    37  }
    38  
    39  func isActive(gc closeClient, org, repo string, number int) (bool, error) {
    40  	labels, err := gc.GetIssueLabels(org, repo, number)
    41  	if err != nil {
    42  		return true, fmt.Errorf("list issue labels error: %v", err)
    43  	}
    44  	for _, label := range []string{"lifecycle/stale", "lifecycle/rotten"} {
    45  		if github.HasLabel(label, labels) {
    46  			return false, nil
    47  		}
    48  	}
    49  	return true, nil
    50  }
    51  
    52  func handleClose(gc closeClient, log *logrus.Entry, e *github.GenericCommentEvent) error {
    53  	// Only consider open issues and new comments.
    54  	if e.IssueState != "open" || e.Action != github.GenericCommentActionCreated {
    55  		return nil
    56  	}
    57  
    58  	if !closeRe.MatchString(e.Body) {
    59  		return nil
    60  	}
    61  
    62  	org := e.Repo.Owner.Login
    63  	repo := e.Repo.Name
    64  	number := e.Number
    65  	commentAuthor := e.User.Login
    66  
    67  	isAuthor := e.IssueAuthor.Login == commentAuthor
    68  
    69  	isCollaborator, err := gc.IsCollaborator(org, repo, commentAuthor)
    70  	if err != nil {
    71  		log.WithError(err).Errorf("Failed IsCollaborator(%s, %s, %s)", org, repo, commentAuthor)
    72  	}
    73  
    74  	active, err := isActive(gc, org, repo, number)
    75  	if err != nil {
    76  		log.Infof("Cannot determine if issue is active: %v", err)
    77  		active = true // Fail active
    78  	}
    79  
    80  	// Only authors and collaborators are allowed to close active issues.
    81  	if !isAuthor && !isCollaborator && active {
    82  		response := "You can't close an active issue/PR unless you authored it or you are a collaborator."
    83  		log.Infof("Commenting \"%s\".", response)
    84  		return gc.CreateComment(
    85  			org,
    86  			repo,
    87  			number,
    88  			plugins.FormatResponseRaw(e.Body, e.HTMLURL, commentAuthor, response),
    89  		)
    90  	}
    91  
    92  	// Add a comment after closing the PR or issue
    93  	// to leave an audit trail of who asked to close it.
    94  	if e.IsPR {
    95  		log.Info("Closing PR.")
    96  		if err := gc.ClosePR(org, repo, number); err != nil {
    97  			return fmt.Errorf("Error closing PR: %v", err)
    98  		}
    99  		response := plugins.FormatResponseRaw(e.Body, e.HTMLURL, commentAuthor, "Closed this PR.")
   100  		return gc.CreateComment(org, repo, number, response)
   101  	}
   102  
   103  	log.Info("Closing issue.")
   104  	if err := gc.CloseIssue(org, repo, number); err != nil {
   105  		return fmt.Errorf("Error closing issue: %v", err)
   106  	}
   107  	response := plugins.FormatResponseRaw(e.Body, e.HTMLURL, commentAuthor, "Closing this issue.")
   108  	return gc.CreateComment(org, repo, number, response)
   109  }