code.gitea.io/gitea@v1.22.3/cmd/main.go (about) 1 // Copyright 2023 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package cmd 5 6 import ( 7 "fmt" 8 "os" 9 "strings" 10 11 "code.gitea.io/gitea/modules/log" 12 "code.gitea.io/gitea/modules/setting" 13 14 "github.com/urfave/cli/v2" 15 ) 16 17 // cmdHelp is our own help subcommand with more information 18 // 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 19 func cmdHelp() *cli.Command { 20 c := &cli.Command{ 21 Name: "help", 22 Aliases: []string{"h"}, 23 Usage: "Shows a list of commands or help for one command", 24 ArgsUsage: "[command]", 25 Action: func(c *cli.Context) (err error) { 26 lineage := c.Lineage() // The order is from child to parent: help, doctor, Gitea, {Command:nil} 27 targetCmdIdx := 0 28 if c.Command.Name == "help" { 29 targetCmdIdx = 1 30 } 31 if lineage[targetCmdIdx+1].Command != nil { 32 err = cli.ShowCommandHelp(lineage[targetCmdIdx+1], lineage[targetCmdIdx].Command.Name) 33 } else { 34 err = cli.ShowAppHelp(c) 35 } 36 _, _ = fmt.Fprintf(c.App.Writer, ` 37 DEFAULT CONFIGURATION: 38 AppPath: %s 39 WorkPath: %s 40 CustomPath: %s 41 ConfigFile: %s 42 43 `, setting.AppPath, setting.AppWorkPath, setting.CustomPath, setting.CustomConf) 44 return err 45 }, 46 } 47 return c 48 } 49 50 func appGlobalFlags() []cli.Flag { 51 return []cli.Flag{ 52 // make the builtin flags at the top 53 cli.HelpFlag, 54 55 // shared configuration flags, they are for global and for each sub-command at the same time 56 // eg: such command is valid: "./gitea --config /tmp/app.ini web --config /tmp/app.ini", while it's discouraged indeed 57 // keep in mind that the short flags like "-C", "-c" and "-w" are globally polluted, they can't be used for sub-commands anymore. 58 &cli.StringFlag{ 59 Name: "custom-path", 60 Aliases: []string{"C"}, 61 Usage: "Set custom path (defaults to '{WorkPath}/custom')", 62 }, 63 &cli.StringFlag{ 64 Name: "config", 65 Aliases: []string{"c"}, 66 Value: setting.CustomConf, 67 Usage: "Set custom config file (defaults to '{WorkPath}/custom/conf/app.ini')", 68 }, 69 &cli.StringFlag{ 70 Name: "work-path", 71 Aliases: []string{"w"}, 72 Usage: "Set Gitea's working path (defaults to the Gitea's binary directory)", 73 }, 74 } 75 } 76 77 func prepareSubcommandWithConfig(command *cli.Command, globalFlags []cli.Flag) { 78 command.Flags = append(append([]cli.Flag{}, globalFlags...), command.Flags...) 79 command.Action = prepareWorkPathAndCustomConf(command.Action) 80 command.HideHelp = true 81 if command.Name != "help" { 82 command.Subcommands = append(command.Subcommands, cmdHelp()) 83 } 84 for i := range command.Subcommands { 85 prepareSubcommandWithConfig(command.Subcommands[i], globalFlags) 86 } 87 } 88 89 // prepareWorkPathAndCustomConf wraps the Action to prepare the work path and custom config 90 // 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 91 func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(ctx *cli.Context) error { 92 return func(ctx *cli.Context) error { 93 var args setting.ArgWorkPathAndCustomConf 94 // from children to parent, check the global flags 95 for _, curCtx := range ctx.Lineage() { 96 if curCtx.IsSet("work-path") && args.WorkPath == "" { 97 args.WorkPath = curCtx.String("work-path") 98 } 99 if curCtx.IsSet("custom-path") && args.CustomPath == "" { 100 args.CustomPath = curCtx.String("custom-path") 101 } 102 if curCtx.IsSet("config") && args.CustomConf == "" { 103 args.CustomConf = curCtx.String("config") 104 } 105 } 106 setting.InitWorkPathAndCommonConfig(os.Getenv, args) 107 if ctx.Bool("help") || action == nil { 108 // the default behavior of "urfave/cli": "nil action" means "show help" 109 return cmdHelp().Action(ctx) 110 } 111 return action(ctx) 112 } 113 } 114 115 type AppVersion struct { 116 Version string 117 Extra string 118 } 119 120 func NewMainApp(appVer AppVersion) *cli.App { 121 app := cli.NewApp() 122 app.Name = "Gitea" 123 app.HelpName = "gitea" 124 app.Usage = "A painless self-hosted Git service" 125 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.` 126 app.Version = appVer.Version + appVer.Extra 127 app.EnableBashCompletion = true 128 129 // these sub-commands need to use config file 130 subCmdWithConfig := []*cli.Command{ 131 cmdHelp(), // the "help" sub-command was used to show the more information for "work path" and "custom config" 132 CmdWeb, 133 CmdServ, 134 CmdHook, 135 CmdKeys, 136 CmdDump, 137 CmdAdmin, 138 CmdMigrate, 139 CmdDoctor, 140 CmdManager, 141 CmdEmbedded, 142 CmdMigrateStorage, 143 CmdDumpRepository, 144 CmdRestoreRepository, 145 CmdActions, 146 } 147 148 // these sub-commands do not need the config file, and they do not depend on any path or environment variable. 149 subCmdStandalone := []*cli.Command{ 150 CmdCert, 151 CmdGenerate, 152 CmdDocs, 153 } 154 155 app.DefaultCommand = CmdWeb.Name 156 157 globalFlags := appGlobalFlags() 158 app.Flags = append(app.Flags, cli.VersionFlag) 159 app.Flags = append(app.Flags, globalFlags...) 160 app.HideHelp = true // use our own help action to show helps (with more information like default config) 161 app.Before = PrepareConsoleLoggerLevel(log.INFO) 162 for i := range subCmdWithConfig { 163 prepareSubcommandWithConfig(subCmdWithConfig[i], globalFlags) 164 } 165 app.Commands = append(app.Commands, subCmdWithConfig...) 166 app.Commands = append(app.Commands, subCmdStandalone...) 167 168 return app 169 } 170 171 func RunMainApp(app *cli.App, args ...string) error { 172 err := app.Run(args) 173 if err == nil { 174 return nil 175 } 176 if strings.HasPrefix(err.Error(), "flag provided but not defined:") { 177 // the cli package should already have output the error message, so just exit 178 cli.OsExiter(1) 179 return err 180 } 181 _, _ = fmt.Fprintf(app.ErrWriter, "Command error: %v\n", err) 182 cli.OsExiter(1) 183 return err 184 }