Skip to content

Commit

Permalink
support mulitple env-files to be set
Browse files Browse the repository at this point in the history
Signed-off-by: Nicolas De Loof <[email protected]>
  • Loading branch information
ndeloof committed Jan 20, 2023
1 parent cb8843f commit 0ec2b86
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 47 deletions.
84 changes: 39 additions & 45 deletions cli/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package cli

import (
"bytes"
"fmt"
"io"
"os"
Expand All @@ -39,7 +40,7 @@ type ProjectOptions struct {
WorkingDir string
ConfigPaths []string
Environment map[string]string
EnvFile string
EnvFiles []string
loadOptions []func(*loader.Options)
}

Expand Down Expand Up @@ -186,10 +187,10 @@ func WithOsEnv(o *ProjectOptions) error {
return nil
}

// WithEnvFile set an alternate env file
func WithEnvFile(file string) ProjectOptionsFn {
// WithEnvFiles set alternate env files
func WithEnvFiles(file ...string) ProjectOptionsFn {
return func(options *ProjectOptions) error {
options.EnvFile = file
options.EnvFiles = file
return nil
}
}
Expand All @@ -200,7 +201,7 @@ func WithDotEnv(o *ProjectOptions) error {
if err != nil {
return err
}
envMap, err := GetEnvFromFile(o.Environment, wd, o.EnvFile)
envMap, err := GetEnvFromFile(o.Environment, wd, o.EnvFiles)
if err != nil {
return err
}
Expand All @@ -213,55 +214,48 @@ func WithDotEnv(o *ProjectOptions) error {
return nil
}

func GetEnvFromFile(currentEnv map[string]string, workingDir string, filename string) (map[string]string, error) {
func GetEnvFromFile(currentEnv map[string]string, workingDir string, filenames []string) (map[string]string, error) {
envMap := make(map[string]string)

dotEnvFile := filename
if dotEnvFile == "" {
dotEnvFile = filepath.Join(workingDir, ".env")
dotEnvFiles := filenames
if len(dotEnvFiles) == 0 {
dotEnvFiles = append(dotEnvFiles, filepath.Join(workingDir, ".env"))
}
abs, err := filepath.Abs(dotEnvFile)
if err != nil {
return envMap, err
}
dotEnvFile = abs

s, err := os.Stat(dotEnvFile)
if os.IsNotExist(err) {
if filename != "" {
return nil, errors.Errorf("Couldn't find env file: %s", filename)
for _, dotEnvFile := range dotEnvFiles {
abs, err := filepath.Abs(dotEnvFile)
if err != nil {
return envMap, err
}
return envMap, nil
}
if err != nil {
return envMap, err
}
dotEnvFile = abs

if s.IsDir() {
if filename == "" {
b, err := os.ReadFile(dotEnvFile)
if os.IsNotExist(err) {
if len(filenames) > 0 {
return nil, errors.Errorf("Couldn't read env file: %s", dotEnvFile)
}
return envMap, nil
}
return envMap, errors.Errorf("%s is a directory", dotEnvFile)
}

file, err := os.Open(dotEnvFile)
if err != nil {
return envMap, errors.Wrapf(err, "failed to read %s", dotEnvFile)
}
defer file.Close()
if err != nil {
return envMap, err
}

env, err := dotenv.ParseWithLookup(file, func(k string) (string, bool) {
v, ok := currentEnv[k]
if !ok {
return "", false
env, err := dotenv.ParseWithLookup(bytes.NewReader(b), func(k string) (string, bool) {
v, ok := envMap[k]
if ok {
return v, true
}
v, ok = currentEnv[k]
if !ok {
return "", false
}
return v, true
})
if err != nil {
return envMap, errors.Wrapf(err, "failed to read %s", dotEnvFile)
}
for k, v := range env {
envMap[k] = v
}
return v, true
})
if err != nil {
return envMap, errors.Wrapf(err, "failed to read %s", dotEnvFile)
}
for k, v := range env {
envMap[k] = v
}

return envMap, nil
Expand Down
18 changes: 18 additions & 0 deletions cli/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,24 @@ func TestProjectWithDiscardEnvFile(t *testing.T) {
assert.NilError(t, err)
assert.Equal(t, *service.Environment["DEFAULT_PORT"], "8080")
assert.Assert(t, service.EnvFile == nil)
assert.Equal(t, service.Ports[0].Published, "8000")
}

func TestProjectWithMultipleEnvFile(t *testing.T) {
opts, err := NewProjectOptions([]string{
"testdata/env-file/compose-with-env-files.yaml",
}, WithDiscardEnvFile,
WithEnvFiles("testdata/env-file/.env", "testdata/env-file/override.env"),
WithDotEnv)

assert.NilError(t, err)
p, err := ProjectFromOptions(opts)
assert.NilError(t, err)
service, err := p.GetService("simple")
assert.NilError(t, err)
assert.Equal(t, *service.Environment["DEFAULT_PORT"], "9090")
assert.Assert(t, service.EnvFile == nil)
assert.Equal(t, service.Ports[0].Published, "9000")
}

func TestProjectNameFromWorkingDir(t *testing.T) {
Expand Down
3 changes: 2 additions & 1 deletion cli/testdata/env-file/.env
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
COMPOSE_FILE=compose-with-env-file.yaml
COMPOSE_PROJECT_NAME=my_project_from_dot_env
COMPOSE_PROJECT_NAME=my_project_from_dot_env
PORT=8000
9 changes: 9 additions & 0 deletions cli/testdata/env-file/compose-with-env-files.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
version: "3"
services:
simple:
image: nginx
env_file:
- ./simple-env
- ./second-env
ports:
- ${PORT}:80
1 change: 1 addition & 0 deletions cli/testdata/env-file/override.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PORT=9000
1 change: 1 addition & 0 deletions cli/testdata/env-file/second-env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DEFAULT_PORT=9090
2 changes: 1 addition & 1 deletion cli/testdata/env-file/simple-env
Original file line number Diff line number Diff line change
@@ -1 +1 @@
DEFAULT_PORT=8080
DEFAULT_PORT=8080

0 comments on commit 0ec2b86

Please sign in to comment.