Merge remote-tracking branch 'upstream/main' into limit-repo-size

This commit is contained in:
DmitryFrolovTri
2023-06-29 08:04:23 +00:00
359 changed files with 5718 additions and 3433 deletions
+65 -43
View File
@@ -4,6 +4,7 @@
package setting
import (
"errors"
"fmt"
"os"
"path/filepath"
@@ -51,12 +52,18 @@ type ConfigProvider interface {
GetSection(name string) (ConfigSection, error)
Save() error
SaveTo(filename string) error
DisableSaving()
PrepareSaving() (ConfigProvider, error)
IsLoadedFromEmpty() bool
}
type iniConfigProvider struct {
opts *Options
ini *ini.File
newFile bool // whether the file has not existed previously
file string
ini *ini.File
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 {
@@ -175,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) {
cfg := ini.Empty()
newFile := true
func NewConfigProviderFromFile(file string, extraConfigs ...string) (ConfigProvider, error) {
cfg := ini.Empty(ini.LoadOptions{KeyValueDelimiterOnWrite: " = "})
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
}
@@ -252,22 +249,24 @@ func (p *iniConfigProvider) GetSection(name string) (ConfigSection, error) {
return &iniConfigSection{sec: sec}, nil
}
var errDisableSaving = errors.New("this config can't be saved, developers should prepare a new config to save")
// Save saves the content into file
func (p *iniConfigProvider) Save() error {
filename := p.opts.CustomConf
if filename == "" {
if !p.opts.AllowEmpty {
return fmt.Errorf("custom config path must not be empty")
}
return nil
if p.disableSaving {
return errDisableSaving
}
if p.newFile {
filename := p.file
if filename == "" {
return fmt.Errorf("config file path must not be empty")
}
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
@@ -285,9 +284,32 @@ func (p *iniConfigProvider) Save() error {
}
func (p *iniConfigProvider) SaveTo(filename string) error {
if p.disableSaving {
return errDisableSaving
}
return p.ini.SaveTo(filename)
}
// DisableSaving disables the saving function, use PrepareSaving to get clear config options.
func (p *iniConfigProvider) DisableSaving() {
p.disableSaving = true
}
// PrepareSaving loads the ini from file again to get clear config options.
// Otherwise, the "MustXxx" calls would have polluted the current config provider,
// 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) {
if p.file == "" {
return nil, errors.New("no config file to save")
}
return NewConfigProviderFromFile(p.file)
}
func (p *iniConfigProvider) IsLoadedFromEmpty() bool {
return p.loadedFromEmpty
}
func mustMapSetting(rootCfg ConfigProvider, sectionName string, setting any) {
if err := rootCfg.Section(sectionName).MapTo(setting); err != nil {
log.Fatal("Failed to map %s settings: %v", sectionName, err)
@@ -324,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
}
+32 -7
View File
@@ -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")
@@ -84,14 +85,14 @@ func TestNewConfigProviderFromFile(t *testing.T) {
bs, err := os.ReadFile(testFile)
assert.NoError(t, err)
assert.Equal(t, "[foo]\nk1=a\n", string(bs))
assert.Equal(t, "[foo]\nk1 = a\n", string(bs))
bs, err = os.ReadFile(testFile1)
assert.NoError(t, err)
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
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")
@@ -99,7 +100,7 @@ func TestNewConfigProviderFromFile(t *testing.T) {
assert.NoError(t, cfg.Save())
bs, err = os.ReadFile(testFile)
assert.NoError(t, err)
assert.Equal(t, "[foo]\nk1=a\n\n[bar]\nk1=b\n", string(bs))
assert.Equal(t, "[foo]\nk1 = a\n\n[bar]\nk1 = b\n", string(bs))
}
func TestNewConfigProviderForLocale(t *testing.T) {
@@ -119,3 +120,27 @@ func TestNewConfigProviderForLocale(t *testing.T) {
assert.Equal(t, "foo", cfg.Section("").Key("k1").String())
assert.Equal(t, "xxx", cfg.Section("").Key("k2").String())
}
func TestDisableSaving(t *testing.T) {
testFile := t.TempDir() + "/test.ini"
_ = os.WriteFile(testFile, []byte("k1=a\nk2=b"), 0o644)
cfg, err := NewConfigProviderFromFile(testFile)
assert.NoError(t, err)
cfg.DisableSaving()
err = cfg.Save()
assert.ErrorIs(t, err, errDisableSaving)
saveCfg, err := cfg.PrepareSaving()
assert.NoError(t, err)
saveCfg.Section("").Key("k1").MustString("x")
saveCfg.Section("").Key("k2").SetValue("y")
saveCfg.Section("").Key("k3").SetValue("z")
err = saveCfg.Save()
assert.NoError(t, err)
bs, err := os.ReadFile(testFile)
assert.NoError(t, err)
assert.Equal(t, "k1 = a\nk2 = y\nk3 = z\n", string(bs))
}
+9 -19
View File
@@ -12,8 +12,6 @@ import (
"path/filepath"
"strings"
"time"
"code.gitea.io/gitea/modules/log"
)
var (
@@ -36,7 +34,7 @@ var (
SSLMode string
Path string
LogSQL bool
Charset string
MysqlCharset string
Timeout int // seconds
SQLiteJournalMode string
DBConnectRetries int
@@ -60,11 +58,6 @@ func LoadDBSetting() {
func loadDBSetting(rootCfg ConfigProvider) {
sec := rootCfg.Section("database")
Database.Type = DatabaseType(sec.Key("DB_TYPE").String())
defaultCharset := "utf8"
if Database.Type.IsMySQL() {
defaultCharset = "utf8mb4"
}
Database.Host = sec.Key("HOST").String()
Database.Name = sec.Key("NAME").String()
@@ -74,10 +67,7 @@ func loadDBSetting(rootCfg ConfigProvider) {
}
Database.Schema = sec.Key("SCHEMA").String()
Database.SSLMode = sec.Key("SSL_MODE").MustString("disable")
Database.Charset = sec.Key("CHARSET").In(defaultCharset, []string{"utf8", "utf8mb4"})
if Database.Type.IsMySQL() && defaultCharset != "utf8mb4" {
log.Error("Deprecated database mysql charset utf8 support, please use utf8mb4 or convert utf8 to utf8mb4.")
}
Database.MysqlCharset = sec.Key("MYSQL_CHARSET").MustString("utf8mb4") // do not document it, end users won't need it.
Database.Path = sec.Key("PATH").MustString(filepath.Join(AppDataPath, "gitea.db"))
Database.Timeout = sec.Key("SQLITE_TIMEOUT").MustInt(500)
@@ -101,9 +91,9 @@ func loadDBSetting(rootCfg ConfigProvider) {
// DBConnStr returns database connection string
func DBConnStr() (string, error) {
var connStr string
Param := "?"
if strings.Contains(Database.Name, Param) {
Param = "&"
paramSep := "?"
if strings.Contains(Database.Name, paramSep) {
paramSep = "&"
}
switch Database.Type {
case "mysql":
@@ -116,15 +106,15 @@ func DBConnStr() (string, error) {
tls = "false"
}
connStr = fmt.Sprintf("%s:%s@%s(%s)/%s%scharset=%s&parseTime=true&tls=%s",
Database.User, Database.Passwd, connType, Database.Host, Database.Name, Param, Database.Charset, tls)
Database.User, Database.Passwd, connType, Database.Host, Database.Name, paramSep, Database.MysqlCharset, tls)
case "postgres":
connStr = getPostgreSQLConnectionString(Database.Host, Database.User, Database.Passwd, Database.Name, Param, Database.SSLMode)
connStr = getPostgreSQLConnectionString(Database.Host, Database.User, Database.Passwd, Database.Name, paramSep, Database.SSLMode)
case "mssql":
host, port := ParseMSSQLHostPort(Database.Host)
connStr = fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", host, port, Database.Name, Database.User, Database.Passwd)
case "sqlite3":
if !EnableSQLite3 {
return "", errors.New("this binary version does not build support for SQLite3")
return "", errors.New("this Gitea binary was not built with SQLite3 support")
}
if err := os.MkdirAll(path.Dir(Database.Path), os.ModePerm); err != nil {
return "", fmt.Errorf("Failed to create directories: %w", err)
@@ -136,7 +126,7 @@ func DBConnStr() (string, error) {
connStr = fmt.Sprintf("file:%s?cache=shared&mode=rwc&_busy_timeout=%d&_txlock=immediate%s",
Database.Path, Database.Timeout, journalMode)
default:
return "", fmt.Errorf("Unknown database type: %s", Database.Type)
return "", fmt.Errorf("unknown database type: %s", Database.Type)
}
return connStr, nil
+11 -4
View File
@@ -53,19 +53,26 @@ func loadLFSFrom(rootCfg ConfigProvider) error {
return nil
}
LFS.JWTSecretBase64 = loadSecret(rootCfg.Section("lfs"), "LFS_JWT_SECRET_URI", "LFS_JWT_SECRET")
LFS.JWTSecretBytes = make([]byte, 32)
n, err := base64.RawURLEncoding.Decode(LFS.JWTSecretBytes, []byte(LFS.JWTSecretBase64))
if err != nil || n != 32 {
LFS.JWTSecretBase64, err = generate.NewJwtSecretBase64()
if err != nil {
return fmt.Errorf("Error generating JWT Secret for custom config: %v", err)
return fmt.Errorf("error generating JWT Secret for custom config: %v", err)
}
// Save secret
sec.Key("LFS_JWT_SECRET").SetValue(LFS.JWTSecretBase64)
if err := rootCfg.Save(); err != nil {
return fmt.Errorf("Error saving JWT Secret for custom config: %v", err)
saveCfg, err := rootCfg.PrepareSaving()
if err != nil {
return fmt.Errorf("error saving JWT Secret for custom config: %v", err)
}
rootCfg.Section("server").Key("LFS_JWT_SECRET").SetValue(LFS.JWTSecretBase64)
saveCfg.Section("server").Key("LFS_JWT_SECRET").SetValue(LFS.JWTSecretBase64)
if err := saveCfg.Save(); err != nil {
return fmt.Errorf("error saving JWT Secret for custom config: %v", err)
}
}
+1 -1
View File
@@ -244,7 +244,7 @@ func initLoggerByName(manager *log.LoggerManager, rootCfg ConfigProvider, logger
eventWriters = append(eventWriters, eventWriter)
}
manager.GetLogger(loggerName).RemoveAllWriters().AddWriters(eventWriters...)
manager.GetLogger(loggerName).ReplaceAllWriters(eventWriters...)
}
func InitSQLLoggersForCli(level log.Level) {
+12 -1
View File
@@ -116,6 +116,12 @@ func loadOAuth2From(rootCfg ConfigProvider) {
return
}
if !OAuth2.Enable {
return
}
OAuth2.JWTSecretBase64 = loadSecret(rootCfg.Section("oauth2"), "JWT_SECRET_URI", "JWT_SECRET")
if !filepath.IsAbs(OAuth2.JWTSigningPrivateKeyFile) {
OAuth2.JWTSigningPrivateKeyFile = filepath.Join(AppDataPath, OAuth2.JWTSigningPrivateKeyFile)
}
@@ -130,8 +136,13 @@ func loadOAuth2From(rootCfg ConfigProvider) {
}
secretBase64 := base64.RawURLEncoding.EncodeToString(key)
saveCfg, err := rootCfg.PrepareSaving()
if err != nil {
log.Fatal("save oauth2.JWT_SECRET failed: %v", err)
}
rootCfg.Section("oauth2").Key("JWT_SECRET").SetValue(secretBase64)
if err := rootCfg.Save(); err != nil {
saveCfg.Section("oauth2").Key("JWT_SECRET").SetValue(secretBase64)
if err := saveCfg.Save(); err != nil {
log.Fatal("save oauth2.JWT_SECRET failed: %v", err)
}
}
+195
View File
@@ -0,0 +1,195 @@
// 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) {
InitWorkPathAndCfgProvider(getEnvFn, args)
LoadCommonSettings()
}
// InitWorkPathAndCfgProvider will set AppWorkPath, CustomPath and CustomConf, init default config provider by CustomConf
func InitWorkPathAndCfgProvider(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
}
+151
View File
@@ -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)
})
}
+7 -2
View File
@@ -76,7 +76,7 @@ func loadSecret(sec ConfigSection, uriKey, verbatimKey string) string {
// only file URIs are allowed
default:
log.Fatal("Unsupported URI-Scheme %q (INTERNAL_TOKEN_URI = %q)", tempURI.Scheme, uri)
log.Fatal("Unsupported URI-Scheme %q (%q = %q)", tempURI.Scheme, uriKey, uri)
return ""
}
}
@@ -89,8 +89,13 @@ func generateSaveInternalToken(rootCfg ConfigProvider) {
}
InternalToken = token
saveCfg, err := rootCfg.PrepareSaving()
if err != nil {
log.Fatal("Error saving internal token: %v", err)
}
rootCfg.Section("security").Key("INTERNAL_TOKEN").SetValue(token)
if err := rootCfg.Save(); err != nil {
saveCfg.Section("security").Key("INTERNAL_TOKEN").SetValue(token)
if err = saveCfg.Save(); err != nil {
log.Fatal("Error saving internal token: %v", err)
}
}
+1 -1
View File
@@ -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))
}
+16 -118
View File
@@ -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,25 +90,29 @@ 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 err != nil {
log.Fatal("newConfigProviderFromFile[%v]: %v", opts, err)
if CfgProvider, err = NewConfigProviderFromFile(file, extraConfigs...); err != nil {
log.Fatal("Unable to init config provider from %q: %v", file, err)
}
if !opts.DisableLoadCommonSettings {
if err := loadCommonSettingsFrom(CfgProvider); err != nil {
log.Fatal("loadCommonSettingsFrom[%v]: %v", opts, err)
}
CfgProvider.DisableSaving() // do not allow saving the CfgProvider into file, it will be polluted by the "MustXxx" calls
}
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 {
log.Fatal("Unable to load settings from config: %v", err)
}
}
// loadCommonSettingsFrom loads common configurations from a configuration provider.
func loadCommonSettingsFrom(cfg ConfigProvider) error {
// WARNNING: don't change the sequence except you know what you are doing.
// WARNING: don't change the sequence except you know what you are doing.
loadRunModeFrom(cfg)
loadLogGlobalFrom(cfg)
loadServerFrom(cfg)