Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
9 changes: 2 additions & 7 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,16 @@ WORKDIR ${GOPATH}/src/code.gitea.io/gitea
RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
&& make clean-all build

# Begin env-to-ini build
RUN go build contrib/environment-to-ini/environment-to-ini.go

# Copy local files
COPY docker/root /tmp/local

# Set permissions
RUN chmod 755 /tmp/local/usr/bin/entrypoint \
/tmp/local/usr/local/bin/gitea \
/tmp/local/usr/local/bin/* \
/tmp/local/etc/s6/gitea/* \
/tmp/local/etc/s6/openssh/* \
/tmp/local/etc/s6/.s6-svscan/* \
/go/src/code.gitea.io/gitea/gitea \
/go/src/code.gitea.io/gitea/environment-to-ini
/go/src/code.gitea.io/gitea/gitea

FROM docker.io/library/alpine:3.22
LABEL maintainer="[email protected]"
Expand Down Expand Up @@ -82,4 +78,3 @@ CMD ["/usr/bin/s6-svscan", "/etc/s6"]

COPY --from=build-env /tmp/local /
COPY --from=build-env /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
COPY --from=build-env /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini
11 changes: 2 additions & 9 deletions Dockerfile.rootless
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,12 @@ WORKDIR ${GOPATH}/src/code.gitea.io/gitea
RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
&& make clean-all build

# Begin env-to-ini build
RUN go build contrib/environment-to-ini/environment-to-ini.go

# Copy local files
COPY docker/rootless /tmp/local

# Set permissions
RUN chmod 755 /tmp/local/usr/local/bin/docker-entrypoint.sh \
/tmp/local/usr/local/bin/docker-setup.sh \
/tmp/local/usr/local/bin/gitea \
/go/src/code.gitea.io/gitea/gitea \
/go/src/code.gitea.io/gitea/environment-to-ini
RUN chmod 755 /tmp/local/usr/local/bin/* \
/go/src/code.gitea.io/gitea/gitea

FROM docker.io/library/alpine:3.22
LABEL maintainer="[email protected]"
Expand Down Expand Up @@ -71,7 +65,6 @@ RUN chown git:git /var/lib/gitea /etc/gitea

COPY --from=build-env /tmp/local /
COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini

# git:git
USER 1000:1000
Expand Down
145 changes: 145 additions & 0 deletions cmd/config_update.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package cmd

import (
"context"
"errors"
"fmt"
"os"

"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"

"github.com/urfave/cli/v3"
)

func cmdConfig() *cli.Command {
subcmdConfigUpdateIni := &cli.Command{
Name: "update-ini",
Usage: "Load an existing INI file, apply environment variables, keep specified keys, and output to a new INI file.",
Description: `
Help users to update the gitea configuration INI file:
* Keep specified keys to for the new INI file.
* Map environment variables to values in the INI.

# Keep Specified Keys

If you need to re-create the configuration file with only a subset of keys,
you can provide an INI template file and use the "--config-key-template" flag.
For example, if a helm chart needs to reset the settings and only keep SECRET_KEY,
it can use a template file like:

[security]
SECRET_KEY=

$ ./gitea config update-ini --config app-old.ini --config-key-template app-template.ini --out app-new.ini

# Map Environment Variables to INI Configuration

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 of the form "GITEA__section_name__KEY_NAME__FILE"
will be mapped to the ini section "[section_name]" and the key
"KEY_NAME" with the value loaded from the specified file.

Environment variable keys can only contain characters "0-9A-Z_",
if a section or key name contains dot ".", it needs to be escaped as _0x2E_.
For example, to apply this config:

[git.config]
foo.bar=val

$ export GITEA__git_0x2E_config__foo_0x2E_bar=val

# Put All Together

$ ./gitea config update-ini --config app.ini --config-key-template app-template.ini --apply-env
`,
Flags: []cli.Flag{
// "--config" flag is provided by global flags, and this flag is also used by "environment-to-ini" script wrapper
&cli.StringFlag{
Name: "config-key-template",
Usage: "An INI template file containing keys for keeping. Only the keys defined in the INI template will be kept from old config. If not set, all keys will be kept.",
},
&cli.BoolFlag{
Name: "apply-env",
Usage: "Apply all GITEA__* variables from the environment to the config.",
},
&cli.StringFlag{
Name: "out",
Usage: "Destination config file to write to. If not set, will overwrite the source config file.",
},
},
Action: runConfigUpdateIni,
}

return &cli.Command{
Name: "config",
Usage: "Manage Gitea configuration",
Commands: []*cli.Command{
subcmdConfigUpdateIni,
},
}
}

func runConfigUpdateIni(_ context.Context, c *cli.Command) error {
// the config system may change the environment variables, so get a copy first, to be used later
env := append([]string{}, os.Environ()...)
if !c.IsSet("config") {
return errors.New("flag is required but not set: --config")
}

configFileIn := c.String("config")
cfgIn, err := setting.NewConfigProviderFromFile(configFileIn)
if err != nil {
return fmt.Errorf("failed to load config file %q: %v", configFileIn, err)
}

configFileOut := c.String("out")
configFileOut = util.IfZero(configFileOut, configFileIn)
needWriteOut := configFileOut != configFileIn

cfgOut := cfgIn
if c.IsSet("config-key-template") {
needWriteOut = true
configKeepTemplate := c.String("config-key-template")
cfgOut, err = setting.NewConfigProviderFromFile(configKeepTemplate)
if err != nil {
return fmt.Errorf("failed to load config keep template file %q: %v", configKeepTemplate, err)
}

for _, secOut := range cfgOut.Sections() {
for _, keyOut := range secOut.Keys() {
secIn := cfgIn.Section(secOut.Name())
keyIn := setting.ConfigSectionKey(secIn, keyOut.Name())
if keyIn != nil {
keyOut.SetValue(keyIn.String())
} else {
secOut.DeleteKey(keyOut.Name())
}
}
if len(secOut.Keys()) == 0 {
cfgOut.DeleteSection(secOut.Name())
}
}
}

if c.Bool("apply-env") {
if setting.EnvironmentToConfig(cfgOut, env) {
needWriteOut = true
}
}

if needWriteOut {
_, _ = fmt.Fprintf(c.Writer, "Saving config to: %q\n", configFileOut)
err = cfgOut.SaveTo(configFileOut)
if err != nil {
return err
}
}
return nil
}
72 changes: 72 additions & 0 deletions cmd/config_update_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package cmd

import (
"os"
"testing"

"github.com/stretchr/testify/require"
)

func TestConfigUpdate(t *testing.T) {
configOld := t.TempDir() + "/app-old.ini"
configTemplate := t.TempDir() + "/app-template.ini"
_ = os.WriteFile(configOld, []byte(`
[sec]
k1=v1
k2=v2
`), os.ModePerm)

_ = os.WriteFile(configTemplate, []byte(`
[sec]
k1=in-template

[sec2]
k3=v3
`), os.ModePerm)

t.Setenv("GITEA__EnV__KeY", "val")

t.Run("OutputToNewWithEnv", func(t *testing.T) {
configNew := t.TempDir() + "/app-new.ini"
err := NewMainApp(AppVersion{}).Run(t.Context(), []string{
"./gitea", "--config", configOld, "config", "update-ini",
"--apply-env",
"--config-key-template", configTemplate,
"--out", configNew,
})
require.NoError(t, err)

// "k1" old value is kept because its key is in the template
// "k2" is removed because it isn't in the template
// "k3" isn't in new config because it isn't in the old config
// [env] is applied from environment variable
data, _ := os.ReadFile(configNew)
require.Equal(t, `[sec]
k1 = v1

[env]
KeY = val
`, string(data))
})

t.Run("OutputToExisting(environment-to-ini)", func(t *testing.T) {
err := NewMainApp(AppVersion{}).Run(t.Context(), []string{
"./gitea", "config", "update-ini",
"--apply-env",
"--config", configOld,
})
require.NoError(t, err)

data, _ := os.ReadFile(configOld)
require.Equal(t, `[sec]
k1 = v1
k2 = v2

[env]
KeY = val
`, string(data))
})
}
1 change: 1 addition & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ func NewMainApp(appVer AppVersion) *cli.Command {

// these sub-commands do not need the config file, and they do not depend on any path or environment variable.
subCmdStandalone := []*cli.Command{
cmdConfig(),
cmdCert(),
CmdGenerate,
CmdDocs,
Expand Down
47 changes: 0 additions & 47 deletions contrib/environment-to-ini/README

This file was deleted.

Loading