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

     1  /*
     2  Copyright 2018 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 main
    18  
    19  import (
    20  	"errors"
    21  	"flag"
    22  	"fmt"
    23  	"os"
    24  	"strings"
    25  
    26  	"github.com/sirupsen/logrus"
    27  
    28  	"k8s.io/test-infra/robots/pr-creator/updater"
    29  	"sigs.k8s.io/prow/pkg/flagutil"
    30  )
    31  
    32  type options struct {
    33  	github flagutil.GitHubOptions
    34  
    35  	branch    string
    36  	allowMods bool
    37  	confirm   bool
    38  	local     bool
    39  	org       string
    40  	repo      string
    41  	source    string
    42  
    43  	title      string
    44  	headBranch string
    45  	matchTitle string
    46  	body       string
    47  	labels     string
    48  }
    49  
    50  func (o options) validate() error {
    51  	switch {
    52  	case o.org == "":
    53  		return errors.New("--org must be set")
    54  	case o.repo == "":
    55  		return errors.New("--repo must be set")
    56  	case o.branch == "":
    57  		return errors.New("--branch must be set")
    58  	case o.source == "":
    59  		return errors.New("--source must be set")
    60  	case !o.local && !strings.Contains(o.source, ":"):
    61  		return fmt.Errorf("--source=%s requires --local", o.source)
    62  	}
    63  	if err := o.github.Validate(!o.confirm); err != nil {
    64  		return err
    65  	}
    66  	return nil
    67  }
    68  
    69  func (o options) getLabels() []string {
    70  	if o.labels != "" {
    71  		return strings.Split(o.labels, ",")
    72  	}
    73  	return nil
    74  }
    75  
    76  func optionsFromFlags() options {
    77  	var o options
    78  	fs := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
    79  	o.github.AddFlags(fs)
    80  	fs.StringVar(&o.repo, "repo", "", "GitHub repo")
    81  	fs.StringVar(&o.org, "org", "", "GitHub org")
    82  	fs.StringVar(&o.branch, "branch", "", "Repo branch to merge into")
    83  	fs.StringVar(&o.source, "source", "", "The user:branch to merge from")
    84  
    85  	fs.BoolVar(&o.allowMods, "allow-mods", updater.PreventMods, "Indicates whether maintainers can modify the pull request")
    86  	fs.BoolVar(&o.confirm, "confirm", false, "Set to mutate github instead of a dry run")
    87  	fs.BoolVar(&o.local, "local", false, "Allow source to be local-branch instead of remote-user:branch")
    88  	fs.StringVar(&o.title, "title", "", "Title of PR")
    89  	fs.StringVar(&o.headBranch, "head-branch", "", "Reuse any self-authored open PR from this branch. This takes priority over match-title")
    90  	fs.StringVar(&o.matchTitle, "match-title", "", "Reuse any self-autohred open PR that matches this title. If both this and head-branch are set, this will be overwritten by head-branch")
    91  	fs.StringVar(&o.body, "body", "", "Body of PR")
    92  	fs.StringVar(&o.labels, "labels", "", "labels to attach to PR")
    93  	fs.Parse(os.Args[1:])
    94  	return o
    95  }
    96  
    97  func main() {
    98  	o := optionsFromFlags()
    99  	if err := o.validate(); err != nil {
   100  		logrus.WithError(err).Fatal("bad flags")
   101  	}
   102  
   103  	gc, err := o.github.GitHubClient(!o.confirm)
   104  	if err != nil {
   105  		logrus.WithError(err).Fatal("Failed to create github client")
   106  	}
   107  
   108  	var queryTokensString string
   109  	// Prioritize using headBranch as it is less flakey
   110  	if o.headBranch != "" {
   111  		queryTokensString = "head:" + o.headBranch
   112  	} else {
   113  		queryTokensString = "in:title " + o.matchTitle
   114  	}
   115  	n, err := updater.EnsurePRWithQueryTokensAndLabels(o.org, o.repo, o.title, o.body, o.source, o.branch, queryTokensString, o.allowMods, o.getLabels(), gc)
   116  	if err != nil {
   117  		logrus.WithError(err).Fatal("Failed to ensure PR exists.")
   118  	}
   119  
   120  	logrus.Infof("PR %s/%s#%d will merge %s into %s: %s", o.org, o.repo, *n, o.source, o.branch, o.title)
   121  	fmt.Println(*n)
   122  }