diff --git a/cli/options.go b/cli/options.go index f20803b0..215cbeea 100644 --- a/cli/options.go +++ b/cli/options.go @@ -66,7 +66,7 @@ type ProjectOptions struct { // NOTE: For security, the loader does not automatically expose any // process environment variables. For convenience, WithOsEnv can be // used if appropriate. - Environment map[string]string + Environment types.Mapping // EnvFiles are file paths to ".env" files with additional environment // variable data. @@ -256,11 +256,7 @@ func WithDotEnv(o *ProjectOptions) error { if err != nil { return err } - for k, v := range envMap { - if _, set := o.Environment[k]; !set { - o.Environment[k] = v - } - } + o.Environment.Merge(envMap) return nil } diff --git a/cli/options_test.go b/cli/options_test.go index 893e0738..56087597 100644 --- a/cli/options_test.go +++ b/cli/options_test.go @@ -22,6 +22,7 @@ import ( "path/filepath" "testing" + "github.com/compose-spec/compose-go/types" "gotest.tools/v3/assert" "github.com/compose-spec/compose-go/consts" @@ -314,13 +315,13 @@ func TestEnvVariablePrecedence(t *testing.T) { name string dotEnv string osEnv []string - expected map[string]string + expected types.Mapping }{ { "no value set in environment", "FOO=foo\nBAR=${FOO}", nil, - map[string]string{ + types.Mapping{ "FOO": "foo", "BAR": "foo", }, @@ -329,7 +330,7 @@ func TestEnvVariablePrecedence(t *testing.T) { "conflict with value set in environment", "FOO=foo\nBAR=${FOO}", []string{"FOO=zot"}, - map[string]string{ + types.Mapping{ "FOO": "zot", "BAR": "zot", }, diff --git a/loader/include.go b/loader/include.go index 7a736aae..78d68a2e 100644 --- a/loader/include.go +++ b/loader/include.go @@ -73,7 +73,7 @@ func loadInclude(ctx context.Context, filename string, configDetails types.Confi loadOptions.SkipNormalization = true loadOptions.SkipConsistencyCheck = true - env, err := dotenv.GetEnvFromFile(configDetails.Environment, r.ProjectDirectory, r.EnvFile) + envFromFile, err := dotenv.GetEnvFromFile(configDetails.Environment, r.ProjectDirectory, r.EnvFile) if err != nil { return nil, nil, err } @@ -81,7 +81,7 @@ func loadInclude(ctx context.Context, filename string, configDetails types.Confi config := types.ConfigDetails{ WorkingDir: r.ProjectDirectory, ConfigFiles: types.ToConfigFiles(r.Path), - Environment: env, + Environment: configDetails.Environment.Clone().Merge(envFromFile), } loadOptions.Interpolate = &interp.Options{ Substitute: options.Interpolate.Substitute, diff --git a/loader/loader_test.go b/loader/loader_test.go index 130c7787..6dad1e78 100644 --- a/loader/loader_test.go +++ b/loader/loader_test.go @@ -2555,6 +2555,25 @@ services: }, }) assert.NilError(t, err) + + p, err = Load(buildConfigDetails(` +name: 'test-include' + +include: + - path: ./testdata/subdir/compose-test-extends-imported.yaml + env_file: ./testdata/subdir/extra.env + +services: + foo: + image: busybox + depends_on: + - imported +`, map[string]string{"SOURCE": "override"}), func(options *Options) { + options.SkipNormalization = true + options.ResolvePaths = true + }) + assert.NilError(t, err) + assert.Equal(t, p.Services[1].ContainerName, "override") } func TestLoadWithIncludeCycle(t *testing.T) { diff --git a/types/config.go b/types/config.go index 517e6122..25e6f82e 100644 --- a/types/config.go +++ b/types/config.go @@ -34,7 +34,7 @@ type ConfigDetails struct { Version string WorkingDir string ConfigFiles []ConfigFile - Environment map[string]string + Environment Mapping } // LookupEnv provides a lookup function for environment variables diff --git a/types/types.go b/types/types.go index 517d9a4d..5864dc00 100644 --- a/types/types.go +++ b/types/types.go @@ -516,6 +516,24 @@ func (m Mapping) Resolve(s string) (string, bool) { return v, ok } +func (m Mapping) Clone() Mapping { + clone := Mapping{} + for k, v := range m { + clone[k] = v + } + return clone +} + +// Merge adds all values from second mapping which are not already defined +func (m Mapping) Merge(o Mapping) Mapping { + for k, v := range o { + if _, set := m[k]; !set { + m[k] = v + } + } + return m +} + // Labels is a mapping type for labels type Labels map[string]string