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  }