diff --git a/cmd/hook.go b/cmd/hook.go index 2ce272b411..b741127ca3 100644 --- a/cmd/hook.go +++ b/cmd/hook.go @@ -32,6 +32,7 @@ var ( CmdHook = &cli.Command{ Name: "hook", Usage: "(internal) Should only be called by Git", + Hidden: true, // internal commands shouldn't be visible Description: "Delegate commands to corresponding Git hooks", Before: PrepareConsoleLoggerLevel(log.FATAL), Commands: []*cli.Command{ diff --git a/cmd/keys.go b/cmd/keys.go index 8710756a81..5ca3b91e15 100644 --- a/cmd/keys.go +++ b/cmd/keys.go @@ -19,6 +19,7 @@ import ( var CmdKeys = &cli.Command{ Name: "keys", Usage: "(internal) Should only be called by SSH server", + Hidden: true, // internal commands shouldn't not be visible Description: "Queries the Gitea database to get the authorized command for a given ssh key fingerprint", Before: PrepareConsoleLoggerLevel(log.FATAL), Action: runKeys, diff --git a/cmd/main.go b/cmd/main.go index 3b8a8a9311..3fdaf48ed9 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -6,6 +6,7 @@ package cmd import ( "context" "fmt" + "io" "os" "strings" @@ -15,26 +16,28 @@ import ( "github.com/urfave/cli/v3" ) -// cmdHelp is our own help subcommand with more information -// Keep in mind that the "./gitea help"(subcommand) is different from "./gitea --help"(flag), the flag doesn't parse the config or output "DEFAULT CONFIGURATION:" information -func cmdHelp() *cli.Command { - c := &cli.Command{ - Name: "help", - Aliases: []string{"h"}, - Usage: "Shows a list of commands or help for one command", - ArgsUsage: "[command]", - Action: func(ctx context.Context, c *cli.Command) (err error) { - lineage := c.Lineage() // The order is from child to parent: help, doctor, Gitea - targetCmdIdx := 0 - if c.Name == "help" { - targetCmdIdx = 1 - } - if lineage[targetCmdIdx] != lineage[targetCmdIdx].Root() { - err = cli.ShowCommandHelp(ctx, lineage[targetCmdIdx+1] /* parent cmd */, lineage[targetCmdIdx].Name /* sub cmd */) - } else { - err = cli.ShowAppHelp(c) - } - _, _ = fmt.Fprintf(c.Root().Writer, ` +var cliHelpPrinterOld = cli.HelpPrinter + +func init() { + cli.HelpPrinter = cliHelpPrinterNew +} + +// cliHelpPrinterNew helps to print "DEFAULT CONFIGURATION" for the following cases ( "-c" can apper in any position): +// * ./gitea -c /dev/null -h +// * ./gitea -c help /dev/null help +// * ./gitea help -c /dev/null +// * ./gitea help -c /dev/null web +// * ./gitea help web -c /dev/null +// * ./gitea web help -c /dev/null +// * ./gitea web -h -c /dev/null +func cliHelpPrinterNew(out io.Writer, templ string, data any) { + cmd, _ := data.(*cli.Command) + if cmd != nil { + prepareWorkPathAndCustomConf(cmd) + } + cliHelpPrinterOld(out, templ, data) + if setting.CustomConf != "" { + _, _ = fmt.Fprintf(out, ` DEFAULT CONFIGURATION: AppPath: %s WorkPath: %s @@ -42,77 +45,36 @@ DEFAULT CONFIGURATION: ConfigFile: %s `, setting.AppPath, setting.AppWorkPath, setting.CustomPath, setting.CustomConf) - return err - }, - } - return c -} - -func appGlobalFlags() []cli.Flag { - return []cli.Flag{ - // make the builtin flags at the top - cli.HelpFlag, - - // shared configuration flags, they are for global and for each sub-command at the same time - // eg: such command is valid: "./gitea --config /tmp/app.ini web --config /tmp/app.ini", while it's discouraged indeed - // keep in mind that the short flags like "-C", "-c" and "-w" are globally polluted, they can't be used for sub-commands anymore. - &cli.StringFlag{ - Name: "custom-path", - Aliases: []string{"C"}, - Usage: "Set custom path (defaults to '{WorkPath}/custom')", - }, - &cli.StringFlag{ - Name: "config", - Aliases: []string{"c"}, - Value: setting.CustomConf, - Usage: "Set custom config file (defaults to '{WorkPath}/custom/conf/app.ini')", - }, - &cli.StringFlag{ - Name: "work-path", - Aliases: []string{"w"}, - Usage: "Set Gitea's working path (defaults to the Gitea's binary directory)", - }, } } -func prepareSubcommandWithGlobalFlags(command *cli.Command) { - command.Flags = append(append([]cli.Flag{}, appGlobalFlags()...), command.Flags...) - command.Action = prepareWorkPathAndCustomConf(command.Action) - command.HideHelp = true - if command.Name != "help" { - command.Commands = append(command.Commands, cmdHelp()) - } - for i := range command.Commands { - prepareSubcommandWithGlobalFlags(command.Commands[i]) - } -} - -// prepareWorkPathAndCustomConf wraps the Action to prepare the work path and custom config -// It can't use "Before", because each level's sub-command's Before will be called one by one, so the "init" would be done multiple times -func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(context.Context, *cli.Command) error { - return func(ctx context.Context, cmd *cli.Command) error { - var args setting.ArgWorkPathAndCustomConf - // from children to parent, check the global flags - for _, curCtx := range cmd.Lineage() { - if curCtx.IsSet("work-path") && args.WorkPath == "" { - args.WorkPath = curCtx.String("work-path") - } - if curCtx.IsSet("custom-path") && args.CustomPath == "" { - args.CustomPath = curCtx.String("custom-path") - } - if curCtx.IsSet("config") && args.CustomConf == "" { - args.CustomConf = curCtx.String("config") - } +func prepareSubcommandWithGlobalFlags(originCmd *cli.Command) { + originBefore := originCmd.Before + originCmd.Before = func(ctx context.Context, cmd *cli.Command) (context.Context, error) { + prepareWorkPathAndCustomConf(cmd) + if originBefore != nil { + return originBefore(ctx, cmd) } - setting.InitWorkPathAndCommonConfig(os.Getenv, args) - if cmd.Bool("help") || action == nil { - // the default behavior of "urfave/cli": "nil action" means "show help" - return cmdHelp().Action(ctx, cmd) - } - return action(ctx, cmd) + return ctx, nil } } +// prepareWorkPathAndCustomConf tries to prepare the work path, custom path and custom config from various inputs: +// command line flags, environment variables, config file +func prepareWorkPathAndCustomConf(cmd *cli.Command) { + var args setting.ArgWorkPathAndCustomConf + if cmd.IsSet("work-path") { + args.WorkPath = cmd.String("work-path") + } + if cmd.IsSet("custom-path") { + args.CustomPath = cmd.String("custom-path") + } + if cmd.IsSet("config") { + args.CustomConf = cmd.String("config") + } + setting.InitWorkPathAndCommonConfig(os.Getenv, args) +} + type AppVersion struct { Version string Extra string @@ -125,10 +87,29 @@ func NewMainApp(appVer AppVersion) *cli.Command { app.Description = `Gitea program contains "web" and other subcommands. If no subcommand is given, it starts the web server by default. Use "web" subcommand for more web server arguments, use other subcommands for other purposes.` app.Version = appVer.Version + appVer.Extra app.EnableShellCompletion = true - - // these sub-commands need to use config file + app.Flags = []cli.Flag{ + &cli.StringFlag{ + Name: "work-path", + Aliases: []string{"w"}, + TakesFile: true, + Usage: "Set Gitea's working path (defaults to the Gitea's binary directory)", + }, + &cli.StringFlag{ + Name: "config", + Aliases: []string{"c"}, + TakesFile: true, + Value: setting.CustomConf, + Usage: "Set custom config file (defaults to '{WorkPath}/custom/conf/app.ini')", + }, + &cli.StringFlag{ + Name: "custom-path", + Aliases: []string{"C"}, + TakesFile: true, + Usage: "Set custom path (defaults to '{WorkPath}/custom')", + }, + } + // these sub-commands need to use a config file subCmdWithConfig := []*cli.Command{ - cmdHelp(), // the "help" sub-command was used to show the more information for "work path" and "custom config" CmdWeb, CmdServ, CmdHook, @@ -156,9 +137,6 @@ func NewMainApp(appVer AppVersion) *cli.Command { // but not sure whether it would break Windows users who used to double-click the EXE to run. app.DefaultCommand = CmdWeb.Name - app.Flags = append(app.Flags, cli.VersionFlag) - app.Flags = append(app.Flags, appGlobalFlags()...) - app.HideHelp = true // use our own help action to show helps (with more information like default config) app.Before = PrepareConsoleLoggerLevel(log.INFO) for i := range subCmdWithConfig { prepareSubcommandWithGlobalFlags(subCmdWithConfig[i]) diff --git a/cmd/main_test.go b/cmd/main_test.go index 7dfa87a0ef..d49ebfd4df 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -74,12 +74,56 @@ func TestCliCmd(t *testing.T) { cmd string exp string }{ - // main command help + // help commands + { + cmd: "./gitea -h", + exp: "DEFAULT CONFIGURATION:", + }, { cmd: "./gitea help", exp: "DEFAULT CONFIGURATION:", }, + { + cmd: "./gitea -c /dev/null -h", + exp: "ConfigFile: /dev/null", + }, + + { + cmd: "./gitea -c /dev/null help", + exp: "ConfigFile: /dev/null", + }, + { + cmd: "./gitea help -c /dev/null", + exp: "ConfigFile: /dev/null", + }, + + { + cmd: "./gitea -c /dev/null test-cmd -h", + exp: "ConfigFile: /dev/null", + }, + { + cmd: "./gitea test-cmd -c /dev/null -h", + exp: "ConfigFile: /dev/null", + }, + { + cmd: "./gitea test-cmd -h -c /dev/null", + exp: "ConfigFile: /dev/null", + }, + + { + cmd: "./gitea -c /dev/null test-cmd help", + exp: "ConfigFile: /dev/null", + }, + { + cmd: "./gitea test-cmd -c /dev/null help", + exp: "ConfigFile: /dev/null", + }, + { + cmd: "./gitea test-cmd help -c /dev/null", + exp: "ConfigFile: /dev/null", + }, + // parse paths { cmd: "./gitea test-cmd", diff --git a/cmd/serv.go b/cmd/serv.go index 8c6001e727..38c79f68cd 100644 --- a/cmd/serv.go +++ b/cmd/serv.go @@ -41,6 +41,7 @@ var CmdServ = &cli.Command{ Name: "serv", Usage: "(internal) Should only be called by SSH shell", Description: "Serv provides access auth for repositories", + Hidden: true, // Internal commands shouldn't be visible in help Before: PrepareConsoleLoggerLevel(log.FATAL), Action: runServ, Flags: []cli.Flag{ diff --git a/contrib/autocompletion/README b/contrib/autocompletion/README deleted file mode 100644 index 1defd219d8..0000000000 --- a/contrib/autocompletion/README +++ /dev/null @@ -1,17 +0,0 @@ -Bash and Zsh completion -======================= - -From within the gitea root run: - -```bash -source contrib/autocompletion/bash_autocomplete -``` - -or for zsh run: - -```bash -source contrib/autocompletion/zsh_autocomplete -``` - -These scripts will check if gitea is on the path and if so add autocompletion for `gitea`. Or if not autocompletion will work for `./gitea`. -If gitea has been installed as a different program pass in the `PROG` environment variable to set the correct program name. diff --git a/contrib/autocompletion/bash_autocomplete b/contrib/autocompletion/bash_autocomplete deleted file mode 100755 index 5cb62f26a7..0000000000 --- a/contrib/autocompletion/bash_autocomplete +++ /dev/null @@ -1,30 +0,0 @@ -#! /bin/bash -# Heavily inspired by https://github.com/urfave/cli - -_cli_bash_autocomplete() { - if [[ "${COMP_WORDS[0]}" != "source" ]]; then - local cur opts base - COMPREPLY=() - cur="${COMP_WORDS[COMP_CWORD]}" - if [[ "$cur" == "-"* ]]; then - opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} ${cur} --generate-bash-completion ) - else - opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion ) - fi - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) - return 0 - fi -} - -if [ -z "$PROG" ] && [ ! "$(command -v gitea &> /dev/null)" ] ; then - complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete gitea -elif [ -z "$PROG" ]; then - complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete ./gitea - complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete "$PWD/gitea" -else - complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete "$PROG" - unset PROG -fi - - - diff --git a/contrib/autocompletion/zsh_autocomplete b/contrib/autocompletion/zsh_autocomplete deleted file mode 100644 index b3b40df503..0000000000 --- a/contrib/autocompletion/zsh_autocomplete +++ /dev/null @@ -1,30 +0,0 @@ -#compdef ${PROG:=gitea} - - -# Heavily inspired by https://github.com/urfave/cli - -_cli_zsh_autocomplete() { - - local -a opts - local cur - cur=${words[-1]} - if [[ "$cur" == "-"* ]]; then - opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} ${cur} --generate-bash-completion)}") - else - opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} --generate-bash-completion)}") - fi - - if [[ "${opts[1]}" != "" ]]; then - _describe 'values' opts - else - _files - fi - - return -} - -if [ -z $PROG ] ; then - compdef _cli_zsh_autocomplete gitea -else - compdef _cli_zsh_autocomplete $(basename $PROG) -fi