github.com/wmuizelaar/kpt@v0.0.0-20221018115725-bd564717b2ed/run/run.go (about) 1 // Copyright 2019 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package run 16 17 import ( 18 "bytes" 19 "context" 20 "flag" 21 "fmt" 22 "os" 23 "os/exec" 24 "strconv" 25 "strings" 26 27 kptcommands "github.com/GoogleContainerTools/kpt/commands" 28 "github.com/GoogleContainerTools/kpt/internal/docs/generated/overview" 29 "github.com/GoogleContainerTools/kpt/internal/printer" 30 "github.com/GoogleContainerTools/kpt/internal/util/cmdutil" 31 "github.com/spf13/cobra" 32 "sigs.k8s.io/kustomize/kyaml/commandutil" 33 ) 34 35 var pgr []string 36 37 func GetMain(ctx context.Context) *cobra.Command { 38 os.Setenv(commandutil.EnableAlphaCommmandsEnvName, "true") 39 cmd := &cobra.Command{ 40 Use: "kpt", 41 Short: overview.CliShort, 42 Long: overview.CliLong, 43 SilenceUsage: true, 44 // We handle all errors in main after return from cobra so we can 45 // adjust the error message coming from libraries 46 SilenceErrors: true, 47 RunE: func(cmd *cobra.Command, args []string) error { 48 h, err := cmd.Flags().GetBool("help") 49 if err != nil { 50 return err 51 } 52 if h { 53 return cmd.Help() 54 } 55 return cmd.Usage() 56 }, 57 } 58 59 cmd.PersistentFlags().AddGoFlagSet(flag.CommandLine) 60 61 cmd.PersistentFlags().BoolVar(&printer.TruncateOutput, "truncate-output", true, 62 "Enable the truncation for output") 63 // wire the global printer 64 pr := printer.New(cmd.OutOrStdout(), cmd.ErrOrStderr()) 65 66 // create context with associated printer 67 ctx = printer.WithContext(ctx, pr) 68 69 // find the pager if one exists 70 func() { 71 if val, found := os.LookupEnv("KPT_NO_PAGER_HELP"); !found || val != "1" { 72 // use a pager for printing tutorials 73 e, found := os.LookupEnv("PAGER") 74 var err error 75 if found { 76 pgr = []string{e} 77 return 78 } 79 e, err = exec.LookPath("pager") 80 if err == nil { 81 pgr = []string{e} 82 return 83 } 84 e, err = exec.LookPath("less") 85 if err == nil { 86 pgr = []string{e, "-R"} 87 return 88 } 89 } 90 }() 91 92 // help and documentation 93 cmd.InitDefaultHelpCmd() 94 cmd.AddCommand(kptcommands.GetKptCommands(ctx, "kpt", version)...) 95 96 // enable stack traces 97 cmd.PersistentFlags().BoolVar(&cmdutil.StackOnError, "stack-trace", false, 98 "Print a stack-trace on failure") 99 100 if _, err := exec.LookPath("git"); err != nil { 101 fmt.Fprintf(os.Stderr, "kpt requires that `git` is installed and on the PATH") 102 os.Exit(1) 103 } 104 105 replace(cmd) 106 107 cmd.AddCommand(versionCmd) 108 hideFlags(cmd) 109 return cmd 110 } 111 112 func replace(c *cobra.Command) { 113 for i := range c.Commands() { 114 replace(c.Commands()[i]) 115 } 116 c.SetHelpFunc(newHelp(pgr, c)) 117 } 118 119 func newHelp(e []string, c *cobra.Command) func(command *cobra.Command, strings []string) { 120 if len(pgr) == 0 { 121 return c.HelpFunc() 122 } 123 124 fn := c.HelpFunc() 125 return func(command *cobra.Command, args []string) { 126 stty := exec.Command("stty", "size") 127 stty.Stdin = os.Stdin 128 out, err := stty.Output() 129 if err == nil { 130 terminalHeight, err := strconv.Atoi(strings.Split(string(out), " ")[0]) 131 helpHeight := strings.Count(command.Long, "\n") + 132 strings.Count(command.UsageString(), "\n") 133 if err == nil && terminalHeight > helpHeight { 134 // don't use a pager if the help is shorter than the console 135 fn(command, args) 136 return 137 } 138 } 139 140 b := &bytes.Buffer{} 141 pager := exec.Command(e[0]) 142 if len(e) > 1 { 143 pager.Args = append(pager.Args, e[1:]...) 144 } 145 pager.Stdin = b 146 pager.Stdout = c.OutOrStdout() 147 c.SetOut(b) 148 fn(command, args) 149 if err := pager.Run(); err != nil { 150 fmt.Fprintf(c.ErrOrStderr(), "%v", err) 151 os.Exit(1) 152 } 153 } 154 } 155 156 var version = "unknown" 157 158 var versionCmd = &cobra.Command{ 159 Use: "version", 160 Short: "Print the version number of kpt", 161 Run: func(cmd *cobra.Command, args []string) { 162 fmt.Printf("%s\n", version) 163 }, 164 } 165 166 // hideFlags hides any cobra flags that are unlikely to be used by 167 // customers. 168 func hideFlags(cmd *cobra.Command) { 169 flags := []string{ 170 // Flags related to logging 171 "add_dir_header", 172 "alsologtostderr", 173 "log_backtrace_at", 174 "log_dir", 175 "log_file", 176 "log_file_max_size", 177 "logtostderr", 178 "one_output", 179 "skip_headers", 180 "skip_log_headers", 181 "stack-trace", 182 "stderrthreshold", 183 "vmodule", 184 185 // Flags related to apiserver 186 "as", 187 "as-group", 188 "cache-dir", 189 "certificate-authority", 190 "client-certificate", 191 "client-key", 192 "insecure-skip-tls-verify", 193 "match-server-version", 194 "password", 195 "token", 196 "username", 197 } 198 for _, f := range flags { 199 _ = cmd.PersistentFlags().MarkHidden(f) 200 } 201 202 // We need to recurse into subcommands otherwise flags aren't hidden on leaf commands 203 for _, child := range cmd.Commands() { 204 hideFlags(child) 205 } 206 }