Refactor path & config system (#25330)
# The problem There were many "path tricks": * By default, Gitea uses its program directory as its work path * Gitea tries to use the "work path" to guess its "custom path" and "custom conf (app.ini)" * Users might want to use other directories as work path * The non-default work path should be passed to Gitea by GITEA_WORK_DIR or "--work-path" * But some Gitea processes are started without these values * The "serv" process started by OpenSSH server * The CLI sub-commands started by site admin * The paths are guessed by SetCustomPathAndConf again and again * The default values of "work path / custom path / custom conf" can be changed when compiling # The solution * Use `InitWorkPathAndCommonConfig` to handle these path tricks, and use test code to cover its behaviors. * When Gitea's web server runs, write the WORK_PATH to "app.ini", this value must be the most correct one, because if this value is not right, users would find that the web UI doesn't work and then they should be able to fix it. * Then all other sub-commands can use the WORK_PATH in app.ini to initialize their paths. * By the way, when Gitea starts for git protocol, it shouldn't output any log, otherwise the git protocol gets broken and client blocks forever. The "work path" priority is: WORK_PATH in app.ini > cmd arg --work-path > env var GITEA_WORK_DIR > builtin default The "app.ini" searching order is: cmd arg --config > cmd arg "work path / custom path" > env var "work path / custom path" > builtin default ## ⚠️ BREAKING If your instance's "work path / custom path / custom conf" doesn't meet the requirements (eg: work path must be absolute), Gitea will report a fatal error and exit. You need to set these values according to the error log. ---- Close #24818 Close #24222 Close #21606 Close #21498 Close #25107 Close #24981 Maybe close #24503 Replace #23301 Replace #22754 And maybe more
This commit is contained in:
parent
1454f9dafc
commit
2cdf260f42
|
@ -53,8 +53,6 @@ cpu.out
|
||||||
/bin
|
/bin
|
||||||
/dist
|
/dist
|
||||||
/custom/*
|
/custom/*
|
||||||
!/custom/conf
|
|
||||||
/custom/conf/*
|
|
||||||
!/custom/conf/app.example.ini
|
!/custom/conf/app.example.ini
|
||||||
/data
|
/data
|
||||||
/indexers
|
/indexers
|
||||||
|
|
|
@ -42,7 +42,7 @@ func runGenerateActionsRunnerToken(c *cli.Context) error {
|
||||||
ctx, cancel := installSignals()
|
ctx, cancel := installSignals()
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
setting.Init(&setting.Options{})
|
setting.MustInstalled()
|
||||||
|
|
||||||
scope := c.String("scope")
|
scope := c.String("scope")
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,7 @@ func confirm() (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func initDB(ctx context.Context) error {
|
func initDB(ctx context.Context) error {
|
||||||
setting.Init(&setting.Options{})
|
setting.MustInstalled()
|
||||||
setting.LoadDBSetting()
|
setting.LoadDBSetting()
|
||||||
setting.InitSQLLoggersForCli(log.INFO)
|
setting.InitSQLLoggersForCli(log.INFO)
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,7 @@ func runRecreateTable(ctx *cli.Context) error {
|
||||||
golog.SetOutput(log.LoggerToWriter(log.GetLogger(log.DEFAULT).Info))
|
golog.SetOutput(log.LoggerToWriter(log.GetLogger(log.DEFAULT).Info))
|
||||||
|
|
||||||
debug := ctx.Bool("debug")
|
debug := ctx.Bool("debug")
|
||||||
setting.Init(&setting.Options{})
|
setting.MustInstalled()
|
||||||
setting.LoadDBSetting()
|
setting.LoadDBSetting()
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
|
|
|
@ -182,7 +182,7 @@ func runDump(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
fileName += "." + outType
|
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
|
// make sure we are logging to the console no matter what the configuration tells us do to
|
||||||
// FIXME: don't use CfgProvider directly
|
// FIXME: don't use CfgProvider directly
|
||||||
|
|
|
@ -99,11 +99,6 @@ type assetFile struct {
|
||||||
func initEmbeddedExtractor(c *cli.Context) error {
|
func initEmbeddedExtractor(c *cli.Context) error {
|
||||||
setupConsoleLogger(log.ERROR, log.CanColorStderr, os.Stderr)
|
setupConsoleLogger(log.ERROR, log.CanColorStderr, os.Stderr)
|
||||||
|
|
||||||
// Read configuration file
|
|
||||||
setting.Init(&setting.Options{
|
|
||||||
AllowEmpty: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
patterns, err := compileCollectPatterns(c.Args())
|
patterns, err := compileCollectPatterns(c.Args())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -16,7 +16,7 @@ func runSendMail(c *cli.Context) error {
|
||||||
ctx, cancel := installSignals()
|
ctx, cancel := installSignals()
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
setting.Init(&setting.Options{})
|
setting.MustInstalled()
|
||||||
|
|
||||||
if err := argsSet(c, "title"); err != nil {
|
if err := argsSet(c, "title"); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -51,7 +51,7 @@ func runRestoreRepository(c *cli.Context) error {
|
||||||
ctx, cancel := installSignals()
|
ctx, cancel := installSignals()
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
setting.Init(&setting.Options{})
|
setting.MustInstalled()
|
||||||
var units []string
|
var units []string
|
||||||
if s := c.String("units"); s != "" {
|
if s := c.String("units"); s != "" {
|
||||||
units = strings.Split(s, ",")
|
units = strings.Split(s, ",")
|
||||||
|
|
|
@ -61,7 +61,7 @@ func setup(ctx context.Context, debug bool) {
|
||||||
} else {
|
} else {
|
||||||
setupConsoleLogger(log.FATAL, false, os.Stderr)
|
setupConsoleLogger(log.FATAL, false, os.Stderr)
|
||||||
}
|
}
|
||||||
setting.Init(&setting.Options{})
|
setting.MustInstalled()
|
||||||
if debug {
|
if debug {
|
||||||
setting.RunMode = "dev"
|
setting.RunMode = "dev"
|
||||||
}
|
}
|
||||||
|
|
168
cmd/web.go
168
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 {
|
func runWeb(ctx *cli.Context) error {
|
||||||
if ctx.Bool("verbose") {
|
if ctx.Bool("verbose") {
|
||||||
setupConsoleLogger(log.TRACE, log.CanColorStdout, os.Stdout)
|
setupConsoleLogger(log.TRACE, log.CanColorStdout, os.Stdout)
|
||||||
|
@ -128,75 +232,19 @@ func runWeb(ctx *cli.Context) error {
|
||||||
createPIDFile(ctx.String("pid"))
|
createPIDFile(ctx.String("pid"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform pre-initialization
|
if !setting.InstallLock {
|
||||||
needsInstall := install.PreloadSettings(graceful.GetManager().HammerContext())
|
if err := serveInstall(ctx); err != nil {
|
||||||
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
|
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:
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
NoInstallListener()
|
NoInstallListener()
|
||||||
}
|
}
|
||||||
|
|
||||||
if setting.EnablePprof {
|
if setting.EnablePprof {
|
||||||
go func() {
|
go 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()
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Global init")
|
return serveInstalled(ctx)
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setPort(port string) error {
|
func setPort(port string) error {
|
||||||
|
|
|
@ -81,8 +81,6 @@ func main() {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
app.Action = runEnvironmentToIni
|
app.Action = runEnvironmentToIni
|
||||||
setting.SetCustomPathAndConf("", "", "")
|
|
||||||
|
|
||||||
err := app.Run(os.Args)
|
err := app.Run(os.Args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Failed to run app with %s: %v", os.Args, err)
|
log.Fatal("Failed to run app with %s: %v", os.Args, err)
|
||||||
|
@ -90,12 +88,13 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func runEnvironmentToIni(c *cli.Context) error {
|
func runEnvironmentToIni(c *cli.Context) error {
|
||||||
providedCustom := c.String("custom-path")
|
setting.InitWorkPathAndCommonConfig(os.Getenv, setting.ArgWorkPathAndCustomConf{
|
||||||
providedConf := c.String("config")
|
WorkPath: c.String("work-path"),
|
||||||
providedWorkPath := c.String("work-path")
|
CustomPath: c.String("custom-path"),
|
||||||
setting.SetCustomPathAndConf(providedCustom, providedConf, providedWorkPath)
|
CustomConf: c.String("config"),
|
||||||
|
})
|
||||||
|
|
||||||
cfg, err := setting.NewConfigProviderFromFile(&setting.Options{CustomConf: setting.CustomConf, AllowEmpty: true})
|
cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Failed to load custom conf '%s': %v", setting.CustomConf, err)
|
log.Fatal("Failed to load custom conf '%s': %v", setting.CustomConf, err)
|
||||||
}
|
}
|
||||||
|
|
167
main.go
167
main.go
|
@ -33,30 +33,58 @@ var (
|
||||||
Tags = ""
|
Tags = ""
|
||||||
// MakeVersion holds the current Make version if built with make
|
// MakeVersion holds the current Make version if built with make
|
||||||
MakeVersion = ""
|
MakeVersion = ""
|
||||||
|
|
||||||
originalAppHelpTemplate = ""
|
|
||||||
originalCommandHelpTemplate = ""
|
|
||||||
originalSubcommandHelpTemplate = ""
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
setting.AppVer = Version
|
setting.AppVer = Version
|
||||||
setting.AppBuiltWith = formatBuiltWith()
|
setting.AppBuiltWith = formatBuiltWith()
|
||||||
setting.AppStartTime = time.Now().UTC()
|
setting.AppStartTime = time.Now().UTC()
|
||||||
|
}
|
||||||
|
|
||||||
// Grab the original help templates
|
// cmdHelp is our own help subcommand with more information
|
||||||
originalAppHelpTemplate = cli.AppHelpTemplate
|
// test cases:
|
||||||
originalCommandHelpTemplate = cli.CommandHelpTemplate
|
// ./gitea help
|
||||||
originalSubcommandHelpTemplate = cli.SubcommandHelpTemplate
|
// ./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() {
|
func main() {
|
||||||
app := cli.NewApp()
|
app := cli.NewApp()
|
||||||
app.Name = "Gitea"
|
app.Name = "Gitea"
|
||||||
app.Usage = "A painless self-hosted Git service"
|
app.Usage = "A painless self-hosted Git service"
|
||||||
app.Description = `By default, gitea will start serving using the webserver with no
|
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".`
|
||||||
arguments - which can alternatively be run by running the subcommand web.`
|
|
||||||
app.Version = Version + formatBuiltWith()
|
app.Version = Version + formatBuiltWith()
|
||||||
|
app.EnableBashCompletion = true
|
||||||
app.Commands = []cli.Command{
|
app.Commands = []cli.Command{
|
||||||
cmd.CmdWeb,
|
cmd.CmdWeb,
|
||||||
cmd.CmdServ,
|
cmd.CmdServ,
|
||||||
|
@ -77,118 +105,83 @@ arguments - which can alternatively be run by running the subcommand web.`
|
||||||
cmd.CmdRestoreRepository,
|
cmd.CmdRestoreRepository,
|
||||||
cmd.CmdActions,
|
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
|
// default configuration flags
|
||||||
defaultFlags := []cli.Flag{
|
globalFlags := []cli.Flag{
|
||||||
|
cli.HelpFlag,
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "custom-path, C",
|
Name: "custom-path, C",
|
||||||
Value: setting.CustomPath,
|
Usage: "Set custom path (defaults to '{WorkPath}/custom')",
|
||||||
Usage: "Custom path file path",
|
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "config, c",
|
Name: "config, c",
|
||||||
Value: setting.CustomConf,
|
Value: setting.CustomConf,
|
||||||
Usage: "Custom configuration file path",
|
Usage: "Set custom config file (defaults to '{WorkPath}/custom/conf/app.ini')",
|
||||||
},
|
},
|
||||||
cli.VersionFlag,
|
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "work-path, w",
|
Name: "work-path, w",
|
||||||
Value: setting.AppWorkPath,
|
Usage: "Set Gitea's working path (defaults to the Gitea's binary directory)",
|
||||||
Usage: "Set the gitea working path",
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the default to be equivalent to cmdWeb and add the default flags
|
// 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, cmd.CmdWeb.Flags...)
|
||||||
app.Flags = append(app.Flags, defaultFlags...)
|
app.Action = prepareWorkPathAndCustomConf(cmd.CmdWeb.Action)
|
||||||
app.Action = 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)
|
||||||
// Add functions to set these paths and these flags to the commands
|
|
||||||
app.Before = establishCustomPath
|
|
||||||
for i := range app.Commands {
|
for i := range app.Commands {
|
||||||
setFlagsAndBeforeOnSubcommands(&app.Commands[i], defaultFlags, establishCustomPath)
|
prepareSubcommands(&app.Commands[i], globalFlags)
|
||||||
}
|
}
|
||||||
|
|
||||||
app.EnableBashCompletion = true
|
|
||||||
|
|
||||||
err := app.Run(os.Args)
|
err := app.Run(os.Args)
|
||||||
if err != nil {
|
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()
|
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.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 {
|
for i := range command.Subcommands {
|
||||||
setFlagsAndBeforeOnSubcommands(&command.Subcommands[i], defaultFlags, before)
|
prepareSubcommands(&command.Subcommands[i], defaultFlags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func establishCustomPath(ctx *cli.Context) error {
|
// prepareWorkPathAndCustomConf wraps the Action to prepare the work path and custom config
|
||||||
var providedCustom string
|
// 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
|
||||||
var providedConf string
|
func prepareWorkPathAndCustomConf(a any) func(ctx *cli.Context) error {
|
||||||
var providedWorkPath string
|
if a == nil {
|
||||||
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
if currentCtx.IsSet("work-path") && len(providedWorkPath) == 0 {
|
|
||||||
providedWorkPath = currentCtx.String("work-path")
|
|
||||||
}
|
|
||||||
currentCtx = currentCtx.Parent()
|
|
||||||
|
|
||||||
}
|
|
||||||
setting.SetCustomPathAndConf(providedCustom, providedConf, providedWorkPath)
|
|
||||||
|
|
||||||
setAppHelpTemplates()
|
|
||||||
|
|
||||||
if ctx.IsSet("version") {
|
|
||||||
cli.ShowVersion(ctx)
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
action := a.(func(*cli.Context) error)
|
||||||
func setAppHelpTemplates() {
|
return func(ctx *cli.Context) error {
|
||||||
cli.AppHelpTemplate = adjustHelpTemplate(originalAppHelpTemplate)
|
var args setting.ArgWorkPathAndCustomConf
|
||||||
cli.CommandHelpTemplate = adjustHelpTemplate(originalCommandHelpTemplate)
|
curCtx := ctx
|
||||||
cli.SubcommandHelpTemplate = adjustHelpTemplate(originalSubcommandHelpTemplate)
|
for curCtx != nil {
|
||||||
|
if curCtx.IsSet("work-path") && args.WorkPath == "" {
|
||||||
|
args.WorkPath = curCtx.String("work-path")
|
||||||
}
|
}
|
||||||
|
if curCtx.IsSet("custom-path") && args.CustomPath == "" {
|
||||||
func adjustHelpTemplate(originalTemplate string) string {
|
args.CustomPath = curCtx.String("custom-path")
|
||||||
overridden := ""
|
}
|
||||||
if _, ok := os.LookupEnv("GITEA_CUSTOM"); ok {
|
if curCtx.IsSet("config") && args.CustomConf == "" {
|
||||||
overridden = "(GITEA_CUSTOM)"
|
args.CustomConf = curCtx.String("config")
|
||||||
|
}
|
||||||
|
curCtx = curCtx.Parent()
|
||||||
|
}
|
||||||
|
setting.InitWorkPathAndCommonConfig(os.Getenv, args)
|
||||||
|
if ctx.Bool("help") {
|
||||||
|
return cmdHelp.Action.(func(ctx *cli.Context) error)(ctx)
|
||||||
|
}
|
||||||
|
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 {
|
func formatBuiltWith() string {
|
||||||
|
|
|
@ -147,9 +147,9 @@ func MainTest(m *testing.M) {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setting.CustomPath = filepath.Join(setting.AppWorkPath, "custom")
|
||||||
setting.AppDataPath = tmpDataPath
|
setting.AppDataPath = tmpDataPath
|
||||||
|
|
||||||
setting.SetCustomPathAndConf("", "", "")
|
|
||||||
unittest.InitSettings()
|
unittest.InitSettings()
|
||||||
if err = git.InitFull(context.Background()); err != nil {
|
if err = git.InitFull(context.Background()); err != nil {
|
||||||
fmt.Printf("Unable to InitFull: %v\n", err)
|
fmt.Printf("Unable to InitFull: %v\n", err)
|
||||||
|
|
|
@ -42,12 +42,14 @@ func fatalTestError(fmtStr string, args ...interface{}) {
|
||||||
os.Exit(1)
|
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) {
|
func InitSettings(extraConfigs ...string) {
|
||||||
setting.Init(&setting.Options{
|
if setting.CustomConf == "" {
|
||||||
AllowEmpty: true,
|
setting.CustomConf = filepath.Join(setting.CustomPath, "conf/app-unittest-tmp.ini")
|
||||||
ExtraConfig: strings.Join(extraConfigs, "\n"),
|
_ = os.Remove(setting.CustomConf)
|
||||||
})
|
}
|
||||||
|
setting.InitCfgProvider(setting.CustomConf, strings.Join(extraConfigs, "\n"))
|
||||||
|
setting.LoadCommonSettings()
|
||||||
|
|
||||||
if err := setting.PrepareAppDataPath(); err != nil {
|
if err := setting.PrepareAppDataPath(); err != nil {
|
||||||
log.Fatalf("Can not prepare APP_DATA_PATH: %v", err)
|
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
|
// MainTest a reusable TestMain(..) function for unit tests that need to use a
|
||||||
// test database. Creates the test database, and sets necessary settings.
|
// test database. Creates the test database, and sets necessary settings.
|
||||||
func MainTest(m *testing.M, testOpts *TestOptions) {
|
func MainTest(m *testing.M, testOpts *TestOptions) {
|
||||||
setting.SetCustomPathAndConf("", "", "")
|
setting.CustomPath = filepath.Join(testOpts.GiteaRootPath, "custom")
|
||||||
InitSettings()
|
InitSettings()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
|
@ -28,7 +28,7 @@ type Check struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func initDBSkipLogger(ctx context.Context) error {
|
func initDBSkipLogger(ctx context.Context) error {
|
||||||
setting.Init(&setting.Options{})
|
setting.MustInstalled()
|
||||||
setting.LoadDBSetting()
|
setting.LoadDBSetting()
|
||||||
if err := db.InitEngine(ctx); err != nil {
|
if err := db.InitEngine(ctx); err != nil {
|
||||||
return fmt.Errorf("db.InitEngine: %w", err)
|
return fmt.Errorf("db.InitEngine: %w", err)
|
||||||
|
|
|
@ -66,7 +66,7 @@ func checkConfigurationFiles(ctx context.Context, logger log.Logger, autofix boo
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
setting.Init(&setting.Options{})
|
setting.MustInstalled()
|
||||||
|
|
||||||
configurationFiles := []configurationFile{
|
configurationFiles := []configurationFile{
|
||||||
{"Configuration File Path", setting.CustomConf, false, true, false},
|
{"Configuration File Path", setting.CustomConf, false, true, false},
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/unittest"
|
||||||
"code.gitea.io/gitea/modules/emoji"
|
"code.gitea.io/gitea/modules/emoji"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
@ -28,9 +29,7 @@ var localMetas = map[string]string{
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
setting.Init(&setting.Options{
|
unittest.InitSettings()
|
||||||
AllowEmpty: true,
|
|
||||||
})
|
|
||||||
if err := git.InitSimple(context.Background()); err != nil {
|
if err := git.InitSimple(context.Background()); err != nil {
|
||||||
log.Fatal("git init failed, err: %v", err)
|
log.Fatal("git init failed, err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/unittest"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/markup"
|
"code.gitea.io/gitea/modules/markup"
|
||||||
|
@ -33,9 +34,7 @@ var localMetas = map[string]string{
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
setting.Init(&setting.Options{
|
unittest.InitSettings()
|
||||||
AllowEmpty: true,
|
|
||||||
})
|
|
||||||
if err := git.InitSimple(context.Background()); err != nil {
|
if err := git.InitSimple(context.Background()); err != nil {
|
||||||
log.Fatal("git init failed, err: %v", err)
|
log.Fatal("git init failed, err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,15 +55,15 @@ type ConfigProvider interface {
|
||||||
|
|
||||||
DisableSaving()
|
DisableSaving()
|
||||||
PrepareSaving() (ConfigProvider, error)
|
PrepareSaving() (ConfigProvider, error)
|
||||||
|
IsLoadedFromEmpty() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type iniConfigProvider struct {
|
type iniConfigProvider struct {
|
||||||
opts *Options
|
file string
|
||||||
ini *ini.File
|
ini *ini.File
|
||||||
|
|
||||||
disableSaving bool
|
disableSaving bool // disable the "Save" method because the config options could be polluted
|
||||||
|
loadedFromEmpty bool // whether the file has not existed previously
|
||||||
newFile bool // whether the file has not existed previously
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type iniConfigSection struct {
|
type iniConfigSection struct {
|
||||||
|
@ -183,52 +183,42 @@ func NewConfigProviderFromData(configContent string) (ConfigProvider, error) {
|
||||||
cfg.NameMapper = ini.SnackCase
|
cfg.NameMapper = ini.SnackCase
|
||||||
return &iniConfigProvider{
|
return &iniConfigProvider{
|
||||||
ini: cfg,
|
ini: cfg,
|
||||||
newFile: true,
|
loadedFromEmpty: true,
|
||||||
}, nil
|
}, 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.
|
// NewConfigProviderFromFile load configuration from file.
|
||||||
// NOTE: do not print any log except error.
|
// 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: " = "})
|
cfg := ini.Empty(ini.LoadOptions{KeyValueDelimiterOnWrite: " = "})
|
||||||
newFile := true
|
loadedFromEmpty := true
|
||||||
|
|
||||||
if opts.CustomConf != "" {
|
if file != "" {
|
||||||
isFile, err := util.IsFile(opts.CustomConf)
|
isFile, err := util.IsFile(file)
|
||||||
if err != nil {
|
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 isFile {
|
||||||
if err := cfg.Append(opts.CustomConf); err != nil {
|
if err = cfg.Append(file); err != nil {
|
||||||
return nil, fmt.Errorf("failed to load custom conf '%s': %v", opts.CustomConf, err)
|
return nil, fmt.Errorf("failed to load config file %q: %v", file, err)
|
||||||
}
|
}
|
||||||
newFile = false
|
loadedFromEmpty = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if newFile && !opts.AllowEmpty {
|
if len(extraConfigs) > 0 {
|
||||||
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)
|
for _, s := range extraConfigs {
|
||||||
}
|
if err := cfg.Append([]byte(s)); err != nil {
|
||||||
|
|
||||||
if opts.ExtraConfig != "" {
|
|
||||||
if err := cfg.Append([]byte(opts.ExtraConfig)); err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to append more config: %v", err)
|
return nil, fmt.Errorf("unable to append more config: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cfg.NameMapper = ini.SnackCase
|
cfg.NameMapper = ini.SnackCase
|
||||||
return &iniConfigProvider{
|
return &iniConfigProvider{
|
||||||
opts: opts,
|
file: file,
|
||||||
ini: cfg,
|
ini: cfg,
|
||||||
newFile: newFile,
|
loadedFromEmpty: loadedFromEmpty,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,20 +256,17 @@ func (p *iniConfigProvider) Save() error {
|
||||||
if p.disableSaving {
|
if p.disableSaving {
|
||||||
return errDisableSaving
|
return errDisableSaving
|
||||||
}
|
}
|
||||||
filename := p.opts.CustomConf
|
filename := p.file
|
||||||
if filename == "" {
|
if filename == "" {
|
||||||
if !p.opts.AllowEmpty {
|
return fmt.Errorf("config file path must not be empty")
|
||||||
return fmt.Errorf("custom config path must not be empty")
|
|
||||||
}
|
}
|
||||||
return nil
|
if p.loadedFromEmpty {
|
||||||
}
|
|
||||||
if p.newFile {
|
|
||||||
if err := os.MkdirAll(filepath.Dir(filename), os.ModePerm); err != nil {
|
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 {
|
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
|
// Change permissions to be more restrictive
|
||||||
|
@ -313,11 +300,14 @@ func (p *iniConfigProvider) DisableSaving() {
|
||||||
// it makes the "Save" outputs a lot of garbage options
|
// it makes the "Save" outputs a lot of garbage options
|
||||||
// After the INI package gets refactored, no "MustXxx" pollution, this workaround can be dropped.
|
// After the INI package gets refactored, no "MustXxx" pollution, this workaround can be dropped.
|
||||||
func (p *iniConfigProvider) PrepareSaving() (ConfigProvider, error) {
|
func (p *iniConfigProvider) PrepareSaving() (ConfigProvider, error) {
|
||||||
cfgFile := p.opts.CustomConf
|
if p.file == "" {
|
||||||
if cfgFile == "" {
|
|
||||||
return nil, errors.New("no config file to save")
|
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) {
|
func mustMapSetting(rootCfg ConfigProvider, sectionName string, setting any) {
|
||||||
|
@ -357,7 +347,7 @@ func NewConfigProviderForLocale(source any, others ...any) (ConfigProvider, erro
|
||||||
iniFile.BlockMode = false
|
iniFile.BlockMode = false
|
||||||
return &iniConfigProvider{
|
return &iniConfigProvider{
|
||||||
ini: iniFile,
|
ini: iniFile,
|
||||||
newFile: true,
|
loadedFromEmpty: true,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,13 +67,14 @@ key = 123
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewConfigProviderFromFile(t *testing.T) {
|
func TestNewConfigProviderFromFile(t *testing.T) {
|
||||||
_, err := NewConfigProviderFromFile(&Options{CustomConf: "no-such.ini", AllowEmpty: false})
|
cfg, err := NewConfigProviderFromFile("no-such.ini")
|
||||||
assert.ErrorContains(t, err, "unable to find configuration file")
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, cfg.IsLoadedFromEmpty())
|
||||||
|
|
||||||
// load non-existing file and save
|
// load non-existing file and save
|
||||||
testFile := t.TempDir() + "/test.ini"
|
testFile := t.TempDir() + "/test.ini"
|
||||||
testFile1 := t.TempDir() + "/test1.ini"
|
testFile1 := t.TempDir() + "/test1.ini"
|
||||||
cfg, err := NewConfigProviderFromFile(&Options{CustomConf: testFile, AllowEmpty: true})
|
cfg, err = NewConfigProviderFromFile(testFile)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
sec, _ := cfg.NewSection("foo")
|
sec, _ := cfg.NewSection("foo")
|
||||||
|
@ -91,7 +92,7 @@ func TestNewConfigProviderFromFile(t *testing.T) {
|
||||||
assert.Equal(t, "[foo]\nk1 = a\nk2 = b\n", string(bs))
|
assert.Equal(t, "[foo]\nk1 = a\nk2 = b\n", string(bs))
|
||||||
|
|
||||||
// load existing file and save
|
// load existing file and save
|
||||||
cfg, err = NewConfigProviderFromFile(&Options{CustomConf: testFile, AllowEmpty: true})
|
cfg, err = NewConfigProviderFromFile(testFile)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "a", cfg.Section("foo").Key("k1").String())
|
assert.Equal(t, "a", cfg.Section("foo").Key("k1").String())
|
||||||
sec, _ = cfg.NewSection("bar")
|
sec, _ = cfg.NewSection("bar")
|
||||||
|
@ -123,7 +124,7 @@ func TestNewConfigProviderForLocale(t *testing.T) {
|
||||||
func TestDisableSaving(t *testing.T) {
|
func TestDisableSaving(t *testing.T) {
|
||||||
testFile := t.TempDir() + "/test.ini"
|
testFile := t.TempDir() + "/test.ini"
|
||||||
_ = os.WriteFile(testFile, []byte("k1=a\nk2=b"), 0o644)
|
_ = 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)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
cfg.DisableSaving()
|
cfg.DisableSaving()
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
|
@ -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)
|
||||||
|
})
|
||||||
|
}
|
|
@ -61,6 +61,7 @@ var (
|
||||||
AssetVersion string
|
AssetVersion string
|
||||||
|
|
||||||
// Server settings
|
// Server settings
|
||||||
|
|
||||||
Protocol Scheme
|
Protocol Scheme
|
||||||
UseProxyProtocol bool // `ini:"USE_PROXY_PROTOCOL"`
|
UseProxyProtocol bool // `ini:"USE_PROXY_PROTOCOL"`
|
||||||
ProxyProtocolTLSBridging bool //`ini:"PROXY_PROTOCOL_TLS_BRIDGING"`
|
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)
|
StaticCacheTime = sec.Key("STATIC_CACHE_TIME").MustDuration(6 * time.Hour)
|
||||||
AppDataPath = sec.Key("APP_DATA_PATH").MustString(path.Join(AppWorkPath, "data"))
|
AppDataPath = sec.Key("APP_DATA_PATH").MustString(path.Join(AppWorkPath, "data"))
|
||||||
if !filepath.IsAbs(AppDataPath) {
|
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))
|
AppDataPath = filepath.ToSlash(filepath.Join(AppWorkPath, AppDataPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,12 +5,8 @@
|
||||||
package setting
|
package setting
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -28,19 +24,9 @@ var (
|
||||||
// AppStartTime store time gitea has started
|
// AppStartTime store time gitea has started
|
||||||
AppStartTime time.Time
|
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
|
// Other global setting objects
|
||||||
|
|
||||||
CfgProvider ConfigProvider
|
CfgProvider ConfigProvider
|
||||||
CustomPath string // Custom directory path
|
|
||||||
CustomConf string
|
|
||||||
RunMode string
|
RunMode string
|
||||||
RunUser string
|
RunUser string
|
||||||
IsProd bool
|
IsProd bool
|
||||||
|
@ -51,62 +37,6 @@ var (
|
||||||
IsInTesting = false
|
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() {
|
func init() {
|
||||||
IsWindows = runtime.GOOS == "windows"
|
IsWindows = runtime.GOOS == "windows"
|
||||||
if AppVer == "" {
|
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
|
// 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.
|
// 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)
|
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
|
// IsRunUserMatchCurrentUser returns false if configured run user does not match
|
||||||
|
@ -137,36 +61,6 @@ func IsRunUserMatchCurrentUser(runUser string) (string, bool) {
|
||||||
return currentUser, runUser == currentUser
|
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
|
// PrepareAppDataPath creates app data directory if necessary
|
||||||
func PrepareAppDataPath() error {
|
func PrepareAppDataPath() error {
|
||||||
// FIXME: There are too many calls to MkdirAll in old code. It is incorrect.
|
// FIXME: There are too many calls to MkdirAll in old code. It is incorrect.
|
||||||
|
@ -196,20 +90,23 @@ func PrepareAppDataPath() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Init(opts *Options) {
|
func InitCfgProvider(file string, extraConfigs ...string) {
|
||||||
if opts.CustomConf == "" {
|
|
||||||
opts.CustomConf = CustomConf
|
|
||||||
}
|
|
||||||
var err error
|
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
|
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)
|
|
||||||
}
|
}
|
||||||
if !opts.DisableLoadCommonSettings {
|
|
||||||
|
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.`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadCommonSettings() {
|
||||||
if err := loadCommonSettingsFrom(CfgProvider); err != nil {
|
if err := loadCommonSettingsFrom(CfgProvider); err != nil {
|
||||||
log.Fatal("loadCommonSettingsFrom[%v]: %v", opts, err)
|
log.Fatal("Unable to load settings from config: %v", err)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,7 @@ func sessionHandler(session ssh.Session) {
|
||||||
|
|
||||||
log.Trace("SSH: Payload: %v", command)
|
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)
|
log.Trace("SSH: Arguments: %v", args)
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(session.Context())
|
ctx, cancel := context.WithCancel(session.Context())
|
||||||
|
|
|
@ -90,10 +90,11 @@ func (w *testLoggerWriterCloser) Reset() {
|
||||||
|
|
||||||
// PrintCurrentTest prints the current test to os.Stdout
|
// PrintCurrentTest prints the current test to os.Stdout
|
||||||
func PrintCurrentTest(t testing.TB, skip ...int) func() {
|
func PrintCurrentTest(t testing.TB, skip ...int) func() {
|
||||||
|
t.Helper()
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
actualSkip := 1
|
actualSkip := 1
|
||||||
if len(skip) > 0 {
|
if len(skip) > 0 {
|
||||||
actualSkip = skip[0]
|
actualSkip = skip[0] + 1
|
||||||
}
|
}
|
||||||
_, filename, line, _ := runtime.Caller(actualSkip)
|
_, filename, line, _ := runtime.Caller(actualSkip)
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ import (
|
||||||
"code.gitea.io/gitea/modules/system"
|
"code.gitea.io/gitea/modules/system"
|
||||||
"code.gitea.io/gitea/modules/templates"
|
"code.gitea.io/gitea/modules/templates"
|
||||||
"code.gitea.io/gitea/modules/translation"
|
"code.gitea.io/gitea/modules/translation"
|
||||||
"code.gitea.io/gitea/modules/util"
|
|
||||||
"code.gitea.io/gitea/modules/web"
|
"code.gitea.io/gitea/modules/web"
|
||||||
actions_router "code.gitea.io/gitea/routers/api/actions"
|
actions_router "code.gitea.io/gitea/routers/api/actions"
|
||||||
packages_router "code.gitea.io/gitea/routers/api/packages"
|
packages_router "code.gitea.io/gitea/routers/api/packages"
|
||||||
|
@ -101,21 +100,16 @@ func syncAppConfForGit(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GlobalInitInstalled is for global installed configuration.
|
func InitWebInstallPage(ctx context.Context) {
|
||||||
func GlobalInitInstalled(ctx context.Context) {
|
translation.InitLocales(ctx)
|
||||||
if !setting.InstallLock {
|
setting.LoadSettingsForInstall()
|
||||||
log.Fatal("Gitea is not installed")
|
mustInit(svg.Init)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InitWebInstalled is for global installed configuration.
|
||||||
|
func InitWebInstalled(ctx context.Context) {
|
||||||
mustInitCtx(ctx, git.InitFull)
|
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("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))
|
|
||||||
|
|
||||||
// Setup i18n
|
// Setup i18n
|
||||||
translation.InitLocales(ctx)
|
translation.InitLocales(ctx)
|
||||||
|
|
|
@ -32,6 +32,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
"code.gitea.io/gitea/modules/web"
|
"code.gitea.io/gitea/modules/web"
|
||||||
"code.gitea.io/gitea/modules/web/middleware"
|
"code.gitea.io/gitea/modules/web/middleware"
|
||||||
|
"code.gitea.io/gitea/routers/common"
|
||||||
"code.gitea.io/gitea/services/forms"
|
"code.gitea.io/gitea/services/forms"
|
||||||
|
|
||||||
"gitea.com/go-chi/session"
|
"gitea.com/go-chi/session"
|
||||||
|
@ -370,11 +371,16 @@ func SubmitInstall(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save settings.
|
// Save settings.
|
||||||
cfg, err := setting.NewConfigProviderFromFile(&setting.Options{CustomConf: setting.CustomConf, AllowEmpty: true})
|
cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Failed to load custom conf '%s': %v", setting.CustomConf, err)
|
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("DB_TYPE").SetValue(setting.Database.Type.String())
|
||||||
cfg.Section("database").Key("HOST").SetValue(setting.Database.Host)
|
cfg.Section("database").Key("HOST").SetValue(setting.Database.Host)
|
||||||
cfg.Section("database").Key("NAME").SetValue(setting.Database.Name)
|
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("PATH").SetValue(setting.Database.Path)
|
||||||
cfg.Section("database").Key("LOG_SQL").SetValue("false") // LOG_SQL is rarely helpful
|
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("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("SSH_DOMAIN").SetValue(form.Domain)
|
||||||
cfg.Section("server").Key("DOMAIN").SetValue(form.Domain)
|
cfg.Section("server").Key("DOMAIN").SetValue(form.Domain)
|
||||||
cfg.Section("server").Key("HTTP_PORT").SetValue(form.HTTPPort)
|
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("service").Key("NO_REPLY_ADDRESS").SetValue(fmt.Sprint(form.NoReplyAddress))
|
||||||
cfg.Section("cron.update_checker").Key("ENABLED").SetValue(fmt.Sprint(form.EnableUpdateChecker))
|
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("session").Key("PROVIDER").SetValue("file")
|
||||||
|
|
||||||
cfg.Section("log").Key("MODE").MustString("console")
|
cfg.Section("log").Key("MODE").MustString("console")
|
||||||
|
@ -514,7 +516,13 @@ func SubmitInstall(ctx *context.Context) {
|
||||||
// ---- All checks are passed
|
// ---- All checks are passed
|
||||||
|
|
||||||
// Reload settings (and re-initialize database connection)
|
// 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
|
// Create admin account
|
||||||
if len(form.AdminName) > 0 {
|
if len(form.AdminName) > 0 {
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -167,20 +166,6 @@ func Config(ctx *context.Context) {
|
||||||
ctx.Data["SessionConfig"] = sessionCfg
|
ctx.Data["SessionConfig"] = sessionCfg
|
||||||
|
|
||||||
ctx.Data["Git"] = setting.Git
|
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["AccessLogTemplate"] = setting.Log.AccessLogTemplate
|
||||||
ctx.Data["LogSQL"] = setting.Database.LogSQL
|
ctx.Data["LogSQL"] = setting.Database.LogSQL
|
||||||
|
|
||||||
|
|
|
@ -46,15 +46,6 @@
|
||||||
<dd>{{.ScriptType}}</dd>
|
<dd>{{.ScriptType}}</dd>
|
||||||
<dt>{{.locale.Tr "admin.config.reverse_auth_user"}}</dt>
|
<dt>{{.locale.Tr "admin.config.reverse_auth_user"}}</dt>
|
||||||
<dd>{{.ReverseProxyAuthUser}}</dd>
|
<dd>{{.ReverseProxyAuthUser}}</dd>
|
||||||
|
|
||||||
{{if .EnvVars}}
|
|
||||||
<div class="ui divider"></div>
|
|
||||||
{{range .EnvVars}}
|
|
||||||
<dt>{{.Name}}</dt>
|
|
||||||
<dd>{{.Value}}</dd>
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,10 @@ func InitTest(requireGitea bool) {
|
||||||
if giteaRoot == "" {
|
if giteaRoot == "" {
|
||||||
exitf("Environment variable $GITEA_ROOT not set")
|
exitf("Environment variable $GITEA_ROOT not set")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setting.IsInTesting = true
|
||||||
setting.AppWorkPath = giteaRoot
|
setting.AppWorkPath = giteaRoot
|
||||||
|
setting.CustomPath = filepath.Join(setting.AppWorkPath, "custom")
|
||||||
if requireGitea {
|
if requireGitea {
|
||||||
giteaBinary := "gitea"
|
giteaBinary := "gitea"
|
||||||
if setting.IsWindows {
|
if setting.IsWindows {
|
||||||
|
@ -53,7 +56,6 @@ func InitTest(requireGitea bool) {
|
||||||
exitf("Could not find gitea binary at %s", setting.AppPath)
|
exitf("Could not find gitea binary at %s", setting.AppPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
giteaConf := os.Getenv("GITEA_CONF")
|
giteaConf := os.Getenv("GITEA_CONF")
|
||||||
if giteaConf == "" {
|
if giteaConf == "" {
|
||||||
// By default, use sqlite.ini for testing, then IDE like GoLand can start the test process with debugger.
|
// 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`)
|
exitf(`sqlite3 requires: import _ "github.com/mattn/go-sqlite3" or -tags sqlite,sqlite_unlock_notify`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setting.IsInTesting = true
|
|
||||||
|
|
||||||
if !path.IsAbs(giteaConf) {
|
if !path.IsAbs(giteaConf) {
|
||||||
setting.CustomConf = path.Join(giteaRoot, giteaConf)
|
setting.CustomConf = filepath.Join(giteaRoot, giteaConf)
|
||||||
} else {
|
} else {
|
||||||
setting.CustomConf = giteaConf
|
setting.CustomConf = giteaConf
|
||||||
}
|
}
|
||||||
|
|
||||||
setting.SetCustomPathAndConf("", "", "")
|
|
||||||
unittest.InitSettings()
|
unittest.InitSettings()
|
||||||
setting.Repository.DefaultBranch = "master" // many test code still assume that default branch is called "master"
|
setting.Repository.DefaultBranch = "master" // many test code still assume that default branch is called "master"
|
||||||
_ = util.RemoveAll(repo_module.LocalCopyPath())
|
_ = util.RemoveAll(repo_module.LocalCopyPath())
|
||||||
|
@ -175,7 +173,7 @@ func InitTest(requireGitea bool) {
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
routers.GlobalInitInstalled(graceful.GetManager().HammerContext())
|
routers.InitWebInstalled(graceful.GetManager().HammerContext())
|
||||||
}
|
}
|
||||||
|
|
||||||
func PrepareTestEnv(t testing.TB, skip ...int) func() {
|
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() {
|
func PrintCurrentTest(t testing.TB, skip ...int) func() {
|
||||||
if len(skip) == 1 {
|
t.Helper()
|
||||||
skip = []int{skip[0] + 1}
|
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
|
// Printf takes a format and args and prints the string to os.Stdout
|
||||||
|
|
Loading…
Reference in New Issue