github.com/abayer/test-infra@v0.0.5/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  	CreateComment(owner, repo string, number int, comment string) error
    33  	CloseIssue(owner, repo string, number int) error
    34  	ClosePR(owner, repo string, number int) error
    35  	IsMember(owner, login string) (bool, error)
    36  	AssignIssue(owner, repo string, number int, assignees []string) error
    37  	GetIssueLabels(owner, repo string, number int) ([]github.Label, error)
    38  }
    39  
    40  func isActive(gc closeClient, org, repo string, number int) (bool, error) {
    41  	labels, err := gc.GetIssueLabels(org, repo, number)
    42  	if err != nil {
    43  		return true, fmt.Errorf("list issue labels error: %v", err)
    44  	}
    45  	for _, label := range []string{"lifecycle/stale", "lifecycle/rotten"} {
    46  		if github.HasLabel(label, labels) {
    47  			return false, nil
    48  		}
    49  	}
    50  	return true, nil
    51  }
    52  
    53  func handleClose(gc closeClient, log *logrus.Entry, e *github.GenericCommentEvent) error {
    54  	// Only consider open issues and new comments.
    55  	if e.IssueState != "open" || e.Action != github.GenericCommentActionCreated {
    56  		return nil
    57  	}
    58  
    59  	if !closeRe.MatchString(e.Body) {
    60  		return nil
    61  	}
    62  
    63  	org := e.Repo.Owner.Login
    64  	repo := e.Repo.Name
    65  	number := e.Number
    66  	commentAuthor := e.User.Login
    67  
    68  	// Allow assignees and authors to close issues.
    69  	isAssignee := false
    70  	for _, assignee := range e.Assignees {
    71  		if commentAuthor == assignee.Login {
    72  			isAssignee = true
    73  			break
    74  		}
    75  	}
    76  	isAuthor := e.IssueAuthor.Login == commentAuthor
    77  
    78  	if !isAssignee && !isAuthor {
    79  		active, err := isActive(gc, org, repo, number)
    80  		if err != nil {
    81  			log.Infof("Cannot determine if issue is active: %v", err)
    82  			active = true // Fail active
    83  		}
    84  
    85  		if active {
    86  			// Try to assign the issue to the comment author
    87  			log.Infof("Assign to %s", commentAuthor)
    88  			if err := gc.AssignIssue(org, repo, number, []string{commentAuthor}); err != nil {
    89  				msg := "Assigning you to the issue failed."
    90  				if ok, merr := gc.IsMember(org, commentAuthor); merr == nil && !ok {
    91  					msg = "Can only assign issues to org members and/or repo collaborators."
    92  				} else if merr != nil {
    93  					log.WithError(merr).Errorf("Failed IsMember(%s, %s)", org, commentAuthor)
    94  				} else {
    95  					log.WithError(err).Errorf("Failed AssignIssue(%s, %s, %d, %s)", org, repo, number, commentAuthor)
    96  				}
    97  				resp := fmt.Sprintf("you can't close an active issue unless you authored it or you are assigned to it, %s.", msg)
    98  				log.Infof("Commenting \"%s\".", resp)
    99  				return gc.CreateComment(org, repo, number, plugins.FormatResponseRaw(e.Body, e.HTMLURL, commentAuthor, resp))
   100  			}
   101  		}
   102  	}
   103  
   104  	if e.IsPR {
   105  		log.Info("Closing PR.")
   106  		return gc.ClosePR(org, repo, number)
   107  	}
   108  
   109  	log.Info("Closing issue.")
   110  	return gc.CloseIssue(org, repo, number)
   111  }