github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/cmd/external-plugins/cherrypicker/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  	"flag"
    21  	"fmt"
    22  	"net/http"
    23  	"os"
    24  	"strconv"
    25  	"time"
    26  
    27  	"github.com/sirupsen/logrus"
    28  
    29  	"sigs.k8s.io/prow/pkg/config/secret"
    30  	"sigs.k8s.io/prow/pkg/flagutil"
    31  	prowflagutil "sigs.k8s.io/prow/pkg/flagutil"
    32  	"sigs.k8s.io/prow/pkg/interrupts"
    33  	"sigs.k8s.io/prow/pkg/logrusutil"
    34  	"sigs.k8s.io/prow/pkg/pjutil"
    35  	"sigs.k8s.io/prow/pkg/pluginhelp/externalplugins"
    36  )
    37  
    38  type options struct {
    39  	port int
    40  
    41  	dryRun                 bool
    42  	github                 prowflagutil.GitHubOptions
    43  	labels                 prowflagutil.Strings
    44  	instrumentationOptions prowflagutil.InstrumentationOptions
    45  	logLevel               string
    46  
    47  	webhookSecretFile string
    48  	prowAssignments   bool
    49  	allowAll          bool
    50  	issueOnConflict   bool
    51  	labelPrefix       string
    52  }
    53  
    54  func (o *options) Validate() error {
    55  	for idx, group := range []flagutil.OptionGroup{&o.github} {
    56  		if err := group.Validate(o.dryRun); err != nil {
    57  			return fmt.Errorf("%d: %w", idx, err)
    58  		}
    59  	}
    60  
    61  	return nil
    62  }
    63  
    64  func gatherOptions() options {
    65  	o := options{}
    66  	fs := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
    67  	fs.IntVar(&o.port, "port", 8888, "Port to listen on.")
    68  	fs.BoolVar(&o.dryRun, "dry-run", true, "Dry run for testing. Uses API tokens but does not mutate.")
    69  	fs.Var(&o.labels, "labels", "Labels to apply to the cherrypicked PR.")
    70  	fs.StringVar(&o.webhookSecretFile, "hmac-secret-file", "/etc/webhook/hmac", "Path to the file containing the GitHub HMAC secret.")
    71  	fs.StringVar(&o.logLevel, "log-level", "debug", fmt.Sprintf("Log level is one of %v.", logrus.AllLevels))
    72  	fs.BoolVar(&o.prowAssignments, "use-prow-assignments", true, "Use prow commands to assign cherrypicked PRs.")
    73  	fs.BoolVar(&o.allowAll, "allow-all", false, "Allow anybody to use automated cherrypicks by skipping GitHub organization membership checks.")
    74  	fs.BoolVar(&o.issueOnConflict, "create-issue-on-conflict", false, "Create a GitHub issue and assign it to the requestor on cherrypick conflict.")
    75  	fs.StringVar(&o.labelPrefix, "label-prefix", defaultLabelPrefix, "Set a custom label prefix.")
    76  	for _, group := range []flagutil.OptionGroup{&o.github, &o.instrumentationOptions} {
    77  		group.AddFlags(fs)
    78  	}
    79  	fs.Parse(os.Args[1:])
    80  	return o
    81  }
    82  
    83  func main() {
    84  	logrusutil.ComponentInit()
    85  	o := gatherOptions()
    86  	if err := o.Validate(); err != nil {
    87  		logrus.Fatalf("Invalid options: %v", err)
    88  	}
    89  
    90  	logLevel, err := logrus.ParseLevel(o.logLevel)
    91  	if err != nil {
    92  		logrus.WithError(err).Fatal("Failed to parse loglevel")
    93  	}
    94  	logrus.SetLevel(logLevel)
    95  	log := logrus.StandardLogger().WithField("plugin", pluginName)
    96  
    97  	if err := secret.Add(o.webhookSecretFile); err != nil {
    98  		logrus.WithError(err).Fatal("Error starting secrets agent.")
    99  	}
   100  
   101  	githubClient, err := o.github.GitHubClient(o.dryRun)
   102  	if err != nil {
   103  		logrus.WithError(err).Fatal("Error getting GitHub client.")
   104  	}
   105  	gitClient, err := o.github.GitClientFactory("", nil, o.dryRun, false)
   106  	if err != nil {
   107  		logrus.WithError(err).Fatal("Error getting Git client.")
   108  	}
   109  	interrupts.OnInterrupt(func() {
   110  		if err := gitClient.Clean(); err != nil {
   111  			logrus.WithError(err).Error("Could not clean up git client cache.")
   112  		}
   113  	})
   114  
   115  	email, err := githubClient.Email()
   116  	if err != nil {
   117  		log.WithError(err).Fatal("Error getting bot e-mail.")
   118  	}
   119  
   120  	botUser, err := githubClient.BotUser()
   121  	if err != nil {
   122  		logrus.WithError(err).Fatal("Error getting bot name.")
   123  	}
   124  	repos, err := githubClient.GetRepos(botUser.Login, true)
   125  	if err != nil {
   126  		log.WithError(err).Fatal("Error listing bot repositories.")
   127  	}
   128  
   129  	server := &Server{
   130  		tokenGenerator: secret.GetTokenGenerator(o.webhookSecretFile),
   131  		botUser:        botUser,
   132  		email:          email,
   133  
   134  		gc:  gitClient,
   135  		ghc: githubClient,
   136  		log: log,
   137  
   138  		labels:          o.labels.Strings(),
   139  		prowAssignments: o.prowAssignments,
   140  		allowAll:        o.allowAll,
   141  		issueOnConflict: o.issueOnConflict,
   142  		labelPrefix:     o.labelPrefix,
   143  
   144  		bare:     &http.Client{},
   145  		patchURL: "https://patch-diff.githubusercontent.com",
   146  
   147  		repos: repos,
   148  	}
   149  
   150  	health := pjutil.NewHealthOnPort(o.instrumentationOptions.HealthPort)
   151  	health.ServeReady()
   152  
   153  	mux := http.NewServeMux()
   154  	mux.Handle("/", server)
   155  	externalplugins.ServeExternalPluginHelp(mux, log, HelpProvider)
   156  	httpServer := &http.Server{Addr: ":" + strconv.Itoa(o.port), Handler: mux}
   157  	defer interrupts.WaitForGracefulShutdown()
   158  	interrupts.ListenAndServe(httpServer, 5*time.Second)
   159  }