k8s.io/test-infra@v0.0.0-20240520184403-27c6b4c223d8/robots/pr-creator/updater/updater.go (about)

     1  /*
     2  Copyright 2019 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 updater
    18  
    19  import (
    20  	"fmt"
    21  
    22  	"github.com/sirupsen/logrus"
    23  
    24  	"sigs.k8s.io/prow/pkg/github"
    25  )
    26  
    27  // Indicates whether maintainers can modify a pull request in fork.
    28  const (
    29  	AllowMods   = true
    30  	PreventMods = false
    31  )
    32  
    33  type updateClient interface {
    34  	UpdatePullRequest(org, repo string, number int, title, body *string, open *bool, branch *string, canModify *bool) error
    35  	BotUser() (*github.UserData, error)
    36  	FindIssues(query, sort string, asc bool) ([]github.Issue, error)
    37  }
    38  
    39  type ensureClient interface {
    40  	updateClient
    41  	AddLabel(org, repo string, number int, label string) error
    42  	CreatePullRequest(org, repo, title, body, head, base string, canModify bool) (int, error)
    43  	GetIssue(org, repo string, number int) (*github.Issue, error)
    44  }
    45  
    46  func UpdatePR(org, repo, title, body, headBranch string, gc updateClient) (*int, error) {
    47  	return updatePRWithQueryTokens(org, repo, title, body, "head:"+headBranch, gc)
    48  }
    49  
    50  func EnsurePR(org, repo, title, body, source, branch, headBranch string, allowMods bool, gc ensureClient) (*int, error) {
    51  	return EnsurePRWithLabels(org, repo, title, body, source, branch, headBranch, allowMods, gc, nil)
    52  }
    53  
    54  func EnsurePRWithQueryTokens(org, repo, title, body, source, baseBranch, queryTokensString string, allowMods bool, gc ensureClient) (*int, error) {
    55  	n, err := updatePRWithQueryTokens(org, repo, title, body, queryTokensString, gc)
    56  	if err != nil {
    57  		return nil, fmt.Errorf("update error: %w", err)
    58  	}
    59  	if n == nil {
    60  		pr, err := gc.CreatePullRequest(org, repo, title, body, source, baseBranch, allowMods)
    61  		if err != nil {
    62  			return nil, fmt.Errorf("create error: %w", err)
    63  		}
    64  		n = &pr
    65  	}
    66  
    67  	return n, nil
    68  }
    69  
    70  func updatePRWithQueryTokens(org, repo, title, body, queryTokensString string, gc updateClient) (*int, error) {
    71  	logrus.Info("Looking for a PR to reuse...")
    72  	me, err := gc.BotUser()
    73  	if err != nil {
    74  		return nil, fmt.Errorf("bot name: %w", err)
    75  	}
    76  
    77  	issues, err := gc.FindIssues(fmt.Sprintf("is:open is:pr archived:false repo:%s/%s author:%s %s", org, repo, me.Login, queryTokensString), "updated", false)
    78  	if err != nil {
    79  		return nil, fmt.Errorf("find issues: %w", err)
    80  	} else if len(issues) == 0 {
    81  		logrus.Info("No reusable issues found")
    82  		return nil, nil
    83  	}
    84  	n := issues[0].Number
    85  	logrus.Infof("Found %d", n)
    86  	var ignoreOpen *bool
    87  	var ignoreBranch *string
    88  	var ignoreModify *bool
    89  	if err := gc.UpdatePullRequest(org, repo, n, &title, &body, ignoreOpen, ignoreBranch, ignoreModify); err != nil {
    90  		return nil, fmt.Errorf("update %d: %w", n, err)
    91  	}
    92  
    93  	return &n, nil
    94  }
    95  
    96  func EnsurePRWithLabels(org, repo, title, body, source, baseBranch, headBranch string, allowMods bool, gc ensureClient, labels []string) (*int, error) {
    97  	return EnsurePRWithQueryTokensAndLabels(org, repo, title, body, source, baseBranch, "head:"+headBranch, allowMods, labels, gc)
    98  }
    99  
   100  func EnsurePRWithQueryTokensAndLabels(org, repo, title, body, source, baseBranch, queryTokensString string, allowMods bool, labels []string, gc ensureClient) (*int, error) {
   101  	n, err := EnsurePRWithQueryTokens(org, repo, title, body, source, baseBranch, queryTokensString, allowMods, gc)
   102  	if err != nil {
   103  		return n, err
   104  	}
   105  
   106  	if len(labels) == 0 {
   107  		return n, nil
   108  	}
   109  
   110  	issue, err := gc.GetIssue(org, repo, *n)
   111  	if err != nil {
   112  		return n, fmt.Errorf("failed to get PR: %w", err)
   113  	}
   114  
   115  	for _, label := range labels {
   116  		if issue.HasLabel(label) {
   117  			continue
   118  		}
   119  
   120  		if err := gc.AddLabel(org, repo, *n, label); err != nil {
   121  			return n, fmt.Errorf("failed to add label %q: %w", label, err)
   122  		}
   123  		logrus.WithField("label", label).Info("Added label")
   124  	}
   125  	return n, nil
   126  }