gitea源码

config_env.go 5.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. // Copyright 2023 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package setting
  4. import (
  5. "bytes"
  6. "os"
  7. "regexp"
  8. "strconv"
  9. "strings"
  10. "code.gitea.io/gitea/modules/log"
  11. )
  12. const (
  13. EnvConfigKeyPrefixGitea = "GITEA__"
  14. EnvConfigKeySuffixFile = "__FILE"
  15. )
  16. const escapeRegexpString = "_0[xX](([0-9a-fA-F][0-9a-fA-F])+)_"
  17. var escapeRegex = regexp.MustCompile(escapeRegexpString)
  18. func CollectEnvConfigKeys() (keys []string) {
  19. for _, env := range os.Environ() {
  20. if strings.HasPrefix(env, EnvConfigKeyPrefixGitea) {
  21. k, _, _ := strings.Cut(env, "=")
  22. keys = append(keys, k)
  23. }
  24. }
  25. return keys
  26. }
  27. func ClearEnvConfigKeys() {
  28. for _, k := range CollectEnvConfigKeys() {
  29. _ = os.Unsetenv(k)
  30. }
  31. }
  32. // decodeEnvSectionKey will decode a portable string encoded Section__Key pair
  33. // Portable strings are considered to be of the form [A-Z0-9_]*
  34. // We will encode a disallowed value as the UTF8 byte string preceded by _0X and
  35. // followed by _. E.g. _0X2C_ for a '-' and _0X2E_ for '.'
  36. // Section and Key are separated by a plain '__'.
  37. // The entire section can be encoded as a UTF8 byte string
  38. func decodeEnvSectionKey(encoded string) (ok bool, section, key string) {
  39. inKey := false
  40. last := 0
  41. escapeStringIndices := escapeRegex.FindAllStringIndex(encoded, -1)
  42. for _, unescapeIdx := range escapeStringIndices {
  43. preceding := encoded[last:unescapeIdx[0]]
  44. if !inKey {
  45. if splitter := strings.Index(preceding, "__"); splitter > -1 {
  46. section += preceding[:splitter]
  47. inKey = true
  48. key += preceding[splitter+2:]
  49. } else {
  50. section += preceding
  51. }
  52. } else {
  53. key += preceding
  54. }
  55. toDecode := encoded[unescapeIdx[0]+3 : unescapeIdx[1]-1]
  56. decodedBytes := make([]byte, len(toDecode)/2)
  57. for i := 0; i < len(toDecode)/2; i++ {
  58. // Can ignore error here as we know these should be hexadecimal from the regexp
  59. byteInt, _ := strconv.ParseInt(toDecode[2*i:2*i+2], 16, 0)
  60. decodedBytes[i] = byte(byteInt)
  61. }
  62. if inKey {
  63. key += string(decodedBytes)
  64. } else {
  65. section += string(decodedBytes)
  66. }
  67. last = unescapeIdx[1]
  68. }
  69. remaining := encoded[last:]
  70. if !inKey {
  71. if splitter := strings.Index(remaining, "__"); splitter > -1 {
  72. section += remaining[:splitter]
  73. key += remaining[splitter+2:]
  74. } else {
  75. section += remaining
  76. }
  77. } else {
  78. key += remaining
  79. }
  80. section = strings.ToLower(section)
  81. ok = key != ""
  82. if !ok {
  83. section = ""
  84. key = ""
  85. }
  86. return ok, section, key
  87. }
  88. // decodeEnvironmentKey decode the environment key to section and key
  89. // The environment key is in the form of GITEA__SECTION__KEY or GITEA__SECTION__KEY__FILE
  90. func decodeEnvironmentKey(prefixGitea, suffixFile, envKey string) (ok bool, section, key string, useFileValue bool) {
  91. if !strings.HasPrefix(envKey, prefixGitea) {
  92. return false, "", "", false
  93. }
  94. if strings.HasSuffix(envKey, suffixFile) {
  95. useFileValue = true
  96. envKey = envKey[:len(envKey)-len(suffixFile)]
  97. }
  98. ok, section, key = decodeEnvSectionKey(envKey[len(prefixGitea):])
  99. return ok, section, key, useFileValue
  100. }
  101. func EnvironmentToConfig(cfg ConfigProvider, envs []string) (changed bool) {
  102. for _, kv := range envs {
  103. idx := strings.IndexByte(kv, '=')
  104. if idx < 0 {
  105. continue
  106. }
  107. // parse the environment variable to config section name and key name
  108. envKey := kv[:idx]
  109. envValue := kv[idx+1:]
  110. ok, sectionName, keyName, useFileValue := decodeEnvironmentKey(EnvConfigKeyPrefixGitea, EnvConfigKeySuffixFile, envKey)
  111. if !ok {
  112. continue
  113. }
  114. // use environment value as config value, or read the file content as value if the key indicates a file
  115. keyValue := envValue
  116. if useFileValue {
  117. fileContent, err := os.ReadFile(envValue)
  118. if err != nil {
  119. log.Error("Error reading file for %s : %v", envKey, envValue, err)
  120. continue
  121. }
  122. if bytes.HasSuffix(fileContent, []byte("\r\n")) {
  123. fileContent = fileContent[:len(fileContent)-2]
  124. } else if bytes.HasSuffix(fileContent, []byte("\n")) {
  125. fileContent = fileContent[:len(fileContent)-1]
  126. }
  127. keyValue = string(fileContent)
  128. }
  129. // try to set the config value if necessary
  130. section, err := cfg.GetSection(sectionName)
  131. if err != nil {
  132. section, err = cfg.NewSection(sectionName)
  133. if err != nil {
  134. log.Error("Error creating section: %s : %v", sectionName, err)
  135. continue
  136. }
  137. }
  138. key := ConfigSectionKey(section, keyName)
  139. if key == nil {
  140. changed = true
  141. key, err = section.NewKey(keyName, keyValue)
  142. if err != nil {
  143. log.Error("Error creating key: %s in section: %s with value: %s : %v", keyName, sectionName, keyValue, err)
  144. continue
  145. }
  146. }
  147. oldValue := key.Value()
  148. if !changed && oldValue != keyValue {
  149. changed = true
  150. }
  151. key.SetValue(keyValue)
  152. }
  153. return changed
  154. }
  155. // InitGiteaEnvVars initializes the environment variables for gitea
  156. func InitGiteaEnvVars() {
  157. // Ideally Gitea should only accept the environment variables which it clearly knows instead of unsetting the ones it doesn't want,
  158. // but the ideal behavior would be a breaking change, and it seems not bringing enough benefits to end users,
  159. // so at the moment we could still keep "unsetting the unnecessary environments"
  160. // HOME is managed by Gitea, Gitea's git should use "HOME/.gitconfig".
  161. // But git would try "XDG_CONFIG_HOME/git/config" first if "HOME/.gitconfig" does not exist,
  162. // then our git.InitFull would still write to "XDG_CONFIG_HOME/git/config" if XDG_CONFIG_HOME is set.
  163. _ = os.Unsetenv("XDG_CONFIG_HOME")
  164. }
  165. func InitGiteaEnvVarsForTesting() {
  166. InitGiteaEnvVars()
  167. _ = os.Unsetenv("GIT_AUTHOR_NAME")
  168. _ = os.Unsetenv("GIT_AUTHOR_EMAIL")
  169. _ = os.Unsetenv("GIT_AUTHOR_DATE")
  170. _ = os.Unsetenv("GIT_COMMITTER_NAME")
  171. _ = os.Unsetenv("GIT_COMMITTER_EMAIL")
  172. _ = os.Unsetenv("GIT_COMMITTER_DATE")
  173. }