Skip to content

Commit 3989770

Browse files
committed
bake: support setting the context to null in bake
Setting the context to null in bake will cause the dockerfile to be sent over stdin to the build and will prevent using the default context of "." similar to how this would be used on the command line. When the JSON is printed from bake, it will reproduce the null so subsequent uses can keep the value. Signed-off-by: Jonathan A. Sternberg <[email protected]>
1 parent 16edf5d commit 3989770

File tree

6 files changed

+177
-66
lines changed

6 files changed

+177
-66
lines changed

bake/bake.go

+58-32
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package bake
22

33
import (
4+
"bytes"
45
"context"
56
"encoding"
67
"io"
@@ -638,10 +639,6 @@ func (c Config) ResolveTarget(name string, overrides map[string]map[string]Overr
638639
return nil, err
639640
}
640641
t.Inherits = nil
641-
if t.Context == nil {
642-
s := "."
643-
t.Context = &s
644-
}
645642
if t.Dockerfile == nil {
646643
s := "Dockerfile"
647644
t.Dockerfile = &s
@@ -702,7 +699,7 @@ type Target struct {
702699

703700
Annotations []string `json:"annotations,omitempty" hcl:"annotations,optional" cty:"annotations"`
704701
Attest buildflags.Attests `json:"attest,omitempty" hcl:"attest,optional" cty:"attest"`
705-
Context *string `json:"context,omitempty" hcl:"context,optional" cty:"context"`
702+
Context **string `json:"context" hcl:"context,optional" cty:"context"`
706703
Contexts map[string]string `json:"contexts,omitempty" hcl:"contexts,optional" cty:"contexts"`
707704
Dockerfile *string `json:"dockerfile,omitempty" hcl:"dockerfile,optional" cty:"dockerfile"`
708705
DockerfileInline *string `json:"dockerfile-inline,omitempty" hcl:"dockerfile-inline,optional" cty:"dockerfile-inline"`
@@ -761,11 +758,23 @@ func (t *Target) normalize() {
761758
delete(t.Contexts, k)
762759
}
763760
}
761+
if t.Context == nil {
762+
s := t.ContextPath()
763+
t.Context = &s
764+
}
764765
if len(t.Contexts) == 0 {
765766
t.Contexts = nil
766767
}
767768
}
768769

770+
func (t *Target) ContextPath() *string {
771+
if t.Context == nil {
772+
p := "."
773+
return &p
774+
}
775+
return (*string)(*t.Context)
776+
}
777+
769778
func (t *Target) Merge(t2 *Target) {
770779
if t2.Context != nil {
771780
t.Context = t2.Context
@@ -866,7 +875,8 @@ func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementCon
866875
keys := strings.SplitN(key, ".", 2)
867876
switch keys[0] {
868877
case "context":
869-
t.Context = &value
878+
ptr := &value
879+
t.Context = &ptr
870880
case "dockerfile":
871881
t.Dockerfile = &value
872882
case "args":
@@ -1210,37 +1220,18 @@ func isLocalPath(p string) (string, bool) {
12101220
}
12111221

12121222
func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
1213-
if v := t.Context; v != nil && *v == "-" {
1223+
if v := t.ContextPath(); v != nil && *v == "-" {
12141224
return nil, errors.Errorf("context from stdin not allowed in bake")
12151225
}
12161226
if v := t.Dockerfile; v != nil && *v == "-" {
12171227
return nil, errors.Errorf("dockerfile from stdin not allowed in bake")
12181228
}
12191229

1220-
contextPath := "."
1221-
if t.Context != nil {
1222-
contextPath = *t.Context
1223-
}
1224-
if !strings.HasPrefix(contextPath, "cwd://") && !build.IsRemoteURL(contextPath) {
1225-
contextPath = path.Clean(contextPath)
1226-
}
1227-
dockerfilePath := "Dockerfile"
1228-
if t.Dockerfile != nil {
1229-
dockerfilePath = *t.Dockerfile
1230-
}
1231-
if !strings.HasPrefix(dockerfilePath, "cwd://") {
1232-
dockerfilePath = path.Clean(dockerfilePath)
1233-
}
1230+
bi := build.Inputs{}
12341231

1235-
bi := build.Inputs{
1236-
ContextPath: contextPath,
1237-
DockerfilePath: dockerfilePath,
1238-
NamedContexts: toNamedContexts(t.Contexts),
1239-
}
1240-
if t.DockerfileInline != nil {
1241-
bi.DockerfileInline = *t.DockerfileInline
1242-
}
1232+
setBuildOptContexts(&bi, t)
12431233
updateContext(&bi, inp)
1234+
12441235
if strings.HasPrefix(bi.DockerfilePath, "cwd://") {
12451236
// If Dockerfile is local for a remote invocation, we first check if
12461237
// it's not outside the working directory and then resolve it to an
@@ -1273,7 +1264,7 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
12731264
if strings.HasPrefix(bi.ContextPath, "cwd://") {
12741265
bi.ContextPath = path.Clean(strings.TrimPrefix(bi.ContextPath, "cwd://"))
12751266
}
1276-
if !build.IsRemoteURL(bi.ContextPath) && bi.ContextState == nil && !path.IsAbs(bi.DockerfilePath) {
1267+
if !build.IsRemoteURL(bi.ContextPath) && bi.ContextState == nil && bi.DockerfilePath != "" && !path.IsAbs(bi.DockerfilePath) {
12771268
bi.DockerfilePath = path.Join(bi.ContextPath, bi.DockerfilePath)
12781269
}
12791270
for k, v := range bi.NamedContexts {
@@ -1282,8 +1273,6 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
12821273
}
12831274
}
12841275

1285-
t.Context = &bi.ContextPath
1286-
12871276
args := map[string]string{}
12881277
for k, v := range t.Args {
12891278
if v == nil {
@@ -1410,6 +1399,43 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
14101399
return bo, nil
14111400
}
14121401

1402+
func setBuildOptContexts(bi *build.Inputs, t *Target) error {
1403+
dockerfilePath := "Dockerfile"
1404+
if t.Dockerfile != nil {
1405+
dockerfilePath = *t.Dockerfile
1406+
}
1407+
1408+
if !strings.HasPrefix(dockerfilePath, "cwd://") {
1409+
dockerfilePath = path.Clean(dockerfilePath)
1410+
}
1411+
bi.DockerfilePath = dockerfilePath
1412+
1413+
if contextPath := t.ContextPath(); contextPath != nil {
1414+
bi.ContextPath = *contextPath
1415+
if !strings.HasPrefix(bi.ContextPath, "cwd://") && !build.IsRemoteURL(bi.ContextPath) {
1416+
bi.ContextPath = path.Clean(bi.ContextPath)
1417+
}
1418+
1419+
if t.DockerfileInline != nil {
1420+
bi.DockerfileInline = *t.DockerfileInline
1421+
}
1422+
} else {
1423+
bi.ContextPath = "-"
1424+
if t.DockerfileInline != nil {
1425+
bi.InStream = build.NewSyncMultiReader(strings.NewReader(*t.DockerfileInline))
1426+
} else {
1427+
dockerfile, err := os.ReadFile(bi.DockerfilePath)
1428+
if err != nil {
1429+
return err
1430+
}
1431+
bi.DockerfilePath = ""
1432+
bi.InStream = build.NewSyncMultiReader(bytes.NewReader(dockerfile))
1433+
}
1434+
}
1435+
bi.NamedContexts = toNamedContexts(t.Contexts)
1436+
return nil
1437+
}
1438+
14131439
func defaultTarget() *Target {
14141440
return &Target{}
14151441
}

bake/bake_test.go

+15-15
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ target "webapp" {
4646
require.Equal(t, 1, len(m))
4747

4848
require.Equal(t, "Dockerfile.webapp", *m["webapp"].Dockerfile)
49-
require.Equal(t, ".", *m["webapp"].Context)
49+
require.Equal(t, ".", *m["webapp"].ContextPath())
5050
require.Equal(t, ptrstr("webDEP"), m["webapp"].Args["VAR_INHERITED"])
5151
require.Equal(t, true, *m["webapp"].NoCache)
5252
require.Equal(t, "128m", *m["webapp"].ShmSize)
@@ -79,7 +79,7 @@ target "webapp" {
7979
require.NoError(t, err)
8080

8181
require.Equal(t, "Dockerfile.webapp", *m["webapp"].Dockerfile)
82-
require.Equal(t, ".", *m["webapp"].Context)
82+
require.Equal(t, ".", *m["webapp"].ContextPath())
8383

8484
_, isSet := m["webapp"].Args["VAR_UNSET"]
8585
require.False(t, isSet, m["webapp"].Args["VAR_UNSET"])
@@ -121,7 +121,7 @@ target "webapp" {
121121

122122
m, g, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.context=foo"}, nil, &EntitlementConf{})
123123
require.NoError(t, err)
124-
require.Equal(t, "foo", *m["webapp"].Context)
124+
require.Equal(t, "foo", *m["webapp"].ContextPath())
125125
require.Equal(t, 1, len(g))
126126
require.Equal(t, []string{"webapp"}, g["default"].Targets)
127127
})
@@ -518,7 +518,7 @@ services:
518518

519519
require.True(t, ok)
520520
require.Equal(t, "Dockerfile.webapp", *m["webapp"].Dockerfile)
521-
require.Equal(t, ".", *m["webapp"].Context)
521+
require.Equal(t, ".", *m["webapp"].ContextPath())
522522
require.Equal(t, ptrstr("1"), m["webapp"].Args["buildno"])
523523
require.Equal(t, ptrstr("12"), m["webapp"].Args["buildno2"])
524524

@@ -579,7 +579,7 @@ services:
579579
_, ok = m["web_app"]
580580
require.True(t, ok)
581581
require.Equal(t, "Dockerfile.webapp", *m["web_app"].Dockerfile)
582-
require.Equal(t, ".", *m["web_app"].Context)
582+
require.Equal(t, ".", *m["web_app"].ContextPath())
583583
require.Equal(t, ptrstr("1"), m["web_app"].Args["buildno"])
584584
require.Equal(t, ptrstr("12"), m["web_app"].Args["buildno2"])
585585

@@ -610,7 +610,7 @@ func TestHCLContextCwdPrefix(t *testing.T) {
610610
require.Equal(t, 1, len(m))
611611
require.Contains(t, m, "app")
612612
assert.Equal(t, "test", *m["app"].Dockerfile)
613-
assert.Equal(t, "foo", *m["app"].Context)
613+
assert.Equal(t, "cwd://foo", *m["app"].ContextPath())
614614
assert.Equal(t, "foo/test", bo["app"].Inputs.DockerfilePath)
615615
assert.Equal(t, "foo", bo["app"].Inputs.ContextPath)
616616
}
@@ -641,7 +641,7 @@ func TestHCLDockerfileCwdPrefix(t *testing.T) {
641641
require.Equal(t, 1, len(m))
642642
require.Contains(t, m, "app")
643643
assert.Equal(t, "cwd://Dockerfile.app", *m["app"].Dockerfile)
644-
assert.Equal(t, ".", *m["app"].Context)
644+
assert.Equal(t, ".", *m["app"].ContextPath())
645645
assert.Equal(t, filepath.Join(cwd, "Dockerfile.app"), bo["app"].Inputs.DockerfilePath)
646646
assert.Equal(t, ".", bo["app"].Inputs.ContextPath)
647647
}
@@ -798,9 +798,9 @@ services:
798798
require.True(t, ok)
799799

800800
require.Equal(t, "Dockerfile", *m["app1"].Dockerfile)
801-
require.Equal(t, ".", *m["app1"].Context)
801+
require.Equal(t, ".", *m["app1"].ContextPath())
802802
require.Equal(t, "Dockerfile", *m["app2"].Dockerfile)
803-
require.Equal(t, ".", *m["app2"].Context)
803+
require.Equal(t, ".", *m["app2"].ContextPath())
804804
}
805805

806806
func TestReadContextFromTargetChain(t *testing.T) {
@@ -1130,7 +1130,7 @@ services:
11301130
require.Equal(t, 1, len(g))
11311131
require.Equal(t, []string{"image", "image-release"}, g["default"].Targets)
11321132
require.Equal(t, 2, len(m))
1133-
require.Equal(t, ".", *m["image"].Context)
1133+
require.Equal(t, ".", *m["image"].ContextPath())
11341134
require.Equal(t, 1, len(m["image-release"].Outputs))
11351135
require.Equal(t, "type=image,push=true", m["image-release"].Outputs[0].String())
11361136

@@ -1139,14 +1139,14 @@ services:
11391139
require.Equal(t, 1, len(g))
11401140
require.Equal(t, []string{"image"}, g["default"].Targets)
11411141
require.Equal(t, 1, len(m))
1142-
require.Equal(t, ".", *m["image"].Context)
1142+
require.Equal(t, ".", *m["image"].ContextPath())
11431143

11441144
m, g, err = ReadTargets(ctx, []File{fjson}, []string{"default"}, nil, nil, &EntitlementConf{})
11451145
require.NoError(t, err)
11461146
require.Equal(t, 1, len(g))
11471147
require.Equal(t, []string{"image"}, g["default"].Targets)
11481148
require.Equal(t, 1, len(m))
1149-
require.Equal(t, ".", *m["image"].Context)
1149+
require.Equal(t, ".", *m["image"].ContextPath())
11501150

11511151
m, g, err = ReadTargets(ctx, []File{fyml}, []string{"default"}, nil, nil, &EntitlementConf{})
11521152
require.NoError(t, err)
@@ -1172,7 +1172,7 @@ services:
11721172
sort.Strings(g["default"].Targets)
11731173
require.Equal(t, []string{"addon", "aws", "image"}, g["default"].Targets)
11741174
require.Equal(t, 3, len(m))
1175-
require.Equal(t, ".", *m["image"].Context)
1175+
require.Equal(t, ".", *m["image"].ContextPath())
11761176
require.Equal(t, "./Dockerfile", *m["addon"].Dockerfile)
11771177
require.Equal(t, "./aws.Dockerfile", *m["aws"].Dockerfile)
11781178
}
@@ -1537,7 +1537,7 @@ target "f" {
15371537
require.Equal(t, tt.targets, g["default"].Targets)
15381538

15391539
require.Equal(t, tt.count, len(m))
1540-
require.Equal(t, ".", *m["d"].Context)
1540+
require.Equal(t, ".", *m["d"].ContextPath())
15411541
require.Equal(t, "./testdockerfile", *m["d"].Dockerfile)
15421542
})
15431543
}
@@ -1571,7 +1571,7 @@ services:
15711571
require.Equal(t, "app", c.Targets[0].Name)
15721572
require.Equal(t, ptrstr("foo"), c.Targets[0].Args["v1"])
15731573
require.Equal(t, ptrstr("bar"), c.Targets[0].Args["v2"])
1574-
require.Equal(t, "dir", *c.Targets[0].Context)
1574+
require.Equal(t, "dir", *c.Targets[0].ContextPath())
15751575
require.Equal(t, "Dockerfile-alternate", *c.Targets[0].Dockerfile)
15761576
}
15771577

bake/compose.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,10 @@ func ParseCompose(cfgs []composetypes.ConfigFile, envs map[string]string) (*Conf
7272
return nil, errors.Wrapf(err, "invalid service name %q", targetName)
7373
}
7474

75-
var contextPathP *string
75+
var contextPathP **string
7676
if s.Build.Context != "" {
77-
contextPath := s.Build.Context
78-
contextPathP = &contextPath
77+
ptr := &s.Build.Context
78+
contextPathP = &ptr
7979
}
8080
var dockerfilePathP *string
8181
if s.Build.Dockerfile != "" {

bake/compose_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,11 @@ secrets:
6565
return c.Targets[i].Name < c.Targets[j].Name
6666
})
6767
require.Equal(t, "db", c.Targets[0].Name)
68-
require.Equal(t, "db", *c.Targets[0].Context)
68+
require.Equal(t, "db", *c.Targets[0].ContextPath())
6969
require.Equal(t, []string{"docker.io/tonistiigi/db"}, c.Targets[0].Tags)
7070

7171
require.Equal(t, "webapp", c.Targets[1].Name)
72-
require.Equal(t, "dir", *c.Targets[1].Context)
72+
require.Equal(t, "dir", *c.Targets[1].ContextPath())
7373
require.Equal(t, map[string]string{"foo": "bar"}, c.Targets[1].Contexts)
7474
require.Equal(t, "Dockerfile-alternate", *c.Targets[1].Dockerfile)
7575
require.Equal(t, 1, len(c.Targets[1].Args))
@@ -84,7 +84,7 @@ secrets:
8484
}, stringify(c.Targets[1].Secrets))
8585

8686
require.Equal(t, "webapp2", c.Targets[2].Name)
87-
require.Equal(t, "dir", *c.Targets[2].Context)
87+
require.Equal(t, "dir", *c.Targets[2].ContextPath())
8888
require.Equal(t, "FROM alpine\n", *c.Targets[2].DockerfileInline)
8989
}
9090

0 commit comments

Comments
 (0)