github.com/nimakaviani/cli@v6.37.1-0.20180619223813-e734901a73fa+incompatible/main.go (about)

     1  package main
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"reflect"
     8  	"strings"
     9  
    10  	"code.cloudfoundry.org/cli/cf/cmd"
    11  	"code.cloudfoundry.org/cli/command"
    12  	"code.cloudfoundry.org/cli/command/common"
    13  	"code.cloudfoundry.org/cli/command/flag"
    14  	"code.cloudfoundry.org/cli/command/translatableerror"
    15  	"code.cloudfoundry.org/cli/util/configv3"
    16  	"code.cloudfoundry.org/cli/util/panichandler"
    17  	"code.cloudfoundry.org/cli/util/ui"
    18  	"github.com/jessevdk/go-flags"
    19  	log "github.com/sirupsen/logrus"
    20  	"golang.org/x/crypto/ssh"
    21  )
    22  
    23  type UI interface {
    24  	DisplayError(err error)
    25  	DisplayWarning(template string, templateValues ...map[string]interface{})
    26  	DisplayText(template string, templateValues ...map[string]interface{})
    27  }
    28  
    29  type DisplayUsage interface {
    30  	DisplayUsage()
    31  }
    32  
    33  type TriggerLegacyMain interface {
    34  	LegacyMain()
    35  	error
    36  }
    37  
    38  var ErrFailed = errors.New("command failed")
    39  var ParseErr = errors.New("incorrect type for arg")
    40  
    41  func main() {
    42  	defer panichandler.HandlePanic()
    43  	exitStatus := parse(os.Args[1:])
    44  	if exitStatus != 0 {
    45  		os.Exit(exitStatus)
    46  	}
    47  }
    48  
    49  func parse(args []string) int {
    50  	parser := flags.NewParser(&common.Commands, flags.HelpFlag)
    51  	parser.CommandHandler = executionWrapper
    52  	extraArgs, err := parser.ParseArgs(args)
    53  	if err == nil {
    54  		return 0
    55  	}
    56  
    57  	if flagErr, ok := err.(*flags.Error); ok {
    58  		return handleFlagErrorAndCommandHelp(flagErr, parser, extraArgs, args)
    59  	} else if err == ErrFailed {
    60  		return 1
    61  	} else if err == ParseErr {
    62  		fmt.Println()
    63  		parse([]string{"help", args[0]})
    64  		return 1
    65  	} else if exitError, ok := err.(*ssh.ExitError); ok {
    66  		return exitError.ExitStatus()
    67  	}
    68  
    69  	fmt.Fprintf(os.Stderr, "Unexpected error: %s\n", err.Error())
    70  	return 1
    71  }
    72  
    73  func handleFlagErrorAndCommandHelp(flagErr *flags.Error, parser *flags.Parser, extraArgs []string, originalArgs []string) int {
    74  	switch flagErr.Type {
    75  	case flags.ErrHelp, flags.ErrUnknownFlag, flags.ErrExpectedArgument, flags.ErrInvalidChoice:
    76  		_, found := reflect.TypeOf(common.Commands).FieldByNameFunc(
    77  			func(fieldName string) bool {
    78  				field, _ := reflect.TypeOf(common.Commands).FieldByName(fieldName)
    79  				return parser.Active != nil && parser.Active.Name == field.Tag.Get("command")
    80  			},
    81  		)
    82  
    83  		if found && flagErr.Type == flags.ErrUnknownFlag && (parser.Active.Name == "set-env" || parser.Active.Name == "v3-set-env") {
    84  			newArgs := []string{}
    85  			for _, arg := range originalArgs {
    86  				if arg[0] == '-' {
    87  					newArgs = append(newArgs, fmt.Sprintf("%s%s", flag.WorkAroundPrefix, arg))
    88  				} else {
    89  					newArgs = append(newArgs, arg)
    90  				}
    91  			}
    92  			parse(newArgs)
    93  			return 0
    94  		}
    95  
    96  		if flagErr.Type == flags.ErrUnknownFlag || flagErr.Type == flags.ErrExpectedArgument || flagErr.Type == flags.ErrInvalidChoice {
    97  			fmt.Fprintf(os.Stderr, "Incorrect Usage: %s\n\n", flagErr.Error())
    98  		}
    99  
   100  		var helpErrored int
   101  		if found {
   102  			helpErrored = parse([]string{"help", parser.Active.Name})
   103  		} else {
   104  			switch len(extraArgs) {
   105  			case 0:
   106  				helpErrored = parse([]string{"help"})
   107  			case 1:
   108  				if !isOption(extraArgs[0]) || (len(originalArgs) > 1 && extraArgs[0] == "-a") {
   109  					helpErrored = parse([]string{"help", extraArgs[0]})
   110  				} else {
   111  					helpErrored = parse([]string{"help"})
   112  				}
   113  			default:
   114  				if isCommand(extraArgs[0]) {
   115  					helpErrored = parse([]string{"help", extraArgs[0]})
   116  				} else {
   117  					helpErrored = parse(extraArgs[1:])
   118  				}
   119  			}
   120  		}
   121  
   122  		if helpErrored > 0 || flagErr.Type == flags.ErrUnknownFlag || flagErr.Type == flags.ErrExpectedArgument || flagErr.Type == flags.ErrInvalidChoice {
   123  			return 1
   124  		}
   125  	case flags.ErrRequired, flags.ErrMarshal:
   126  		fmt.Fprintf(os.Stderr, "Incorrect Usage: %s\n\n", flagErr.Error())
   127  		parse([]string{"help", originalArgs[0]})
   128  		return 1
   129  	case flags.ErrUnknownCommand:
   130  		cmd.Main(os.Getenv("CF_TRACE"), os.Args)
   131  	case flags.ErrCommandRequired:
   132  		if common.Commands.VerboseOrVersion {
   133  			parse([]string{"version"})
   134  		} else {
   135  			parse([]string{"help"})
   136  		}
   137  	default:
   138  		fmt.Fprintf(os.Stderr, "Unexpected flag error\ntype: %s\nmessage: %s\n", flagErr.Type, flagErr.Error())
   139  	}
   140  	return 0
   141  }
   142  
   143  func isCommand(s string) bool {
   144  	_, found := reflect.TypeOf(common.Commands).FieldByNameFunc(
   145  		func(fieldName string) bool {
   146  			field, _ := reflect.TypeOf(common.Commands).FieldByName(fieldName)
   147  			return s == field.Tag.Get("command") || s == field.Tag.Get("alias")
   148  		})
   149  
   150  	return found
   151  }
   152  
   153  func isOption(s string) bool {
   154  	return strings.HasPrefix(s, "-")
   155  }
   156  
   157  func executionWrapper(cmd flags.Commander, args []string) error {
   158  	cfConfig, configErr := configv3.LoadConfig(configv3.FlagOverride{
   159  		Verbose: common.Commands.VerboseOrVersion,
   160  	})
   161  	if configErr != nil {
   162  		if _, ok := configErr.(translatableerror.EmptyConfigError); !ok {
   163  			return configErr
   164  		}
   165  	}
   166  
   167  	commandUI, err := ui.NewUI(cfConfig)
   168  	if err != nil {
   169  		return err
   170  	}
   171  
   172  	err = cfConfig.CreatePluginHome()
   173  	if err != nil {
   174  		return err
   175  	}
   176  
   177  	// TODO: when the line in the old code under `cf` which calls
   178  	// configv3.LoadConfig() is finally removed, then we should replace the code
   179  	// path above with the following:
   180  	//
   181  	// var configErrTemplate string
   182  	// if configErr != nil {
   183  	// 	if ce, ok := configErr.(translatableerror.EmptyConfigError); ok {
   184  	// 		configErrTemplate = ce.Error()
   185  	// 	} else {
   186  	// 		return configErr
   187  	// 	}
   188  	// }
   189  
   190  	// commandUI, err := ui.NewUI(cfConfig)
   191  	// if err != nil {
   192  	// 	return err
   193  	// }
   194  
   195  	// if configErr != nil {
   196  	//   commandUI.DisplayWarning(configErrTemplate, map[string]interface{}{
   197  	//   	"FilePath": configv3.ConfigFilePath(),
   198  	//   })
   199  	// }
   200  
   201  	defer func() {
   202  		configWriteErr := configv3.WriteConfig(cfConfig)
   203  		if configWriteErr != nil {
   204  			fmt.Fprintf(os.Stderr, "Error writing config: %s", configWriteErr.Error())
   205  		}
   206  	}()
   207  
   208  	if extendedCmd, ok := cmd.(command.ExtendedCommander); ok {
   209  		log.SetOutput(os.Stderr)
   210  		log.SetLevel(log.Level(cfConfig.LogLevel()))
   211  
   212  		err = extendedCmd.Setup(cfConfig, commandUI)
   213  		if err != nil {
   214  			return handleError(err, commandUI)
   215  		}
   216  		return handleError(extendedCmd.Execute(args), commandUI)
   217  	}
   218  
   219  	return fmt.Errorf("command does not conform to ExtendedCommander")
   220  }
   221  
   222  func handleError(passedErr error, commandUI UI) error {
   223  	if passedErr == nil {
   224  		return nil
   225  	}
   226  
   227  	translatedErr := translatableerror.ConvertToTranslatableError(passedErr)
   228  
   229  	switch typedErr := translatedErr.(type) {
   230  	case TriggerLegacyMain:
   231  		if typedErr.Error() != "" {
   232  			commandUI.DisplayWarning("")
   233  			commandUI.DisplayWarning(typedErr.Error())
   234  		}
   235  
   236  		// TODO: Replace this section with os.Args when v2-push has been replaced
   237  		// var args []string
   238  		// for _, arg := range os.Args {
   239  		// 	if arg == "v2-push" {
   240  		// 		args = append(args, "push")
   241  		// 	} else {
   242  		// 		args = append(args, arg)
   243  		// 	}
   244  		// }
   245  		cmd.Main(os.Getenv("CF_TRACE"), os.Args)
   246  	case *ssh.ExitError:
   247  		exitStatus := typedErr.ExitStatus()
   248  		if sig := typedErr.Signal(); sig != "" {
   249  			commandUI.DisplayText("Process terminated by signal: {{.Signal}}. Exited with {{.ExitCode}}", map[string]interface{}{
   250  				"Signal":   sig,
   251  				"ExitCode": exitStatus,
   252  			})
   253  		}
   254  		return passedErr
   255  	}
   256  
   257  	commandUI.DisplayError(translatedErr)
   258  
   259  	if _, ok := translatedErr.(DisplayUsage); ok {
   260  		return ParseErr
   261  	}
   262  
   263  	return ErrFailed
   264  }