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  }