-
-
Notifications
You must be signed in to change notification settings - Fork 5.8k
Add contrib/environment-to-ini #9519
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
424434d
Add contrib/environment-to-ini
zeripath adc68fd
Merge branch 'master' into contrib-environment-to-ini
sapk ac848e8
Update contrib/environment-to-ini/environment-to-ini.go
zeripath 04299d7
Merge branch 'master' into contrib-environment-to-ini
zeripath File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
Environment To Ini | ||
================== | ||
|
||
Multiple docker users have requested that the Gitea docker is changed | ||
to permit arbitrary configuration via environment variables. | ||
|
||
Gitea needs to use an ini file for configuration because the running | ||
environment that starts the docker may not be the same as that used | ||
by the hooks. An ini file also gives a good default and means that | ||
users do not have to completely provide a full environment. | ||
|
||
With those caveats above, this command provides a generic way of | ||
converting suitably structured environment variables into any ini | ||
value. | ||
|
||
To use the command is very simple just run it and the default gitea | ||
app.ini will be rewritten to take account of the variables provided, | ||
however there are various options to give slightly different | ||
behavior and these can be interrogated with the `-h` option. | ||
|
||
The environment variables should be of the form: | ||
|
||
GITEA__SECTION_NAME__KEY_NAME | ||
|
||
Environment variables are usually restricted to a reduced character | ||
set "0-9A-Z_" - in order to allow the setting of sections with | ||
characters outside of that set, they should be escaped as following: | ||
"_0X2E_" for ".". The entire section and key names can be escaped as | ||
a UTF8 byte string if necessary. E.g. to configure: | ||
|
||
""" | ||
... | ||
[log.console] | ||
COLORIZE=false | ||
STDERR=true | ||
... | ||
""" | ||
|
||
You would set the environment variables: "GITEA__LOG_0x2E_CONSOLE__COLORIZE=false" | ||
and "GITEA__LOG_0x2E_CONSOLE__STDERR=false". Other examples can be found | ||
on the configuration cheat sheet. | ||
|
||
To plug this command in to the docker, you simply compile the provided go file using: | ||
|
||
go build environment-to-ini.go | ||
|
||
And copy the resulting `environment-to-ini` command to /app/gitea in the docker. | ||
|
||
Apply the below patch to /etc/s6/gitea.setup to wire this in. | ||
|
||
If you find this useful please comment on #7287 | ||
|
||
|
||
diff --git a/docker/root/etc/s6/gitea/setup b/docker/root/etc/s6/gitea/setup | ||
index f87ce9115..565bfcba9 100755 | ||
--- a/docker/root/etc/s6/gitea/setup | ||
+++ b/docker/root/etc/s6/gitea/setup | ||
@@ -44,6 +44,8 @@ if [ ! -f ${GITEA_CUSTOM}/conf/app.ini ]; then | ||
SECRET_KEY=${SECRET_KEY:-""} \ | ||
envsubst < /etc/templates/app.ini > ${GITEA_CUSTOM}/conf/app.ini | ||
|
||
+ /app/gitea/environment-to-ini -c ${GITEA_CUSTOM}/conf/app.ini | ||
+ | ||
chown ${USER}:git ${GITEA_CUSTOM}/conf/app.ini | ||
fi | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
// Copyright 2019 The Gitea Authors. All rights reserved. | ||
// Use of this source code is governed by a MIT-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package main | ||
|
||
import ( | ||
"os" | ||
"regexp" | ||
"strconv" | ||
"strings" | ||
|
||
"code.gitea.io/gitea/modules/log" | ||
"code.gitea.io/gitea/modules/setting" | ||
|
||
"github.com/unknwon/com" | ||
"github.com/urfave/cli" | ||
ini "gopkg.in/ini.v1" | ||
) | ||
|
||
// EnvironmentPrefix environment variables prefixed with this represent ini values to write | ||
const EnvironmentPrefix = "GITEA" | ||
|
||
func main() { | ||
app := cli.NewApp() | ||
app.Name = "environment-to-ini" | ||
app.Usage = "Use provided environment to update configuration ini" | ||
app.Description = `As a helper to allow docker users to update the gitea configuration | ||
through the environment, this command allows environment variables to | ||
be mapped to values in the ini. | ||
|
||
Environment variables of the form "GITEA__SECTION_NAME__KEY_NAME" | ||
will be mapped to the ini section "[section_name]" and the key | ||
"KEY_NAME" with the value as provided. | ||
|
||
Environment variables are usually restricted to a reduced character | ||
set "0-9A-Z_" - in order to allow the setting of sections with | ||
characters outside of that set, they should be escaped as following: | ||
"_0X2E_" for ".". The entire section and key names can be escaped as | ||
a UTF8 byte string if necessary. E.g. to configure: | ||
|
||
""" | ||
... | ||
[log.console] | ||
COLORIZE=false | ||
STDERR=true | ||
... | ||
""" | ||
|
||
You would set the environment variables: "GITEA__LOG_0x2E_CONSOLE__COLORIZE=false" | ||
and "GITEA__LOG_0x2E_CONSOLE__STDERR=false". Other examples can be found | ||
on the configuration cheat sheet.` | ||
app.Flags = []cli.Flag{ | ||
cli.StringFlag{ | ||
Name: "custom-path, C", | ||
Value: setting.CustomPath, | ||
Usage: "Custom path file path", | ||
}, | ||
cli.StringFlag{ | ||
Name: "config, c", | ||
Value: setting.CustomConf, | ||
Usage: "Custom configuration file path", | ||
}, | ||
cli.StringFlag{ | ||
Name: "work-path, w", | ||
Value: setting.AppWorkPath, | ||
Usage: "Set the gitea working path", | ||
}, | ||
cli.StringFlag{ | ||
Name: "out, o", | ||
Value: "", | ||
Usage: "Destination file to write to", | ||
}, | ||
cli.BoolFlag{ | ||
Name: "clear", | ||
Usage: "Clears the matched variables from the environment", | ||
}, | ||
cli.StringFlag{ | ||
Name: "prefix, p", | ||
Value: EnvironmentPrefix, | ||
Usage: "Environment prefix to look for - will be suffixed by __ (2 underscores)", | ||
}, | ||
} | ||
app.Action = runEnvironmentToIni | ||
setting.SetCustomPathAndConf("", "", "") | ||
|
||
err := app.Run(os.Args) | ||
if err != nil { | ||
log.Fatal("Failed to run app with %s: %v", os.Args, err) | ||
} | ||
} | ||
|
||
func runEnvironmentToIni(c *cli.Context) error { | ||
providedCustom := c.String("custom-path") | ||
providedConf := c.String("config") | ||
providedWorkPath := c.String("work-path") | ||
setting.SetCustomPathAndConf(providedCustom, providedConf, providedWorkPath) | ||
|
||
cfg := ini.Empty() | ||
if com.IsFile(setting.CustomConf) { | ||
if err := cfg.Append(setting.CustomConf); err != nil { | ||
log.Fatal("Failed to load custom conf '%s': %v", setting.CustomConf, err) | ||
} | ||
} else { | ||
log.Warn("Custom config '%s' not found, ignore this if you're running first time", setting.CustomConf) | ||
} | ||
cfg.NameMapper = ini.AllCapsUnderscore | ||
|
||
prefix := c.String("prefix") + "__" | ||
|
||
for _, kv := range os.Environ() { | ||
idx := strings.IndexByte(kv, '=') | ||
if idx < 0 { | ||
continue | ||
} | ||
eKey := kv[:idx] | ||
value := kv[idx+1:] | ||
if !strings.HasPrefix(eKey, prefix) { | ||
continue | ||
} | ||
eKey = eKey[len(prefix):] | ||
sectionName, keyName := DecodeSectionKey(eKey) | ||
if len(keyName) == 0 { | ||
continue | ||
} | ||
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 := section.Key(keyName) | ||
if key == nil { | ||
key, err = section.NewKey(keyName, value) | ||
if err != nil { | ||
log.Error("Error creating key: %s in section: %s with value: %s : %v", keyName, sectionName, value, err) | ||
continue | ||
} | ||
} | ||
key.SetValue(value) | ||
} | ||
destination := c.String("out") | ||
if len(destination) == 0 { | ||
destination = setting.CustomConf | ||
} | ||
err := cfg.SaveTo(destination) | ||
if err != nil { | ||
return err | ||
} | ||
if c.Bool("clear") { | ||
for _, kv := range os.Environ() { | ||
idx := strings.IndexByte(kv, '=') | ||
if idx < 0 { | ||
continue | ||
} | ||
eKey := kv[:idx] | ||
if strings.HasPrefix(eKey, prefix) { | ||
_ = os.Unsetenv(eKey) | ||
} | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
const escapeRegexpString = "_0[xX](([0-9a-fA-F][0-9a-fA-F])+)_" | ||
|
||
var escapeRegex = regexp.MustCompile(escapeRegexpString) | ||
|
||
// DecodeSectionKey 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 DecodeSectionKey(encoded string) (string, string) { | ||
section := "" | ||
key := "" | ||
|
||
inKey := false | ||
last := 0 | ||
escapeStringIndices := escapeRegex.FindAllStringIndex(encoded, -1) | ||
for _, unescapeIdx := range escapeStringIndices { | ||
preceding := encoded[last:unescapeIdx[0]] | ||
if !inKey { | ||
if splitter := strings.Index(preceding, "__"); splitter > -1 { | ||
section += preceding[:splitter] | ||
inKey = true | ||
key += preceding[splitter+2:] | ||
} 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, 0) | ||
decodedBytes[i] = byte(byteInt) | ||
} | ||
if inKey { | ||
key += string(decodedBytes) | ||
} else { | ||
section += string(decodedBytes) | ||
} | ||
last = unescapeIdx[1] | ||
} | ||
remaining := encoded[last:] | ||
if !inKey { | ||
if splitter := strings.Index(remaining, "__"); splitter > -1 { | ||
section += remaining[:splitter] | ||
inKey = true | ||
key += remaining[splitter+2:] | ||
} else { | ||
section += remaining | ||
} | ||
} else { | ||
key += remaining | ||
} | ||
return section, key | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.