mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-04 08:34:30 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			479 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			479 lines
		
	
	
		
			11 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{}
 | 
						|
 | 
						|
	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.EnableBashCompletion {
 | 
						|
		a.appendFlag(BashCompletionFlag)
 | 
						|
	}
 | 
						|
 | 
						|
	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()
 | 
						|
 | 
						|
	// parse flags
 | 
						|
	set := flagSet(a.Name, a.Flags)
 | 
						|
	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
 | 
						|
	}
 | 
						|
 | 
						|
	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 {
 | 
						|
			fmt.Fprintf(a.Writer, "%v\n\n", beforeErr)
 | 
						|
			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)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// 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
 | 
						|
 | 
						|
	// append flags
 | 
						|
	if a.EnableBashCompletion {
 | 
						|
		a.appendFlag(BashCompletionFlag)
 | 
						|
	}
 | 
						|
 | 
						|
	// parse flags
 | 
						|
	set := flagSet(a.Name, a.Flags)
 | 
						|
	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.(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
 | 
						|
	}
 | 
						|
}
 |