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 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2016 The Gitea Authors. All rights reserved.
  3. // SPDX-License-Identifier: MIT
  4. // Gitea (git with a cup of tea) is a painless self-hosted Git Service.
  5. package main // import "code.gitea.io/gitea"
  6. import (
  7. "fmt"
  8. "os"
  9. "runtime"
  10. "strings"
  11. "time"
  12. "code.gitea.io/gitea/cmd"
  13. "code.gitea.io/gitea/modules/log"
  14. "code.gitea.io/gitea/modules/setting"
  15. // register supported doc types
  16. _ "code.gitea.io/gitea/modules/markup/asciicast"
  17. _ "code.gitea.io/gitea/modules/markup/console"
  18. _ "code.gitea.io/gitea/modules/markup/csv"
  19. _ "code.gitea.io/gitea/modules/markup/markdown"
  20. _ "code.gitea.io/gitea/modules/markup/orgmode"
  21. "github.com/urfave/cli"
  22. )
  23. var (
  24. // Version holds the current Gitea version
  25. Version = "development"
  26. // Tags holds the build tags used
  27. Tags = ""
  28. // MakeVersion holds the current Make version if built with make
  29. MakeVersion = ""
  30. )
  31. func init() {
  32. setting.AppVer = Version
  33. setting.AppBuiltWith = formatBuiltWith()
  34. setting.AppStartTime = time.Now().UTC()
  35. }
  36. // cmdHelp is our own help subcommand with more information
  37. // test cases:
  38. // ./gitea help
  39. // ./gitea -h
  40. // ./gitea web help
  41. // ./gitea web -h (due to cli lib limitation, this won't call our cmdHelp, so no extra info)
  42. // ./gitea admin
  43. // ./gitea admin help
  44. // ./gitea admin auth help
  45. // ./gitea -c /tmp/app.ini -h
  46. // ./gitea -c /tmp/app.ini help
  47. // ./gitea help -c /tmp/app.ini
  48. // GITEA_WORK_DIR=/tmp ./gitea help
  49. // GITEA_WORK_DIR=/tmp ./gitea help --work-path /tmp/other
  50. // GITEA_WORK_DIR=/tmp ./gitea help --config /tmp/app-other.ini
  51. var cmdHelp = cli.Command{
  52. Name: "help",
  53. Aliases: []string{"h"},
  54. Usage: "Shows a list of commands or help for one command",
  55. ArgsUsage: "[command]",
  56. Action: func(c *cli.Context) (err error) {
  57. args := c.Args()
  58. if args.Present() {
  59. err = cli.ShowCommandHelp(c, args.First())
  60. } else {
  61. err = cli.ShowAppHelp(c)
  62. }
  63. _, _ = fmt.Fprintf(c.App.Writer, `
  64. DEFAULT CONFIGURATION:
  65. AppPath: %s
  66. WorkPath: %s
  67. CustomPath: %s
  68. ConfigFile: %s
  69. `, setting.AppPath, setting.AppWorkPath, setting.CustomPath, setting.CustomConf)
  70. return err
  71. },
  72. }
  73. func main() {
  74. app := cli.NewApp()
  75. app.Name = "Gitea"
  76. app.Usage = "A painless self-hosted Git service"
  77. app.Description = `By default, Gitea will start serving using the web-server with no argument, which can alternatively be run by running the subcommand "web".`
  78. app.Version = Version + formatBuiltWith()
  79. app.EnableBashCompletion = true
  80. // these sub-commands need to use config file
  81. subCmdWithIni := []cli.Command{
  82. cmd.CmdWeb,
  83. cmd.CmdServ,
  84. cmd.CmdHook,
  85. cmd.CmdDump,
  86. cmd.CmdAdmin,
  87. cmd.CmdMigrate,
  88. cmd.CmdKeys,
  89. cmd.CmdConvert,
  90. cmd.CmdDoctor,
  91. cmd.CmdManager,
  92. cmd.CmdEmbedded,
  93. cmd.CmdMigrateStorage,
  94. cmd.CmdDumpRepository,
  95. cmd.CmdRestoreRepository,
  96. cmd.CmdActions,
  97. cmdHelp, // TODO: the "help" sub-command was used to show the more information for "work path" and "custom config", in the future, it should avoid doing so
  98. }
  99. // these sub-commands do not need the config file, and they do not depend on any path or environment variable.
  100. subCmdStandalone := []cli.Command{
  101. cmd.CmdCert,
  102. cmd.CmdGenerate,
  103. cmd.CmdDocs,
  104. }
  105. // shared configuration flags, they are for global and for each sub-command at the same time
  106. // eg: such command is valid: "./gitea --config /tmp/app.ini web --config /tmp/app.ini", while it's discouraged indeed
  107. // keep in mind that the short flags like "-C", "-c" and "-w" are globally polluted, they can't be used for sub-commands anymore.
  108. globalFlags := []cli.Flag{
  109. cli.HelpFlag,
  110. cli.StringFlag{
  111. Name: "custom-path, C",
  112. Usage: "Set custom path (defaults to '{WorkPath}/custom')",
  113. },
  114. cli.StringFlag{
  115. Name: "config, c",
  116. Value: setting.CustomConf,
  117. Usage: "Set custom config file (defaults to '{WorkPath}/custom/conf/app.ini')",
  118. },
  119. cli.StringFlag{
  120. Name: "work-path, w",
  121. Usage: "Set Gitea's working path (defaults to the Gitea's binary directory)",
  122. },
  123. }
  124. // Set the default to be equivalent to cmdWeb and add the default flags
  125. app.Flags = append(app.Flags, globalFlags...)
  126. app.Flags = append(app.Flags, cmd.CmdWeb.Flags...) // TODO: the web flags polluted the global flags, they are not really global flags
  127. app.Action = prepareWorkPathAndCustomConf(cmd.CmdWeb.Action)
  128. app.HideHelp = true // use our own help action to show helps (with more information like default config)
  129. app.Before = cmd.PrepareConsoleLoggerLevel(log.INFO)
  130. for i := range subCmdWithIni {
  131. prepareSubcommands(&subCmdWithIni[i], globalFlags)
  132. }
  133. app.Commands = append(app.Commands, subCmdWithIni...)
  134. app.Commands = append(app.Commands, subCmdStandalone...)
  135. cli.OsExiter = func(code int) {
  136. log.GetManager().Close()
  137. os.Exit(code)
  138. }
  139. app.ErrWriter = os.Stderr
  140. _ = cmd.RunMainApp(app, os.Args...) // all errors should have been handled by the RunMainApp
  141. log.GetManager().Close()
  142. }
  143. func prepareSubcommands(command *cli.Command, defaultFlags []cli.Flag) {
  144. command.Flags = append(command.Flags, defaultFlags...)
  145. command.Action = prepareWorkPathAndCustomConf(command.Action)
  146. command.HideHelp = true
  147. if command.Name != "help" {
  148. command.Subcommands = append(command.Subcommands, cmdHelp)
  149. }
  150. for i := range command.Subcommands {
  151. prepareSubcommands(&command.Subcommands[i], defaultFlags)
  152. }
  153. }
  154. // prepareWorkPathAndCustomConf wraps the Action to prepare the work path and custom config
  155. // 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
  156. func prepareWorkPathAndCustomConf(action any) func(ctx *cli.Context) error {
  157. return func(ctx *cli.Context) error {
  158. var args setting.ArgWorkPathAndCustomConf
  159. curCtx := ctx
  160. for curCtx != nil {
  161. if curCtx.IsSet("work-path") && args.WorkPath == "" {
  162. args.WorkPath = curCtx.String("work-path")
  163. }
  164. if curCtx.IsSet("custom-path") && args.CustomPath == "" {
  165. args.CustomPath = curCtx.String("custom-path")
  166. }
  167. if curCtx.IsSet("config") && args.CustomConf == "" {
  168. args.CustomConf = curCtx.String("config")
  169. }
  170. curCtx = curCtx.Parent()
  171. }
  172. setting.InitWorkPathAndCommonConfig(os.Getenv, args)
  173. if ctx.Bool("help") || action == nil {
  174. // the default behavior of "urfave/cli": "nil action" means "show help"
  175. return cmdHelp.Action.(func(ctx *cli.Context) error)(ctx)
  176. }
  177. return action.(func(*cli.Context) error)(ctx)
  178. }
  179. }
  180. func formatBuiltWith() string {
  181. version := runtime.Version()
  182. if len(MakeVersion) > 0 {
  183. version = MakeVersion + ", " + runtime.Version()
  184. }
  185. if len(Tags) == 0 {
  186. return " built with " + version
  187. }
  188. return " built with " + version + " : " + strings.ReplaceAll(Tags, " ", ", ")
  189. }