From 3107e2392dc2a648206ee04e57cd0d5848d1de0b Mon Sep 17 00:00:00 2001 From: Vigilans Date: Thu, 19 Dec 2024 11:36:49 +0800 Subject: [PATCH 1/2] Fix nested include with env files Signed-off-by: Vigilans --- loader/include.go | 31 ++++++++++------- loader/include_test.go | 79 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 12 deletions(-) diff --git a/loader/include.go b/loader/include.go index 823c2f7a..9e0972a8 100644 --- a/loader/include.go +++ b/loader/include.go @@ -109,28 +109,35 @@ func ApplyInclude(ctx context.Context, workingDir string, environment types.Mapp WorkingDir: r.ProjectDirectory, }) + envFile := r.EnvFile if len(r.EnvFile) == 0 { - f := filepath.Join(r.ProjectDirectory, ".env") - if s, err := os.Stat(f); err == nil && !s.IsDir() { - r.EnvFile = types.StringList{f} - } - } else { - envFile := []string{} - for _, f := range r.EnvFile { - if !filepath.IsAbs(f) { - f = filepath.Join(workingDir, f) - s, err := os.Stat(f) + envFile = types.StringList{".env"} + } + + resolvedEnvFile := []string{} + for _, f := range envFile { + for _, loader := range options.ResourceLoaders { + if loader.Accept(f) { + path, err := loader.Load(ctx, f) + if err != nil { + return err + } + s, err := os.Stat(path) + if os.IsNotExist(err) && len(r.EnvFile) == 0 { + break // Skip if default .env not found + } if err != nil { return err } if s.IsDir() { return fmt.Errorf("%s is not a file", f) } + resolvedEnvFile = append(resolvedEnvFile, path) + break } - envFile = append(envFile, f) } - r.EnvFile = envFile } + r.EnvFile = resolvedEnvFile envFromFile, err := dotenv.GetEnvFromFile(environment, r.EnvFile) if err != nil { diff --git a/loader/include_test.go b/loader/include_test.go index f44d417b..b2383c8d 100644 --- a/loader/include_test.go +++ b/loader/include_test.go @@ -163,6 +163,85 @@ services: } +func TestLoadWithIncludeEnvTripleTimes(t *testing.T) { + fileName := "compose.yml" + tmpdir := t.TempDir() + // file in root + yaml := ` +include: + - path: + - ./module/compose.yml + env_file: + - ./custom.env +services: + a: + image: alpine + environment: + - VAR_NAME` + createFile(t, tmpdir, `VAR_NAME=value`, "custom.env") + path := createFile(t, tmpdir, yaml, fileName) + // file in /module + yaml = ` +include: + - path: + - ./submodule/compose.yml + env_file: + - ../custom.env +services: + b: + image: alpine + environment: + - VAR_NAME` + createFileSubDir(t, tmpdir, "module", yaml, fileName) + + yaml = ` +include: + - path: + - ./subsubmodule/compose.yml + env_file: + - ../../custom.env +services: + c: + image: alpine + environment: + - VAR_NAME` + createFileSubDir(t, tmpdir, "module/submodule", yaml, fileName) + + yaml = ` +services: + d: + image: alpine + environment: + - VAR_NAME` + createFileSubDir(t, tmpdir, "module/submodule/subsubmodule", yaml, fileName) + + p, err := Load(types.ConfigDetails{ + WorkingDir: tmpdir, + ConfigFiles: []types.ConfigFile{{ + Filename: path, + }}, + Environment: nil, + }, func(options *Options) { + options.SkipNormalization = true + options.ResolvePaths = true + options.SetProjectName("project", true) + }) + assert.NilError(t, err) + a := p.Services["a"] + // make sure VAR_NAME is only accessible in include context + assert.Check(t, a.Environment["VAR_NAME"] == nil, "VAR_NAME should not be defined in environment") + b := p.Services["b"] + assert.Check(t, b.Environment["VAR_NAME"] != nil, "VAR_NAME is not defined in environment") + assert.Equal(t, *b.Environment["VAR_NAME"], "value") + c := p.Services["c"] + assert.Check(t, c.Environment["VAR_NAME"] != nil, "VAR_NAME is not defined in environment") + assert.Equal(t, *c.Environment["VAR_NAME"], "value") + d := p.Services["d"] + assert.Check(t, d.Environment["VAR_NAME"] != nil, "VAR_NAME is not defined in environment") + assert.Equal(t, *d.Environment["VAR_NAME"], "value") + +} + func TestIncludeWithProjectDirectory(t *testing.T) { var envs map[string]string if runtime.GOOS == "windows" { From 478b9cb917a4d2d464bb00201b0d70cd38277d77 Mon Sep 17 00:00:00 2001 From: Vigilans Date: Wed, 8 Jan 2025 17:51:13 +0800 Subject: [PATCH 2/2] Fix golangci-lint issue Signed-off-by: Vigilans --- loader/include.go | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/loader/include.go b/loader/include.go index 9e0972a8..9244f2b3 100644 --- a/loader/include.go +++ b/loader/include.go @@ -117,24 +117,25 @@ func ApplyInclude(ctx context.Context, workingDir string, environment types.Mapp resolvedEnvFile := []string{} for _, f := range envFile { for _, loader := range options.ResourceLoaders { - if loader.Accept(f) { - path, err := loader.Load(ctx, f) - if err != nil { - return err - } - s, err := os.Stat(path) - if os.IsNotExist(err) && len(r.EnvFile) == 0 { - break // Skip if default .env not found - } - if err != nil { - return err - } - if s.IsDir() { - return fmt.Errorf("%s is not a file", f) - } - resolvedEnvFile = append(resolvedEnvFile, path) - break + if !loader.Accept(f) { + continue + } + path, err := loader.Load(ctx, f) + if err != nil { + return err + } + s, err := os.Stat(path) + if os.IsNotExist(err) && len(r.EnvFile) == 0 { + break // Skip if default .env not found + } + if err != nil { + return err + } + if s.IsDir() { + return fmt.Errorf("%s is not a file", f) } + resolvedEnvFile = append(resolvedEnvFile, path) + break } } r.EnvFile = resolvedEnvFile