aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--cmd/actions.go2
-rw-r--r--cmd/cmd.go2
-rw-r--r--cmd/doctor.go2
-rw-r--r--cmd/dump.go2
-rw-r--r--cmd/embedded.go5
-rw-r--r--cmd/mailer.go2
-rw-r--r--cmd/restore_repo.go2
-rw-r--r--cmd/serv.go2
-rw-r--r--cmd/web.go168
-rw-r--r--contrib/environment-to-ini/environment-to-ini.go13
-rw-r--r--main.go169
-rw-r--r--models/migrations/base/tests.go2
-rw-r--r--models/unittest/testdb.go14
-rw-r--r--modules/doctor/doctor.go2
-rw-r--r--modules/doctor/paths.go2
-rw-r--r--modules/markup/html_test.go5
-rw-r--r--modules/markup/markdown/markdown_test.go5
-rw-r--r--modules/setting/config_provider.go80
-rw-r--r--modules/setting/config_provider_test.go11
-rw-r--r--modules/setting/path.go191
-rw-r--r--modules/setting/path_test.go151
-rw-r--r--modules/setting/server.go2
-rw-r--r--modules/setting/setting.go131
-rw-r--r--modules/ssh/ssh.go2
-rw-r--r--modules/testlogger/testlogger.go3
-rw-r--r--routers/init.go22
-rw-r--r--routers/install/install.go20
-rw-r--r--routers/install/setting.go51
-rw-r--r--routers/web/admin/config.go15
-rw-r--r--templates/admin/config.tmpl9
-rw-r--r--tests/test_utils.go20
32 files changed, 650 insertions, 459 deletions
diff --git a/.gitignore b/.gitignore
index 581417df61..6851be742c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -53,8 +53,6 @@ cpu.out
/bin
/dist
/custom/*
-!/custom/conf
-/custom/conf/*
!/custom/conf/app.example.ini
/data
/indexers
diff --git a/cmd/actions.go b/cmd/actions.go
index 346de5b21a..f52a91bd55 100644
--- a/cmd/actions.go
+++ b/cmd/actions.go
@@ -42,7 +42,7 @@ func runGenerateActionsRunnerToken(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
- setting.Init(&setting.Options{})
+ setting.MustInstalled()
scope := c.String("scope")
diff --git a/cmd/cmd.go b/cmd/cmd.go
index b148007fbe..8076acecaa 100644
--- a/cmd/cmd.go
+++ b/cmd/cmd.go
@@ -58,7 +58,7 @@ func confirm() (bool, error) {
}
func initDB(ctx context.Context) error {
- setting.Init(&setting.Options{})
+ setting.MustInstalled()
setting.LoadDBSetting()
setting.InitSQLLoggersForCli(log.INFO)
diff --git a/cmd/doctor.go b/cmd/doctor.go
index b596e9ac0c..b79436fc0a 100644
--- a/cmd/doctor.go
+++ b/cmd/doctor.go
@@ -91,7 +91,7 @@ func runRecreateTable(ctx *cli.Context) error {
golog.SetOutput(log.LoggerToWriter(log.GetLogger(log.DEFAULT).Info))
debug := ctx.Bool("debug")
- setting.Init(&setting.Options{})
+ setting.MustInstalled()
setting.LoadDBSetting()
if debug {
diff --git a/cmd/dump.go b/cmd/dump.go
index 7dda7fd2b3..0b7c1d32c5 100644
--- a/cmd/dump.go
+++ b/cmd/dump.go
@@ -182,7 +182,7 @@ func runDump(ctx *cli.Context) error {
}
fileName += "." + outType
}
- setting.Init(&setting.Options{})
+ setting.MustInstalled()
// make sure we are logging to the console no matter what the configuration tells us do to
// FIXME: don't use CfgProvider directly
diff --git a/cmd/embedded.go b/cmd/embedded.go
index e51f8477b4..204a623cf7 100644
--- a/cmd/embedded.go
+++ b/cmd/embedded.go
@@ -99,11 +99,6 @@ type assetFile struct {
func initEmbeddedExtractor(c *cli.Context) error {
setupConsoleLogger(log.ERROR, log.CanColorStderr, os.Stderr)
- // Read configuration file
- setting.Init(&setting.Options{
- AllowEmpty: true,
- })
-
patterns, err := compileCollectPatterns(c.Args())
if err != nil {
return err
diff --git a/cmd/mailer.go b/cmd/mailer.go
index 74bae1ab68..eaa5a1afe1 100644
--- a/cmd/mailer.go
+++ b/cmd/mailer.go
@@ -16,7 +16,7 @@ func runSendMail(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
- setting.Init(&setting.Options{})
+ setting.MustInstalled()
if err := argsSet(c, "title"); err != nil {
return err
diff --git a/cmd/restore_repo.go b/cmd/restore_repo.go
index 5a7ede4939..c19e28f13d 100644
--- a/cmd/restore_repo.go
+++ b/cmd/restore_repo.go
@@ -51,7 +51,7 @@ func runRestoreRepository(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
- setting.Init(&setting.Options{})
+ setting.MustInstalled()
var units []string
if s := c.String("units"); s != "" {
units = strings.Split(s, ",")
diff --git a/cmd/serv.go b/cmd/serv.go
index 87bf1cce20..01102d3800 100644
--- a/cmd/serv.go
+++ b/cmd/serv.go
@@ -61,7 +61,7 @@ func setup(ctx context.Context, debug bool) {
} else {
setupConsoleLogger(log.FATAL, false, os.Stderr)
}
- setting.Init(&setting.Options{})
+ setting.MustInstalled()
if debug {
setting.RunMode = "dev"
}
diff --git a/cmd/web.go b/cmd/web.go
index 115024e8b4..7a257a62a2 100644
--- a/cmd/web.go
+++ b/cmd/web.go
@@ -101,6 +101,110 @@ func createPIDFile(pidPath string) {
}
}
+func serveInstall(ctx *cli.Context) error {
+ log.Info("Gitea version: %s%s", setting.AppVer, setting.AppBuiltWith)
+ log.Info("App path: %s", setting.AppPath)
+ log.Info("Work path: %s", setting.AppWorkPath)
+ log.Info("Custom path: %s", setting.CustomPath)
+ log.Info("Config file: %s", setting.CustomConf)
+ log.Info("Prepare to run install page")
+
+ routers.InitWebInstallPage(graceful.GetManager().HammerContext())
+
+ // Flag for port number in case first time run conflict
+ if ctx.IsSet("port") {
+ if err := setPort(ctx.String("port")); err != nil {
+ return err
+ }
+ }
+ if ctx.IsSet("install-port") {
+ if err := setPort(ctx.String("install-port")); err != nil {
+ return err
+ }
+ }
+ c := install.Routes()
+ err := listen(c, false)
+ if err != nil {
+ log.Critical("Unable to open listener for installer. Is Gitea already running?")
+ graceful.GetManager().DoGracefulShutdown()
+ }
+ select {
+ case <-graceful.GetManager().IsShutdown():
+ <-graceful.GetManager().Done()
+ log.Info("PID: %d Gitea Web Finished", os.Getpid())
+ log.GetManager().Close()
+ return err
+ default:
+ }
+ return nil
+}
+
+func serveInstalled(ctx *cli.Context) error {
+ setting.InitCfgProvider(setting.CustomConf)
+ setting.LoadCommonSettings()
+ setting.MustInstalled()
+
+ log.Info("Gitea version: %s%s", setting.AppVer, setting.AppBuiltWith)
+ log.Info("App path: %s", setting.AppPath)
+ log.Info("Work path: %s", setting.AppWorkPath)
+ log.Info("Custom path: %s", setting.CustomPath)
+ log.Info("Config file: %s", setting.CustomConf)
+ log.Info("Run mode: %s", setting.RunMode)
+ log.Info("Prepare to run web server")
+
+ if setting.AppWorkPathMismatch {
+ log.Error("WORK_PATH from config %q doesn't match other paths from environment variables or command arguments. "+
+ "Only WORK_PATH in config should be set and used. Please remove the other outdated work paths from environment variables and command arguments", setting.CustomConf)
+ }
+
+ rootCfg := setting.CfgProvider
+ if rootCfg.Section("").Key("WORK_PATH").String() == "" {
+ saveCfg, err := rootCfg.PrepareSaving()
+ if err != nil {
+ log.Error("Unable to prepare saving WORK_PATH=%s to config %q: %v\nYou must set it manually, otherwise there might be bugs when accessing the git repositories.", setting.AppWorkPath, setting.CustomConf, err)
+ } else {
+ rootCfg.Section("").Key("WORK_PATH").SetValue(setting.AppWorkPath)
+ saveCfg.Section("").Key("WORK_PATH").SetValue(setting.AppWorkPath)
+ if err = saveCfg.Save(); err != nil {
+ log.Error("Unable to update WORK_PATH=%s to config %q: %v\nYou must set it manually, otherwise there might be bugs when accessing the git repositories.", setting.AppWorkPath, setting.CustomConf, err)
+ }
+ }
+ }
+
+ routers.InitWebInstalled(graceful.GetManager().HammerContext())
+
+ // We check that AppDataPath exists here (it should have been created during installation)
+ // We can't check it in `InitWebInstalled`, because some integration tests
+ // use cmd -> InitWebInstalled, but the AppDataPath doesn't exist during those tests.
+ if _, err := os.Stat(setting.AppDataPath); err != nil {
+ log.Fatal("Can not find APP_DATA_PATH %q", setting.AppDataPath)
+ }
+
+ // Override the provided port number within the configuration
+ if ctx.IsSet("port") {
+ if err := setPort(ctx.String("port")); err != nil {
+ return err
+ }
+ }
+
+ // Set up Chi routes
+ c := routers.NormalRoutes()
+ err := listen(c, true)
+ <-graceful.GetManager().Done()
+ log.Info("PID: %d Gitea Web Finished", os.Getpid())
+ log.GetManager().Close()
+ return err
+}
+
+func servePprof() {
+ http.DefaultServeMux.Handle("/debug/fgprof", fgprof.Handler())
+ _, _, finished := process.GetManager().AddTypedContext(context.Background(), "Web: PProf Server", process.SystemProcessType, true)
+ // The pprof server is for debug purpose only, it shouldn't be exposed on public network. At the moment it's not worth to introduce a configurable option for it.
+ log.Info("Starting pprof server on localhost:6060")
+ log.Info("Stopped pprof server: %v", http.ListenAndServe("localhost:6060", nil))
+ finished()
+}
+
func runWeb(ctx *cli.Context) error {
if ctx.Bool("verbose") {
setupConsoleLogger(log.TRACE, log.CanColorStdout, os.Stdout)
@@ -128,75 +232,19 @@ func runWeb(ctx *cli.Context) error {
createPIDFile(ctx.String("pid"))
}
- // Perform pre-initialization
- needsInstall := install.PreloadSettings(graceful.GetManager().HammerContext())
- if needsInstall {
- // Flag for port number in case first time run conflict
- if ctx.IsSet("port") {
- if err := setPort(ctx.String("port")); err != nil {
- return err
- }
- }
- if ctx.IsSet("install-port") {
- if err := setPort(ctx.String("install-port")); err != nil {
- return err
- }
- }
- c := install.Routes()
- err := listen(c, false)
- if err != nil {
- log.Critical("Unable to open listener for installer. Is Gitea already running?")
- graceful.GetManager().DoGracefulShutdown()
- }
- select {
- case <-graceful.GetManager().IsShutdown():
- <-graceful.GetManager().Done()
- log.Info("PID: %d Gitea Web Finished", os.Getpid())
- log.GetManager().Close()
+ if !setting.InstallLock {
+ if err := serveInstall(ctx); err != nil {
return err
- default:
}
} else {
NoInstallListener()
}
if setting.EnablePprof {
- go func() {
- http.DefaultServeMux.Handle("/debug/fgprof", fgprof.Handler())
- _, _, finished := process.GetManager().AddTypedContext(context.Background(), "Web: PProf Server", process.SystemProcessType, true)
- // The pprof server is for debug purpose only, it shouldn't be exposed on public network. At the moment it's not worth to introduce a configurable option for it.
- log.Info("Starting pprof server on localhost:6060")
- log.Info("Stopped pprof server: %v", http.ListenAndServe("localhost:6060", nil))
- finished()
- }()
+ go servePprof()
}
- log.Info("Global init")
- // Perform global initialization
- setting.Init(&setting.Options{})
- routers.GlobalInitInstalled(graceful.GetManager().HammerContext())
-
- // We check that AppDataPath exists here (it should have been created during installation)
- // We can't check it in `GlobalInitInstalled`, because some integration tests
- // use cmd -> GlobalInitInstalled, but the AppDataPath doesn't exist during those tests.
- if _, err := os.Stat(setting.AppDataPath); err != nil {
- log.Fatal("Can not find APP_DATA_PATH '%s'", setting.AppDataPath)
- }
-
- // Override the provided port number within the configuration
- if ctx.IsSet("port") {
- if err := setPort(ctx.String("port")); err != nil {
- return err
- }
- }
-
- // Set up Chi routes
- c := routers.NormalRoutes()
- err := listen(c, true)
- <-graceful.GetManager().Done()
- log.Info("PID: %d Gitea Web Finished", os.Getpid())
- log.GetManager().Close()
- return err
+ return serveInstalled(ctx)
}
func setPort(port string) error {
diff --git a/contrib/environment-to-ini/environment-to-ini.go b/contrib/environment-to-ini/environment-to-ini.go
index 3405d7d429..2cdf4e3943 100644
--- a/contrib/environment-to-ini/environment-to-ini.go
+++ b/contrib/environment-to-ini/environment-to-ini.go
@@ -81,8 +81,6 @@ func main() {
},
}
app.Action = runEnvironmentToIni
- setting.SetCustomPathAndConf("", "", "")
-
err := app.Run(os.Args)
if err != nil {
log.Fatal("Failed to run app with %s: %v", os.Args, err)
@@ -90,12 +88,13 @@ func main() {
}
func runEnvironmentToIni(c *cli.Context) error {
- providedCustom := c.String("custom-path")
- providedConf := c.String("config")
- providedWorkPath := c.String("work-path")
- setting.SetCustomPathAndConf(providedCustom, providedConf, providedWorkPath)
+ setting.InitWorkPathAndCommonConfig(os.Getenv, setting.ArgWorkPathAndCustomConf{
+ WorkPath: c.String("work-path"),
+ CustomPath: c.String("custom-path"),
+ CustomConf: c.String("config"),
+ })
- cfg, err := setting.NewConfigProviderFromFile(&setting.Options{CustomConf: setting.CustomConf, AllowEmpty: true})
+ cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf)
if err != nil {
log.Fatal("Failed to load custom conf '%s': %v", setting.CustomConf, err)
}
diff --git a/main.go b/main.go
index 49093eb8a7..1c87824c83 100644
--- a/main.go
+++ b/main.go
@@ -33,30 +33,58 @@ var (
Tags = ""
// MakeVersion holds the current Make version if built with make
MakeVersion = ""
-
- originalAppHelpTemplate = ""
- originalCommandHelpTemplate = ""
- originalSubcommandHelpTemplate = ""
)
func init() {
setting.AppVer = Version
setting.AppBuiltWith = formatBuiltWith()
setting.AppStartTime = time.Now().UTC()
+}
- // Grab the original help templates
- originalAppHelpTemplate = cli.AppHelpTemplate
- originalCommandHelpTemplate = cli.CommandHelpTemplate
- originalSubcommandHelpTemplate = cli.SubcommandHelpTemplate
+// cmdHelp is our own help subcommand with more information
+// test cases:
+// ./gitea help
+// ./gitea -h
+// ./gitea web help
+// ./gitea web -h (due to cli lib limitation, this won't call our cmdHelp, so no extra info)
+// ./gitea admin help auth
+// ./gitea -c /tmp/app.ini -h
+// ./gitea -c /tmp/app.ini help
+// ./gitea help -c /tmp/app.ini
+// GITEA_WORK_DIR=/tmp ./gitea help
+// GITEA_WORK_DIR=/tmp ./gitea help --work-path /tmp/other
+// GITEA_WORK_DIR=/tmp ./gitea help --config /tmp/app-other.ini
+var cmdHelp = cli.Command{
+ Name: "help",
+ Aliases: []string{"h"},
+ Usage: "Shows a list of commands or help for one command",
+ ArgsUsage: "[command]",
+ Action: func(c *cli.Context) (err error) {
+ args := c.Args()
+ if args.Present() {
+ err = cli.ShowCommandHelp(c, args.First())
+ } else {
+ err = cli.ShowAppHelp(c)
+ }
+ _, _ = fmt.Fprintf(c.App.Writer, `
+DEFAULT CONFIGURATION:
+ AppPath: %s
+ WorkPath: %s
+ CustomPath: %s
+ ConfigFile: %s
+
+`, setting.AppPath, setting.AppWorkPath, setting.CustomPath, setting.CustomConf)
+ return err
+ },
}
func main() {
app := cli.NewApp()
app.Name = "Gitea"
app.Usage = "A painless self-hosted Git service"
- app.Description = `By default, gitea will start serving using the webserver with no
-arguments - which can alternatively be run by running the subcommand web.`
+ 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".`
app.Version = Version + formatBuiltWith()
+ app.EnableBashCompletion = true
app.Commands = []cli.Command{
cmd.CmdWeb,
cmd.CmdServ,
@@ -77,118 +105,83 @@ arguments - which can alternatively be run by running the subcommand web.`
cmd.CmdRestoreRepository,
cmd.CmdActions,
}
- // Now adjust these commands to add our global configuration options
-
- // First calculate the default paths and set the AppHelpTemplates in this context
- setting.SetCustomPathAndConf("", "", "")
- setAppHelpTemplates()
// default configuration flags
- defaultFlags := []cli.Flag{
+ globalFlags := []cli.Flag{
+ cli.HelpFlag,
cli.StringFlag{
Name: "custom-path, C",
- Value: setting.CustomPath,
- Usage: "Custom path file path",
+ Usage: "Set custom path (defaults to '{WorkPath}/custom')",
},
cli.StringFlag{
Name: "config, c",
Value: setting.CustomConf,
- Usage: "Custom configuration file path",
+ Usage: "Set custom config file (defaults to '{WorkPath}/custom/conf/app.ini')",
},
- cli.VersionFlag,
cli.StringFlag{
Name: "work-path, w",
- Value: setting.AppWorkPath,
- Usage: "Set the gitea working path",
+ Usage: "Set Gitea's working path (defaults to the Gitea's binary directory)",
},
}
// Set the default to be equivalent to cmdWeb and add the default flags
+ app.Flags = append(app.Flags, globalFlags...)
app.Flags = append(app.Flags, cmd.CmdWeb.Flags...)
- app.Flags = append(app.Flags, defaultFlags...)
- app.Action = cmd.CmdWeb.Action
-
- // Add functions to set these paths and these flags to the commands
- app.Before = establishCustomPath
+ app.Action = prepareWorkPathAndCustomConf(cmd.CmdWeb.Action)
+ app.HideHelp = true // use our own help action to show helps (with more information like default config)
+ app.Commands = append(app.Commands, cmdHelp)
for i := range app.Commands {
- setFlagsAndBeforeOnSubcommands(&app.Commands[i], defaultFlags, establishCustomPath)
+ prepareSubcommands(&app.Commands[i], globalFlags)
}
- app.EnableBashCompletion = true
-
err := app.Run(os.Args)
if err != nil {
- log.Fatal("Failed to run app with %s: %v", os.Args, err)
+ _, _ = fmt.Fprintf(app.Writer, "\nFailed to run with %s: %v\n", os.Args, err)
}
log.GetManager().Close()
}
-func setFlagsAndBeforeOnSubcommands(command *cli.Command, defaultFlags []cli.Flag, before cli.BeforeFunc) {
+func prepareSubcommands(command *cli.Command, defaultFlags []cli.Flag) {
command.Flags = append(command.Flags, defaultFlags...)
- command.Before = establishCustomPath
+ command.Action = prepareWorkPathAndCustomConf(command.Action)
+ command.HideHelp = true
+ if command.Name != "help" {
+ command.Subcommands = append(command.Subcommands, cmdHelp)
+ }
for i := range command.Subcommands {
- setFlagsAndBeforeOnSubcommands(&command.Subcommands[i], defaultFlags, before)
+ prepareSubcommands(&command.Subcommands[i], defaultFlags)
}
}
-func establishCustomPath(ctx *cli.Context) error {
- var providedCustom string
- var providedConf string
- var providedWorkPath string
-
- currentCtx := ctx
- for {
- if len(providedCustom) != 0 && len(providedConf) != 0 && len(providedWorkPath) != 0 {
- break
- }
- if currentCtx == nil {
- break
- }
- if currentCtx.IsSet("custom-path") && len(providedCustom) == 0 {
- providedCustom = currentCtx.String("custom-path")
- }
- if currentCtx.IsSet("config") && len(providedConf) == 0 {
- providedConf = currentCtx.String("config")
+// 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(a any) func(ctx *cli.Context) error {
+ if a == nil {
+ return nil
+ }
+ action := a.(func(*cli.Context) error)
+ return func(ctx *cli.Context) error {
+ var args setting.ArgWorkPathAndCustomConf
+ curCtx := ctx
+ for curCtx != nil {
+ 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")
+ }
+ curCtx = curCtx.Parent()
}
- if currentCtx.IsSet("work-path") && len(providedWorkPath) == 0 {
- providedWorkPath = currentCtx.String("work-path")
+ setting.InitWorkPathAndCommonConfig(os.Getenv, args)
+ if ctx.Bool("help") {
+ return cmdHelp.Action.(func(ctx *cli.Context) error)(ctx)
}
- currentCtx = currentCtx.Parent()
-
- }
- setting.SetCustomPathAndConf(providedCustom, providedConf, providedWorkPath)
-
- setAppHelpTemplates()
-
- if ctx.IsSet("version") {
- cli.ShowVersion(ctx)
- os.Exit(0)
- }
-
- return nil
-}
-
-func setAppHelpTemplates() {
- cli.AppHelpTemplate = adjustHelpTemplate(originalAppHelpTemplate)
- cli.CommandHelpTemplate = adjustHelpTemplate(originalCommandHelpTemplate)
- cli.SubcommandHelpTemplate = adjustHelpTemplate(originalSubcommandHelpTemplate)
-}
-
-func adjustHelpTemplate(originalTemplate string) string {
- overridden := ""
- if _, ok := os.LookupEnv("GITEA_CUSTOM"); ok {
- overridden = "(GITEA_CUSTOM)"
+ return action(ctx)
}
-
- return fmt.Sprintf(`%s
-DEFAULT CONFIGURATION:
- CustomPath: %s %s
- CustomConf: %s
- AppPath: %s
- AppWorkPath: %s
-
-`, originalTemplate, setting.CustomPath, overridden, setting.CustomConf, setting.AppPath, setting.AppWorkPath)
}
func formatBuiltWith() string {
diff --git a/models/migrations/base/tests.go b/models/migrations/base/tests.go
index dd99a1eda2..c3100ba665 100644
--- a/models/migrations/base/tests.go
+++ b/models/migrations/base/tests.go
@@ -147,9 +147,9 @@ func MainTest(m *testing.M) {
os.Exit(1)
}
+ setting.CustomPath = filepath.Join(setting.AppWorkPath, "custom")
setting.AppDataPath = tmpDataPath
- setting.SetCustomPathAndConf("", "", "")
unittest.InitSettings()
if err = git.InitFull(context.Background()); err != nil {
fmt.Printf("Unable to InitFull: %v\n", err)
diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go
index 5351ff1139..f926a65538 100644
--- a/models/unittest/testdb.go
+++ b/models/unittest/testdb.go
@@ -42,12 +42,14 @@ func fatalTestError(fmtStr string, args ...interface{}) {
os.Exit(1)
}
-// InitSettings initializes config provider and load common setttings for tests
+// InitSettings initializes config provider and load common settings for tests
func InitSettings(extraConfigs ...string) {
- setting.Init(&setting.Options{
- AllowEmpty: true,
- ExtraConfig: strings.Join(extraConfigs, "\n"),
- })
+ if setting.CustomConf == "" {
+ setting.CustomConf = filepath.Join(setting.CustomPath, "conf/app-unittest-tmp.ini")
+ _ = os.Remove(setting.CustomConf)
+ }
+ setting.InitCfgProvider(setting.CustomConf, strings.Join(extraConfigs, "\n"))
+ setting.LoadCommonSettings()
if err := setting.PrepareAppDataPath(); err != nil {
log.Fatalf("Can not prepare APP_DATA_PATH: %v", err)
@@ -69,7 +71,7 @@ type TestOptions struct {
// MainTest a reusable TestMain(..) function for unit tests that need to use a
// test database. Creates the test database, and sets necessary settings.
func MainTest(m *testing.M, testOpts *TestOptions) {
- setting.SetCustomPathAndConf("", "", "")
+ setting.CustomPath = filepath.Join(testOpts.GiteaRootPath, "custom")
InitSettings()
var err error
diff --git a/modules/doctor/doctor.go b/modules/doctor/doctor.go
index 10838a7512..ceee322852 100644
--- a/modules/doctor/doctor.go
+++ b/modules/doctor/doctor.go
@@ -28,7 +28,7 @@ type Check struct {
}
func initDBSkipLogger(ctx context.Context) error {
- setting.Init(&setting.Options{})
+ setting.MustInstalled()
setting.LoadDBSetting()
if err := db.InitEngine(ctx); err != nil {
return fmt.Errorf("db.InitEngine: %w", err)
diff --git a/modules/doctor/paths.go b/modules/doctor/paths.go
index 957152349c..3f62d587ab 100644
--- a/modules/doctor/paths.go
+++ b/modules/doctor/paths.go
@@ -66,7 +66,7 @@ func checkConfigurationFiles(ctx context.Context, logger log.Logger, autofix boo
return err
}
- setting.Init(&setting.Options{})
+ setting.MustInstalled()
configurationFiles := []configurationFile{
{"Configuration File Path", setting.CustomConf, false, true, false},
diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go
index 5e5e4fecbb..a8d7ba7948 100644
--- a/modules/markup/html_test.go
+++ b/modules/markup/html_test.go
@@ -10,6 +10,7 @@ import (
"strings"
"testing"
+ "code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/emoji"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
@@ -28,9 +29,7 @@ var localMetas = map[string]string{
}
func TestMain(m *testing.M) {
- setting.Init(&setting.Options{
- AllowEmpty: true,
- })
+ unittest.InitSettings()
if err := git.InitSimple(context.Background()); err != nil {
log.Fatal("git init failed, err: %v", err)
}
diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go
index 4bd2ca8d41..f2322b2554 100644
--- a/modules/markup/markdown/markdown_test.go
+++ b/modules/markup/markdown/markdown_test.go
@@ -9,6 +9,7 @@ import (
"strings"
"testing"
+ "code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
@@ -33,9 +34,7 @@ var localMetas = map[string]string{
}
func TestMain(m *testing.M) {
- setting.Init(&setting.Options{
- AllowEmpty: true,
- })
+ unittest.InitSettings()
if err := git.InitSimple(context.Background()); err != nil {
log.Fatal("git init failed, err: %v", err)
}
diff --git a/modules/setting/config_provider.go b/modules/setting/config_provider.go
index deec5cc586..94dd989850 100644
--- a/modules/setting/config_provider.go
+++ b/modules/setting/config_provider.go
@@ -55,15 +55,15 @@ type ConfigProvider interface {
DisableSaving()
PrepareSaving() (ConfigProvider, error)
+ IsLoadedFromEmpty() bool
}
type iniConfigProvider struct {
- opts *Options
+ file string
ini *ini.File
- disableSaving bool
-
- newFile bool // whether the file has not existed previously
+ disableSaving bool // disable the "Save" method because the config options could be polluted
+ loadedFromEmpty bool // whether the file has not existed previously
}
type iniConfigSection struct {
@@ -182,53 +182,43 @@ func NewConfigProviderFromData(configContent string) (ConfigProvider, error) {
}
cfg.NameMapper = ini.SnackCase
return &iniConfigProvider{
- ini: cfg,
- newFile: true,
+ ini: cfg,
+ loadedFromEmpty: true,
}, nil
}
-type Options struct {
- CustomConf string // the ini file path
- AllowEmpty bool // whether not finding configuration files is allowed
- ExtraConfig string
-
- DisableLoadCommonSettings bool // only used by "Init()", not used by "NewConfigProvider()"
-}
-
// NewConfigProviderFromFile load configuration from file.
// NOTE: do not print any log except error.
-func NewConfigProviderFromFile(opts *Options) (ConfigProvider, error) {
+func NewConfigProviderFromFile(file string, extraConfigs ...string) (ConfigProvider, error) {
cfg := ini.Empty(ini.LoadOptions{KeyValueDelimiterOnWrite: " = "})
- newFile := true
+ loadedFromEmpty := true
- if opts.CustomConf != "" {
- isFile, err := util.IsFile(opts.CustomConf)
+ if file != "" {
+ isFile, err := util.IsFile(file)
if err != nil {
- return nil, fmt.Errorf("unable to check if %s is a file. Error: %v", opts.CustomConf, err)
+ return nil, fmt.Errorf("unable to check if %q is a file. Error: %v", file, err)
}
if isFile {
- if err := cfg.Append(opts.CustomConf); err != nil {
- return nil, fmt.Errorf("failed to load custom conf '%s': %v", opts.CustomConf, err)
+ if err = cfg.Append(file); err != nil {
+ return nil, fmt.Errorf("failed to load config file %q: %v", file, err)
}
- newFile = false
+ loadedFromEmpty = false
}
}
- if newFile && !opts.AllowEmpty {
- return nil, fmt.Errorf("unable to find configuration file: %q, please ensure you are running in the correct environment or set the correct configuration file with -c", CustomConf)
- }
-
- if opts.ExtraConfig != "" {
- if err := cfg.Append([]byte(opts.ExtraConfig)); err != nil {
- return nil, fmt.Errorf("unable to append more config: %v", err)
+ if len(extraConfigs) > 0 {
+ for _, s := range extraConfigs {
+ if err := cfg.Append([]byte(s)); err != nil {
+ return nil, fmt.Errorf("unable to append more config: %v", err)
+ }
}
}
cfg.NameMapper = ini.SnackCase
return &iniConfigProvider{
- opts: opts,
- ini: cfg,
- newFile: newFile,
+ file: file,
+ ini: cfg,
+ loadedFromEmpty: loadedFromEmpty,
}, nil
}
@@ -266,20 +256,17 @@ func (p *iniConfigProvider) Save() error {
if p.disableSaving {
return errDisableSaving
}
- filename := p.opts.CustomConf
+ filename := p.file
if filename == "" {
- if !p.opts.AllowEmpty {
- return fmt.Errorf("custom config path must not be empty")
- }
- return nil
+ return fmt.Errorf("config file path must not be empty")
}
- if p.newFile {
+ if p.loadedFromEmpty {
if err := os.MkdirAll(filepath.Dir(filename), os.ModePerm); err != nil {
- return fmt.Errorf("failed to create '%s': %v", filename, err)
+ return fmt.Errorf("failed to create %q: %v", filename, err)
}
}
if err := p.ini.SaveTo(filename); err != nil {
- return fmt.Errorf("failed to save '%s': %v", filename, err)
+ return fmt.Errorf("failed to save %q: %v", filename, err)
}
// Change permissions to be more restrictive
@@ -313,11 +300,14 @@ func (p *iniConfigProvider) DisableSaving() {
// it makes the "Save" outputs a lot of garbage options
// After the INI package gets refactored, no "MustXxx" pollution, this workaround can be dropped.
func (p *iniConfigProvider) PrepareSaving() (ConfigProvider, error) {
- cfgFile := p.opts.CustomConf
- if cfgFile == "" {
+ if p.file == "" {
return nil, errors.New("no config file to save")
}
- return NewConfigProviderFromFile(p.opts)
+ return NewConfigProviderFromFile(p.file)
+}
+
+func (p *iniConfigProvider) IsLoadedFromEmpty() bool {
+ return p.loadedFromEmpty
}
func mustMapSetting(rootCfg ConfigProvider, sectionName string, setting any) {
@@ -356,8 +346,8 @@ func NewConfigProviderForLocale(source any, others ...any) (ConfigProvider, erro
}
iniFile.BlockMode = false
return &iniConfigProvider{
- ini: iniFile,
- newFile: true,
+ ini: iniFile,
+ loadedFromEmpty: true,
}, nil
}
diff --git a/modules/setting/config_provider_test.go b/modules/setting/config_provider_test.go
index c5c5196e04..7e7c6be2bb 100644
--- a/modules/setting/config_provider_test.go
+++ b/modules/setting/config_provider_test.go
@@ -67,13 +67,14 @@ key = 123
}
func TestNewConfigProviderFromFile(t *testing.T) {
- _, err := NewConfigProviderFromFile(&Options{CustomConf: "no-such.ini", AllowEmpty: false})
- assert.ErrorContains(t, err, "unable to find configuration file")
+ cfg, err := NewConfigProviderFromFile("no-such.ini")
+ assert.NoError(t, err)
+ assert.True(t, cfg.IsLoadedFromEmpty())
// load non-existing file and save
testFile := t.TempDir() + "/test.ini"
testFile1 := t.TempDir() + "/test1.ini"
- cfg, err := NewConfigProviderFromFile(&Options{CustomConf: testFile, AllowEmpty: true})
+ cfg, err = NewConfigProviderFromFile(testFile)
assert.NoError(t, err)
sec, _ := cfg.NewSection("foo")
@@ -91,7 +92,7 @@ func TestNewConfigProviderFromFile(t *testing.T) {
assert.Equal(t, "[foo]\nk1 = a\nk2 = b\n", string(bs))
// load existing file and save
- cfg, err = NewConfigProviderFromFile(&Options{CustomConf: testFile, AllowEmpty: true})
+ cfg, err = NewConfigProviderFromFile(testFile)
assert.NoError(t, err)
assert.Equal(t, "a", cfg.Section("foo").Key("k1").String())
sec, _ = cfg.NewSection("bar")
@@ -123,7 +124,7 @@ func TestNewConfigProviderForLocale(t *testing.T) {
func TestDisableSaving(t *testing.T) {
testFile := t.TempDir() + "/test.ini"
_ = os.WriteFile(testFile, []byte("k1=a\nk2=b"), 0o644)
- cfg, err := NewConfigProviderFromFile(&Options{CustomConf: testFile, AllowEmpty: true})
+ cfg, err := NewConfigProviderFromFile(testFile)
assert.NoError(t, err)
cfg.DisableSaving()
diff --git a/modules/setting/path.go b/modules/setting/path.go
new file mode 100644
index 0000000000..91bb2e9bb7
--- /dev/null
+++ b/modules/setting/path.go
@@ -0,0 +1,191 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package setting
+
+import (
+ "errors"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+
+ "code.gitea.io/gitea/modules/log"
+)
+
+var (
+ // AppPath represents the path to the gitea binary
+ AppPath string
+
+ // AppWorkPath is the "working directory" of Gitea. It maps to the environment variable GITEA_WORK_DIR.
+ // If that is not set it is the default set here by the linker or failing that the directory of AppPath.
+ // It is used as the base path for several other paths.
+ AppWorkPath string
+ CustomPath string // Custom directory path. Env: GITEA_CUSTOM
+ CustomConf string
+
+ appWorkPathBuiltin string
+ customPathBuiltin string
+ customConfBuiltin string
+
+ AppWorkPathMismatch bool
+)
+
+func getAppPath() (string, error) {
+ var appPath string
+ var err error
+ if IsWindows && filepath.IsAbs(os.Args[0]) {
+ appPath = filepath.Clean(os.Args[0])
+ } else {
+ appPath, err = exec.LookPath(os.Args[0])
+ }
+ if err != nil {
+ if !errors.Is(err, exec.ErrDot) {
+ return "", err
+ }
+ appPath, err = filepath.Abs(os.Args[0])
+ }
+ if err != nil {
+ return "", err
+ }
+ appPath, err = filepath.Abs(appPath)
+ if err != nil {
+ return "", err
+ }
+ // Note: (legacy code) we don't use path.Dir here because it does not handle case which path starts with two "/" in Windows: "//psf/Home/..."
+ return strings.ReplaceAll(appPath, "\\", "/"), err
+}
+
+func init() {
+ var err error
+ if AppPath, err = getAppPath(); err != nil {
+ log.Fatal("Failed to get app path: %v", err)
+ }
+
+ if AppWorkPath == "" {
+ AppWorkPath = filepath.Dir(AppPath)
+ }
+
+ appWorkPathBuiltin = AppWorkPath
+ customPathBuiltin = CustomPath
+ customConfBuiltin = CustomConf
+}
+
+type ArgWorkPathAndCustomConf struct {
+ WorkPath string
+ CustomPath string
+ CustomConf string
+}
+
+type stringWithDefault struct {
+ Value string
+ IsSet bool
+}
+
+func (s *stringWithDefault) Set(v string) {
+ s.Value = v
+ s.IsSet = true
+}
+
+// InitWorkPathAndCommonConfig will set AppWorkPath, CustomPath and CustomConf, init default config provider by CustomConf and load common settings,
+func InitWorkPathAndCommonConfig(getEnvFn func(name string) string, args ArgWorkPathAndCustomConf) {
+ tryAbsPath := func(paths ...string) string {
+ s := paths[len(paths)-1]
+ for i := len(paths) - 2; i >= 0; i-- {
+ if filepath.IsAbs(s) {
+ break
+ }
+ s = filepath.Join(paths[i], s)
+ }
+ return s
+ }
+
+ var err error
+ tmpWorkPath := stringWithDefault{Value: appWorkPathBuiltin}
+ if tmpWorkPath.Value == "" {
+ tmpWorkPath.Value = filepath.Dir(AppPath)
+ }
+ tmpCustomPath := stringWithDefault{Value: customPathBuiltin}
+ if tmpCustomPath.Value == "" {
+ tmpCustomPath.Value = "custom"
+ }
+ tmpCustomConf := stringWithDefault{Value: customConfBuiltin}
+ if tmpCustomConf.Value == "" {
+ tmpCustomConf.Value = "conf/app.ini"
+ }
+
+ readFromEnv := func() {
+ envWorkPath := getEnvFn("GITEA_WORK_DIR")
+ if envWorkPath != "" {
+ tmpWorkPath.Set(envWorkPath)
+ if !filepath.IsAbs(tmpWorkPath.Value) {
+ log.Fatal("GITEA_WORK_DIR (work path) must be absolute path")
+ }
+ }
+
+ envCustomPath := getEnvFn("GITEA_CUSTOM")
+ if envCustomPath != "" {
+ tmpCustomPath.Set(envCustomPath)
+ if !filepath.IsAbs(tmpCustomPath.Value) {
+ log.Fatal("GITEA_CUSTOM (custom path) must be absolute path")
+ }
+ }
+ }
+
+ readFromArgs := func() {
+ if args.WorkPath != "" {
+ tmpWorkPath.Set(args.WorkPath)
+ if !filepath.IsAbs(tmpWorkPath.Value) {
+ log.Fatal("--work-path must be absolute path")
+ }
+ }
+ if args.CustomPath != "" {
+ tmpCustomPath.Set(args.CustomPath) // if it is not abs, it will be based on work-path, it shouldn't happen
+ if !filepath.IsAbs(tmpCustomPath.Value) {
+ log.Error("--custom-path must be absolute path")
+ }
+ }
+ if args.CustomConf != "" {
+ tmpCustomConf.Set(args.CustomConf)
+ if !filepath.IsAbs(tmpCustomConf.Value) {
+ // the config path can be relative to the real current working path
+ if tmpCustomConf.Value, err = filepath.Abs(tmpCustomConf.Value); err != nil {
+ log.Fatal("Failed to get absolute path of config %q: %v", tmpCustomConf.Value, err)
+ }
+ }
+ }
+ }
+
+ readFromEnv()
+ readFromArgs()
+
+ if !tmpCustomConf.IsSet {
+ tmpCustomConf.Set(tryAbsPath(tmpWorkPath.Value, tmpCustomPath.Value, tmpCustomConf.Value))
+ }
+
+ // only read the config but do not load/init anything more, because the AppWorkPath and CustomPath are not ready
+ InitCfgProvider(tmpCustomConf.Value)
+ configWorkPath := ConfigSectionKeyString(CfgProvider.Section(""), "WORK_PATH")
+ if configWorkPath != "" {
+ if !filepath.IsAbs(configWorkPath) {
+ log.Fatal("WORK_PATH in %q must be absolute path", configWorkPath)
+ }
+ configWorkPath = filepath.Clean(configWorkPath)
+ if tmpWorkPath.Value != "" && (getEnvFn("GITEA_WORK_DIR") != "" || args.WorkPath != "") {
+ fi1, err1 := os.Stat(tmpWorkPath.Value)
+ fi2, err2 := os.Stat(configWorkPath)
+ if err1 != nil || err2 != nil || !os.SameFile(fi1, fi2) {
+ AppWorkPathMismatch = true
+ }
+ }
+ tmpWorkPath.Set(configWorkPath)
+ }
+
+ tmpCustomPath.Set(tryAbsPath(tmpWorkPath.Value, tmpCustomPath.Value))
+
+ AppWorkPath = tmpWorkPath.Value
+ CustomPath = tmpCustomPath.Value
+ CustomConf = tmpCustomConf.Value
+
+ LoadCommonSettings()
+}
diff --git a/modules/setting/path_test.go b/modules/setting/path_test.go
new file mode 100644
index 0000000000..fc6a2116dc
--- /dev/null
+++ b/modules/setting/path_test.go
@@ -0,0 +1,151 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package setting
+
+import (
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+type envVars map[string]string
+
+func (e envVars) Getenv(key string) string {
+ return e[key]
+}
+
+func TestInitWorkPathAndCommonConfig(t *testing.T) {
+ testInit := func(defaultWorkPath, defaultCustomPath, defaultCustomConf string) {
+ AppWorkPathMismatch = false
+ AppWorkPath = defaultWorkPath
+ appWorkPathBuiltin = defaultWorkPath
+ CustomPath = defaultCustomPath
+ customPathBuiltin = defaultCustomPath
+ CustomConf = defaultCustomConf
+ customConfBuiltin = defaultCustomConf
+ }
+
+ fp := filepath.Join
+
+ tmpDir := t.TempDir()
+ dirFoo := fp(tmpDir, "foo")
+ dirBar := fp(tmpDir, "bar")
+ dirXxx := fp(tmpDir, "xxx")
+ dirYyy := fp(tmpDir, "yyy")
+
+ t.Run("Default", func(t *testing.T) {
+ testInit(dirFoo, "", "")
+ InitWorkPathAndCommonConfig(envVars{}.Getenv, ArgWorkPathAndCustomConf{})
+ assert.Equal(t, dirFoo, AppWorkPath)
+ assert.Equal(t, fp(dirFoo, "custom"), CustomPath)
+ assert.Equal(t, fp(dirFoo, "custom/conf/app.ini"), CustomConf)
+ })
+
+ t.Run("WorkDir(env)", func(t *testing.T) {
+ testInit(dirFoo, "", "")
+ InitWorkPathAndCommonConfig(envVars{"GITEA_WORK_DIR": dirBar}.Getenv, ArgWorkPathAndCustomConf{})
+ assert.Equal(t, dirBar, AppWorkPath)
+ assert.Equal(t, fp(dirBar, "custom"), CustomPath)
+ assert.Equal(t, fp(dirBar, "custom/conf/app.ini"), CustomConf)
+ })
+
+ t.Run("WorkDir(env,arg)", func(t *testing.T) {
+ testInit(dirFoo, "", "")
+ InitWorkPathAndCommonConfig(envVars{"GITEA_WORK_DIR": dirBar}.Getenv, ArgWorkPathAndCustomConf{WorkPath: dirXxx})
+ assert.Equal(t, dirXxx, AppWorkPath)
+ assert.Equal(t, fp(dirXxx, "custom"), CustomPath)
+ assert.Equal(t, fp(dirXxx, "custom/conf/app.ini"), CustomConf)
+ })
+
+ t.Run("CustomPath(env)", func(t *testing.T) {
+ testInit(dirFoo, "", "")
+ InitWorkPathAndCommonConfig(envVars{"GITEA_CUSTOM": fp(dirBar, "custom1")}.Getenv, ArgWorkPathAndCustomConf{})
+ assert.Equal(t, dirFoo, AppWorkPath)
+ assert.Equal(t, fp(dirBar, "custom1"), CustomPath)
+ assert.Equal(t, fp(dirBar, "custom1/conf/app.ini"), CustomConf)
+ })
+
+ t.Run("CustomPath(env,arg)", func(t *testing.T) {
+ testInit(dirFoo, "", "")
+ InitWorkPathAndCommonConfig(envVars{"GITEA_CUSTOM": fp(dirBar, "custom1")}.Getenv, ArgWorkPathAndCustomConf{CustomPath: "custom2"})
+ assert.Equal(t, dirFoo, AppWorkPath)
+ assert.Equal(t, fp(dirFoo, "custom2"), CustomPath)
+ assert.Equal(t, fp(dirFoo, "custom2/conf/app.ini"), CustomConf)
+ })
+
+ t.Run("CustomConf", func(t *testing.T) {
+ testInit(dirFoo, "", "")
+ InitWorkPathAndCommonConfig(envVars{}.Getenv, ArgWorkPathAndCustomConf{CustomConf: "app1.ini"})
+ assert.Equal(t, dirFoo, AppWorkPath)
+ cwd, _ := os.Getwd()
+ assert.Equal(t, fp(cwd, "app1.ini"), CustomConf)
+
+ testInit(dirFoo, "", "")
+ InitWorkPathAndCommonConfig(envVars{}.Getenv, ArgWorkPathAndCustomConf{CustomConf: fp(dirBar, "app1.ini")})
+ assert.Equal(t, dirFoo, AppWorkPath)
+ assert.Equal(t, fp(dirBar, "app1.ini"), CustomConf)
+ })
+
+ t.Run("CustomConfOverrideWorkPath", func(t *testing.T) {
+ iniWorkPath := fp(tmpDir, "app-workpath.ini")
+ _ = os.WriteFile(iniWorkPath, []byte("WORK_PATH="+dirXxx), 0o644)
+
+ testInit(dirFoo, "", "")
+ InitWorkPathAndCommonConfig(envVars{}.Getenv, ArgWorkPathAndCustomConf{CustomConf: iniWorkPath})
+ assert.Equal(t, dirXxx, AppWorkPath)
+ assert.Equal(t, fp(dirXxx, "custom"), CustomPath)
+ assert.Equal(t, iniWorkPath, CustomConf)
+ assert.False(t, AppWorkPathMismatch)
+
+ testInit(dirFoo, "", "")
+ InitWorkPathAndCommonConfig(envVars{"GITEA_WORK_DIR": dirBar}.Getenv, ArgWorkPathAndCustomConf{CustomConf: iniWorkPath})
+ assert.Equal(t, dirXxx, AppWorkPath)
+ assert.Equal(t, fp(dirXxx, "custom"), CustomPath)
+ assert.Equal(t, iniWorkPath, CustomConf)
+ assert.True(t, AppWorkPathMismatch)
+
+ testInit(dirFoo, "", "")
+ InitWorkPathAndCommonConfig(envVars{}.Getenv, ArgWorkPathAndCustomConf{WorkPath: dirBar, CustomConf: iniWorkPath})
+ assert.Equal(t, dirXxx, AppWorkPath)
+ assert.Equal(t, fp(dirXxx, "custom"), CustomPath)
+ assert.Equal(t, iniWorkPath, CustomConf)
+ assert.True(t, AppWorkPathMismatch)
+ })
+
+ t.Run("Builtin", func(t *testing.T) {
+ testInit(dirFoo, dirBar, dirXxx)
+ InitWorkPathAndCommonConfig(envVars{}.Getenv, ArgWorkPathAndCustomConf{})
+ assert.Equal(t, dirFoo, AppWorkPath)
+ assert.Equal(t, dirBar, CustomPath)
+ assert.Equal(t, dirXxx, CustomConf)
+
+ testInit(dirFoo, "custom1", "cfg.ini")
+ InitWorkPathAndCommonConfig(envVars{}.Getenv, ArgWorkPathAndCustomConf{})
+ assert.Equal(t, dirFoo, AppWorkPath)
+ assert.Equal(t, fp(dirFoo, "custom1"), CustomPath)
+ assert.Equal(t, fp(dirFoo, "custom1/cfg.ini"), CustomConf)
+
+ testInit(dirFoo, "custom1", "cfg.ini")
+ InitWorkPathAndCommonConfig(envVars{"GITEA_WORK_DIR": dirYyy}.Getenv, ArgWorkPathAndCustomConf{})
+ assert.Equal(t, dirYyy, AppWorkPath)
+ assert.Equal(t, fp(dirYyy, "custom1"), CustomPath)
+ assert.Equal(t, fp(dirYyy, "custom1/cfg.ini"), CustomConf)
+
+ testInit(dirFoo, "custom1", "cfg.ini")
+ InitWorkPathAndCommonConfig(envVars{"GITEA_CUSTOM": dirYyy}.Getenv, ArgWorkPathAndCustomConf{})
+ assert.Equal(t, dirFoo, AppWorkPath)
+ assert.Equal(t, dirYyy, CustomPath)
+ assert.Equal(t, fp(dirYyy, "cfg.ini"), CustomConf)
+
+ iniWorkPath := fp(tmpDir, "app-workpath.ini")
+ _ = os.WriteFile(iniWorkPath, []byte("WORK_PATH="+dirXxx), 0o644)
+ testInit(dirFoo, "custom1", "cfg.ini")
+ InitWorkPathAndCommonConfig(envVars{}.Getenv, ArgWorkPathAndCustomConf{CustomConf: iniWorkPath})
+ assert.Equal(t, dirXxx, AppWorkPath)
+ assert.Equal(t, fp(dirXxx, "custom1"), CustomPath)
+ assert.Equal(t, iniWorkPath, CustomConf)
+ })
+}
diff --git a/modules/setting/server.go b/modules/setting/server.go
index d937faca10..7c033bcc6b 100644
--- a/modules/setting/server.go
+++ b/modules/setting/server.go
@@ -61,6 +61,7 @@ var (
AssetVersion string
// Server settings
+
Protocol Scheme
UseProxyProtocol bool // `ini:"USE_PROXY_PROTOCOL"`
ProxyProtocolTLSBridging bool //`ini:"PROXY_PROTOCOL_TLS_BRIDGING"`
@@ -324,7 +325,6 @@ func loadServerFrom(rootCfg ConfigProvider) {
StaticCacheTime = sec.Key("STATIC_CACHE_TIME").MustDuration(6 * time.Hour)
AppDataPath = sec.Key("APP_DATA_PATH").MustString(path.Join(AppWorkPath, "data"))
if !filepath.IsAbs(AppDataPath) {
- log.Info("The provided APP_DATA_PATH: %s is not absolute - it will be made absolute against the work path: %s", AppDataPath, AppWorkPath)
AppDataPath = filepath.ToSlash(filepath.Join(AppWorkPath, AppDataPath))
}
diff --git a/modules/setting/setting.go b/modules/setting/setting.go
index 6eaddbe2b5..0d69847dbe 100644
--- a/modules/setting/setting.go
+++ b/modules/setting/setting.go
@@ -5,12 +5,8 @@
package setting
import (
- "errors"
"fmt"
"os"
- "os/exec"
- "path"
- "path/filepath"
"runtime"
"strings"
"time"
@@ -28,19 +24,9 @@ var (
// AppStartTime store time gitea has started
AppStartTime time.Time
- // AppPath represents the path to the gitea binary
- AppPath string
- // AppWorkPath is the "working directory" of Gitea. It maps to the environment variable GITEA_WORK_DIR.
- // If that is not set it is the default set here by the linker or failing that the directory of AppPath.
- //
- // AppWorkPath is used as the base path for several other paths.
- AppWorkPath string
-
// Other global setting objects
CfgProvider ConfigProvider
- CustomPath string // Custom directory path
- CustomConf string
RunMode string
RunUser string
IsProd bool
@@ -51,62 +37,6 @@ var (
IsInTesting = false
)
-func getAppPath() (string, error) {
- var appPath string
- var err error
- if IsWindows && filepath.IsAbs(os.Args[0]) {
- appPath = filepath.Clean(os.Args[0])
- } else {
- appPath, err = exec.LookPath(os.Args[0])
- }
-
- if err != nil {
- if !errors.Is(err, exec.ErrDot) {
- return "", err
- }
- appPath, err = filepath.Abs(os.Args[0])
- }
- if err != nil {
- return "", err
- }
- appPath, err = filepath.Abs(appPath)
- if err != nil {
- return "", err
- }
- // Note: we don't use path.Dir here because it does not handle case
- // which path starts with two "/" in Windows: "//psf/Home/..."
- return strings.ReplaceAll(appPath, "\\", "/"), err
-}
-
-func getWorkPath(appPath string) string {
- workPath := AppWorkPath
-
- if giteaWorkPath, ok := os.LookupEnv("GITEA_WORK_DIR"); ok {
- workPath = giteaWorkPath
- }
- if len(workPath) == 0 {
- i := strings.LastIndex(appPath, "/")
- if i == -1 {
- workPath = appPath
- } else {
- workPath = appPath[:i]
- }
- }
- workPath = strings.ReplaceAll(workPath, "\\", "/")
- if !filepath.IsAbs(workPath) {
- log.Info("Provided work path %s is not absolute - will be made absolute against the current working directory", workPath)
-
- absPath, err := filepath.Abs(workPath)
- if err != nil {
- log.Error("Unable to absolute %s against the current working directory %v. Will absolute against the AppPath %s", workPath, err, appPath)
- workPath = filepath.Join(appPath, workPath)
- } else {
- workPath = absPath
- }
- }
- return strings.ReplaceAll(workPath, "\\", "/")
-}
-
func init() {
IsWindows = runtime.GOOS == "windows"
if AppVer == "" {
@@ -116,12 +46,6 @@ func init() {
// We can rely on log.CanColorStdout being set properly because modules/log/console_windows.go comes before modules/setting/setting.go lexicographically
// By default set this logger at Info - we'll change it later, but we need to start with something.
log.SetConsoleLogger(log.DEFAULT, "console", log.INFO)
-
- var err error
- if AppPath, err = getAppPath(); err != nil {
- log.Fatal("Failed to get app path: %v", err)
- }
- AppWorkPath = getWorkPath(AppPath)
}
// IsRunUserMatchCurrentUser returns false if configured run user does not match
@@ -137,36 +61,6 @@ func IsRunUserMatchCurrentUser(runUser string) (string, bool) {
return currentUser, runUser == currentUser
}
-// SetCustomPathAndConf will set CustomPath and CustomConf with reference to the
-// GITEA_CUSTOM environment variable and with provided overrides before stepping
-// back to the default
-func SetCustomPathAndConf(providedCustom, providedConf, providedWorkPath string) {
- if len(providedWorkPath) != 0 {
- AppWorkPath = filepath.ToSlash(providedWorkPath)
- }
- if giteaCustom, ok := os.LookupEnv("GITEA_CUSTOM"); ok {
- CustomPath = giteaCustom
- }
- if len(providedCustom) != 0 {
- CustomPath = providedCustom
- }
- if len(CustomPath) == 0 {
- CustomPath = path.Join(AppWorkPath, "custom")
- } else if !filepath.IsAbs(CustomPath) {
- CustomPath = path.Join(AppWorkPath, CustomPath)
- }
-
- if len(providedConf) != 0 {
- CustomConf = providedConf
- }
- if len(CustomConf) == 0 {
- CustomConf = path.Join(CustomPath, "conf/app.ini")
- } else if !filepath.IsAbs(CustomConf) {
- CustomConf = path.Join(CustomPath, CustomConf)
- log.Warn("Using 'custom' directory as relative origin for configuration file: '%s'", CustomConf)
- }
-}
-
// PrepareAppDataPath creates app data directory if necessary
func PrepareAppDataPath() error {
// FIXME: There are too many calls to MkdirAll in old code. It is incorrect.
@@ -196,20 +90,23 @@ func PrepareAppDataPath() error {
return nil
}
-func Init(opts *Options) {
- if opts.CustomConf == "" {
- opts.CustomConf = CustomConf
- }
+func InitCfgProvider(file string, extraConfigs ...string) {
var err error
- CfgProvider, err = NewConfigProviderFromFile(opts)
+ if CfgProvider, err = NewConfigProviderFromFile(file, extraConfigs...); err != nil {
+ log.Fatal("Unable to init config provider from %q: %v", file, err)
+ }
CfgProvider.DisableSaving() // do not allow saving the CfgProvider into file, it will be polluted by the "MustXxx" calls
- if err != nil {
- log.Fatal("newConfigProviderFromFile[%v]: %v", opts, err)
+}
+
+func MustInstalled() {
+ if !InstallLock {
+ log.Fatal(`Unable to load config file for a installed Gitea instance, you should either use "--config" to set your config file (app.ini), or run "gitea web" command to install Gitea.`)
}
- if !opts.DisableLoadCommonSettings {
- if err := loadCommonSettingsFrom(CfgProvider); err != nil {
- log.Fatal("loadCommonSettingsFrom[%v]: %v", opts, err)
- }
+}
+
+func LoadCommonSettings() {
+ if err := loadCommonSettingsFrom(CfgProvider); err != nil {
+ log.Fatal("Unable to load settings from config: %v", err)
}
}
diff --git a/modules/ssh/ssh.go b/modules/ssh/ssh.go
index 4bf57eafb7..923fa51d22 100644
--- a/modules/ssh/ssh.go
+++ b/modules/ssh/ssh.go
@@ -68,7 +68,7 @@ func sessionHandler(session ssh.Session) {
log.Trace("SSH: Payload: %v", command)
- args := []string{"serv", "key-" + keyID, "--config=" + setting.CustomConf}
+ args := []string{"--config=" + setting.CustomConf, "serv", "key-" + keyID}
log.Trace("SSH: Arguments: %v", args)
ctx, cancel := context.WithCancel(session.Context())
diff --git a/modules/testlogger/testlogger.go b/modules/testlogger/testlogger.go
index b4275e6005..6a0cee4a29 100644
--- a/modules/testlogger/testlogger.go
+++ b/modules/testlogger/testlogger.go
@@ -90,10 +90,11 @@ func (w *testLoggerWriterCloser) Reset() {
// PrintCurrentTest prints the current test to os.Stdout
func PrintCurrentTest(t testing.TB, skip ...int) func() {
+ t.Helper()
start := time.Now()
actualSkip := 1
if len(skip) > 0 {
- actualSkip = skip[0]
+ actualSkip = skip[0] + 1
}
_, filename, line, _ := runtime.Caller(actualSkip)
diff --git a/routers/init.go b/routers/init.go
index 54e8d2b8b3..ddbabcc397 100644
--- a/routers/init.go
+++ b/routers/init.go
@@ -28,7 +28,6 @@ import (
"code.gitea.io/gitea/modules/system"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/translation"
- "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
actions_router "code.gitea.io/gitea/routers/api/actions"
packages_router "code.gitea.io/gitea/routers/api/packages"
@@ -101,21 +100,16 @@ func syncAppConfForGit(ctx context.Context) error {
return nil
}
-// GlobalInitInstalled is for global installed configuration.
-func GlobalInitInstalled(ctx context.Context) {
- if !setting.InstallLock {
- log.Fatal("Gitea is not installed")
- }
+func InitWebInstallPage(ctx context.Context) {
+ translation.InitLocales(ctx)
+ setting.LoadSettingsForInstall()
+ mustInit(svg.Init)
+}
+// InitWebInstalled is for global installed configuration.
+func InitWebInstalled(ctx context.Context) {
mustInitCtx(ctx, git.InitFull)
- log.Info("Gitea Version: %s%s", setting.AppVer, setting.AppBuiltWith)
- log.Info("Git Version: %s (home: %s)", git.VersionInfo(), git.HomeDir())
- log.Info("AppPath: %s", setting.AppPath)
- log.Info("AppWorkPath: %s", setting.AppWorkPath)
- log.Info("Custom path: %s", setting.CustomPath)
- log.Info("Log path: %s", setting.Log.RootPath)
- log.Info("Configuration file: %s", setting.CustomConf)
- log.Info("Run Mode: %s", util.ToTitleCase(setting.RunMode))
+ log.Info("Git version: %s (home: %s)", git.VersionInfo(), git.HomeDir())
// Setup i18n
translation.InitLocales(ctx)
diff --git a/routers/install/install.go b/routers/install/install.go
index 16bb55b685..c94a30b89f 100644
--- a/routers/install/install.go
+++ b/routers/install/install.go
@@ -32,6 +32,7 @@ import (
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/modules/web/middleware"
+ "code.gitea.io/gitea/routers/common"
"code.gitea.io/gitea/services/forms"
"gitea.com/go-chi/session"
@@ -370,11 +371,16 @@ func SubmitInstall(ctx *context.Context) {
}
// Save settings.
- cfg, err := setting.NewConfigProviderFromFile(&setting.Options{CustomConf: setting.CustomConf, AllowEmpty: true})
+ cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf)
if err != nil {
log.Error("Failed to load custom conf '%s': %v", setting.CustomConf, err)
}
+ cfg.Section("").Key("APP_NAME").SetValue(form.AppName)
+ cfg.Section("").Key("RUN_USER").SetValue(form.RunUser)
+ cfg.Section("").Key("WORK_PATH").SetValue(setting.AppWorkPath)
+ cfg.Section("").Key("RUN_MODE").SetValue("prod")
+
cfg.Section("database").Key("DB_TYPE").SetValue(setting.Database.Type.String())
cfg.Section("database").Key("HOST").SetValue(setting.Database.Host)
cfg.Section("database").Key("NAME").SetValue(setting.Database.Name)
@@ -386,9 +392,7 @@ func SubmitInstall(ctx *context.Context) {
cfg.Section("database").Key("PATH").SetValue(setting.Database.Path)
cfg.Section("database").Key("LOG_SQL").SetValue("false") // LOG_SQL is rarely helpful
- cfg.Section("").Key("APP_NAME").SetValue(form.AppName)
cfg.Section("repository").Key("ROOT").SetValue(form.RepoRootPath)
- cfg.Section("").Key("RUN_USER").SetValue(form.RunUser)
cfg.Section("server").Key("SSH_DOMAIN").SetValue(form.Domain)
cfg.Section("server").Key("DOMAIN").SetValue(form.Domain)
cfg.Section("server").Key("HTTP_PORT").SetValue(form.HTTPPort)
@@ -450,8 +454,6 @@ func SubmitInstall(ctx *context.Context) {
cfg.Section("service").Key("NO_REPLY_ADDRESS").SetValue(fmt.Sprint(form.NoReplyAddress))
cfg.Section("cron.update_checker").Key("ENABLED").SetValue(fmt.Sprint(form.EnableUpdateChecker))
- cfg.Section("").Key("RUN_MODE").SetValue("prod")
-
cfg.Section("session").Key("PROVIDER").SetValue("file")
cfg.Section("log").Key("MODE").MustString("console")
@@ -514,7 +516,13 @@ func SubmitInstall(ctx *context.Context) {
// ---- All checks are passed
// Reload settings (and re-initialize database connection)
- reloadSettings(ctx)
+ setting.InitCfgProvider(setting.CustomConf)
+ setting.LoadCommonSettings()
+ setting.MustInstalled()
+ setting.LoadDBSetting()
+ if err := common.InitDBEngine(ctx); err != nil {
+ log.Fatal("ORM engine initialization failed: %v", err)
+ }
// Create admin account
if len(form.AdminName) > 0 {
diff --git a/routers/install/setting.go b/routers/install/setting.go
deleted file mode 100644
index c14843d8ee..0000000000
--- a/routers/install/setting.go
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2021 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package install
-
-import (
- "context"
-
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/svg"
- "code.gitea.io/gitea/modules/translation"
- "code.gitea.io/gitea/routers/common"
-)
-
-// PreloadSettings preloads the configuration to check if we need to run install
-func PreloadSettings(ctx context.Context) bool {
- setting.Init(&setting.Options{
- AllowEmpty: true,
- })
- if !setting.InstallLock {
- log.Info("AppPath: %s", setting.AppPath)
- log.Info("AppWorkPath: %s", setting.AppWorkPath)
- log.Info("Custom path: %s", setting.CustomPath)
- log.Info("Log path: %s", setting.Log.RootPath)
- log.Info("Configuration file: %s", setting.CustomConf)
- log.Info("Prepare to run install page")
- translation.InitLocales(ctx)
- if setting.EnableSQLite3 {
- log.Info("SQLite3 is supported")
- }
-
- setting.LoadSettingsForInstall()
- _ = svg.Init()
- }
-
- return !setting.InstallLock
-}
-
-// reloadSettings reloads the existing settings and starts up the database
-func reloadSettings(ctx context.Context) {
- setting.Init(&setting.Options{})
- setting.LoadDBSetting()
- if setting.InstallLock {
- if err := common.InitDBEngine(ctx); err == nil {
- log.Info("ORM engine initialization successful!")
- } else {
- log.Fatal("ORM engine initialization failed: %v", err)
- }
- }
-}
diff --git a/routers/web/admin/config.go b/routers/web/admin/config.go
index be662c22ef..2c6989a71d 100644
--- a/routers/web/admin/config.go
+++ b/routers/web/admin/config.go
@@ -8,7 +8,6 @@ import (
"fmt"
"net/http"
"net/url"
- "os"
"strconv"
"strings"
@@ -167,20 +166,6 @@ func Config(ctx *context.Context) {
ctx.Data["SessionConfig"] = sessionCfg
ctx.Data["Git"] = setting.Git
-
- type envVar struct {
- Name, Value string
- }
-
- envVars := map[string]*envVar{}
- if len(os.Getenv("GITEA_WORK_DIR")) > 0 {
- envVars["GITEA_WORK_DIR"] = &envVar{"GITEA_WORK_DIR", os.Getenv("GITEA_WORK_DIR")}
- }
- if len(os.Getenv("GITEA_CUSTOM")) > 0 {
- envVars["GITEA_CUSTOM"] = &envVar{"GITEA_CUSTOM", os.Getenv("GITEA_CUSTOM")}
- }
-
- ctx.Data["EnvVars"] = envVars
ctx.Data["AccessLogTemplate"] = setting.Log.AccessLogTemplate
ctx.Data["LogSQL"] = setting.Database.LogSQL
diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl
index 2850cc8d37..2ddc0c1ac6 100644
--- a/templates/admin/config.tmpl
+++ b/templates/admin/config.tmpl
@@ -46,15 +46,6 @@
<dd>{{.ScriptType}}</dd>
<dt>{{.locale.Tr "admin.config.reverse_auth_user"}}</dt>
<dd>{{.ReverseProxyAuthUser}}</dd>
-
- {{if .EnvVars}}
- <div class="ui divider"></div>
- {{range .EnvVars}}
- <dt>{{.Name}}</dt>
- <dd>{{.Value}}</dd>
- {{end}}
- {{end}}
-
</dl>
</div>
diff --git a/tests/test_utils.go b/tests/test_utils.go
index 5540d92e92..bf7d1b3fbe 100644
--- a/tests/test_utils.go
+++ b/tests/test_utils.go
@@ -42,7 +42,10 @@ func InitTest(requireGitea bool) {
if giteaRoot == "" {
exitf("Environment variable $GITEA_ROOT not set")
}
+
+ setting.IsInTesting = true
setting.AppWorkPath = giteaRoot
+ setting.CustomPath = filepath.Join(setting.AppWorkPath, "custom")
if requireGitea {
giteaBinary := "gitea"
if setting.IsWindows {
@@ -53,7 +56,6 @@ func InitTest(requireGitea bool) {
exitf("Could not find gitea binary at %s", setting.AppPath)
}
}
-
giteaConf := os.Getenv("GITEA_CONF")
if giteaConf == "" {
// By default, use sqlite.ini for testing, then IDE like GoLand can start the test process with debugger.
@@ -66,16 +68,12 @@ func InitTest(requireGitea bool) {
exitf(`sqlite3 requires: import _ "github.com/mattn/go-sqlite3" or -tags sqlite,sqlite_unlock_notify`)
}
}
-
- setting.IsInTesting = true
-
if !path.IsAbs(giteaConf) {
- setting.CustomConf = path.Join(giteaRoot, giteaConf)
+ setting.CustomConf = filepath.Join(giteaRoot, giteaConf)
} else {
setting.CustomConf = giteaConf
}
- setting.SetCustomPathAndConf("", "", "")
unittest.InitSettings()
setting.Repository.DefaultBranch = "master" // many test code still assume that default branch is called "master"
_ = util.RemoveAll(repo_module.LocalCopyPath())
@@ -175,7 +173,7 @@ func InitTest(requireGitea bool) {
defer db.Close()
}
- routers.GlobalInitInstalled(graceful.GetManager().HammerContext())
+ routers.InitWebInstalled(graceful.GetManager().HammerContext())
}
func PrepareTestEnv(t testing.TB, skip ...int) func() {
@@ -240,10 +238,12 @@ func PrepareTestEnv(t testing.TB, skip ...int) func() {
}
func PrintCurrentTest(t testing.TB, skip ...int) func() {
- if len(skip) == 1 {
- skip = []int{skip[0] + 1}
+ t.Helper()
+ actualSkip := 1
+ if len(skip) > 0 {
+ actualSkip = skip[0] + 1
}
- return testlogger.PrintCurrentTest(t, skip...)
+ return testlogger.PrintCurrentTest(t, actualSkip)
}
// Printf takes a format and args and prints the string to os.Stdout