Skip to content
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

Add cmd to allow generalised mapping of environment variables to Gitea Ini #7287

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
94426b6
Add cmd to allow generalised mapping of environment variables to Gite…
zeripath Jun 24, 2019
890d539
spelling mistake
zeripath Jun 24, 2019
51338a1
Use full path to gitea
zeripath Jun 24, 2019
64c15c2
Move to use double underscore as a separator
zeripath Jun 24, 2019
f73377b
Add encoding to the environment strings
zeripath Jun 26, 2019
730e300
Remove unused separator
zeripath Jun 26, 2019
e0ca836
Add Documentation
zeripath Jun 26, 2019
30a289f
Merge branch 'master' into environment-to-ini
zeripath Jun 26, 2019
f7d10ea
Remove unused args struct
zeripath Jun 26, 2019
4ae3b4d
Merge branch 'environment-to-ini' of github.com:zeripath/gitea into e…
zeripath Jun 26, 2019
aba65bf
Merge branch 'master' into environment-to-ini
zeripath Jul 7, 2019
80b54d1
Merge branch 'master' into environment-to-ini
zeripath Aug 16, 2019
fd82c14
Merge branch 'master' into environment-to-ini
zeripath Aug 16, 2019
fc52e23
Merge branch 'master' into environment-to-ini
zeripath Aug 17, 2019
3f5e1ba
Provide example as per @silverwind
zeripath Aug 17, 2019
aa42fb8
Merge branch 'master' into environment-to-ini
zeripath Oct 9, 2019
019e124
Fix Unknwon to unknwon in cmd/environment_to_ini
zeripath Oct 9, 2019
f157062
Remove Prefix before passing to DecodeSectionKey
zeripath Oct 9, 2019
8a5a3b8
Merge branch 'master' into environment-to-ini
zeripath Oct 14, 2019
bd44265
Merge branch 'master' into environment-to-ini
sapk Oct 19, 2019
cbd9404
Merge branch 'master' into environment-to-ini
zeripath Dec 23, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 146 additions & 0 deletions cmd/environment_to_ini.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// 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 cmd

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__"

// CmdEnvironmentToIni represents the command to use a provided environment to update the configuration ini
var CmdEnvironmentToIni = cli.Command{
Name: "environment-to-ini",
Usage: "Use provided environment to update configuration ini",
Action: runEnvironmentToIni,
Flags: []cli.Flag{
cli.StringFlag{
Name: "out, o",
Value: "",
Usage: "Destination file to write to",
},
},
}

func runEnvironmentToIni(c *cli.Context) error {
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

for _, kv := range os.Environ() {
idx := strings.IndexByte(kv, '=')
if idx < 0 {
continue
}
eKey := kv[:idx]
value := kv[idx+1:]
if !strings.HasPrefix(eKey, EnvironmentPrefix) {
continue
}
eKey = eKey[len(EnvironmentPrefix):]
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)
return err
}

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
}
64 changes: 64 additions & 0 deletions cmd/environment_to_ini_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// 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 cmd

import "testing"

func TestDecodeSectionKey(t *testing.T) {
tests := []struct {
name string
encoded string
section string
key string
}{
{
name: "Simple",
encoded: "Section__Key",
zeripath marked this conversation as resolved.
Show resolved Hide resolved
section: "Section",
key: "Key",
},
{
name: "LessSimple",
encoded: "Section_SubSection__Key_SubKey",
section: "Section_SubSection",
key: "Key_SubKey",
},
{
name: "OneDotOneDash",
encoded: "Section_0X2E_SubSection__Key_0X2D_SubKey",
section: "Section.SubSection",
key: "Key-SubKey",
},
{
name: "OneDotOneEncodedOneDash",
encoded: "Section_0X2E_0X2E_Sub_0X2D_Section__Key_0X2D_SubKey",
section: "Section.0X2E_Sub-Section",
key: "Key-SubKey",
},
{
name: "EncodedUnderscore",
encoded: "Section__0X5F_0X2E_Sub_0X2D_Section__Key_0X2D__0X2D_SubKey",
section: "Section__0X2E_Sub-Section",
key: "Key--SubKey",
},
{
name: "EncodedUtf8",
encoded: "Section__0XE280A6_Sub_0X2D_Section__Key_0X2D__0X2D_SubKey",
section: "Section_…Sub-Section",
key: "Key--SubKey",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotSection, gotKey := DecodeSectionKey(tt.encoded)
if gotSection != tt.section {
t.Errorf("DecodeSectionKey() gotSection = %v, want %v", gotSection, tt.section)
}
if gotKey != tt.key {
t.Errorf("DecodeSectionKey() gotKey = %v, want %v", gotKey, tt.key)
}
})
}
}
2 changes: 2 additions & 0 deletions docker/root/etc/s6/gitea/setup
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Member

@sapk sapk Oct 19, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we drop the template and for example do ?

SECRET_KEY=${SECRET_KEY:-""}
GITEA__security__SECRET_KEY=${GITEA__security__SECRET_KEY:-"$SECRET_KEY"}

(maybe they can get in one line)
The goal could be to deprecate the old variables and use the new generic only later ?
So that ${GITEA_CUSTOM}/conf/app.ini is only write once and drop template app.ini


/app/gitea/gitea environment-to-ini -c ${GITEA_CUSTOM}/conf/app.ini

chown ${USER}:git ${GITEA_CUSTOM}/conf/app.ini
fi

Expand Down
4 changes: 4 additions & 0 deletions docs/content/doc/installation/with-docker.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,10 @@ You can configure some of Gitea's settings via environment variables:
* `USER_UID`: **1000**: The UID (Unix user ID) of the user that runs Gitea within the container. Match this to the UID of the owner of the `/data` volume if using host volumes (this is not necessary with named volumes).
* `USER_GID`: **1000**: The GID (Unix group ID) of the user that runs Gitea within the container. Match this to the GID of the owner of the `/data` volume if using host volumes (this is not necessary with named volumes).

Any other configuration value can be set using environment variables
of the form: `GITEA__SECTION_NAME__KEY_NAME`. See the
`environment-to-ini` command for more information.

# Customization

Customization files described [here](https://docs.gitea.io/en-us/customizing-gitea/) should
Expand Down
34 changes: 34 additions & 0 deletions docs/content/doc/usage/command-line.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,3 +281,37 @@ provided key. You should also set the value
NB: opensshd requires the gitea program to be owned by root and not
writable by group or others. The program must be specified by an absolute
path.

#### environment-to-ini

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:

```ini
...

[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.


- Options:
- `--out name`, `-o name`: Name of the adjusted ini file to be created. Optional. (default: The gitea conf file will be changed in place).
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ arguments - which can alternatively be run by running the subcommand web.`
cmd.CmdMigrate,
cmd.CmdKeys,
cmd.CmdConvert,
cmd.CmdEnvironmentToIni,
}
// Now adjust these commands to add our global configuration options

Expand Down