ada552aebf
Universal: Auto Version Bump / Version Bump (push) Successful in 17s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Successful in 14s
Generic: Project CI / Lint & Validate (pull_request) Successful in 43s
Universal: PR Check / Secret Scan (pull_request) Successful in 1m24s
Branch Cleanup / Delete merged branch (pull_request) Successful in 2s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Generic: Project CI / Tests (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Step 1 of the coordinated GITEA__ -> MOKOGIT__ rebrand. The binary previously
read config env overrides only under the GITEA__ prefix, so renaming the compose
vars first would make Gitea silently ignore every override. Teach the binary to
accept BOTH prefixes (MOKOGIT__ and GITEA__), including the __FILE suffix, so the
compose vars can be migrated safely AFTER this binary is deployed to all tiers.
- add EnvConfigKeyPrefixMokoGit ("MOKOGIT__") + envConfigKeyPrefixes list
- CollectEnvConfigKeys / EnvironmentToConfig iterate both prefixes
- test: MOKOGIT__ override applies (existing GITEA__ + __FILE behavior unchanged)
Compose var rename (GITEA__* -> MOKOGIT__*) is a follow-up, gated on this being
live everywhere. Upstream MIT header preserved.
Authored-by: Moko Consulting
198 lines
6.0 KiB
Go
198 lines
6.0 KiB
Go
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package setting
|
|
|
|
import (
|
|
"bytes"
|
|
"os"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"code.mokoconsulting.tech/MokoConsulting/MokoGIT/modules/log"
|
|
)
|
|
|
|
const (
|
|
EnvConfigKeyPrefixGitea = "GITEA__"
|
|
// EnvConfigKeyPrefixMokoGit is the MokoGIT-branded env override prefix. It is
|
|
// accepted alongside the upstream GITEA__ prefix so config keeps working whether
|
|
// the deployment sets GITEA__* or MOKOGIT__* (coordinated rebrand transition).
|
|
EnvConfigKeyPrefixMokoGit = "MOKOGIT__"
|
|
EnvConfigKeySuffixFile = "__FILE"
|
|
)
|
|
|
|
// envConfigKeyPrefixes lists the accepted env-override prefixes. A given env var
|
|
// starts with exactly one of them, so the order does not affect precedence.
|
|
var envConfigKeyPrefixes = []string{EnvConfigKeyPrefixMokoGit, EnvConfigKeyPrefixGitea}
|
|
|
|
const escapeRegexpString = "_0[xX](([0-9a-fA-F][0-9a-fA-F])+)_"
|
|
|
|
var escapeRegex = regexp.MustCompile(escapeRegexpString)
|
|
|
|
func CollectEnvConfigKeys() (keys []string) {
|
|
for _, env := range os.Environ() {
|
|
for _, prefix := range envConfigKeyPrefixes {
|
|
if strings.HasPrefix(env, prefix) {
|
|
k, _, _ := strings.Cut(env, "=")
|
|
keys = append(keys, k)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return keys
|
|
}
|
|
|
|
func ClearEnvConfigKeys() {
|
|
for _, k := range CollectEnvConfigKeys() {
|
|
_ = os.Unsetenv(k)
|
|
}
|
|
}
|
|
|
|
// decodeEnvSectionKey will decode a portable string encoded Section__Key pair
|
|
// Portable strings are considered to be of the form [A-Z0-9_]*
|
|
// We will encode a disallowed value as the UTF8 byte string preceded by _0X and
|
|
// followed by _. E.g. _0X2C_ for a '-' and _0X2E_ for '.'
|
|
// Section and Key are separated by a plain '__'.
|
|
// The entire section can be encoded as a UTF8 byte string
|
|
func decodeEnvSectionKey(encoded string) (ok bool, section, key string) {
|
|
inKey := false
|
|
last := 0
|
|
escapeStringIndices := escapeRegex.FindAllStringIndex(encoded, -1)
|
|
for _, unescapeIdx := range escapeStringIndices {
|
|
preceding := encoded[last:unescapeIdx[0]]
|
|
if !inKey {
|
|
if before, after, cutOk := strings.Cut(preceding, "__"); cutOk {
|
|
section += before
|
|
inKey = true
|
|
key += after
|
|
} else {
|
|
section += preceding
|
|
}
|
|
} else {
|
|
key += preceding
|
|
}
|
|
toDecode := encoded[unescapeIdx[0]+3 : unescapeIdx[1]-1]
|
|
decodedBytes := make([]byte, len(toDecode)/2)
|
|
for i := 0; i < len(toDecode)/2; i++ {
|
|
// Can ignore error here as we know these should be hexadecimal from the regexp
|
|
byteInt, _ := strconv.ParseInt(toDecode[2*i:2*i+2], 16, 8)
|
|
decodedBytes[i] = byte(byteInt)
|
|
}
|
|
if inKey {
|
|
key += string(decodedBytes)
|
|
} else {
|
|
section += string(decodedBytes)
|
|
}
|
|
last = unescapeIdx[1]
|
|
}
|
|
remaining := encoded[last:]
|
|
if !inKey {
|
|
if before, after, cutOk := strings.Cut(remaining, "__"); cutOk {
|
|
section += before
|
|
key += after
|
|
} else {
|
|
section += remaining
|
|
}
|
|
} else {
|
|
key += remaining
|
|
}
|
|
section = strings.ToLower(section)
|
|
ok = key != ""
|
|
if !ok {
|
|
section = ""
|
|
key = ""
|
|
}
|
|
return ok, section, key
|
|
}
|
|
|
|
// decodeEnvironmentKey decode the environment key to section and key
|
|
// The environment key is in the form of GITEA__SECTION__KEY or GITEA__SECTION__KEY__FILE
|
|
func decodeEnvironmentKey(prefixGitea, suffixFile, envKey string) (ok bool, section, key string, useFileValue bool) {
|
|
if !strings.HasPrefix(envKey, prefixGitea) {
|
|
return false, "", "", false
|
|
}
|
|
if strings.HasSuffix(envKey, suffixFile) {
|
|
useFileValue = true
|
|
envKey = envKey[:len(envKey)-len(suffixFile)]
|
|
}
|
|
ok, section, key = decodeEnvSectionKey(envKey[len(prefixGitea):])
|
|
return ok, section, key, useFileValue
|
|
}
|
|
|
|
func EnvironmentToConfig(cfg ConfigProvider, envs []string) (changed bool) {
|
|
for _, kv := range envs {
|
|
before, after, ok := strings.Cut(kv, "=")
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
// parse the environment variable to config section name and key name,
|
|
// accepting either the MOKOGIT__ or upstream GITEA__ override prefix.
|
|
envKey := before
|
|
envValue := after
|
|
var sectionName, keyName string
|
|
var useFileValue bool
|
|
for _, prefix := range envConfigKeyPrefixes {
|
|
if ok, sectionName, keyName, useFileValue = decodeEnvironmentKey(prefix, EnvConfigKeySuffixFile, envKey); ok {
|
|
break
|
|
}
|
|
}
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
// use environment value as config value, or read the file content as value if the key indicates a file
|
|
keyValue := envValue //nolint:staticcheck // false positive
|
|
if useFileValue {
|
|
fileContent, err := os.ReadFile(envValue)
|
|
if err != nil {
|
|
log.Error("Error reading file for %s : %v", envKey, envValue, err)
|
|
continue
|
|
}
|
|
if bytes.HasSuffix(fileContent, []byte("\r\n")) {
|
|
fileContent = fileContent[:len(fileContent)-2]
|
|
} else if bytes.HasSuffix(fileContent, []byte("\n")) {
|
|
fileContent = fileContent[:len(fileContent)-1]
|
|
}
|
|
keyValue = string(fileContent)
|
|
}
|
|
|
|
// try to set the config value if necessary
|
|
section, err := cfg.GetSection(sectionName)
|
|
if err != nil {
|
|
section, err = cfg.NewSection(sectionName)
|
|
if err != nil {
|
|
log.Error("Error creating section: %s : %v", sectionName, err)
|
|
continue
|
|
}
|
|
}
|
|
key := ConfigSectionKey(section, keyName)
|
|
if key == nil {
|
|
changed = true
|
|
key, err = section.NewKey(keyName, keyValue)
|
|
if err != nil {
|
|
log.Error("Error creating key: %s in section: %s with value: %s : %v", keyName, sectionName, keyValue, err)
|
|
continue
|
|
}
|
|
}
|
|
oldValue := key.Value()
|
|
if !changed && oldValue != keyValue {
|
|
changed = true
|
|
}
|
|
key.SetValue(keyValue)
|
|
}
|
|
return changed
|
|
}
|
|
|
|
func UnsetUnnecessaryEnvVars() {
|
|
// Ideally Gitea should only accept the environment variables which it clearly knows instead of unsetting the ones it doesn't want,
|
|
// but the ideal behavior would be a breaking change, and it seems not bringing enough benefits to end users.
|
|
// So at the moment we just keep "unsetting the unnecessary environment variables".
|
|
|
|
// HOME is managed by Gitea, Gitea's git should use "HOME/.gitconfig".
|
|
// But git would try "XDG_CONFIG_HOME/git/config" first if "HOME/.gitconfig" does not exist,
|
|
// then our git.InitFull would still write to "XDG_CONFIG_HOME/git/config" if XDG_CONFIG_HOME is set.
|
|
_ = os.Unsetenv("XDG_CONFIG_HOME")
|
|
}
|