Skip to content

Commit 4b174e4

Browse files
Improve CLI commands (#34973)
Improve help related commands and flags and add tests Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
1 parent 091b3e6 commit 4b174e4

File tree

8 files changed

+112
-164
lines changed

8 files changed

+112
-164
lines changed

cmd/hook.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ var (
3232
CmdHook = &cli.Command{
3333
Name: "hook",
3434
Usage: "(internal) Should only be called by Git",
35+
Hidden: true, // internal commands shouldn't be visible
3536
Description: "Delegate commands to corresponding Git hooks",
3637
Before: PrepareConsoleLoggerLevel(log.FATAL),
3738
Commands: []*cli.Command{

cmd/keys.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
var CmdKeys = &cli.Command{
2020
Name: "keys",
2121
Usage: "(internal) Should only be called by SSH server",
22+
Hidden: true, // internal commands shouldn't not be visible
2223
Description: "Queries the Gitea database to get the authorized command for a given ssh key fingerprint",
2324
Before: PrepareConsoleLoggerLevel(log.FATAL),
2425
Action: runKeys,

cmd/main.go

Lines changed: 64 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package cmd
66
import (
77
"context"
88
"fmt"
9+
"io"
910
"os"
1011
"strings"
1112

@@ -15,102 +16,63 @@ import (
1516
"github.com/urfave/cli/v3"
1617
)
1718

18-
// cmdHelp is our own help subcommand with more information
19-
// 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
20-
func cmdHelp() *cli.Command {
21-
c := &cli.Command{
22-
Name: "help",
23-
Aliases: []string{"h"},
24-
Usage: "Shows a list of commands or help for one command",
25-
ArgsUsage: "[command]",
26-
Action: func(ctx context.Context, c *cli.Command) (err error) {
27-
lineage := c.Lineage() // The order is from child to parent: help, doctor, Gitea
28-
targetCmdIdx := 0
29-
if c.Name == "help" {
30-
targetCmdIdx = 1
31-
}
32-
if lineage[targetCmdIdx] != lineage[targetCmdIdx].Root() {
33-
err = cli.ShowCommandHelp(ctx, lineage[targetCmdIdx+1] /* parent cmd */, lineage[targetCmdIdx].Name /* sub cmd */)
34-
} else {
35-
err = cli.ShowAppHelp(c)
36-
}
37-
_, _ = fmt.Fprintf(c.Root().Writer, `
19+
var cliHelpPrinterOld = cli.HelpPrinter
20+
21+
func init() {
22+
cli.HelpPrinter = cliHelpPrinterNew
23+
}
24+
25+
// cliHelpPrinterNew helps to print "DEFAULT CONFIGURATION" for the following cases ( "-c" can apper in any position):
26+
// * ./gitea -c /dev/null -h
27+
// * ./gitea -c help /dev/null help
28+
// * ./gitea help -c /dev/null
29+
// * ./gitea help -c /dev/null web
30+
// * ./gitea help web -c /dev/null
31+
// * ./gitea web help -c /dev/null
32+
// * ./gitea web -h -c /dev/null
33+
func cliHelpPrinterNew(out io.Writer, templ string, data any) {
34+
cmd, _ := data.(*cli.Command)
35+
if cmd != nil {
36+
prepareWorkPathAndCustomConf(cmd)
37+
}
38+
cliHelpPrinterOld(out, templ, data)
39+
if setting.CustomConf != "" {
40+
_, _ = fmt.Fprintf(out, `
3841
DEFAULT CONFIGURATION:
3942
AppPath: %s
4043
WorkPath: %s
4144
CustomPath: %s
4245
ConfigFile: %s
4346
4447
`, setting.AppPath, setting.AppWorkPath, setting.CustomPath, setting.CustomConf)
45-
return err
46-
},
4748
}
48-
return c
4949
}
5050

51-
func appGlobalFlags() []cli.Flag {
52-
return []cli.Flag{
53-
// make the builtin flags at the top
54-
cli.HelpFlag,
55-
56-
// shared configuration flags, they are for global and for each sub-command at the same time
57-
// eg: such command is valid: "./gitea --config /tmp/app.ini web --config /tmp/app.ini", while it's discouraged indeed
58-
// keep in mind that the short flags like "-C", "-c" and "-w" are globally polluted, they can't be used for sub-commands anymore.
59-
&cli.StringFlag{
60-
Name: "custom-path",
61-
Aliases: []string{"C"},
62-
Usage: "Set custom path (defaults to '{WorkPath}/custom')",
63-
},
64-
&cli.StringFlag{
65-
Name: "config",
66-
Aliases: []string{"c"},
67-
Value: setting.CustomConf,
68-
Usage: "Set custom config file (defaults to '{WorkPath}/custom/conf/app.ini')",
69-
},
70-
&cli.StringFlag{
71-
Name: "work-path",
72-
Aliases: []string{"w"},
73-
Usage: "Set Gitea's working path (defaults to the Gitea's binary directory)",
74-
},
51+
func prepareSubcommandWithGlobalFlags(originCmd *cli.Command) {
52+
originBefore := originCmd.Before
53+
originCmd.Before = func(ctx context.Context, cmd *cli.Command) (context.Context, error) {
54+
prepareWorkPathAndCustomConf(cmd)
55+
if originBefore != nil {
56+
return originBefore(ctx, cmd)
57+
}
58+
return ctx, nil
7559
}
7660
}
7761

78-
func prepareSubcommandWithGlobalFlags(command *cli.Command) {
79-
command.Flags = append(append([]cli.Flag{}, appGlobalFlags()...), command.Flags...)
80-
command.Action = prepareWorkPathAndCustomConf(command.Action)
81-
command.HideHelp = true
82-
if command.Name != "help" {
83-
command.Commands = append(command.Commands, cmdHelp())
62+
// prepareWorkPathAndCustomConf tries to prepare the work path, custom path and custom config from various inputs:
63+
// command line flags, environment variables, config file
64+
func prepareWorkPathAndCustomConf(cmd *cli.Command) {
65+
var args setting.ArgWorkPathAndCustomConf
66+
if cmd.IsSet("work-path") {
67+
args.WorkPath = cmd.String("work-path")
8468
}
85-
for i := range command.Commands {
86-
prepareSubcommandWithGlobalFlags(command.Commands[i])
69+
if cmd.IsSet("custom-path") {
70+
args.CustomPath = cmd.String("custom-path")
8771
}
88-
}
89-
90-
// prepareWorkPathAndCustomConf wraps the Action to prepare the work path and custom config
91-
// 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
92-
func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(context.Context, *cli.Command) error {
93-
return func(ctx context.Context, cmd *cli.Command) error {
94-
var args setting.ArgWorkPathAndCustomConf
95-
// from children to parent, check the global flags
96-
for _, curCtx := range cmd.Lineage() {
97-
if curCtx.IsSet("work-path") && args.WorkPath == "" {
98-
args.WorkPath = curCtx.String("work-path")
99-
}
100-
if curCtx.IsSet("custom-path") && args.CustomPath == "" {
101-
args.CustomPath = curCtx.String("custom-path")
102-
}
103-
if curCtx.IsSet("config") && args.CustomConf == "" {
104-
args.CustomConf = curCtx.String("config")
105-
}
106-
}
107-
setting.InitWorkPathAndCommonConfig(os.Getenv, args)
108-
if cmd.Bool("help") || action == nil {
109-
// the default behavior of "urfave/cli": "nil action" means "show help"
110-
return cmdHelp().Action(ctx, cmd)
111-
}
112-
return action(ctx, cmd)
72+
if cmd.IsSet("config") {
73+
args.CustomConf = cmd.String("config")
11374
}
75+
setting.InitWorkPathAndCommonConfig(os.Getenv, args)
11476
}
11577

11678
type AppVersion struct {
@@ -125,10 +87,29 @@ func NewMainApp(appVer AppVersion) *cli.Command {
12587
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.`
12688
app.Version = appVer.Version + appVer.Extra
12789
app.EnableShellCompletion = true
128-
129-
// these sub-commands need to use config file
90+
app.Flags = []cli.Flag{
91+
&cli.StringFlag{
92+
Name: "work-path",
93+
Aliases: []string{"w"},
94+
TakesFile: true,
95+
Usage: "Set Gitea's working path (defaults to the Gitea's binary directory)",
96+
},
97+
&cli.StringFlag{
98+
Name: "config",
99+
Aliases: []string{"c"},
100+
TakesFile: true,
101+
Value: setting.CustomConf,
102+
Usage: "Set custom config file (defaults to '{WorkPath}/custom/conf/app.ini')",
103+
},
104+
&cli.StringFlag{
105+
Name: "custom-path",
106+
Aliases: []string{"C"},
107+
TakesFile: true,
108+
Usage: "Set custom path (defaults to '{WorkPath}/custom')",
109+
},
110+
}
111+
// these sub-commands need to use a config file
130112
subCmdWithConfig := []*cli.Command{
131-
cmdHelp(), // the "help" sub-command was used to show the more information for "work path" and "custom config"
132113
CmdWeb,
133114
CmdServ,
134115
CmdHook,
@@ -156,9 +137,6 @@ func NewMainApp(appVer AppVersion) *cli.Command {
156137
// but not sure whether it would break Windows users who used to double-click the EXE to run.
157138
app.DefaultCommand = CmdWeb.Name
158139

159-
app.Flags = append(app.Flags, cli.VersionFlag)
160-
app.Flags = append(app.Flags, appGlobalFlags()...)
161-
app.HideHelp = true // use our own help action to show helps (with more information like default config)
162140
app.Before = PrepareConsoleLoggerLevel(log.INFO)
163141
for i := range subCmdWithConfig {
164142
prepareSubcommandWithGlobalFlags(subCmdWithConfig[i])

cmd/main_test.go

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,56 @@ func TestCliCmd(t *testing.T) {
7474
cmd string
7575
exp string
7676
}{
77-
// main command help
77+
// help commands
78+
{
79+
cmd: "./gitea -h",
80+
exp: "DEFAULT CONFIGURATION:",
81+
},
7882
{
7983
cmd: "./gitea help",
8084
exp: "DEFAULT CONFIGURATION:",
8185
},
8286

87+
{
88+
cmd: "./gitea -c /dev/null -h",
89+
exp: "ConfigFile: /dev/null",
90+
},
91+
92+
{
93+
cmd: "./gitea -c /dev/null help",
94+
exp: "ConfigFile: /dev/null",
95+
},
96+
{
97+
cmd: "./gitea help -c /dev/null",
98+
exp: "ConfigFile: /dev/null",
99+
},
100+
101+
{
102+
cmd: "./gitea -c /dev/null test-cmd -h",
103+
exp: "ConfigFile: /dev/null",
104+
},
105+
{
106+
cmd: "./gitea test-cmd -c /dev/null -h",
107+
exp: "ConfigFile: /dev/null",
108+
},
109+
{
110+
cmd: "./gitea test-cmd -h -c /dev/null",
111+
exp: "ConfigFile: /dev/null",
112+
},
113+
114+
{
115+
cmd: "./gitea -c /dev/null test-cmd help",
116+
exp: "ConfigFile: /dev/null",
117+
},
118+
{
119+
cmd: "./gitea test-cmd -c /dev/null help",
120+
exp: "ConfigFile: /dev/null",
121+
},
122+
{
123+
cmd: "./gitea test-cmd help -c /dev/null",
124+
exp: "ConfigFile: /dev/null",
125+
},
126+
83127
// parse paths
84128
{
85129
cmd: "./gitea test-cmd",

cmd/serv.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ var CmdServ = &cli.Command{
4141
Name: "serv",
4242
Usage: "(internal) Should only be called by SSH shell",
4343
Description: "Serv provides access auth for repositories",
44+
Hidden: true, // Internal commands shouldn't be visible in help
4445
Before: PrepareConsoleLoggerLevel(log.FATAL),
4546
Action: runServ,
4647
Flags: []cli.Flag{

contrib/autocompletion/README

Lines changed: 0 additions & 17 deletions
This file was deleted.

contrib/autocompletion/bash_autocomplete

Lines changed: 0 additions & 30 deletions
This file was deleted.

contrib/autocompletion/zsh_autocomplete

Lines changed: 0 additions & 30 deletions
This file was deleted.

0 commit comments

Comments
 (0)