github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/plugins/heart/heart.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 heart
    18  
    19  import (
    20  	"fmt"
    21  	"math/rand"
    22  	"path/filepath"
    23  	"regexp"
    24  	"strings"
    25  
    26  	"github.com/sirupsen/logrus"
    27  
    28  	"k8s.io/test-infra/prow/github"
    29  	"k8s.io/test-infra/prow/pluginhelp"
    30  	"k8s.io/test-infra/prow/plugins"
    31  )
    32  
    33  const (
    34  	pluginName            = "heart"
    35  	ownersFilename        = "OWNERS"
    36  	ownersAliasesFilename = "OWNERS_ALIASES"
    37  )
    38  
    39  var reactions = []string{
    40  	github.ReactionThumbsUp,
    41  	github.ReactionLaugh,
    42  	github.ReactionHeart,
    43  	github.ReactionHooray,
    44  }
    45  
    46  func init() {
    47  	plugins.RegisterIssueCommentHandler(pluginName, handleIssueComment, helpProvider)
    48  	plugins.RegisterPullRequestHandler(pluginName, handlePullRequest, helpProvider)
    49  }
    50  
    51  func helpProvider(config *plugins.Configuration, enabledRepos []string) (*pluginhelp.PluginHelp, error) {
    52  	// The {WhoCanUse, Usage, Examples} fields are omitted because this plugin is not triggered with commands.
    53  	return &pluginhelp.PluginHelp{
    54  			Description: "The heart plugin celebrates certain Github actions with the reaction emojis. Emojis are added to pull requests that make additions to OWNERS or OWNERS_ALIASES files and to comments left by specified \"adorees\".",
    55  			Config: map[string]string{
    56  				"": fmt.Sprintf(
    57  					"The heart plugin is configured to react to comments,  satisfying the regular expression %s, left by the following Github users: %s.",
    58  					config.Heart.CommentRegexp,
    59  					strings.Join(config.Heart.Adorees, ", "),
    60  				),
    61  			},
    62  		},
    63  		nil
    64  }
    65  
    66  type githubClient interface {
    67  	CreateCommentReaction(org, repo string, ID int, reaction string) error
    68  	CreateIssueReaction(org, repo string, ID int, reaction string) error
    69  	GetPullRequestChanges(org, repo string, number int) ([]github.PullRequestChange, error)
    70  }
    71  
    72  type client struct {
    73  	GitHubClient githubClient
    74  	Logger       *logrus.Entry
    75  }
    76  
    77  func getClient(pc plugins.Agent) client {
    78  	return client{
    79  		GitHubClient: pc.GitHubClient,
    80  		Logger:       pc.Logger,
    81  	}
    82  }
    83  
    84  func handleIssueComment(pc plugins.Agent, ic github.IssueCommentEvent) error {
    85  	if (pc.PluginConfig.Heart.Adorees == nil || len(pc.PluginConfig.Heart.Adorees) == 0) || len(pc.PluginConfig.Heart.CommentRegexp) == 0 {
    86  		return nil
    87  	}
    88  	return handleIC(getClient(pc), pc.PluginConfig.Heart.Adorees, pc.PluginConfig.Heart.CommentRe, ic)
    89  }
    90  
    91  func handlePullRequest(pc plugins.Agent, pre github.PullRequestEvent) error {
    92  	return handlePR(getClient(pc), pre)
    93  }
    94  
    95  func handleIC(c client, adorees []string, commentRe *regexp.Regexp, ic github.IssueCommentEvent) error {
    96  	// Only consider new comments on PRs.
    97  	if !ic.Issue.IsPullRequest() || ic.Action != github.IssueCommentActionCreated {
    98  		return nil
    99  	}
   100  	adoredLogin := false
   101  	for _, login := range adorees {
   102  		if ic.Comment.User.Login == login {
   103  			adoredLogin = true
   104  			break
   105  		}
   106  	}
   107  	if !adoredLogin {
   108  		return nil
   109  	}
   110  
   111  	if !commentRe.MatchString(ic.Comment.Body) {
   112  		return nil
   113  	}
   114  
   115  	c.Logger.Info("This is a wonderful thing!")
   116  	return c.GitHubClient.CreateCommentReaction(
   117  		ic.Repo.Owner.Login,
   118  		ic.Repo.Name,
   119  		ic.Comment.ID,
   120  		reactions[rand.Intn(len(reactions))])
   121  }
   122  
   123  func handlePR(c client, pre github.PullRequestEvent) error {
   124  	// Only consider newly opened PRs
   125  	if pre.Action != github.PullRequestActionOpened {
   126  		return nil
   127  	}
   128  
   129  	org := pre.PullRequest.Base.Repo.Owner.Login
   130  	repo := pre.PullRequest.Base.Repo.Name
   131  
   132  	changes, err := c.GitHubClient.GetPullRequestChanges(org, repo, pre.PullRequest.Number)
   133  	if err != nil {
   134  		return err
   135  	}
   136  
   137  	// Smile at any change that adds to OWNERS files
   138  	for _, change := range changes {
   139  		_, filename := filepath.Split(change.Filename)
   140  		if (filename == ownersFilename || filename == ownersAliasesFilename) && change.Additions > 0 {
   141  			c.Logger.Info("Adding new OWNERS makes me happy!")
   142  			return c.GitHubClient.CreateIssueReaction(
   143  				pre.PullRequest.Base.Repo.Owner.Login,
   144  				pre.PullRequest.Base.Repo.Name,
   145  				pre.Number,
   146  				reactions[rand.Intn(len(reactions))])
   147  		}
   148  	}
   149  
   150  	return nil
   151  }