mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-30 19:08:37 +00:00 
			
		
		
		
	Intorduce "config edit-ini" sub command to help maintaining INI config file (#35735)
Ref: #32669. Helps addressing https://gitea.com/gitea/helm-chart/issues/356.
This commit is contained in:
		
							
								
								
									
										156
									
								
								cmd/config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								cmd/config.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,156 @@ | ||||
| // 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" | ||||
|  | ||||
| 	"github.com/urfave/cli/v3" | ||||
| ) | ||||
|  | ||||
| func cmdConfig() *cli.Command { | ||||
| 	subcmdConfigEditIni := &cli.Command{ | ||||
| 		Name:  "edit-ini", | ||||
| 		Usage: "Load an existing INI file, apply environment variables, keep specified keys, and output to a new INI file.", | ||||
| 		Description: ` | ||||
| Help users to edit the Gitea configuration INI file. | ||||
|  | ||||
| # 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 for the kept keys and use the "--config-keep-keys" flag. | ||||
| For example, if a helm chart needs to reset the settings and only keep SECRET_KEY, | ||||
| it can use a template file (only keys take effect, values are ignored): | ||||
|  | ||||
|   [security] | ||||
|   SECRET_KEY= | ||||
|  | ||||
| $ ./gitea config edit-ini --config app-old.ini --config-keep-keys app-keys.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 edit-ini --config app.ini --config-keep-keys app-keys.ini --apply-env {--in-place|--out app-new.ini} | ||||
| `, | ||||
| 		Flags: []cli.Flag{ | ||||
| 			// "--config" flag is provided by global flags, and this flag is also used by "environment-to-ini" script wrapper | ||||
| 			// "--in-place" is also used by "environment-to-ini" script wrapper for its old behavior: always overwrite the existing config file | ||||
| 			&cli.BoolFlag{ | ||||
| 				Name:  "in-place", | ||||
| 				Usage: "Output to the same config file as input. This flag will be ignored if --out is set.", | ||||
| 			}, | ||||
| 			&cli.StringFlag{ | ||||
| 				Name:  "config-keep-keys", | ||||
| 				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.", | ||||
| 			}, | ||||
| 		}, | ||||
| 		Action: runConfigEditIni, | ||||
| 	} | ||||
|  | ||||
| 	return &cli.Command{ | ||||
| 		Name:  "config", | ||||
| 		Usage: "Manage Gitea configuration", | ||||
| 		Commands: []*cli.Command{ | ||||
| 			subcmdConfigEditIni, | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func runConfigEditIni(_ 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()...) | ||||
|  | ||||
| 	// don't use the guessed setting.CustomConf, instead, require the user to provide --config explicitly | ||||
| 	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) | ||||
| 	} | ||||
|  | ||||
| 	// determine output config file: use "--out" flag or use "--in-place" flag to overwrite input file | ||||
| 	inPlace := c.Bool("in-place") | ||||
| 	configFileOut := c.String("out") | ||||
| 	if configFileOut == "" { | ||||
| 		if !inPlace { | ||||
| 			return errors.New("either --in-place or --out must be specified") | ||||
| 		} | ||||
| 		configFileOut = configFileIn // in-place edit | ||||
| 	} | ||||
|  | ||||
| 	needWriteOut := configFileOut != configFileIn | ||||
|  | ||||
| 	cfgOut := cfgIn | ||||
| 	configKeepKeys := c.String("config-keep-keys") | ||||
| 	if configKeepKeys != "" { | ||||
| 		needWriteOut = true | ||||
| 		cfgOut, err = setting.NewConfigProviderFromFile(configKeepKeys) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("failed to load config-keep-keys template file %q: %v", configKeepKeys, 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 { | ||||
| 		err = cfgOut.SaveTo(configFileOut) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
		Reference in New Issue
	
	Block a user