feat: support more HELMFILE_* env vars as flag fallbacks
Adds env-var fallbacks for global flags, mirroring the existing
HELMFILE_ENVIRONMENT / HELMFILE_KUBE_CONTEXT pattern:
* --helm-binary -> HELMFILE_HELM_BINARY
* --kustomize-binary -> HELMFILE_KUSTOMIZE_BINARY
* --log-level -> HELMFILE_LOG_LEVEL
* --debug -> HELMFILE_DEBUG (expecting "true" lower case)
* --quiet -> HELMFILE_QUIET (expecting "true" lower case)
* --no-color -> HELMFILE_NO_COLOR (expecting "true" lower case),
additionally honors NO_COLOR per no-color.org
(any non-empty value disables color)
Flag values still take precedence; env vars are consulted only when the
flag is unset. The string-flag default values ("helm", "kustomize",
"info") move into the accessor methods so the env-var fallback can
actually trigger when no flag is passed.
Signed-off-by: Dominik Schmidt <dev@dominik-schmidt.de>
This commit is contained in:
parent
8e2ddb863f
commit
987172454e
22
cmd/root.go
22
cmd/root.go
|
|
@ -48,6 +48,8 @@ func toCLIError(g *config.GlobalImpl, err error) error {
|
|||
|
||||
// NewRootCmd creates the root command for the CLI.
|
||||
func NewRootCmd(globalConfig *config.GlobalOptions) (*cobra.Command, error) {
|
||||
globalImpl := config.NewGlobalImpl(globalConfig)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "helmfile",
|
||||
Short: globalUsage,
|
||||
|
|
@ -58,11 +60,11 @@ func NewRootCmd(globalConfig *config.GlobalOptions) (*cobra.Command, error) {
|
|||
PersistentPreRunE: func(c *cobra.Command, args []string) error {
|
||||
// Valid levels:
|
||||
// https://github.com/uber-go/zap/blob/7e7e266a8dbce911a49554b945538c5b950196b8/zapcore/level.go#L126
|
||||
logLevel := globalConfig.LogLevel
|
||||
logLevel := globalImpl.LogLevel()
|
||||
switch {
|
||||
case globalConfig.Debug:
|
||||
case globalImpl.Debug():
|
||||
logLevel = "debug"
|
||||
case globalConfig.Quiet:
|
||||
case globalImpl.Quiet():
|
||||
logLevel = "warn"
|
||||
}
|
||||
|
||||
|
|
@ -83,8 +85,6 @@ func NewRootCmd(globalConfig *config.GlobalOptions) (*cobra.Command, error) {
|
|||
|
||||
flags.ParseErrorsAllowlist.UnknownFlags = true
|
||||
|
||||
globalImpl := config.NewGlobalImpl(globalConfig)
|
||||
|
||||
// when set environment HELMFILE_UPGRADE_NOTICE_DISABLED any value, skip upgrade notice.
|
||||
var versionOpts []extension.CobraOption
|
||||
if os.Getenv(envvar.UpgradeNoticeDisabled) == "" {
|
||||
|
|
@ -121,8 +121,8 @@ func NewRootCmd(globalConfig *config.GlobalOptions) (*cobra.Command, error) {
|
|||
}
|
||||
|
||||
func setGlobalOptionsForRootCmd(fs *pflag.FlagSet, globalOptions *config.GlobalOptions) {
|
||||
fs.StringVarP(&globalOptions.HelmBinary, "helm-binary", "b", app.DefaultHelmBinary, "Path to the helm binary")
|
||||
fs.StringVarP(&globalOptions.KustomizeBinary, "kustomize-binary", "k", app.DefaultKustomizeBinary, "Path to the kustomize binary")
|
||||
fs.StringVarP(&globalOptions.HelmBinary, "helm-binary", "b", "", fmt.Sprintf(`Path to the helm binary. Overrides "HELMFILE_HELM_BINARY" OS environment variable when specified (default %q)`, app.DefaultHelmBinary))
|
||||
fs.StringVarP(&globalOptions.KustomizeBinary, "kustomize-binary", "k", "", fmt.Sprintf(`Path to the kustomize binary. Overrides "HELMFILE_KUSTOMIZE_BINARY" OS environment variable when specified (default %q)`, app.DefaultKustomizeBinary))
|
||||
fs.StringVarP(&globalOptions.File, "file", "f", "", "load config from file or directory. defaults to \"`helmfile.yaml`\" or \"helmfile.yaml.gotmpl\" or \"helmfile.d\" (means \"helmfile.d/*.yaml\" or \"helmfile.d/*.yaml.gotmpl\") in this preference. Specify - to load the config from the standard input.")
|
||||
fs.StringVarP(&globalOptions.Environment, "environment", "e", "", `specify the environment name. Overrides "HELMFILE_ENVIRONMENT" OS environment variable when specified. defaults to "default"`)
|
||||
fs.StringArrayVar(&globalOptions.StateValuesSet, "state-values-set", nil, "set state values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2). Used to override .Values within the helmfile template (not values template).")
|
||||
|
|
@ -134,13 +134,13 @@ func setGlobalOptionsForRootCmd(fs *pflag.FlagSet, globalOptions *config.GlobalO
|
|||
fs.BoolVar(&globalOptions.DisableForceUpdate, "disable-force-update", false, `do not force helm repos to update when executing "helm repo add" (Helm 3 only)`)
|
||||
fs.BoolVar(&globalOptions.EnforcePluginVerification, "enforce-plugin-verification", false, `fail plugin installation if verification is not supported (for security purposes)`)
|
||||
fs.BoolVar(&globalOptions.HelmOCIPlainHTTP, "oci-plain-http", false, `use plain HTTP for OCI registries (required for local/insecure registries in Helm 4)`)
|
||||
fs.BoolVarP(&globalOptions.Quiet, "quiet", "q", false, "Silence output. Equivalent to log-level warn")
|
||||
fs.BoolVarP(&globalOptions.Quiet, "quiet", "q", false, `Silence output. Equivalent to log-level warn. Overrides "HELMFILE_QUIET" OS environment variable when specified`)
|
||||
fs.StringVar(&globalOptions.Kubeconfig, "kubeconfig", "", "Use a particular kubeconfig file")
|
||||
fs.StringVar(&globalOptions.KubeContext, "kube-context", "", `Set kubectl context. Overrides "HELMFILE_KUBE_CONTEXT" OS environment variable when specified. Uses current kubectl context by default`)
|
||||
fs.BoolVar(&globalOptions.Debug, "debug", false, "Enable verbose output for Helm and set log-level to debug, this disables --quiet/-q effect")
|
||||
fs.BoolVar(&globalOptions.Debug, "debug", false, `Enable verbose output for Helm and set log-level to debug, this disables --quiet/-q effect. Overrides "HELMFILE_DEBUG" OS environment variable when specified`)
|
||||
fs.BoolVar(&globalOptions.Color, "color", false, "Output with color")
|
||||
fs.BoolVar(&globalOptions.NoColor, "no-color", false, "Output without color")
|
||||
fs.StringVar(&globalOptions.LogLevel, "log-level", "info", "Set log level, default info")
|
||||
fs.BoolVar(&globalOptions.NoColor, "no-color", false, `Output without color. Overrides "HELMFILE_NO_COLOR" and "NO_COLOR" OS environment variables when specified`)
|
||||
fs.StringVar(&globalOptions.LogLevel, "log-level", "", `Set log level. Overrides "HELMFILE_LOG_LEVEL" OS environment variable when specified (default "info")`)
|
||||
fs.StringVarP(&globalOptions.Namespace, "namespace", "n", "", `Set namespace. Overrides "HELMFILE_NAMESPACE" OS environment variable when specified. Uses the namespace set in the context by default, and is available in templates as {{ .Namespace }}`)
|
||||
fs.StringVarP(&globalOptions.Chart, "chart", "c", "", "Set chart. Uses the chart set in release by default, and is available in template as {{ .Chart }}")
|
||||
fs.StringArrayVarP(&globalOptions.Selector, "selector", "l", nil, `Only run using the releases that match labels. Labels can take the form of foo=bar or foo!=bar.
|
||||
|
|
|
|||
|
|
@ -109,12 +109,63 @@ func (g *GlobalImpl) SetSet(set map[string]any) {
|
|||
|
||||
// HelmBinary returns the path to the Helm binary.
|
||||
func (g *GlobalImpl) HelmBinary() string {
|
||||
return g.GlobalOptions.HelmBinary
|
||||
var helmBinary string
|
||||
|
||||
switch {
|
||||
case g.GlobalOptions.HelmBinary != "":
|
||||
helmBinary = g.GlobalOptions.HelmBinary
|
||||
case os.Getenv("HELMFILE_HELM_BINARY") != "":
|
||||
helmBinary = os.Getenv("HELMFILE_HELM_BINARY")
|
||||
default:
|
||||
helmBinary = state.DefaultHelmBinary
|
||||
}
|
||||
return helmBinary
|
||||
}
|
||||
|
||||
// KustomizeBinary returns the path to the Kustomize binary.
|
||||
func (g *GlobalImpl) KustomizeBinary() string {
|
||||
return g.GlobalOptions.KustomizeBinary
|
||||
var kustomizeBinary string
|
||||
|
||||
switch {
|
||||
case g.GlobalOptions.KustomizeBinary != "":
|
||||
kustomizeBinary = g.GlobalOptions.KustomizeBinary
|
||||
case os.Getenv("HELMFILE_KUSTOMIZE_BINARY") != "":
|
||||
kustomizeBinary = os.Getenv("HELMFILE_KUSTOMIZE_BINARY")
|
||||
default:
|
||||
kustomizeBinary = state.DefaultKustomizeBinary
|
||||
}
|
||||
return kustomizeBinary
|
||||
}
|
||||
|
||||
// LogLevel returns the log level to use.
|
||||
func (g *GlobalImpl) LogLevel() string {
|
||||
var logLevel string
|
||||
|
||||
switch {
|
||||
case g.GlobalOptions.LogLevel != "":
|
||||
logLevel = g.GlobalOptions.LogLevel
|
||||
case os.Getenv("HELMFILE_LOG_LEVEL") != "":
|
||||
logLevel = os.Getenv("HELMFILE_LOG_LEVEL")
|
||||
default:
|
||||
logLevel = "info"
|
||||
}
|
||||
return logLevel
|
||||
}
|
||||
|
||||
// Debug returns whether debug output is enabled.
|
||||
func (g *GlobalImpl) Debug() bool {
|
||||
if g.GlobalOptions.Debug {
|
||||
return true
|
||||
}
|
||||
return os.Getenv(envvar.Debug) == "true"
|
||||
}
|
||||
|
||||
// Quiet returns whether quiet output is enabled.
|
||||
func (g *GlobalImpl) Quiet() bool {
|
||||
if g.GlobalOptions.Quiet {
|
||||
return true
|
||||
}
|
||||
return os.Getenv(envvar.Quiet) == "true"
|
||||
}
|
||||
|
||||
// Kubeconfig returns the path to the kubeconfig file to use.
|
||||
|
|
@ -259,7 +310,14 @@ func (g *GlobalImpl) Color() bool {
|
|||
|
||||
// NoColor returns the no color flag
|
||||
func (g *GlobalImpl) NoColor() bool {
|
||||
return g.GlobalOptions.NoColor
|
||||
if g.GlobalOptions.NoColor {
|
||||
return true
|
||||
}
|
||||
if os.Getenv(envvar.NoColor) == "true" {
|
||||
return true
|
||||
}
|
||||
// Honor the de-facto https://no-color.org/ standard: any non-empty value disables color.
|
||||
return os.Getenv("NO_COLOR") != ""
|
||||
}
|
||||
|
||||
// Env returns the environment to use.
|
||||
|
|
@ -296,7 +354,7 @@ func (g *GlobalImpl) Interactive() bool {
|
|||
// Args returns the args to use for helm
|
||||
func (g *GlobalImpl) Args() string {
|
||||
args := g.GlobalOptions.Args
|
||||
enableHelmDebug := g.Debug
|
||||
enableHelmDebug := g.Debug()
|
||||
|
||||
if enableHelmDebug {
|
||||
args = fmt.Sprintf("%s %s", args, "--debug")
|
||||
|
|
|
|||
|
|
@ -119,3 +119,254 @@ func TestNamespace(t *testing.T) {
|
|||
}
|
||||
os.Unsetenv(envvar.Namespace)
|
||||
}
|
||||
|
||||
// TestHelmBinary tests the helm-binary flag and HELMFILE_HELM_BINARY env var fallback
|
||||
func TestHelmBinary(t *testing.T) {
|
||||
tests := []struct {
|
||||
opts GlobalOptions
|
||||
env string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
opts: GlobalOptions{},
|
||||
env: "",
|
||||
expected: "helm",
|
||||
},
|
||||
{
|
||||
opts: GlobalOptions{},
|
||||
env: "envset",
|
||||
expected: "envset",
|
||||
},
|
||||
{
|
||||
opts: GlobalOptions{HelmBinary: "flagset"},
|
||||
env: "",
|
||||
expected: "flagset",
|
||||
},
|
||||
{
|
||||
opts: GlobalOptions{HelmBinary: "flagset"},
|
||||
env: "envset",
|
||||
expected: "flagset",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
os.Setenv(envvar.HelmBinary, test.env)
|
||||
received := NewGlobalImpl(&test.opts).HelmBinary()
|
||||
require.Equalf(t, test.expected, received, "HelmBinary expected %s, received %s", test.expected, received)
|
||||
}
|
||||
os.Unsetenv(envvar.HelmBinary)
|
||||
}
|
||||
|
||||
// TestKustomizeBinary tests the kustomize-binary flag and HELMFILE_KUSTOMIZE_BINARY env var fallback
|
||||
func TestKustomizeBinary(t *testing.T) {
|
||||
tests := []struct {
|
||||
opts GlobalOptions
|
||||
env string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
opts: GlobalOptions{},
|
||||
env: "",
|
||||
expected: "kustomize",
|
||||
},
|
||||
{
|
||||
opts: GlobalOptions{},
|
||||
env: "envset",
|
||||
expected: "envset",
|
||||
},
|
||||
{
|
||||
opts: GlobalOptions{KustomizeBinary: "flagset"},
|
||||
env: "",
|
||||
expected: "flagset",
|
||||
},
|
||||
{
|
||||
opts: GlobalOptions{KustomizeBinary: "flagset"},
|
||||
env: "envset",
|
||||
expected: "flagset",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
os.Setenv(envvar.KustomizeBinary, test.env)
|
||||
received := NewGlobalImpl(&test.opts).KustomizeBinary()
|
||||
require.Equalf(t, test.expected, received, "KustomizeBinary expected %s, received %s", test.expected, received)
|
||||
}
|
||||
os.Unsetenv(envvar.KustomizeBinary)
|
||||
}
|
||||
|
||||
// TestLogLevel tests the log-level flag and HELMFILE_LOG_LEVEL env var fallback
|
||||
func TestLogLevel(t *testing.T) {
|
||||
tests := []struct {
|
||||
opts GlobalOptions
|
||||
env string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
opts: GlobalOptions{},
|
||||
env: "",
|
||||
expected: "info",
|
||||
},
|
||||
{
|
||||
opts: GlobalOptions{},
|
||||
env: "envset",
|
||||
expected: "envset",
|
||||
},
|
||||
{
|
||||
opts: GlobalOptions{LogLevel: "flagset"},
|
||||
env: "",
|
||||
expected: "flagset",
|
||||
},
|
||||
{
|
||||
opts: GlobalOptions{LogLevel: "flagset"},
|
||||
env: "envset",
|
||||
expected: "flagset",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
os.Setenv(envvar.LogLevel, test.env)
|
||||
received := NewGlobalImpl(&test.opts).LogLevel()
|
||||
require.Equalf(t, test.expected, received, "LogLevel expected %s, received %s", test.expected, received)
|
||||
}
|
||||
os.Unsetenv(envvar.LogLevel)
|
||||
}
|
||||
|
||||
// TestDebug tests the debug flag and HELMFILE_DEBUG env var fallback
|
||||
func TestDebug(t *testing.T) {
|
||||
tests := []struct {
|
||||
opts GlobalOptions
|
||||
env string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
opts: GlobalOptions{},
|
||||
env: "",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
opts: GlobalOptions{},
|
||||
env: "true",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
opts: GlobalOptions{},
|
||||
env: "anything",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
opts: GlobalOptions{Debug: true},
|
||||
env: "",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
opts: GlobalOptions{Debug: true},
|
||||
env: "true",
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
os.Setenv(envvar.Debug, test.env)
|
||||
received := NewGlobalImpl(&test.opts).Debug()
|
||||
require.Equalf(t, test.expected, received, "Debug expected %t, received %t", test.expected, received)
|
||||
}
|
||||
os.Unsetenv(envvar.Debug)
|
||||
}
|
||||
|
||||
// TestQuiet tests the quiet flag and HELMFILE_QUIET env var fallback
|
||||
func TestQuiet(t *testing.T) {
|
||||
tests := []struct {
|
||||
opts GlobalOptions
|
||||
env string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
opts: GlobalOptions{},
|
||||
env: "",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
opts: GlobalOptions{},
|
||||
env: "true",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
opts: GlobalOptions{},
|
||||
env: "anything",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
opts: GlobalOptions{Quiet: true},
|
||||
env: "",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
opts: GlobalOptions{Quiet: true},
|
||||
env: "true",
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
os.Setenv(envvar.Quiet, test.env)
|
||||
received := NewGlobalImpl(&test.opts).Quiet()
|
||||
require.Equalf(t, test.expected, received, "Quiet expected %t, received %t", test.expected, received)
|
||||
}
|
||||
os.Unsetenv(envvar.Quiet)
|
||||
}
|
||||
|
||||
// TestNoColor tests the no-color flag, HELMFILE_NO_COLOR and NO_COLOR env var fallbacks
|
||||
func TestNoColor(t *testing.T) {
|
||||
tests := []struct {
|
||||
opts GlobalOptions
|
||||
helmfileEnv string
|
||||
standardEnv string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
opts: GlobalOptions{},
|
||||
helmfileEnv: "",
|
||||
standardEnv: "",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
opts: GlobalOptions{},
|
||||
helmfileEnv: "true",
|
||||
standardEnv: "",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
opts: GlobalOptions{},
|
||||
helmfileEnv: "anything",
|
||||
standardEnv: "",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
opts: GlobalOptions{},
|
||||
helmfileEnv: "",
|
||||
standardEnv: "1",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
opts: GlobalOptions{},
|
||||
helmfileEnv: "",
|
||||
standardEnv: "anything",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
opts: GlobalOptions{NoColor: true},
|
||||
helmfileEnv: "",
|
||||
standardEnv: "",
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
os.Setenv(envvar.NoColor, test.helmfileEnv)
|
||||
os.Setenv("NO_COLOR", test.standardEnv)
|
||||
received := NewGlobalImpl(&test.opts).NoColor()
|
||||
require.Equalf(t, test.expected, received, "NoColor expected %t, received %t", test.expected, received)
|
||||
}
|
||||
os.Unsetenv(envvar.NoColor)
|
||||
os.Unsetenv("NO_COLOR")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,12 @@ const (
|
|||
Environment = "HELMFILE_ENVIRONMENT"
|
||||
KubeContext = "HELMFILE_KUBE_CONTEXT"
|
||||
Namespace = "HELMFILE_NAMESPACE"
|
||||
HelmBinary = "HELMFILE_HELM_BINARY"
|
||||
KustomizeBinary = "HELMFILE_KUSTOMIZE_BINARY"
|
||||
LogLevel = "HELMFILE_LOG_LEVEL"
|
||||
Debug = "HELMFILE_DEBUG"
|
||||
Quiet = "HELMFILE_QUIET"
|
||||
NoColor = "HELMFILE_NO_COLOR"
|
||||
FilePath = "HELMFILE_FILE_PATH"
|
||||
TempDir = "HELMFILE_TEMPDIR"
|
||||
UpgradeNoticeDisabled = "HELMFILE_UPGRADE_NOTICE_DISABLED"
|
||||
|
|
|
|||
Loading…
Reference in New Issue