Skip to content

Commit

Permalink
allow a local .env file to override compose.yaml sibling .env
Browse files Browse the repository at this point in the history
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
  • Loading branch information
ndeloof committed May 21, 2024
1 parent da8189c commit 9e8c8ca
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 10 deletions.
36 changes: 27 additions & 9 deletions cmd/compose/compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ func (o *ProjectOptions) addProjectFlags(f *pflag.FlagSet) {
f.StringArrayVar(&o.Profiles, "profile", []string{}, "Specify a profile to enable")
f.StringVarP(&o.ProjectName, "project-name", "p", "", "Project name")
f.StringArrayVarP(&o.ConfigPaths, "file", "f", []string{}, "Compose configuration files")
f.StringArrayVar(&o.EnvFiles, "env-file", nil, "Specify an alternate environment file")
f.StringArrayVar(&o.EnvFiles, "env-file", defaultStringArrayVar(ComposeEnvFiles), "Specify an alternate environment file")
f.StringVar(&o.ProjectDir, "project-directory", "", "Specify an alternate working directory\n(default: the path of the, first specified, Compose file)")
f.StringVar(&o.WorkDir, "workdir", "", "DEPRECATED! USE --project-directory INSTEAD.\nSpecify an alternate working directory\n(default: the path of the, first specified, Compose file)")
f.BoolVar(&o.Compatibility, "compatibility", false, "Run compose in backward compatibility mode")
Expand All @@ -180,6 +180,13 @@ func (o *ProjectOptions) addProjectFlags(f *pflag.FlagSet) {
_ = f.MarkHidden("workdir")
}

// get default value for a command line flag that is set by a coma-separated value in environment variable
func defaultStringArrayVar(env string) []string {
return strings.FieldsFunc(os.Getenv(env), func(c rune) bool {
return c == ','
})
}

func (o *ProjectOptions) projectOrName(ctx context.Context, dockerCli command.Cli, services ...string) (*types.Project, string, error) {
name := o.ProjectName
var project *types.Project
Expand Down Expand Up @@ -384,7 +391,7 @@ func RootCommand(dockerCli command.Cli, backend Backend) *cobra.Command { //noli
ctx := cmd.Context()

// (1) process env vars
err := setEnvWithDotEnv(&opts)
err := setEnvWithLocalDotEnv(&opts)
if err != nil {
return err
}
Expand Down Expand Up @@ -594,18 +601,29 @@ func RootCommand(dockerCli command.Cli, backend Backend) *cobra.Command { //noli
return c
}

func setEnvWithDotEnv(prjOpts *ProjectOptions) error {
if len(prjOpts.EnvFiles) == 0 {
if envFiles := os.Getenv(ComposeEnvFiles); envFiles != "" {
prjOpts.EnvFiles = strings.Split(envFiles, ",")
}
// If user has a local .env file, load it as os.environment so it can be used to set COMPOSE_ variables
// This also allows to override values set by the default .env in a compose project when ran from a distinct folder
func setEnvWithLocalDotEnv(prjOpts *ProjectOptions) error {
if len(prjOpts.EnvFiles) > 0 {
return nil
}
options, err := prjOpts.toProjectOptions()

wd, err := os.Getwd()
if err != nil {
return compose.WrapComposeError(err)
}

envFromFile, err := dotenv.GetEnvFromFile(composegoutils.GetAsEqualsMap(os.Environ()), options.EnvFiles)
defaultDotEnv := filepath.Join(wd, ".env")

s, err := os.Stat(defaultDotEnv)
if os.IsNotExist(err) || s.IsDir() {
return nil
}
if err != nil {
return err
}

envFromFile, err := dotenv.GetEnvFromFile(composegoutils.GetAsEqualsMap(os.Environ()), []string{defaultDotEnv})
if err != nil {
return err
}
Expand Down
3 changes: 2 additions & 1 deletion pkg/e2e/compose_environment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ func TestEnvPriority(t *testing.T) {
"run", "--rm", "-e", "WHEREAMI", "env-compose-priority")
cmd.Env = append(cmd.Env, "COMPOSE_ENV_FILES=./fixtures/environment/env-priority/.env.override.with.default")
res := icmd.RunCmd(cmd)
assert.Equal(t, strings.TrimSpace(res.Stdout()), "EnvFileDefaultValue")
stdout := res.Stdout()
assert.Equal(t, strings.TrimSpace(stdout), "EnvFileDefaultValue")
})

// No Compose file and env variable pass to the run command
Expand Down
21 changes: 21 additions & 0 deletions pkg/e2e/compose_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,3 +329,24 @@ func TestResolveDotEnv(t *testing.T) {
Out: "image: backend:latest",
})
}

func TestNestedDotEnv(t *testing.T) {
c := NewCLI(t)

cmd := c.NewDockerComposeCmd(t, "run", "echo")
cmd.Dir = filepath.Join(".", "fixtures", "nested")
res := icmd.RunCmd(cmd)
res.Assert(t, icmd.Expected{
ExitCode: 0,
Out: "root win=root",
})

cmd = c.NewDockerComposeCmd(t, "run", "echo")
cmd.Dir = filepath.Join(".", "fixtures", "nested", "sub")
res = icmd.RunCmd(cmd)
res.Assert(t, icmd.Expected{
ExitCode: 0,
Out: "root sub win=sub",
})

}
2 changes: 2 additions & 0 deletions pkg/e2e/fixtures/environment/env-priority/compose.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
services:
env-compose-priority:
image: env-compose-priority
build:
context: .
2 changes: 2 additions & 0 deletions pkg/e2e/fixtures/nested/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ROOT=root
WIN=root
4 changes: 4 additions & 0 deletions pkg/e2e/fixtures/nested/compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
services:
echo:
image: alpine
command: echo $ROOT $SUB win=$WIN
2 changes: 2 additions & 0 deletions pkg/e2e/fixtures/nested/sub/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
SUB=sub
WIN=sub

0 comments on commit 9e8c8ca

Please sign in to comment.