Skip to content

Commit d981d63

Browse files
ndeloofOrlovEvgeny
authored andcommitted
fix ability to override values set in env file by interpolation
Signed-off-by: Nicolas De Loof <[email protected]> Signed-off-by: Evgenii Orlov <[email protected]>
1 parent 5d3409a commit d981d63

File tree

4 files changed

+41
-55
lines changed

4 files changed

+41
-55
lines changed

dotenv/env.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func GetEnvFromFile(currentEnv map[string]string, filenames []string) (map[strin
5656
return envMap, err
5757
}
5858

59-
env, err := ParseWithLookup(bytes.NewReader(b), func(k string) (string, bool) {
59+
err = parseWithLookup(bytes.NewReader(b), envMap, func(k string) (string, bool) {
6060
v, ok := currentEnv[k]
6161
if ok {
6262
return v, true
@@ -67,9 +67,6 @@ func GetEnvFromFile(currentEnv map[string]string, filenames []string) (map[strin
6767
if err != nil {
6868
return envMap, fmt.Errorf("failed to read %s: %w", dotEnvFile, err)
6969
}
70-
for k, v := range env {
71-
envMap[k] = v
72-
}
7370
}
7471

7572
return envMap, nil

dotenv/godotenv_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,3 +741,34 @@ func TestLoadWithFormat(t *testing.T) {
741741
assert.NilError(t, err)
742742
assert.DeepEqual(t, expectedValues, env)
743743
}
744+
745+
func TestMultipleFiles(t *testing.T) {
746+
base := filepath.Join(t.TempDir(), "base.env")
747+
err := os.WriteFile(base, []byte(`
748+
ENV_HOSTNAME=localhost
749+
ENV_MY_URL="http://${ENV_HOSTNAME}"
750+
`), 0o600)
751+
assert.NilError(t, err)
752+
753+
override := filepath.Join(t.TempDir(), "override.env")
754+
err = os.WriteFile(override, []byte(`
755+
ENV_HOSTNAME=dev.my-company.com
756+
ENV_MY_URL="http://${ENV_HOSTNAME}"
757+
`), 0o600)
758+
assert.NilError(t, err)
759+
760+
env, err := GetEnvFromFile(nil, []string{base, override})
761+
assert.NilError(t, err)
762+
assert.DeepEqual(t, env, map[string]string{
763+
"ENV_HOSTNAME": "dev.my-company.com",
764+
"ENV_MY_URL": "http://dev.my-company.com",
765+
})
766+
767+
osEnv := map[string]string{"ENV_HOSTNAME": "host.local"}
768+
env, err = GetEnvFromFile(osEnv, []string{base, override})
769+
assert.NilError(t, err)
770+
assert.DeepEqual(t, env, map[string]string{
771+
"ENV_HOSTNAME": "dev.my-company.com",
772+
"ENV_MY_URL": "http://host.local",
773+
})
774+
}

transform/defaults.go

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,30 +23,19 @@ import (
2323
// DefaultValues contains the default value transformers for compose fields
2424
var DefaultValues = map[tree.Path]TransformFunc{}
2525

26-
// For backward compatibility
27-
var defaultValues = DefaultValues
28-
2926
func init() {
30-
defaultValues["services.*.build"] = defaultBuildContext
31-
defaultValues["services.*.secrets.*"] = defaultSecretMount
32-
defaultValues["services.*.ports.*"] = portDefaults
33-
defaultValues["services.*.deploy.resources.reservations.devices.*"] = deviceRequestDefaults
34-
defaultValues["services.*.gpus.*"] = deviceRequestDefaults
27+
DefaultValues["services.*.build"] = defaultBuildContext
28+
DefaultValues["services.*.secrets.*"] = defaultSecretMount
29+
DefaultValues["services.*.ports.*"] = portDefaults
30+
DefaultValues["services.*.deploy.resources.reservations.devices.*"] = deviceRequestDefaults
31+
DefaultValues["services.*.gpus.*"] = deviceRequestDefaults
3532
}
3633

37-
// RegisterDefaultTransformer registers a custom transformer for the given path pattern
38-
func RegisterDefaultTransformer(path string, transformer TransformFunc) {
34+
// RegisterDefaultValue registers a custom transformer for the given path pattern
35+
func RegisterDefaultValue(path string, transformer TransformFunc) {
3936
DefaultValues[tree.Path(path)] = transformer
4037
}
4138

42-
// GetDefaultTransformers returns a copy of the current default transformers
43-
func GetDefaultTransformers() map[string]TransformFunc {
44-
result := make(map[string]TransformFunc)
45-
for path, transformer := range DefaultValues {
46-
result[string(path)] = transformer
47-
}
48-
return result
49-
}
5039

5140
// SetDefaultValues transforms a compose model to set default values to missing attributes
5241
func SetDefaultValues(yaml map[string]any) (map[string]any, error) {
@@ -58,7 +47,7 @@ func SetDefaultValues(yaml map[string]any) (map[string]any, error) {
5847
}
5948

6049
func setDefaults(data any, p tree.Path) (any, error) {
61-
for pattern, transformer := range defaultValues {
50+
for pattern, transformer := range DefaultValues {
6251
if p.Matches(pattern) {
6352
t, err := transformer(data, p, false)
6453
if err != nil {

transform/ports.go

Lines changed: 1 addition & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ package transform
1818

1919
import (
2020
"fmt"
21-
21+
2222
"github.com/compose-spec/compose-go/v2/tree"
2323
"github.com/compose-spec/compose-go/v2/types"
2424
"github.com/go-viper/mapstructure/v2"
@@ -102,34 +102,3 @@ func portDefaults(data any, _ tree.Path, _ bool) (any, error) {
102102
return data, nil
103103
}
104104
}
105-
106-
// EnhancedPortDefaults is an example of an enhanced port defaults function
107-
// that supports additional protocols and domain names in the published field.
108-
// This function can be used as a replacement for the default portDefaults
109-
// by registering it with RegisterDefaultTransformer.
110-
//
111-
// Example usage:
112-
// transform.RegisterDefaultTransformer("services.*.ports.*", transform.EnhancedPortDefaults)
113-
//
114-
// This function supports:
115-
// - Additional protocols: "http", "https", "tcp", "udp"
116-
// - Domain names in the published field (e.g., "example.com:80")
117-
// - All existing functionality of the original portDefaults
118-
func EnhancedPortDefaults(data any, _ tree.Path, _ bool) (any, error) {
119-
switch v := data.(type) {
120-
case map[string]any:
121-
// Set default protocol if not specified
122-
if _, ok := v["protocol"]; !ok {
123-
v["protocol"] = "tcp"
124-
}
125-
// Set default mode if not specified
126-
if _, ok := v["mode"]; !ok {
127-
v["mode"] = "ingress"
128-
}
129-
// Additional logic can be added here to handle domain names
130-
// in the published field or other custom transformations
131-
return v, nil
132-
default:
133-
return data, nil
134-
}
135-
}

0 commit comments

Comments
 (0)