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 }