Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 38 additions & 8 deletions build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -607,16 +607,45 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
// Capture the error from this build function.
defer catchFrontendError(&retErr, &frontendErr)

rKey := resultKey(dp, k)
children := childTargets[rKey]

// A call method replies with the metadata of the subrequest
// instead of a build result, so there would be nothing to link
// into the targets using this one as a named context. Solve the
// build request on its own in that case and register its result
// for them. It is never evaluated, so this only resolves the
// definition and does not build anything.
linked := false
if opt.CallFunc != nil && len(children) > 0 {
linkReq := req
linkReq.FrontendOpt = maps.Clone(req.FrontendOpt)
// checks are reported by the call method below, skip them
// here so that this request is not rejected by a Dockerfile
// that asks for check violations to be errors
linkReq.FrontendOpt["build-arg:BUILDKIT_DOCKERFILE_CHECK"] = "skip=all;error=false"
res, err := solve(ctx, c, linkReq)
if err != nil {
return nil, err
}
results.Set(rKey, res)
linked = true
}

solveReq := req
if opt.CallFunc != nil {
if _, ok := req.FrontendOpt["frontend.caps"]; !ok {
req.FrontendOpt["frontend.caps"] = "moby.buildkit.frontend.subrequests+forward"
// keep the initial request untouched, it may have already
// been sent for the linked result above
solveReq.FrontendOpt = maps.Clone(req.FrontendOpt)
if _, ok := solveReq.FrontendOpt["frontend.caps"]; !ok {
solveReq.FrontendOpt["frontend.caps"] = "moby.buildkit.frontend.subrequests+forward"
} else {
req.FrontendOpt["frontend.caps"] += ",moby.buildkit.frontend.subrequests+forward"
solveReq.FrontendOpt["frontend.caps"] += ",moby.buildkit.frontend.subrequests+forward"
}
req.FrontendOpt["requestid"] = "frontend." + opt.CallFunc.Name
solveReq.FrontendOpt["requestid"] = "frontend." + opt.CallFunc.Name
}

res, err := solve(ctx, c, req)
res, err := solve(ctx, c, solveReq)
if err != nil {
return nil, err
}
Expand All @@ -625,11 +654,12 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
callRes = res.Metadata
}

rKey := resultKey(dp, k)
results.Set(rKey, res)
if !linked {
results.Set(rKey, res)
}

forceEval := false
if children := childTargets[rKey]; len(children) > 0 {
if len(children) > 0 {
// wait for the child targets to register their LLB before evaluating
_, err := results.Get(ctx, children...)
if err != nil {
Expand Down
58 changes: 58 additions & 0 deletions tests/bake.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ var bakeTests = []func(t *testing.T, sb integration.Sandbox){
testBakeListVariables,
testBakeListTypedVariables,
testBakeCallCheck,
testBakeCallCheckLinkedTargets,
testBakeCallCheckFlag,
testBakeCallMetadata,
testBakeMultiPlatform,
Expand Down Expand Up @@ -2450,6 +2451,63 @@ target "validate" {
require.Contains(t, out, "ConsistentInstructionCasing")
}

func testBakeCallCheckLinkedTargets(t *testing.T, sb integration.Sandbox) {
dockerfileBuilder := []byte(`
FROM scratch
COPY foo /foo
`)
dockerfileBase := []byte(`
FROM builder
COPY foo /bar
`)
dockerfileApp := []byte(`
FROM base
COPy foo /baz
`)
bakefile := []byte(`
target "builder" {
dockerfile = "builder.Dockerfile"
}

target "base" {
dockerfile = "base.Dockerfile"
contexts = {
builder = "target:builder"
}
}

target "app" {
dockerfile = "app.Dockerfile"
contexts = {
base = "target:base"
}
}
`)
dir := tmpdir(
t,
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
fstest.CreateFile("builder.Dockerfile", dockerfileBuilder, 0600),
fstest.CreateFile("base.Dockerfile", dockerfileBase, 0600),
fstest.CreateFile("app.Dockerfile", dockerfileApp, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
)

out, err := bakeCmd(
sb,
withDir(dir),
withArgs("app", "--check"),
)
require.Error(t, err, out)

// the "target:" contexts must be resolved for the call method instead of
// being forwarded to the frontend
require.NotContains(t, out, "unsupported context source target")

// the requested target and the ones it links to are all checked
require.Equal(t, 3, strings.Count(out, "Check complete"), out)
require.Contains(t, out, "ConsistentInstructionCasing")
}

func testBakeCallCheckFlag(t *testing.T, sb integration.Sandbox) {
dockerfile := []byte(`
FROM scratch
Expand Down
Loading