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 }