github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/pkg/plugins/reward-owners/reward-owners.go (about)

     1  /*
     2  Copyright 2021 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 rewardowners
    18  
    19  import (
    20  	"fmt"
    21  	"path/filepath"
    22  	"strings"
    23  
    24  	"github.com/sirupsen/logrus"
    25  
    26  	"k8s.io/apimachinery/pkg/util/sets"
    27  
    28  	"sigs.k8s.io/prow/pkg/config"
    29  	"sigs.k8s.io/prow/pkg/github"
    30  	"sigs.k8s.io/prow/pkg/pluginhelp"
    31  	"sigs.k8s.io/prow/pkg/plugins"
    32  	"sigs.k8s.io/prow/pkg/plugins/ownersconfig"
    33  	"sigs.k8s.io/prow/pkg/repoowners"
    34  )
    35  
    36  const (
    37  	// PluginName defines this plugin's registered name.
    38  	PluginName    = "reward-owners"
    39  	RewardMessage = `
    40  Thanks to %s for serving the community in this new capacity!
    41  
    42  Next steps:
    43  1- Reach out in [slack.k8s.io](http://slack.k8s.io/) -> #sig-contribex for your special badge swag!
    44  
    45  2- Join [slack.k8s.io](http://slack.k8s.io/) -> #kubernetes-contributors in slack and [dev@kubernetes.io](https://groups.google.com/a/kubernetes.io/g/dev) for all upstream info
    46  
    47  3- Review the [community-membership.md](https://github.com/kubernetes/community/blob/master/community-membership.md) doc for your role. If for some reason you can't perform the duties associated, Emeritus is a great way to take a break! [OWNERs](https://github.com/kubernetes/community/blob/master/contributors/guide/owners.md) is another great resource for how this works.
    48  
    49  4- Look over our [governance](https://github.com/kubernetes/community/blob/master/governance.md) docs now that you are actively involved in the maintenance of the project.
    50  `
    51  )
    52  
    53  func init() {
    54  	plugins.RegisterPullRequestHandler(PluginName, handlePullRequest, helpProvider)
    55  }
    56  
    57  func helpProvider(_ *plugins.Configuration, _ []config.OrgRepo) (*pluginhelp.PluginHelp, error) {
    58  	pluginHelp := &pluginhelp.PluginHelp{
    59  		Description: fmt.Sprintf("The reward-owners plugin watches in %s and %s files for modifications and welcomes new approvers and reviewers.", ownersconfig.DefaultOwnersFile, ownersconfig.DefaultOwnersAliasesFile),
    60  	}
    61  	return pluginHelp, nil
    62  }
    63  
    64  type githubClient interface {
    65  	CreateComment(owner, repo string, number int, comment string) error
    66  	GetPullRequestChanges(org, repo string, number int) ([]github.PullRequestChange, error)
    67  }
    68  
    69  type ownersClient interface {
    70  	LoadRepoOwnersSha(org, repo, base, sha string, updateCache bool) (repoowners.RepoOwner, error)
    71  }
    72  
    73  type info struct {
    74  	base         github.PullRequestBranch
    75  	head         github.PullRequestBranch
    76  	number       int
    77  	org          string
    78  	repo         string
    79  	repoFullName string
    80  }
    81  
    82  func handlePullRequest(pc plugins.Agent, pre github.PullRequestEvent) error {
    83  	// Only consider closed PRs that got merged
    84  	if pre.Action != github.PullRequestActionClosed || !pre.PullRequest.Merged {
    85  		return nil
    86  	}
    87  
    88  	prInfo := info{
    89  		base:         pre.PullRequest.Base,
    90  		head:         pre.PullRequest.Head,
    91  		number:       pre.Number,
    92  		org:          pre.Repo.Owner.Login,
    93  		repo:         pre.Repo.Name,
    94  		repoFullName: pre.Repo.FullName,
    95  	}
    96  	return handle(pc.GitHubClient, pc.OwnersClient, pc.Logger, prInfo, pc.PluginConfig.OwnersFilenames)
    97  }
    98  
    99  func handle(ghc githubClient, oc ownersClient, log *logrus.Entry, info info, resolver ownersconfig.Resolver) error {
   100  
   101  	// Get changes.
   102  	changes, err := ghc.GetPullRequestChanges(info.org, info.repo, info.number)
   103  	if err != nil {
   104  		return fmt.Errorf("error getting PR changes: %w", err)
   105  	}
   106  
   107  	// Check if OWNERS or OWNERS_ALIASES have been modified.
   108  	var ownersModified bool
   109  	filenames := resolver(info.org, info.repo)
   110  	for _, change := range changes {
   111  		if (filepath.Base(change.Filename) == filenames.Owners || change.Filename == filenames.OwnersAliases) &&
   112  			change.Status != github.PullRequestFileRemoved {
   113  			ownersModified = true
   114  			break
   115  		}
   116  	}
   117  	if !ownersModified {
   118  		return nil
   119  	}
   120  
   121  	log.Debug("Resolving repository owners for base branch...")
   122  	baseRepo, err := oc.LoadRepoOwnersSha(info.org, info.repo, info.base.Ref, info.base.SHA, false)
   123  	if err != nil {
   124  		return err
   125  	}
   126  
   127  	log.Debug("Resolving repository owners for head branch...")
   128  	headRepo, err := oc.LoadRepoOwnersSha(info.org, info.repo, info.head.Ref, info.head.SHA, false)
   129  	if err != nil {
   130  		return err
   131  	}
   132  
   133  	// Reward only new owners.
   134  	newOwners := headRepo.AllOwners().Difference(baseRepo.AllOwners())
   135  	if newOwners.Len() == 0 {
   136  		log.Debug("No new owner to reward, exiting.")
   137  		return nil
   138  	}
   139  
   140  	// Tag users by prepending @ to their names.
   141  	taggedNewOwners := make([]string, newOwners.Len())
   142  	for i, o := range sets.List(newOwners) {
   143  		taggedNewOwners[i] = fmt.Sprintf("@%s", o)
   144  	}
   145  
   146  	return ghc.CreateComment(info.org, info.repo, info.number, fmt.Sprintf(RewardMessage, strings.Join(taggedNewOwners, ", ")))
   147  }