github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/cmd/mkpj/main.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 main 18 19 import ( 20 "errors" 21 "flag" 22 "fmt" 23 "os" 24 25 "github.com/sirupsen/logrus" 26 "sigs.k8s.io/yaml" 27 28 prowapi "sigs.k8s.io/prow/pkg/apis/prowjobs/v1" 29 "sigs.k8s.io/prow/pkg/config" 30 prowflagutil "sigs.k8s.io/prow/pkg/flagutil" 31 configflagutil "sigs.k8s.io/prow/pkg/flagutil/config" 32 "sigs.k8s.io/prow/pkg/github" 33 "sigs.k8s.io/prow/pkg/pjutil" 34 ) 35 36 type options struct { 37 jobName string 38 config configflagutil.ConfigOptions 39 triggerJob bool 40 failWithJob bool 41 kubeOptions prowflagutil.KubernetesOptions 42 baseRef string 43 baseSha string 44 pullNumber int 45 pullSha string 46 pullAuthor string 47 pullHeadRef string 48 org string 49 repo string 50 51 github prowflagutil.GitHubOptions 52 githubClient githubClient 53 pullRequest *github.PullRequest 54 } 55 56 func (o *options) genJobSpec(conf *config.Config) (config.JobBase, prowapi.ProwJobSpec) { 57 for fullRepoName, ps := range conf.PresubmitsStatic { 58 org, repo, err := config.SplitRepoName(fullRepoName) 59 if err != nil { 60 logrus.WithError(err).Warnf("Invalid repo name %s.", fullRepoName) 61 continue 62 } 63 for _, p := range ps { 64 if p.Name == o.jobName { 65 return p.JobBase, pjutil.PresubmitSpec(p, prowapi.Refs{ 66 Org: org, 67 Repo: repo, 68 BaseRef: o.baseRef, 69 BaseSHA: o.baseSha, 70 Pulls: []prowapi.Pull{{ 71 Author: o.pullAuthor, 72 Number: o.pullNumber, 73 SHA: o.pullSha, 74 HeadRef: o.pullHeadRef, 75 }}, 76 }) 77 } 78 } 79 } 80 for fullRepoName, ps := range conf.PostsubmitsStatic { 81 org, repo, err := config.SplitRepoName(fullRepoName) 82 if err != nil { 83 logrus.WithError(err).Warnf("Invalid repo name %s.", fullRepoName) 84 continue 85 } 86 for _, p := range ps { 87 if p.Name == o.jobName { 88 return p.JobBase, pjutil.PostsubmitSpec(p, prowapi.Refs{ 89 Org: org, 90 Repo: repo, 91 BaseRef: o.baseRef, 92 BaseSHA: o.baseSha, 93 }) 94 } 95 } 96 } 97 for _, p := range conf.Periodics { 98 if p.Name == o.jobName { 99 return p.JobBase, pjutil.PeriodicSpec(p) 100 } 101 } 102 return config.JobBase{}, prowapi.ProwJobSpec{} 103 } 104 105 func (o *options) getPullRequest() (*github.PullRequest, error) { 106 if o.pullRequest != nil { 107 return o.pullRequest, nil 108 } 109 pr, err := o.githubClient.GetPullRequest(o.org, o.repo, o.pullNumber) 110 if err != nil { 111 return nil, fmt.Errorf("failed to fetch PullRequest from GitHub: %w", err) 112 } 113 o.pullRequest = pr 114 return pr, nil 115 } 116 117 func (o *options) defaultPR(pjs *prowapi.ProwJobSpec) error { 118 if pjs.Refs.Pulls[0].Number == 0 { 119 fmt.Fprint(os.Stderr, "PR Number: ") 120 var pullNumber int 121 fmt.Scanln(&pullNumber) 122 pjs.Refs.Pulls[0].Number = pullNumber 123 o.pullNumber = pullNumber 124 } 125 if pjs.Refs.Pulls[0].Author == "" { 126 pr, err := o.getPullRequest() 127 if err != nil { 128 return err 129 } 130 pjs.Refs.Pulls[0].Author = pr.User.Login 131 } 132 if pjs.Refs.Pulls[0].SHA == "" { 133 pr, err := o.getPullRequest() 134 if err != nil { 135 return err 136 } 137 pjs.Refs.Pulls[0].SHA = pr.Head.SHA 138 } 139 return nil 140 } 141 142 func (o *options) defaultBaseRef(pjs *prowapi.ProwJobSpec) error { 143 if pjs.Refs.BaseRef == "" { 144 if o.pullNumber != 0 { 145 pr, err := o.getPullRequest() 146 if err != nil { 147 return err 148 } 149 pjs.Refs.BaseRef = pr.Base.Ref 150 } else { 151 fmt.Fprint(os.Stderr, "Base ref (e.g. master): ") 152 fmt.Scanln(&pjs.Refs.BaseRef) 153 } 154 } 155 if pjs.Refs.BaseSHA == "" { 156 if o.pullNumber != 0 { 157 pr, err := o.getPullRequest() 158 if err != nil { 159 return err 160 } 161 pjs.Refs.BaseSHA = pr.Base.SHA 162 } else { 163 baseSHA, err := o.githubClient.GetRef(o.org, o.repo, fmt.Sprintf("heads/%s", pjs.Refs.BaseRef)) 164 if err != nil { 165 logrus.Fatalf("failed to get base sha: %v", err) 166 return err 167 } 168 pjs.Refs.BaseSHA = baseSHA 169 } 170 } 171 return nil 172 } 173 174 type githubClient interface { 175 GetPullRequest(org, repo string, number int) (*github.PullRequest, error) 176 GetRef(org, repo, ref string) (string, error) 177 } 178 179 func (o *options) Validate() error { 180 if o.jobName == "" { 181 return errors.New("required flag --job was unset") 182 } 183 184 if err := o.config.Validate(false); err != nil { 185 return err 186 } 187 188 if err := o.github.Validate(false); err != nil { 189 return err 190 } 191 192 if o.triggerJob { 193 if err := o.kubeOptions.Validate(false); err != nil { 194 return err 195 } 196 } 197 198 return nil 199 } 200 201 func gatherOptions() options { 202 var o options 203 fs := flag.NewFlagSet(os.Args[0], flag.ExitOnError) 204 fs.StringVar(&o.jobName, "job", "", "Job to run.") 205 fs.StringVar(&o.baseRef, "base-ref", "", "Git base ref under test") 206 fs.StringVar(&o.baseSha, "base-sha", "", "Git base SHA under test") 207 fs.IntVar(&o.pullNumber, "pull-number", 0, "Git pull number under test") 208 fs.StringVar(&o.pullSha, "pull-sha", "", "Git pull SHA under test") 209 fs.StringVar(&o.pullAuthor, "pull-author", "", "Git pull author under test") 210 fs.StringVar(&o.pullHeadRef, "pull-head-ref", "", "Git branch name of the proposed change") 211 fs.BoolVar(&o.triggerJob, "trigger-job", false, "Submit the job to Prow and wait for results") 212 fs.BoolVar(&o.failWithJob, "fail-with-job", false, "Exit with a non-zero exit code if the triggered job fails") 213 o.config.AddFlags(fs) 214 o.kubeOptions.AddFlags(fs) 215 o.github.AddFlags(fs) 216 o.github.AllowAnonymous = true 217 o.github.AllowDirectAccess = true 218 fs.Parse(os.Args[1:]) 219 return o 220 } 221 222 func main() { 223 o := gatherOptions() 224 if err := o.Validate(); err != nil { 225 logrus.WithError(err).Fatalf("Bad flags") 226 } 227 228 ca, err := o.config.ConfigAgent() 229 if err != nil { 230 logrus.WithError(err).Fatal("Error loading config") 231 } 232 conf := ca.Config() 233 234 o.githubClient, err = o.github.GitHubClient(false) 235 if err != nil { 236 logrus.WithError(err).Fatal("Failed to get GitHub client") 237 } 238 job, pjs := o.genJobSpec(conf) 239 if job.Name == "" { 240 logrus.Fatalf("Job %s not found.", o.jobName) 241 } 242 if pjs.Refs != nil { 243 o.org = pjs.Refs.Org 244 o.repo = pjs.Refs.Repo 245 if len(pjs.Refs.Pulls) != 0 { 246 if err := o.defaultPR(&pjs); err != nil { 247 logrus.WithError(err).Fatal("Failed to default PR") 248 } 249 } 250 if err := o.defaultBaseRef(&pjs); err != nil { 251 logrus.WithError(err).Fatal("Failed to default base ref") 252 } 253 } 254 pj := pjutil.NewProwJob(pjs, job.Labels, job.Annotations, pjutil.RequireScheduling(conf.Scheduler.Enabled)) 255 if !o.triggerJob { 256 b, err := yaml.Marshal(&pj) 257 if err != nil { 258 logrus.WithError(err).Fatal("Error marshalling YAML.") 259 } 260 fmt.Print(string(b)) 261 return 262 } 263 264 if succeeded, err := pjutil.TriggerAndWatchProwJob(o.kubeOptions, &pj, conf, nil, false); err != nil { 265 logrus.WithError(err).Fatalf("failed while submitting job or watching its result") 266 } else if !succeeded && o.failWithJob { 267 os.Exit(1) 268 } 269 }