mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-26 17:08:25 +00:00 
			
		
		
		
	From un-tagged pervious version : https://github.com/urfave/cli/compare/d86a009f5e13...master
		
			
				
	
	
		
			498 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			498 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package cli
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"io/ioutil"
 | |
| 	"os"
 | |
| 	"path/filepath"
 | |
| 	"sort"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	changeLogURL                    = "https://github.com/urfave/cli/blob/master/CHANGELOG.md"
 | |
| 	appActionDeprecationURL         = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL)
 | |
| 	runAndExitOnErrorDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-runandexitonerror", changeLogURL)
 | |
| 
 | |
| 	contactSysadmin = "This is an error in the application.  Please contact the distributor of this application if this is not you."
 | |
| 
 | |
| 	errInvalidActionType = NewExitError("ERROR invalid Action type. "+
 | |
| 		fmt.Sprintf("Must be `func(*Context`)` or `func(*Context) error).  %s", contactSysadmin)+
 | |
| 		fmt.Sprintf("See %s", appActionDeprecationURL), 2)
 | |
| )
 | |
| 
 | |
| // App is the main structure of a cli application. It is recommended that
 | |
| // an app be created with the cli.NewApp() function
 | |
| type App struct {
 | |
| 	// The name of the program. Defaults to path.Base(os.Args[0])
 | |
| 	Name string
 | |
| 	// Full name of command for help, defaults to Name
 | |
| 	HelpName string
 | |
| 	// Description of the program.
 | |
| 	Usage string
 | |
| 	// Text to override the USAGE section of help
 | |
| 	UsageText string
 | |
| 	// Description of the program argument format.
 | |
| 	ArgsUsage string
 | |
| 	// Version of the program
 | |
| 	Version string
 | |
| 	// Description of the program
 | |
| 	Description string
 | |
| 	// List of commands to execute
 | |
| 	Commands []Command
 | |
| 	// List of flags to parse
 | |
| 	Flags []Flag
 | |
| 	// Boolean to enable bash completion commands
 | |
| 	EnableBashCompletion bool
 | |
| 	// Boolean to hide built-in help command
 | |
| 	HideHelp bool
 | |
| 	// Boolean to hide built-in version flag and the VERSION section of help
 | |
| 	HideVersion bool
 | |
| 	// Populate on app startup, only gettable through method Categories()
 | |
| 	categories CommandCategories
 | |
| 	// An action to execute when the bash-completion flag is set
 | |
| 	BashComplete BashCompleteFunc
 | |
| 	// An action to execute before any subcommands are run, but after the context is ready
 | |
| 	// If a non-nil error is returned, no subcommands are run
 | |
| 	Before BeforeFunc
 | |
| 	// An action to execute after any subcommands are run, but after the subcommand has finished
 | |
| 	// It is run even if Action() panics
 | |
| 	After AfterFunc
 | |
| 
 | |
| 	// The action to execute when no subcommands are specified
 | |
| 	// Expects a `cli.ActionFunc` but will accept the *deprecated* signature of `func(*cli.Context) {}`
 | |
| 	// *Note*: support for the deprecated `Action` signature will be removed in a future version
 | |
| 	Action interface{}
 | |
| 
 | |
| 	// Execute this function if the proper command cannot be found
 | |
| 	CommandNotFound CommandNotFoundFunc
 | |
| 	// Execute this function if an usage error occurs
 | |
| 	OnUsageError OnUsageErrorFunc
 | |
| 	// Compilation date
 | |
| 	Compiled time.Time
 | |
| 	// List of all authors who contributed
 | |
| 	Authors []Author
 | |
| 	// Copyright of the binary if any
 | |
| 	Copyright string
 | |
| 	// Name of Author (Note: Use App.Authors, this is deprecated)
 | |
| 	Author string
 | |
| 	// Email of Author (Note: Use App.Authors, this is deprecated)
 | |
| 	Email string
 | |
| 	// Writer writer to write output to
 | |
| 	Writer io.Writer
 | |
| 	// ErrWriter writes error output
 | |
| 	ErrWriter io.Writer
 | |
| 	// Other custom info
 | |
| 	Metadata map[string]interface{}
 | |
| 	// Carries a function which returns app specific info.
 | |
| 	ExtraInfo func() map[string]string
 | |
| 	// CustomAppHelpTemplate the text template for app help topic.
 | |
| 	// cli.go uses text/template to render templates. You can
 | |
| 	// render custom help text by setting this variable.
 | |
| 	CustomAppHelpTemplate string
 | |
| 
 | |
| 	didSetup bool
 | |
| }
 | |
| 
 | |
| // Tries to find out when this binary was compiled.
 | |
| // Returns the current time if it fails to find it.
 | |
| func compileTime() time.Time {
 | |
| 	info, err := os.Stat(os.Args[0])
 | |
| 	if err != nil {
 | |
| 		return time.Now()
 | |
| 	}
 | |
| 	return info.ModTime()
 | |
| }
 | |
| 
 | |
| // NewApp creates a new cli Application with some reasonable defaults for Name,
 | |
| // Usage, Version and Action.
 | |
| func NewApp() *App {
 | |
| 	return &App{
 | |
| 		Name:         filepath.Base(os.Args[0]),
 | |
| 		HelpName:     filepath.Base(os.Args[0]),
 | |
| 		Usage:        "A new cli application",
 | |
| 		UsageText:    "",
 | |
| 		Version:      "0.0.0",
 | |
| 		BashComplete: DefaultAppComplete,
 | |
| 		Action:       helpCommand.Action,
 | |
| 		Compiled:     compileTime(),
 | |
| 		Writer:       os.Stdout,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Setup runs initialization code to ensure all data structures are ready for
 | |
| // `Run` or inspection prior to `Run`.  It is internally called by `Run`, but
 | |
| // will return early if setup has already happened.
 | |
| func (a *App) Setup() {
 | |
| 	if a.didSetup {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	a.didSetup = true
 | |
| 
 | |
| 	if a.Author != "" || a.Email != "" {
 | |
| 		a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email})
 | |
| 	}
 | |
| 
 | |
| 	newCmds := []Command{}
 | |
| 	for _, c := range a.Commands {
 | |
| 		if c.HelpName == "" {
 | |
| 			c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
 | |
| 		}
 | |
| 		newCmds = append(newCmds, c)
 | |
| 	}
 | |
| 	a.Commands = newCmds
 | |
| 
 | |
| 	if a.Command(helpCommand.Name) == nil && !a.HideHelp {
 | |
| 		a.Commands = append(a.Commands, helpCommand)
 | |
| 		if (HelpFlag != BoolFlag{}) {
 | |
| 			a.appendFlag(HelpFlag)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if !a.HideVersion {
 | |
| 		a.appendFlag(VersionFlag)
 | |
| 	}
 | |
| 
 | |
| 	a.categories = CommandCategories{}
 | |
| 	for _, command := range a.Commands {
 | |
| 		a.categories = a.categories.AddCommand(command.Category, command)
 | |
| 	}
 | |
| 	sort.Sort(a.categories)
 | |
| 
 | |
| 	if a.Metadata == nil {
 | |
| 		a.Metadata = make(map[string]interface{})
 | |
| 	}
 | |
| 
 | |
| 	if a.Writer == nil {
 | |
| 		a.Writer = os.Stdout
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Run is the entry point to the cli app. Parses the arguments slice and routes
 | |
| // to the proper flag/args combination
 | |
| func (a *App) Run(arguments []string) (err error) {
 | |
| 	a.Setup()
 | |
| 
 | |
| 	// handle the completion flag separately from the flagset since
 | |
| 	// completion could be attempted after a flag, but before its value was put
 | |
| 	// on the command line. this causes the flagset to interpret the completion
 | |
| 	// flag name as the value of the flag before it which is undesirable
 | |
| 	// note that we can only do this because the shell autocomplete function
 | |
| 	// always appends the completion flag at the end of the command
 | |
| 	shellComplete, arguments := checkShellCompleteFlag(a, arguments)
 | |
| 
 | |
| 	// parse flags
 | |
| 	set, err := flagSet(a.Name, a.Flags)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	set.SetOutput(ioutil.Discard)
 | |
| 	err = set.Parse(arguments[1:])
 | |
| 	nerr := normalizeFlags(a.Flags, set)
 | |
| 	context := NewContext(a, set, nil)
 | |
| 	if nerr != nil {
 | |
| 		fmt.Fprintln(a.Writer, nerr)
 | |
| 		ShowAppHelp(context)
 | |
| 		return nerr
 | |
| 	}
 | |
| 	context.shellComplete = shellComplete
 | |
| 
 | |
| 	if checkCompletions(context) {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	if err != nil {
 | |
| 		if a.OnUsageError != nil {
 | |
| 			err := a.OnUsageError(context, err, false)
 | |
| 			HandleExitCoder(err)
 | |
| 			return err
 | |
| 		}
 | |
| 		fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
 | |
| 		ShowAppHelp(context)
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	if !a.HideHelp && checkHelp(context) {
 | |
| 		ShowAppHelp(context)
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	if !a.HideVersion && checkVersion(context) {
 | |
| 		ShowVersion(context)
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	if a.After != nil {
 | |
| 		defer func() {
 | |
| 			if afterErr := a.After(context); afterErr != nil {
 | |
| 				if err != nil {
 | |
| 					err = NewMultiError(err, afterErr)
 | |
| 				} else {
 | |
| 					err = afterErr
 | |
| 				}
 | |
| 			}
 | |
| 		}()
 | |
| 	}
 | |
| 
 | |
| 	if a.Before != nil {
 | |
| 		beforeErr := a.Before(context)
 | |
| 		if beforeErr != nil {
 | |
| 			ShowAppHelp(context)
 | |
| 			HandleExitCoder(beforeErr)
 | |
| 			err = beforeErr
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	args := context.Args()
 | |
| 	if args.Present() {
 | |
| 		name := args.First()
 | |
| 		c := a.Command(name)
 | |
| 		if c != nil {
 | |
| 			return c.Run(context)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if a.Action == nil {
 | |
| 		a.Action = helpCommand.Action
 | |
| 	}
 | |
| 
 | |
| 	// Run default Action
 | |
| 	err = HandleAction(a.Action, context)
 | |
| 
 | |
| 	HandleExitCoder(err)
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| // RunAndExitOnError calls .Run() and exits non-zero if an error was returned
 | |
| //
 | |
| // Deprecated: instead you should return an error that fulfills cli.ExitCoder
 | |
| // to cli.App.Run. This will cause the application to exit with the given eror
 | |
| // code in the cli.ExitCoder
 | |
| func (a *App) RunAndExitOnError() {
 | |
| 	if err := a.Run(os.Args); err != nil {
 | |
| 		fmt.Fprintln(a.errWriter(), err)
 | |
| 		OsExiter(1)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // RunAsSubcommand invokes the subcommand given the context, parses ctx.Args() to
 | |
| // generate command-specific flags
 | |
| func (a *App) RunAsSubcommand(ctx *Context) (err error) {
 | |
| 	// append help to commands
 | |
| 	if len(a.Commands) > 0 {
 | |
| 		if a.Command(helpCommand.Name) == nil && !a.HideHelp {
 | |
| 			a.Commands = append(a.Commands, helpCommand)
 | |
| 			if (HelpFlag != BoolFlag{}) {
 | |
| 				a.appendFlag(HelpFlag)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	newCmds := []Command{}
 | |
| 	for _, c := range a.Commands {
 | |
| 		if c.HelpName == "" {
 | |
| 			c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
 | |
| 		}
 | |
| 		newCmds = append(newCmds, c)
 | |
| 	}
 | |
| 	a.Commands = newCmds
 | |
| 
 | |
| 	// parse flags
 | |
| 	set, err := flagSet(a.Name, a.Flags)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	set.SetOutput(ioutil.Discard)
 | |
| 	err = set.Parse(ctx.Args().Tail())
 | |
| 	nerr := normalizeFlags(a.Flags, set)
 | |
| 	context := NewContext(a, set, ctx)
 | |
| 
 | |
| 	if nerr != nil {
 | |
| 		fmt.Fprintln(a.Writer, nerr)
 | |
| 		fmt.Fprintln(a.Writer)
 | |
| 		if len(a.Commands) > 0 {
 | |
| 			ShowSubcommandHelp(context)
 | |
| 		} else {
 | |
| 			ShowCommandHelp(ctx, context.Args().First())
 | |
| 		}
 | |
| 		return nerr
 | |
| 	}
 | |
| 
 | |
| 	if checkCompletions(context) {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	if err != nil {
 | |
| 		if a.OnUsageError != nil {
 | |
| 			err = a.OnUsageError(context, err, true)
 | |
| 			HandleExitCoder(err)
 | |
| 			return err
 | |
| 		}
 | |
| 		fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
 | |
| 		ShowSubcommandHelp(context)
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	if len(a.Commands) > 0 {
 | |
| 		if checkSubcommandHelp(context) {
 | |
| 			return nil
 | |
| 		}
 | |
| 	} else {
 | |
| 		if checkCommandHelp(ctx, context.Args().First()) {
 | |
| 			return nil
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if a.After != nil {
 | |
| 		defer func() {
 | |
| 			afterErr := a.After(context)
 | |
| 			if afterErr != nil {
 | |
| 				HandleExitCoder(err)
 | |
| 				if err != nil {
 | |
| 					err = NewMultiError(err, afterErr)
 | |
| 				} else {
 | |
| 					err = afterErr
 | |
| 				}
 | |
| 			}
 | |
| 		}()
 | |
| 	}
 | |
| 
 | |
| 	if a.Before != nil {
 | |
| 		beforeErr := a.Before(context)
 | |
| 		if beforeErr != nil {
 | |
| 			HandleExitCoder(beforeErr)
 | |
| 			err = beforeErr
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	args := context.Args()
 | |
| 	if args.Present() {
 | |
| 		name := args.First()
 | |
| 		c := a.Command(name)
 | |
| 		if c != nil {
 | |
| 			return c.Run(context)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Run default Action
 | |
| 	err = HandleAction(a.Action, context)
 | |
| 
 | |
| 	HandleExitCoder(err)
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| // Command returns the named command on App. Returns nil if the command does not exist
 | |
| func (a *App) Command(name string) *Command {
 | |
| 	for _, c := range a.Commands {
 | |
| 		if c.HasName(name) {
 | |
| 			return &c
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Categories returns a slice containing all the categories with the commands they contain
 | |
| func (a *App) Categories() CommandCategories {
 | |
| 	return a.categories
 | |
| }
 | |
| 
 | |
| // VisibleCategories returns a slice of categories and commands that are
 | |
| // Hidden=false
 | |
| func (a *App) VisibleCategories() []*CommandCategory {
 | |
| 	ret := []*CommandCategory{}
 | |
| 	for _, category := range a.categories {
 | |
| 		if visible := func() *CommandCategory {
 | |
| 			for _, command := range category.Commands {
 | |
| 				if !command.Hidden {
 | |
| 					return category
 | |
| 				}
 | |
| 			}
 | |
| 			return nil
 | |
| 		}(); visible != nil {
 | |
| 			ret = append(ret, visible)
 | |
| 		}
 | |
| 	}
 | |
| 	return ret
 | |
| }
 | |
| 
 | |
| // VisibleCommands returns a slice of the Commands with Hidden=false
 | |
| func (a *App) VisibleCommands() []Command {
 | |
| 	ret := []Command{}
 | |
| 	for _, command := range a.Commands {
 | |
| 		if !command.Hidden {
 | |
| 			ret = append(ret, command)
 | |
| 		}
 | |
| 	}
 | |
| 	return ret
 | |
| }
 | |
| 
 | |
| // VisibleFlags returns a slice of the Flags with Hidden=false
 | |
| func (a *App) VisibleFlags() []Flag {
 | |
| 	return visibleFlags(a.Flags)
 | |
| }
 | |
| 
 | |
| func (a *App) hasFlag(flag Flag) bool {
 | |
| 	for _, f := range a.Flags {
 | |
| 		if flag == f {
 | |
| 			return true
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| func (a *App) errWriter() io.Writer {
 | |
| 
 | |
| 	// When the app ErrWriter is nil use the package level one.
 | |
| 	if a.ErrWriter == nil {
 | |
| 		return ErrWriter
 | |
| 	}
 | |
| 
 | |
| 	return a.ErrWriter
 | |
| }
 | |
| 
 | |
| func (a *App) appendFlag(flag Flag) {
 | |
| 	if !a.hasFlag(flag) {
 | |
| 		a.Flags = append(a.Flags, flag)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Author represents someone who has contributed to a cli project.
 | |
| type Author struct {
 | |
| 	Name  string // The Authors name
 | |
| 	Email string // The Authors email
 | |
| }
 | |
| 
 | |
| // String makes Author comply to the Stringer interface, to allow an easy print in the templating process
 | |
| func (a Author) String() string {
 | |
| 	e := ""
 | |
| 	if a.Email != "" {
 | |
| 		e = " <" + a.Email + ">"
 | |
| 	}
 | |
| 
 | |
| 	return fmt.Sprintf("%v%v", a.Name, e)
 | |
| }
 | |
| 
 | |
| // HandleAction attempts to figure out which Action signature was used.  If
 | |
| // it's an ActionFunc or a func with the legacy signature for Action, the func
 | |
| // is run!
 | |
| func HandleAction(action interface{}, context *Context) (err error) {
 | |
| 	if a, ok := action.(ActionFunc); ok {
 | |
| 		return a(context)
 | |
| 	} else if a, ok := action.(func(*Context) error); ok {
 | |
| 		return a(context)
 | |
| 	} else if a, ok := action.(func(*Context)); ok { // deprecated function signature
 | |
| 		a(context)
 | |
| 		return nil
 | |
| 	} else {
 | |
| 		return errInvalidActionType
 | |
| 	}
 | |
| }
 |