Skip to content

Commit

Permalink
Fix dot env file to define COMPOSE_* variables
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 Jun 8, 2024
1 parent bf1bd3f commit 10531f6
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 60 deletions.
62 changes: 12 additions & 50 deletions cmd/compose/compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,8 @@ import (
"syscall"

"github.com/compose-spec/compose-go/v2/cli"
"github.com/compose-spec/compose-go/v2/dotenv"
"github.com/compose-spec/compose-go/v2/loader"
"github.com/compose-spec/compose-go/v2/types"
composegoutils "github.com/compose-spec/compose-go/v2/utils"
"github.com/docker/buildx/util/logutil"
dockercli "github.com/docker/cli/cli"
"github.com/docker/cli/cli-plugins/manager"
Expand Down Expand Up @@ -329,11 +327,20 @@ func (o *ProjectOptions) toProjectOptions(po ...cli.ProjectOptionsFn) (*cli.Proj
return cli.NewProjectOptions(o.ConfigPaths,
append(po,
cli.WithWorkingDirectory(o.ProjectDir),
// First apply os.Environment, always win
cli.WithOsEnv,
// Load PWD/.env if present and no explicit --env-file has been set
cli.WithEnvFiles(o.EnvFiles...),
// read dot env file to populate project environment
cli.WithDotEnv,
// get compose file path set by COMPOSE_FILE
cli.WithConfigFileEnv,
// if none was selected, get default compose.yaml file from current dir or parent folder
cli.WithDefaultConfigPath,
// .. and then, a project directory != PWD maybe has been set so let's load .env file
cli.WithEnvFiles(o.EnvFiles...),
cli.WithDotEnv,
// eventually COMPOSE_PROFILES should have been set
cli.WithDefaultProfiles(o.Profiles...),
cli.WithName(o.ProjectName))...)
}
Expand Down Expand Up @@ -389,16 +396,8 @@ func RootCommand(dockerCli command.Cli, backend Backend) *cobra.Command { //noli
},
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()

// (1) process env vars
err := setEnvWithLocalDotEnv(&opts)
if err != nil {
return err
}
parent := cmd.Root()

// (2) call parent pre-run
// TODO(milas): this seems incorrect, remove or document
if parent != nil {
parentPrerun := parent.PersistentPreRunE
if parentPrerun != nil {
Expand All @@ -409,7 +408,6 @@ func RootCommand(dockerCli command.Cli, backend Backend) *cobra.Command { //noli
}
}

// (3) set up display/output
if verbose {
logrus.SetLevel(logrus.TraceLevel)
}
Expand Down Expand Up @@ -469,7 +467,7 @@ func RootCommand(dockerCli command.Cli, backend Backend) *cobra.Command { //noli
}
for i, file := range opts.EnvFiles {
if !filepath.IsAbs(file) {
file, err = filepath.Abs(file)
file, err := filepath.Abs(file)
if err != nil {
return err
}
Expand Down Expand Up @@ -500,8 +498,8 @@ func RootCommand(dockerCli command.Cli, backend Backend) *cobra.Command { //noli
backend.MaxConcurrency(parallel)
}

// (5) dry run detection
ctx, err = backend.DryRunMode(ctx, dryRun)
// dry run detection
ctx, err := backend.DryRunMode(ctx, dryRun)
if err != nil {
return err
}
Expand Down Expand Up @@ -601,42 +599,6 @@ func RootCommand(dockerCli command.Cli, backend Backend) *cobra.Command { //noli
return c
}

// 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
}

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

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
}
for k, v := range envFromFile {
if _, ok := os.LookupEnv(k); !ok { // Precedence to OS Env
if err := os.Setenv(k, v); err != nil {
return err
}
}
}
return nil
}

var printerModes = []string{
ui.ModeAuto,
ui.ModeTTY,
Expand Down
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ require (
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-viper/mapstructure/v2 v2.0.0 // indirect
github.com/gofrs/flock v0.8.1 // indirect
github.com/gogo/googleapis v1.4.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
Expand Down Expand Up @@ -125,8 +126,6 @@ require (
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/spdystream v0.2.0 // indirect
Expand Down Expand Up @@ -191,3 +190,5 @@ require (
sigs.k8s.io/yaml v1.3.0 // indirect
tags.cncf.io/container-device-interface v0.7.2 // indirect
)

replace github.com/compose-spec/compose-go/v2 => github.com/ndeloof/compose-go/v2 v2.0.1-0.20240606144025-9ba1fb10d14c
10 changes: 4 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,6 @@ github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+g
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
github.com/compose-spec/compose-go/v2 v2.1.2-0.20240530052535-7dfa54c9658b h1:tjysHJZrxQVzGbklQsdYOjgZC9rVFa8Ersn82QP8H1M=
github.com/compose-spec/compose-go/v2 v2.1.2-0.20240530052535-7dfa54c9658b/go.mod h1:bEPizBkIojlQ20pi2vNluBa58tevvj0Y18oUSHPyfdc=
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
Expand Down Expand Up @@ -190,6 +188,8 @@ github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc=
github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0=
Expand Down Expand Up @@ -320,15 +320,11 @@ github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyex
github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/moby/buildkit v0.13.2 h1:nXNszM4qD9E7QtG7bFWPnDI1teUQFQglBzon/IU3SzI=
github.com/moby/buildkit v0.13.2/go.mod h1:2cyVOv9NoHM7arphK9ZfHIWKn9YVZRFd1wXB8kKmEzY=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
Expand Down Expand Up @@ -365,6 +361,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/ndeloof/compose-go/v2 v2.0.1-0.20240606144025-9ba1fb10d14c h1:VP4OJnRYZEwJFcxUCEKvn8cC855C7GiK8MmwdfP+m5I=
github.com/ndeloof/compose-go/v2 v2.0.1-0.20240606144025-9ba1fb10d14c/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
Expand Down
40 changes: 39 additions & 1 deletion pkg/e2e/compose_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,18 +318,56 @@ func TestRemoveOrphaned(t *testing.T) {
res.Assert(t, icmd.Expected{Out: fmt.Sprintf("%s-words-1", projectName)})
}

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

cmd := c.NewDockerComposeCmd(t, "config")
cmd.Dir = filepath.Join(".", "fixtures", "dotenv")
res := icmd.RunCmd(cmd)
res.Assert(t, icmd.Expected{
ExitCode: 0,
Out: "image: test:latest",
})
res.Assert(t, icmd.Expected{
Out: "image: enabled:profile",
})
}

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

dir := filepath.Join(".", "fixtures", "dotenv", "development")
cmd := c.NewDockerComposeCmd(t, "--project-directory", dir, "config")
res := icmd.RunCmd(cmd)
res.Assert(t, icmd.Expected{
ExitCode: 0,
Out: "image: backend:latest",
})
}

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

dotEnv, err := os.CreateTemp(t.TempDir(), ".env")
assert.NilError(t, err)
err = os.WriteFile(dotEnv.Name(), []byte(`
COMPOSE_FILE=fixtures/dotenv/development/compose.yaml
IMAGE_NAME=test
IMAGE_TAG=latest
COMPOSE_PROFILES=test
`), 0o700)
assert.NilError(t, err)

cmd := c.NewDockerComposeCmd(t, "--env-file", dotEnv.Name(), "config")
res := icmd.RunCmd(cmd)
res.Assert(t, icmd.Expected{
Out: "image: test:latest",
})
res.Assert(t, icmd.Expected{
Out: "image: enabled:profile",
})
}

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

Expand Down
4 changes: 3 additions & 1 deletion pkg/e2e/fixtures/dotenv/.env
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
COMPOSE_FILE="${COMPOSE_FILE:-development/compose.yaml}"
COMPOSE_FILE="${COMPOSE_FILE:-development/compose.yaml}"
IMAGE_NAME=test
COMPOSE_PROFILES=test
4 changes: 4 additions & 0 deletions pkg/e2e/fixtures/dotenv/development/compose.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
services:
backend:
image: $IMAGE_NAME:$IMAGE_TAG
test:
profiles:
- test
image: enabled:profile

0 comments on commit 10531f6

Please sign in to comment.