mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 18:13:42 +08:00 
			
		
		
		
	Merge pull request #1261 from crazy-max/bake-env
bake: load .env file from working dir for compose files
This commit is contained in:
		
							
								
								
									
										26
									
								
								bake/bake.go
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								bake/bake.go
									
									
									
									
									
								
							@@ -179,6 +179,21 @@ func dedupMap(ms ...map[string]string) map[string]string {
 | 
				
			|||||||
	return res
 | 
						return res
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func sliceToMap(env []string) (res map[string]string) {
 | 
				
			||||||
 | 
						res = make(map[string]string)
 | 
				
			||||||
 | 
						for _, s := range env {
 | 
				
			||||||
 | 
							kv := strings.SplitN(s, "=", 2)
 | 
				
			||||||
 | 
							key := kv[0]
 | 
				
			||||||
 | 
							switch {
 | 
				
			||||||
 | 
							case len(kv) == 1:
 | 
				
			||||||
 | 
								res[key] = ""
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								res[key] = kv[1]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func ParseFiles(files []File, defaults map[string]string) (_ *Config, err error) {
 | 
					func ParseFiles(files []File, defaults map[string]string) (_ *Config, err error) {
 | 
				
			||||||
	defer func() {
 | 
						defer func() {
 | 
				
			||||||
		err = formatHCLError(err, files)
 | 
							err = formatHCLError(err, files)
 | 
				
			||||||
@@ -242,15 +257,22 @@ func ParseFile(dt []byte, fn string) (*Config, error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func ParseComposeFile(dt []byte, fn string) (*Config, bool, error) {
 | 
					func ParseComposeFile(dt []byte, fn string) (*Config, bool, error) {
 | 
				
			||||||
 | 
						envs := sliceToMap(os.Environ())
 | 
				
			||||||
 | 
						if wd, err := os.Getwd(); err == nil {
 | 
				
			||||||
 | 
							envs, err = loadDotEnv(envs, wd)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, true, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	fnl := strings.ToLower(fn)
 | 
						fnl := strings.ToLower(fn)
 | 
				
			||||||
	if strings.HasSuffix(fnl, ".yml") || strings.HasSuffix(fnl, ".yaml") {
 | 
						if strings.HasSuffix(fnl, ".yml") || strings.HasSuffix(fnl, ".yaml") {
 | 
				
			||||||
		cfg, err := ParseCompose(dt)
 | 
							cfg, err := ParseCompose(dt, envs)
 | 
				
			||||||
		return cfg, true, err
 | 
							return cfg, true, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if strings.HasSuffix(fnl, ".json") || strings.HasSuffix(fnl, ".hcl") {
 | 
						if strings.HasSuffix(fnl, ".json") || strings.HasSuffix(fnl, ".hcl") {
 | 
				
			||||||
		return nil, false, nil
 | 
							return nil, false, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	cfg, err := ParseCompose(dt)
 | 
						cfg, err := ParseCompose(dt, envs)
 | 
				
			||||||
	return cfg, err == nil, err
 | 
						return cfg, err == nil, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,8 +3,10 @@ package bake
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/compose-spec/compose-go/dotenv"
 | 
				
			||||||
	"github.com/compose-spec/compose-go/loader"
 | 
						"github.com/compose-spec/compose-go/loader"
 | 
				
			||||||
	compose "github.com/compose-spec/compose-go/types"
 | 
						compose "github.com/compose-spec/compose-go/types"
 | 
				
			||||||
	"github.com/pkg/errors"
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
@@ -14,34 +16,18 @@ import (
 | 
				
			|||||||
// errComposeInvalid is returned when a compose file is invalid
 | 
					// errComposeInvalid is returned when a compose file is invalid
 | 
				
			||||||
var errComposeInvalid = errors.New("invalid compose file")
 | 
					var errComposeInvalid = errors.New("invalid compose file")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func parseCompose(dt []byte) (*compose.Project, error) {
 | 
					func ParseCompose(dt []byte, envs map[string]string) (*Config, error) {
 | 
				
			||||||
	return loader.Load(compose.ConfigDetails{
 | 
						cfg, err := loader.Load(compose.ConfigDetails{
 | 
				
			||||||
		ConfigFiles: []compose.ConfigFile{
 | 
							ConfigFiles: []compose.ConfigFile{
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				Content: dt,
 | 
									Content: dt,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Environment: envMap(os.Environ()),
 | 
							Environment: envs,
 | 
				
			||||||
	}, func(options *loader.Options) {
 | 
						}, func(options *loader.Options) {
 | 
				
			||||||
		options.SkipNormalization = true
 | 
							options.SkipNormalization = true
 | 
				
			||||||
		options.SkipConsistencyCheck = true
 | 
							options.SkipConsistencyCheck = true
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func envMap(env []string) map[string]string {
 | 
					 | 
				
			||||||
	result := make(map[string]string, len(env))
 | 
					 | 
				
			||||||
	for _, s := range env {
 | 
					 | 
				
			||||||
		kv := strings.SplitN(s, "=", 2)
 | 
					 | 
				
			||||||
		if len(kv) != 2 {
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		result[kv[0]] = kv[1]
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return result
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func ParseCompose(dt []byte) (*Config, error) {
 | 
					 | 
				
			||||||
	cfg, err := parseCompose(dt)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -124,6 +110,42 @@ func ParseCompose(dt []byte) (*Config, error) {
 | 
				
			|||||||
	return &c, nil
 | 
						return &c, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func loadDotEnv(curenv map[string]string, workingDir string) (map[string]string, error) {
 | 
				
			||||||
 | 
						if curenv == nil {
 | 
				
			||||||
 | 
							curenv = make(map[string]string)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ef, err := filepath.Abs(filepath.Join(workingDir, ".env"))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err = os.Stat(ef); os.IsNotExist(err) {
 | 
				
			||||||
 | 
							return curenv, nil
 | 
				
			||||||
 | 
						} else if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dt, err := os.ReadFile(ef)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						envs, err := dotenv.UnmarshalBytes(dt)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for k, v := range envs {
 | 
				
			||||||
 | 
							if _, set := curenv[k]; set {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							curenv[k] = v
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return curenv, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func flatten(in compose.MappingWithEquals) compose.Mapping {
 | 
					func flatten(in compose.MappingWithEquals) compose.Mapping {
 | 
				
			||||||
	if len(in) == 0 {
 | 
						if len(in) == 0 {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@ package bake
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
	"sort"
 | 
						"sort"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -37,11 +38,11 @@ secrets:
 | 
				
			|||||||
    file: /root/.aws/credentials
 | 
					    file: /root/.aws/credentials
 | 
				
			||||||
`)
 | 
					`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c, err := ParseCompose(dt)
 | 
						c, err := ParseCompose(dt, nil)
 | 
				
			||||||
	require.NoError(t, err)
 | 
						require.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	require.Equal(t, 1, len(c.Groups))
 | 
						require.Equal(t, 1, len(c.Groups))
 | 
				
			||||||
	require.Equal(t, c.Groups[0].Name, "default")
 | 
						require.Equal(t, "default", c.Groups[0].Name)
 | 
				
			||||||
	sort.Strings(c.Groups[0].Targets)
 | 
						sort.Strings(c.Groups[0].Targets)
 | 
				
			||||||
	require.Equal(t, []string{"db", "webapp"}, c.Groups[0].Targets)
 | 
						require.Equal(t, []string{"db", "webapp"}, c.Groups[0].Targets)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -58,8 +59,8 @@ secrets:
 | 
				
			|||||||
	require.Equal(t, "Dockerfile-alternate", *c.Targets[1].Dockerfile)
 | 
						require.Equal(t, "Dockerfile-alternate", *c.Targets[1].Dockerfile)
 | 
				
			||||||
	require.Equal(t, 1, len(c.Targets[1].Args))
 | 
						require.Equal(t, 1, len(c.Targets[1].Args))
 | 
				
			||||||
	require.Equal(t, "123", c.Targets[1].Args["buildno"])
 | 
						require.Equal(t, "123", c.Targets[1].Args["buildno"])
 | 
				
			||||||
	require.Equal(t, c.Targets[1].CacheFrom, []string{"type=local,src=path/to/cache"})
 | 
						require.Equal(t, []string{"type=local,src=path/to/cache"}, c.Targets[1].CacheFrom)
 | 
				
			||||||
	require.Equal(t, c.Targets[1].CacheTo, []string{"type=local,dest=path/to/cache"})
 | 
						require.Equal(t, []string{"type=local,dest=path/to/cache"}, c.Targets[1].CacheTo)
 | 
				
			||||||
	require.Equal(t, "none", *c.Targets[1].NetworkMode)
 | 
						require.Equal(t, "none", *c.Targets[1].NetworkMode)
 | 
				
			||||||
	require.Equal(t, []string{
 | 
						require.Equal(t, []string{
 | 
				
			||||||
		"id=token,env=ENV_TOKEN",
 | 
							"id=token,env=ENV_TOKEN",
 | 
				
			||||||
@@ -75,7 +76,7 @@ services:
 | 
				
			|||||||
    webapp:
 | 
					    webapp:
 | 
				
			||||||
        build: ./db
 | 
					        build: ./db
 | 
				
			||||||
`)
 | 
					`)
 | 
				
			||||||
	c, err := ParseCompose(dt)
 | 
						c, err := ParseCompose(dt, nil)
 | 
				
			||||||
	require.NoError(t, err)
 | 
						require.NoError(t, err)
 | 
				
			||||||
	require.Equal(t, 1, len(c.Groups))
 | 
						require.Equal(t, 1, len(c.Groups))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -93,7 +94,7 @@ services:
 | 
				
			|||||||
      target: webapp
 | 
					      target: webapp
 | 
				
			||||||
`)
 | 
					`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c, err := ParseCompose(dt)
 | 
						c, err := ParseCompose(dt, nil)
 | 
				
			||||||
	require.NoError(t, err)
 | 
						require.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	require.Equal(t, 2, len(c.Targets))
 | 
						require.Equal(t, 2, len(c.Targets))
 | 
				
			||||||
@@ -118,15 +119,15 @@ services:
 | 
				
			|||||||
      target: webapp
 | 
					      target: webapp
 | 
				
			||||||
`)
 | 
					`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c, err := ParseCompose(dt)
 | 
						c, err := ParseCompose(dt, nil)
 | 
				
			||||||
	require.NoError(t, err)
 | 
						require.NoError(t, err)
 | 
				
			||||||
	require.Equal(t, 2, len(c.Targets))
 | 
						require.Equal(t, 2, len(c.Targets))
 | 
				
			||||||
	sort.Slice(c.Targets, func(i, j int) bool {
 | 
						sort.Slice(c.Targets, func(i, j int) bool {
 | 
				
			||||||
		return c.Targets[i].Name < c.Targets[j].Name
 | 
							return c.Targets[i].Name < c.Targets[j].Name
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	require.Equal(t, c.Targets[0].Name, "db")
 | 
						require.Equal(t, "db", c.Targets[0].Name)
 | 
				
			||||||
	require.Equal(t, "db", *c.Targets[0].Target)
 | 
						require.Equal(t, "db", *c.Targets[0].Target)
 | 
				
			||||||
	require.Equal(t, c.Targets[1].Name, "webapp")
 | 
						require.Equal(t, "webapp", c.Targets[1].Name)
 | 
				
			||||||
	require.Equal(t, "webapp", *c.Targets[1].Target)
 | 
						require.Equal(t, "webapp", *c.Targets[1].Target)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -152,11 +153,11 @@ services:
 | 
				
			|||||||
	os.Setenv("ZZZ_BAR", "zzz_foo")
 | 
						os.Setenv("ZZZ_BAR", "zzz_foo")
 | 
				
			||||||
	defer os.Unsetenv("ZZZ_BAR")
 | 
						defer os.Unsetenv("ZZZ_BAR")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c, err := ParseCompose(dt)
 | 
						c, err := ParseCompose(dt, sliceToMap(os.Environ()))
 | 
				
			||||||
	require.NoError(t, err)
 | 
						require.NoError(t, err)
 | 
				
			||||||
	require.Equal(t, c.Targets[0].Args["FOO"], "bar")
 | 
						require.Equal(t, "bar", c.Targets[0].Args["FOO"])
 | 
				
			||||||
	require.Equal(t, c.Targets[0].Args["BAR"], "zzz_foo")
 | 
						require.Equal(t, "zzz_foo", c.Targets[0].Args["BAR"])
 | 
				
			||||||
	require.Equal(t, c.Targets[0].Args["BRB"], "FOO")
 | 
						require.Equal(t, "FOO", c.Targets[0].Args["BRB"])
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestInconsistentComposeFile(t *testing.T) {
 | 
					func TestInconsistentComposeFile(t *testing.T) {
 | 
				
			||||||
@@ -166,7 +167,7 @@ services:
 | 
				
			|||||||
    entrypoint: echo 1
 | 
					    entrypoint: echo 1
 | 
				
			||||||
`)
 | 
					`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, err := ParseCompose(dt)
 | 
						_, err := ParseCompose(dt, nil)
 | 
				
			||||||
	require.NoError(t, err)
 | 
						require.NoError(t, err)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -191,7 +192,7 @@ networks:
 | 
				
			|||||||
          gateway: 10.5.0.254
 | 
					          gateway: 10.5.0.254
 | 
				
			||||||
`)
 | 
					`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, err := ParseCompose(dt)
 | 
						_, err := ParseCompose(dt, nil)
 | 
				
			||||||
	require.NoError(t, err)
 | 
						require.NoError(t, err)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -208,9 +209,9 @@ services:
 | 
				
			|||||||
        - bar
 | 
					        - bar
 | 
				
			||||||
`)
 | 
					`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c, err := ParseCompose(dt)
 | 
						c, err := ParseCompose(dt, nil)
 | 
				
			||||||
	require.NoError(t, err)
 | 
						require.NoError(t, err)
 | 
				
			||||||
	require.Equal(t, c.Targets[0].Tags, []string{"foo", "bar"})
 | 
						require.Equal(t, []string{"foo", "bar"}, c.Targets[0].Tags)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestDependsOnList(t *testing.T) {
 | 
					func TestDependsOnList(t *testing.T) {
 | 
				
			||||||
@@ -245,7 +246,7 @@ networks:
 | 
				
			|||||||
    name: test-net
 | 
					    name: test-net
 | 
				
			||||||
`)
 | 
					`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, err := ParseCompose(dt)
 | 
						_, err := ParseCompose(dt, nil)
 | 
				
			||||||
	require.NoError(t, err)
 | 
						require.NoError(t, err)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -298,25 +299,25 @@ services:
 | 
				
			|||||||
        no-cache: true
 | 
					        no-cache: true
 | 
				
			||||||
`)
 | 
					`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c, err := ParseCompose(dt)
 | 
						c, err := ParseCompose(dt, nil)
 | 
				
			||||||
	require.NoError(t, err)
 | 
						require.NoError(t, err)
 | 
				
			||||||
	require.Equal(t, 2, len(c.Targets))
 | 
						require.Equal(t, 2, len(c.Targets))
 | 
				
			||||||
	sort.Slice(c.Targets, func(i, j int) bool {
 | 
						sort.Slice(c.Targets, func(i, j int) bool {
 | 
				
			||||||
		return c.Targets[i].Name < c.Targets[j].Name
 | 
							return c.Targets[i].Name < c.Targets[j].Name
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	require.Equal(t, c.Targets[0].Args, map[string]string{"CT_ECR": "foo", "CT_TAG": "bar"})
 | 
						require.Equal(t, map[string]string{"CT_ECR": "foo", "CT_TAG": "bar"}, c.Targets[0].Args)
 | 
				
			||||||
	require.Equal(t, c.Targets[0].Tags, []string{"ct-addon:baz", "ct-addon:foo", "ct-addon:alp"})
 | 
						require.Equal(t, []string{"ct-addon:baz", "ct-addon:foo", "ct-addon:alp"}, c.Targets[0].Tags)
 | 
				
			||||||
	require.Equal(t, c.Targets[0].Platforms, []string{"linux/amd64", "linux/arm64"})
 | 
						require.Equal(t, []string{"linux/amd64", "linux/arm64"}, c.Targets[0].Platforms)
 | 
				
			||||||
	require.Equal(t, c.Targets[0].CacheFrom, []string{"user/app:cache", "type=local,src=path/to/cache"})
 | 
						require.Equal(t, []string{"user/app:cache", "type=local,src=path/to/cache"}, c.Targets[0].CacheFrom)
 | 
				
			||||||
	require.Equal(t, c.Targets[0].CacheTo, []string{"user/app:cache", "type=local,dest=path/to/cache"})
 | 
						require.Equal(t, []string{"user/app:cache", "type=local,dest=path/to/cache"}, c.Targets[0].CacheTo)
 | 
				
			||||||
	require.Equal(t, c.Targets[0].Pull, newBool(true))
 | 
						require.Equal(t, newBool(true), c.Targets[0].Pull)
 | 
				
			||||||
	require.Equal(t, c.Targets[0].Contexts, map[string]string{"alpine": "docker-image://alpine:3.13"})
 | 
						require.Equal(t, map[string]string{"alpine": "docker-image://alpine:3.13"}, c.Targets[0].Contexts)
 | 
				
			||||||
	require.Equal(t, c.Targets[1].Tags, []string{"ct-fake-aws:bar"})
 | 
						require.Equal(t, []string{"ct-fake-aws:bar"}, c.Targets[1].Tags)
 | 
				
			||||||
	require.Equal(t, c.Targets[1].Secrets, []string{"id=mysecret,src=/local/secret", "id=mysecret2,src=/local/secret2"})
 | 
						require.Equal(t, []string{"id=mysecret,src=/local/secret", "id=mysecret2,src=/local/secret2"}, c.Targets[1].Secrets)
 | 
				
			||||||
	require.Equal(t, c.Targets[1].SSH, []string{"default"})
 | 
						require.Equal(t, []string{"default"}, c.Targets[1].SSH)
 | 
				
			||||||
	require.Equal(t, c.Targets[1].Platforms, []string{"linux/arm64"})
 | 
						require.Equal(t, []string{"linux/arm64"}, c.Targets[1].Platforms)
 | 
				
			||||||
	require.Equal(t, c.Targets[1].Outputs, []string{"type=docker"})
 | 
						require.Equal(t, []string{"type=docker"}, c.Targets[1].Outputs)
 | 
				
			||||||
	require.Equal(t, c.Targets[1].NoCache, newBool(true))
 | 
						require.Equal(t, newBool(true), c.Targets[1].NoCache)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestComposeExtDedup(t *testing.T) {
 | 
					func TestComposeExtDedup(t *testing.T) {
 | 
				
			||||||
@@ -342,12 +343,12 @@ services:
 | 
				
			|||||||
          - type=local,dest=path/to/cache
 | 
					          - type=local,dest=path/to/cache
 | 
				
			||||||
`)
 | 
					`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c, err := ParseCompose(dt)
 | 
						c, err := ParseCompose(dt, nil)
 | 
				
			||||||
	require.NoError(t, err)
 | 
						require.NoError(t, err)
 | 
				
			||||||
	require.Equal(t, 1, len(c.Targets))
 | 
						require.Equal(t, 1, len(c.Targets))
 | 
				
			||||||
	require.Equal(t, c.Targets[0].Tags, []string{"ct-addon:foo", "ct-addon:baz"})
 | 
						require.Equal(t, []string{"ct-addon:foo", "ct-addon:baz"}, c.Targets[0].Tags)
 | 
				
			||||||
	require.Equal(t, c.Targets[0].CacheFrom, []string{"user/app:cache", "type=local,src=path/to/cache"})
 | 
						require.Equal(t, []string{"user/app:cache", "type=local,src=path/to/cache"}, c.Targets[0].CacheFrom)
 | 
				
			||||||
	require.Equal(t, c.Targets[0].CacheTo, []string{"user/app:cache", "type=local,dest=path/to/cache"})
 | 
						require.Equal(t, []string{"user/app:cache", "type=local,dest=path/to/cache"}, c.Targets[0].CacheTo)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestEnv(t *testing.T) {
 | 
					func TestEnv(t *testing.T) {
 | 
				
			||||||
@@ -375,9 +376,30 @@ services:
 | 
				
			|||||||
      - ` + envf.Name() + `
 | 
					      - ` + envf.Name() + `
 | 
				
			||||||
`)
 | 
					`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c, err := ParseCompose(dt)
 | 
						c, err := ParseCompose(dt, nil)
 | 
				
			||||||
	require.NoError(t, err)
 | 
						require.NoError(t, err)
 | 
				
			||||||
	require.Equal(t, c.Targets[0].Args, map[string]string{"CT_ECR": "foo", "FOO": "bsdf -csdf", "NODE_ENV": "test"})
 | 
						require.Equal(t, map[string]string{"CT_ECR": "foo", "FOO": "bsdf -csdf", "NODE_ENV": "test"}, c.Targets[0].Args)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestDotEnv(t *testing.T) {
 | 
				
			||||||
 | 
						tmpdir := t.TempDir()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := os.WriteFile(filepath.Join(tmpdir, ".env"), []byte("FOO=bar"), 0644)
 | 
				
			||||||
 | 
						require.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var dt = []byte(`
 | 
				
			||||||
 | 
					services:
 | 
				
			||||||
 | 
					  scratch:
 | 
				
			||||||
 | 
					    build:
 | 
				
			||||||
 | 
					     context: .
 | 
				
			||||||
 | 
					     args:
 | 
				
			||||||
 | 
					        FOO:
 | 
				
			||||||
 | 
					`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						chdir(t, tmpdir)
 | 
				
			||||||
 | 
						c, _, err := ParseComposeFile(dt, "docker-compose.yml")
 | 
				
			||||||
 | 
						require.NoError(t, err)
 | 
				
			||||||
 | 
						require.Equal(t, map[string]string{"FOO": "bar"}, c.Targets[0].Args)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestPorts(t *testing.T) {
 | 
					func TestPorts(t *testing.T) {
 | 
				
			||||||
@@ -397,7 +419,7 @@ services:
 | 
				
			|||||||
        published: "3306"
 | 
					        published: "3306"
 | 
				
			||||||
        protocol: tcp
 | 
					        protocol: tcp
 | 
				
			||||||
`)
 | 
					`)
 | 
				
			||||||
	_, err := ParseCompose(dt)
 | 
						_, err := ParseCompose(dt, nil)
 | 
				
			||||||
	require.NoError(t, err)
 | 
						require.NoError(t, err)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -445,10 +467,10 @@ func TestServiceName(t *testing.T) {
 | 
				
			|||||||
		t.Run(tt.svc, func(t *testing.T) {
 | 
							t.Run(tt.svc, func(t *testing.T) {
 | 
				
			||||||
			_, err := ParseCompose([]byte(`
 | 
								_, err := ParseCompose([]byte(`
 | 
				
			||||||
services:
 | 
					services:
 | 
				
			||||||
  ` + tt.svc + `:
 | 
					  `+tt.svc+`:
 | 
				
			||||||
    build:
 | 
					    build:
 | 
				
			||||||
      context: .
 | 
					      context: .
 | 
				
			||||||
`))
 | 
					`), nil)
 | 
				
			||||||
			if tt.wantErr {
 | 
								if tt.wantErr {
 | 
				
			||||||
				require.Error(t, err)
 | 
									require.Error(t, err)
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
@@ -514,7 +536,7 @@ services:
 | 
				
			|||||||
	for _, tt := range cases {
 | 
						for _, tt := range cases {
 | 
				
			||||||
		tt := tt
 | 
							tt := tt
 | 
				
			||||||
		t.Run(tt.name, func(t *testing.T) {
 | 
							t.Run(tt.name, func(t *testing.T) {
 | 
				
			||||||
			_, err := ParseCompose(tt.dt)
 | 
								_, err := ParseCompose(tt.dt, nil)
 | 
				
			||||||
			if tt.wantErr {
 | 
								if tt.wantErr {
 | 
				
			||||||
				require.Error(t, err)
 | 
									require.Error(t, err)
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
@@ -523,3 +545,21 @@ services:
 | 
				
			|||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// chdir changes the current working directory to the named directory,
 | 
				
			||||||
 | 
					// and then restore the original working directory at the end of the test.
 | 
				
			||||||
 | 
					func chdir(t *testing.T, dir string) {
 | 
				
			||||||
 | 
						olddir, err := os.Getwd()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("chdir: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := os.Chdir(dir); err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("chdir %s: %v", dir, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						t.Cleanup(func() {
 | 
				
			||||||
 | 
							if err := os.Chdir(olddir); err != nil {
 | 
				
			||||||
 | 
								t.Errorf("chdir to original working directory %s: %v", olddir, err)
 | 
				
			||||||
 | 
								os.Exit(1)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -94,6 +94,56 @@ limitations with the compose format:
 | 
				
			|||||||
* Specifying variables or global scope attributes is not yet supported
 | 
					* Specifying variables or global scope attributes is not yet supported
 | 
				
			||||||
* `inherits` service field is not supported, but you can use [YAML anchors](https://docs.docker.com/compose/compose-file/#fragments) to reference other services like the example above
 | 
					* `inherits` service field is not supported, but you can use [YAML anchors](https://docs.docker.com/compose/compose-file/#fragments) to reference other services like the example above
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## `.env` file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You can declare default environment variables in an environment file named
 | 
				
			||||||
 | 
					`.env`. This file will be loaded from the current working directory,
 | 
				
			||||||
 | 
					where the command is executed and applied to compose definitions passed
 | 
				
			||||||
 | 
					with `-f`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```yaml
 | 
				
			||||||
 | 
					# docker-compose.yml
 | 
				
			||||||
 | 
					services:
 | 
				
			||||||
 | 
					  webapp:
 | 
				
			||||||
 | 
					    image: docker.io/username/webapp:${TAG:-v1.0.0}
 | 
				
			||||||
 | 
					    build:
 | 
				
			||||||
 | 
					      dockerfile: Dockerfile
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					# .env
 | 
				
			||||||
 | 
					TAG=v1.1.0
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```console
 | 
				
			||||||
 | 
					$ docker buildx bake --print
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "group": {
 | 
				
			||||||
 | 
					    "default": {
 | 
				
			||||||
 | 
					      "targets": [
 | 
				
			||||||
 | 
					        "webapp"
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "target": {
 | 
				
			||||||
 | 
					    "webapp": {
 | 
				
			||||||
 | 
					      "context": ".",
 | 
				
			||||||
 | 
					      "dockerfile": "Dockerfile",
 | 
				
			||||||
 | 
					      "tags": [
 | 
				
			||||||
 | 
					        "docker.io/username/webapp:v1.1.0"
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					> **Note**
 | 
				
			||||||
 | 
					>
 | 
				
			||||||
 | 
					> System environment variables take precedence over environment variables
 | 
				
			||||||
 | 
					> in `.env` file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Extension field with `x-bake`
 | 
					## Extension field with `x-bake`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Even if some fields are not (yet) available in the compose specification, you
 | 
					Even if some fields are not (yet) available in the compose specification, you
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user