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

path.go 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. // Copyright 2023 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package setting
  4. import (
  5. "errors"
  6. "os"
  7. "os/exec"
  8. "path/filepath"
  9. "strings"
  10. "code.gitea.io/gitea/modules/log"
  11. )
  12. var (
  13. // AppPath represents the path to the gitea binary
  14. AppPath string
  15. // AppWorkPath is the "working directory" of Gitea. It maps to the: WORK_PATH in app.ini, "--work-path" flag, environment variable GITEA_WORK_DIR.
  16. // If that is not set it is the default set here by the linker or failing that the directory of AppPath.
  17. // It is used as the base path for several other paths.
  18. AppWorkPath string
  19. CustomPath string // Custom directory path. Env: GITEA_CUSTOM
  20. CustomConf string
  21. appWorkPathBuiltin string
  22. customPathBuiltin string
  23. customConfBuiltin string
  24. AppWorkPathMismatch bool
  25. )
  26. func getAppPath() (string, error) {
  27. var appPath string
  28. var err error
  29. if IsWindows && filepath.IsAbs(os.Args[0]) {
  30. appPath = filepath.Clean(os.Args[0])
  31. } else {
  32. appPath, err = exec.LookPath(os.Args[0])
  33. }
  34. if err != nil {
  35. if !errors.Is(err, exec.ErrDot) {
  36. return "", err
  37. }
  38. appPath, err = filepath.Abs(os.Args[0])
  39. }
  40. if err != nil {
  41. return "", err
  42. }
  43. appPath, err = filepath.Abs(appPath)
  44. if err != nil {
  45. return "", err
  46. }
  47. // 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/..."
  48. return strings.ReplaceAll(appPath, "\\", "/"), err
  49. }
  50. func init() {
  51. var err error
  52. if AppPath, err = getAppPath(); err != nil {
  53. log.Fatal("Failed to get app path: %v", err)
  54. }
  55. if AppWorkPath == "" {
  56. AppWorkPath = filepath.Dir(AppPath)
  57. }
  58. appWorkPathBuiltin = AppWorkPath
  59. customPathBuiltin = CustomPath
  60. customConfBuiltin = CustomConf
  61. }
  62. type ArgWorkPathAndCustomConf struct {
  63. WorkPath string
  64. CustomPath string
  65. CustomConf string
  66. }
  67. type stringWithDefault struct {
  68. Value string
  69. IsSet bool
  70. }
  71. func (s *stringWithDefault) Set(v string) {
  72. s.Value = v
  73. s.IsSet = true
  74. }
  75. // InitWorkPathAndCommonConfig will set AppWorkPath, CustomPath and CustomConf, init default config provider by CustomConf and load common settings,
  76. func InitWorkPathAndCommonConfig(getEnvFn func(name string) string, args ArgWorkPathAndCustomConf) {
  77. InitWorkPathAndCfgProvider(getEnvFn, args)
  78. LoadCommonSettings()
  79. }
  80. // InitWorkPathAndCfgProvider will set AppWorkPath, CustomPath and CustomConf, init default config provider by CustomConf
  81. func InitWorkPathAndCfgProvider(getEnvFn func(name string) string, args ArgWorkPathAndCustomConf) {
  82. tryAbsPath := func(paths ...string) string {
  83. s := paths[len(paths)-1]
  84. for i := len(paths) - 2; i >= 0; i-- {
  85. if filepath.IsAbs(s) {
  86. break
  87. }
  88. s = filepath.Join(paths[i], s)
  89. }
  90. return s
  91. }
  92. var err error
  93. tmpWorkPath := stringWithDefault{Value: appWorkPathBuiltin}
  94. if tmpWorkPath.Value == "" {
  95. tmpWorkPath.Value = filepath.Dir(AppPath)
  96. }
  97. tmpCustomPath := stringWithDefault{Value: customPathBuiltin}
  98. if tmpCustomPath.Value == "" {
  99. tmpCustomPath.Value = "custom"
  100. }
  101. tmpCustomConf := stringWithDefault{Value: customConfBuiltin}
  102. if tmpCustomConf.Value == "" {
  103. tmpCustomConf.Value = "conf/app.ini"
  104. }
  105. readFromEnv := func() {
  106. envWorkPath := getEnvFn("GITEA_WORK_DIR")
  107. if envWorkPath != "" {
  108. tmpWorkPath.Set(envWorkPath)
  109. if !filepath.IsAbs(tmpWorkPath.Value) {
  110. log.Fatal("GITEA_WORK_DIR (work path) must be absolute path")
  111. }
  112. }
  113. envCustomPath := getEnvFn("GITEA_CUSTOM")
  114. if envCustomPath != "" {
  115. tmpCustomPath.Set(envCustomPath)
  116. if !filepath.IsAbs(tmpCustomPath.Value) {
  117. log.Fatal("GITEA_CUSTOM (custom path) must be absolute path")
  118. }
  119. }
  120. }
  121. readFromArgs := func() {
  122. if args.WorkPath != "" {
  123. tmpWorkPath.Set(args.WorkPath)
  124. if !filepath.IsAbs(tmpWorkPath.Value) {
  125. log.Fatal("--work-path must be absolute path")
  126. }
  127. }
  128. if args.CustomPath != "" {
  129. tmpCustomPath.Set(args.CustomPath) // if it is not abs, it will be based on work-path, it shouldn't happen
  130. if !filepath.IsAbs(tmpCustomPath.Value) {
  131. log.Error("--custom-path must be absolute path")
  132. }
  133. }
  134. if args.CustomConf != "" {
  135. tmpCustomConf.Set(args.CustomConf)
  136. if !filepath.IsAbs(tmpCustomConf.Value) {
  137. // the config path can be relative to the real current working path
  138. if tmpCustomConf.Value, err = filepath.Abs(tmpCustomConf.Value); err != nil {
  139. log.Fatal("Failed to get absolute path of config %q: %v", tmpCustomConf.Value, err)
  140. }
  141. }
  142. }
  143. }
  144. readFromEnv()
  145. readFromArgs()
  146. if !tmpCustomConf.IsSet {
  147. tmpCustomConf.Set(tryAbsPath(tmpWorkPath.Value, tmpCustomPath.Value, tmpCustomConf.Value))
  148. }
  149. // only read the config but do not load/init anything more, because the AppWorkPath and CustomPath are not ready
  150. InitCfgProvider(tmpCustomConf.Value)
  151. if HasInstallLock(CfgProvider) {
  152. ClearEnvConfigKeys() // if the instance has been installed, do not pass the environment variables to sub-processes
  153. }
  154. configWorkPath := ConfigSectionKeyString(CfgProvider.Section(""), "WORK_PATH")
  155. if configWorkPath != "" {
  156. if !filepath.IsAbs(configWorkPath) {
  157. log.Fatal("WORK_PATH in %q must be absolute path", configWorkPath)
  158. }
  159. configWorkPath = filepath.Clean(configWorkPath)
  160. if tmpWorkPath.Value != "" && (getEnvFn("GITEA_WORK_DIR") != "" || args.WorkPath != "") {
  161. fi1, err1 := os.Stat(tmpWorkPath.Value)
  162. fi2, err2 := os.Stat(configWorkPath)
  163. if err1 != nil || err2 != nil || !os.SameFile(fi1, fi2) {
  164. AppWorkPathMismatch = true
  165. }
  166. }
  167. tmpWorkPath.Set(configWorkPath)
  168. }
  169. tmpCustomPath.Set(tryAbsPath(tmpWorkPath.Value, tmpCustomPath.Value))
  170. AppWorkPath = tmpWorkPath.Value
  171. CustomPath = tmpCustomPath.Value
  172. CustomConf = tmpCustomConf.Value
  173. }