Skip to content

Commit 25c7546

Browse files
committed
feat(config): add environment variable expansion with defaults
- Introduced a new function `expandEnvWithDefaults` to support environment variable expansion with default values, accommodating various bash-style syntaxes. - Added comprehensive unit tests to validate the functionality of environment variable expansion, including edge cases and complex scenarios. - Created a new test configuration file and example tests to demonstrate the usage of environment variables with defaults in real-world configurations. - Ensured backward compatibility with existing environment variable expansion methods.
1 parent 1c1b9de commit 25c7546

4 files changed

Lines changed: 874 additions & 1 deletion

File tree

internal/config/sources/file.go

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,10 +460,11 @@ func (fs *FileSource) expandEnvironmentVariables(data map[string]any) map[string
460460
}
461461

462462
// expandValue recursively expands environment variables in a value.
463+
// Supports both standard ${VAR} and bash-style ${VAR:-default} syntax.
463464
func (fs *FileSource) expandValue(value any) any {
464465
switch v := value.(type) {
465466
case string:
466-
return os.ExpandEnv(v)
467+
return expandEnvWithDefaults(v)
467468
case map[string]any:
468469
return fs.expandEnvironmentVariables(v)
469470
case []any:
@@ -478,6 +479,62 @@ func (fs *FileSource) expandValue(value any) any {
478479
}
479480
}
480481

482+
// expandEnvWithDefaults expands environment variables with support for default values.
483+
// Supports the following bash-style syntax:
484+
// - $VAR or ${VAR} - standard expansion (empty string if not set)
485+
// - ${VAR:-default} - use default if VAR is unset or empty
486+
// - ${VAR-default} - use default only if VAR is unset (not if empty)
487+
// - ${VAR:=default} - assign default if VAR is unset or empty (and return it)
488+
// - ${VAR=default} - assign default only if VAR is unset (and return it)
489+
func expandEnvWithDefaults(s string) string {
490+
return os.Expand(s, func(key string) string {
491+
// ${VAR:-default} - use default if unset or empty
492+
if idx := strings.Index(key, ":-"); idx > 0 {
493+
varName := key[:idx]
494+
defaultValue := key[idx+2:]
495+
if value := os.Getenv(varName); value != "" {
496+
return value
497+
}
498+
return defaultValue
499+
}
500+
501+
// ${VAR-default} - use default only if unset
502+
if idx := strings.Index(key, "-"); idx > 0 {
503+
varName := key[:idx]
504+
defaultValue := key[idx+1:]
505+
if value, exists := os.LookupEnv(varName); exists {
506+
return value
507+
}
508+
return defaultValue
509+
}
510+
511+
// ${VAR:=default} - assign and use default if unset or empty
512+
if idx := strings.Index(key, ":="); idx > 0 {
513+
varName := key[:idx]
514+
defaultValue := key[idx+2:]
515+
if value := os.Getenv(varName); value != "" {
516+
return value
517+
}
518+
os.Setenv(varName, defaultValue)
519+
return defaultValue
520+
}
521+
522+
// ${VAR=default} - assign and use default only if unset
523+
if idx := strings.Index(key, "="); idx > 0 {
524+
varName := key[:idx]
525+
defaultValue := key[idx+1:]
526+
if value, exists := os.LookupEnv(varName); exists {
527+
return value
528+
}
529+
os.Setenv(varName, defaultValue)
530+
return defaultValue
531+
}
532+
533+
// Standard expansion
534+
return os.Getenv(key)
535+
})
536+
}
537+
481538
// expandSecrets recursively expands secret references.
482539
func (fs *FileSource) expandSecrets(ctx context.Context, data map[string]any) (map[string]any, error) {
483540
result := make(map[string]any)

0 commit comments

Comments
 (0)