github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/pkg/plugins/wip/wip-label.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 wip will label a PR a work-in-progress if the author provides
    18  // a prefix to their pull request title to the same effect. The submit-
    19  // queue will not merge pull requests with the work-in-progress label.
    20  // The label will be removed when the title changes to no longer begin
    21  // with the prefix.
    22  package wip
    23  
    24  import (
    25  	"fmt"
    26  	"regexp"
    27  
    28  	"github.com/sirupsen/logrus"
    29  
    30  	"sigs.k8s.io/prow/pkg/config"
    31  	"sigs.k8s.io/prow/pkg/github"
    32  	"sigs.k8s.io/prow/pkg/labels"
    33  	"sigs.k8s.io/prow/pkg/pluginhelp"
    34  	"sigs.k8s.io/prow/pkg/plugins"
    35  )
    36  
    37  const (
    38  	// PluginName defines this plugin's registered name.
    39  	PluginName = "wip"
    40  )
    41  
    42  var (
    43  	titleRegex = regexp.MustCompile(`(?i)^\W?WIP\W`)
    44  )
    45  
    46  type event struct {
    47  	org      string
    48  	repo     string
    49  	number   int
    50  	title    string
    51  	draft    bool
    52  	hasLabel bool
    53  }
    54  
    55  func init() {
    56  	plugins.RegisterPullRequestHandler(PluginName, handlePullRequest, helpProvider)
    57  }
    58  
    59  func helpProvider(config *plugins.Configuration, _ []config.OrgRepo) (*pluginhelp.PluginHelp, error) {
    60  	// Only the Description field is specified because this plugin is not triggered with commands and is not configurable.
    61  	return &pluginhelp.PluginHelp{
    62  			Description: "The wip (Work In Progress) plugin applies the '" + labels.WorkInProgress + "' Label to pull requests whose title starts with 'WIP' or are in the 'draft' stage, and removes it from pull requests when they remove the title prefix or become ready for review. The '" + labels.WorkInProgress + "' Label is typically used to block a pull request from merging while it is still in progress.",
    63  		},
    64  		nil
    65  }
    66  
    67  // Strict subset of github.Client methods.
    68  type githubClient interface {
    69  	GetIssueLabels(org, repo string, number int) ([]github.Label, error)
    70  	AddLabel(owner, repo string, number int, label string) error
    71  	RemoveLabel(owner, repo string, number int, label string) error
    72  }
    73  
    74  func handlePullRequest(pc plugins.Agent, pe github.PullRequestEvent) error {
    75  	// These are the only actions indicating the PR title may have changed.
    76  	if pe.Action != github.PullRequestActionOpened &&
    77  		pe.Action != github.PullRequestActionReopened &&
    78  		pe.Action != github.PullRequestActionEdited &&
    79  		pe.Action != github.PullRequestActionReadyForReview &&
    80  		pe.Action != github.PullRequestActionConvertedToDraft {
    81  		return nil
    82  	}
    83  
    84  	var (
    85  		org    = pe.PullRequest.Base.Repo.Owner.Login
    86  		repo   = pe.PullRequest.Base.Repo.Name
    87  		number = pe.PullRequest.Number
    88  		title  = pe.PullRequest.Title
    89  		draft  = pe.PullRequest.Draft
    90  	)
    91  
    92  	currentLabels, err := pc.GitHubClient.GetIssueLabels(org, repo, number)
    93  	if err != nil {
    94  		return fmt.Errorf("could not get labels for PR %s/%s:%d in WIP plugin: %w", org, repo, number, err)
    95  	}
    96  	hasLabel := false
    97  	for _, l := range currentLabels {
    98  		if l.Name == labels.WorkInProgress {
    99  			hasLabel = true
   100  		}
   101  	}
   102  	e := &event{
   103  		org:      org,
   104  		repo:     repo,
   105  		number:   number,
   106  		title:    title,
   107  		draft:    draft,
   108  		hasLabel: hasLabel,
   109  	}
   110  	return handle(pc.GitHubClient, pc.Logger, e)
   111  }
   112  
   113  // handle interacts with GitHub to drive the pull request to the
   114  // proper state by adding and removing comments and labels. If a
   115  // PR has a WIP prefix, it needs an explanatory comment and label.
   116  // Otherwise, neither should be present.
   117  func handle(gc githubClient, le *logrus.Entry, e *event) error {
   118  	needsLabel := e.draft || titleRegex.MatchString(e.title)
   119  
   120  	if needsLabel && !e.hasLabel {
   121  		if err := gc.AddLabel(e.org, e.repo, e.number, labels.WorkInProgress); err != nil {
   122  			le.Warnf("error while adding Label %q: %v", labels.WorkInProgress, err)
   123  			return err
   124  		}
   125  	} else if !needsLabel && e.hasLabel {
   126  		if err := gc.RemoveLabel(e.org, e.repo, e.number, labels.WorkInProgress); err != nil {
   127  			le.Warnf("error while removing Label %q: %v", labels.WorkInProgress, err)
   128  			return err
   129  		}
   130  	}
   131  	return nil
   132  }