You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

main.go 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. // Copyright 2023 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package cmd
  4. import (
  5. "fmt"
  6. "os"
  7. "code.gitea.io/gitea/modules/log"
  8. "code.gitea.io/gitea/modules/setting"
  9. "code.gitea.io/gitea/modules/util"
  10. "github.com/urfave/cli/v2"
  11. )
  12. // cmdHelp is our own help subcommand with more information
  13. func cmdHelp() *cli.Command {
  14. c := &cli.Command{
  15. Name: "help",
  16. Aliases: []string{"h"},
  17. Usage: "Shows a list of commands or help for one command",
  18. ArgsUsage: "[command]",
  19. Action: func(c *cli.Context) (err error) {
  20. lineage := c.Lineage() // The order is from child to parent: help, doctor, Gitea, {Command:nil}
  21. targetCmdIdx := 0
  22. if c.Command.Name == "help" {
  23. targetCmdIdx = 1
  24. }
  25. if lineage[targetCmdIdx+1].Command != nil {
  26. err = cli.ShowCommandHelp(lineage[targetCmdIdx+1], lineage[targetCmdIdx].Command.Name)
  27. } else {
  28. err = cli.ShowAppHelp(c)
  29. }
  30. _, _ = fmt.Fprintf(c.App.Writer, `
  31. DEFAULT CONFIGURATION:
  32. AppPath: %s
  33. WorkPath: %s
  34. CustomPath: %s
  35. ConfigFile: %s
  36. `, setting.AppPath, setting.AppWorkPath, setting.CustomPath, setting.CustomConf)
  37. return err
  38. },
  39. }
  40. return c
  41. }
  42. var helpFlag = cli.HelpFlag
  43. func init() {
  44. // cli.HelpFlag = nil TODO: after https://github.com/urfave/cli/issues/1794 we can use this
  45. }
  46. func appGlobalFlags() []cli.Flag {
  47. return []cli.Flag{
  48. // make the builtin flags at the top
  49. helpFlag,
  50. // shared configuration flags, they are for global and for each sub-command at the same time
  51. // eg: such command is valid: "./gitea --config /tmp/app.ini web --config /tmp/app.ini", while it's discouraged indeed
  52. // keep in mind that the short flags like "-C", "-c" and "-w" are globally polluted, they can't be used for sub-commands anymore.
  53. &cli.StringFlag{
  54. Name: "custom-path",
  55. Aliases: []string{"C"},
  56. Usage: "Set custom path (defaults to '{WorkPath}/custom')",
  57. },
  58. &cli.StringFlag{
  59. Name: "config",
  60. Aliases: []string{"c"},
  61. Value: setting.CustomConf,
  62. Usage: "Set custom config file (defaults to '{WorkPath}/custom/conf/app.ini')",
  63. },
  64. &cli.StringFlag{
  65. Name: "work-path",
  66. Aliases: []string{"w"},
  67. Usage: "Set Gitea's working path (defaults to the Gitea's binary directory)",
  68. },
  69. }
  70. }
  71. func prepareSubcommandWithConfig(command *cli.Command, globalFlags []cli.Flag) {
  72. command.Flags = append(append([]cli.Flag{}, globalFlags...), command.Flags...)
  73. command.Action = prepareWorkPathAndCustomConf(command.Action)
  74. command.HideHelp = true
  75. if command.Name != "help" {
  76. command.Subcommands = append(command.Subcommands, cmdHelp())
  77. }
  78. for i := range command.Subcommands {
  79. prepareSubcommandWithConfig(command.Subcommands[i], globalFlags)
  80. }
  81. }
  82. // prepareWorkPathAndCustomConf wraps the Action to prepare the work path and custom config
  83. // 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
  84. func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(ctx *cli.Context) error {
  85. return func(ctx *cli.Context) error {
  86. var args setting.ArgWorkPathAndCustomConf
  87. // from children to parent, check the global flags
  88. for _, curCtx := range ctx.Lineage() {
  89. if curCtx.IsSet("work-path") && args.WorkPath == "" {
  90. args.WorkPath = curCtx.String("work-path")
  91. }
  92. if curCtx.IsSet("custom-path") && args.CustomPath == "" {
  93. args.CustomPath = curCtx.String("custom-path")
  94. }
  95. if curCtx.IsSet("config") && args.CustomConf == "" {
  96. args.CustomConf = curCtx.String("config")
  97. }
  98. }
  99. setting.InitWorkPathAndCommonConfig(os.Getenv, args)
  100. if ctx.Bool("help") || action == nil {
  101. // the default behavior of "urfave/cli": "nil action" means "show help"
  102. return cmdHelp().Action(ctx)
  103. }
  104. return action(ctx)
  105. }
  106. }
  107. func NewMainApp() *cli.App {
  108. app := cli.NewApp()
  109. app.EnableBashCompletion = true
  110. // these sub-commands need to use config file
  111. subCmdWithConfig := []*cli.Command{
  112. CmdWeb,
  113. CmdServ,
  114. CmdHook,
  115. CmdDump,
  116. CmdAdmin,
  117. CmdMigrate,
  118. CmdKeys,
  119. CmdDoctor,
  120. CmdManager,
  121. CmdEmbedded,
  122. CmdMigrateStorage,
  123. CmdDumpRepository,
  124. CmdRestoreRepository,
  125. CmdActions,
  126. cmdHelp(), // the "help" sub-command was used to show the more information for "work path" and "custom config"
  127. }
  128. cmdConvert := util.ToPointer(*cmdDoctorConvert)
  129. cmdConvert.Hidden = true // still support the legacy "./gitea doctor" by the hidden sub-command, remove it in next release
  130. subCmdWithConfig = append(subCmdWithConfig, cmdConvert)
  131. // these sub-commands do not need the config file, and they do not depend on any path or environment variable.
  132. subCmdStandalone := []*cli.Command{
  133. CmdCert,
  134. CmdGenerate,
  135. CmdDocs,
  136. }
  137. app.DefaultCommand = CmdWeb.Name
  138. globalFlags := appGlobalFlags()
  139. app.Flags = append(app.Flags, cli.VersionFlag)
  140. app.Flags = append(app.Flags, globalFlags...)
  141. app.HideHelp = true // use our own help action to show helps (with more information like default config)
  142. app.Before = PrepareConsoleLoggerLevel(log.INFO)
  143. for i := range subCmdWithConfig {
  144. prepareSubcommandWithConfig(subCmdWithConfig[i], globalFlags)
  145. }
  146. app.Commands = append(app.Commands, subCmdWithConfig...)
  147. app.Commands = append(app.Commands, subCmdStandalone...)
  148. return app
  149. }