Skip to content

Commit 16fb975

Browse files
authored
Merge pull request #5823 from tonistiigi/dockerfile-fix-outline
dockerfile: fix outline cycle on recursive args
2 parents 1a1ee09 + 02e9a89 commit 16fb975

File tree

3 files changed

+100
-3
lines changed

3 files changed

+100
-3
lines changed

frontend/dockerfile/dockerfile2llb/convert.go

+12
Original file line numberDiff line numberDiff line change
@@ -2603,8 +2603,20 @@ func buildMetaArgs(args *llb.EnvList, shlex *shell.Lex, argCommands []instructio
26032603
if err != nil {
26042604
return nil, nil, parser.WithLocation(err, cmd.Location())
26052605
}
2606+
26062607
kp.Value = &result.Result
26072608
info.deps = result.Matched
2609+
if _, ok := result.Matched[kp.Key]; ok {
2610+
delete(info.deps, kp.Key)
2611+
if old, ok := allArgs[kp.Key]; ok {
2612+
for k := range old.deps {
2613+
if info.deps == nil {
2614+
info.deps = make(map[string]struct{})
2615+
}
2616+
info.deps[k] = struct{}{}
2617+
}
2618+
}
2619+
}
26082620
}
26092621
} else {
26102622
kp.Value = &v

frontend/dockerfile/dockerfile2llb/outline.go

+7-3
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,21 @@ func (o outlineCapture) clone() outlineCapture {
5252
}
5353
}
5454

55-
func (o outlineCapture) markAllUsed(in map[string]struct{}) {
55+
func (o outlineCapture) markAllUsed(in map[string]struct{}, visited map[string]struct{}) {
5656
for k := range in {
57+
if _, ok := visited[k]; ok {
58+
continue
59+
}
60+
visited[k] = struct{}{}
5761
if a, ok := o.allArgs[k]; ok {
58-
o.markAllUsed(a.deps)
62+
o.markAllUsed(a.deps, visited)
5963
}
6064
o.usedArgs[k] = struct{}{}
6165
}
6266
}
6367

6468
func (ds *dispatchState) args(visited map[string]struct{}) []outline.Arg {
65-
ds.outline.markAllUsed(ds.outline.usedArgs)
69+
ds.outline.markAllUsed(ds.outline.usedArgs, map[string]struct{}{})
6670

6771
args := make([]outline.Arg, 0, len(ds.outline.usedArgs))
6872
for k := range ds.outline.usedArgs {

frontend/dockerfile/dockerfile_outline_test.go

+81
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ var outlineTests = integration.TestFuncs(
2323
testOutlineArgs,
2424
testOutlineSecrets,
2525
testOutlineDescribeDefinition,
26+
testOutlineRecursiveArgs,
2627
)
2728

2829
func testOutlineArgs(t *testing.T, sb integration.Sandbox) {
@@ -268,6 +269,86 @@ FROM second
268269
require.True(t, called)
269270
}
270271

272+
func testOutlineRecursiveArgs(t *testing.T, sb integration.Sandbox) {
273+
integration.SkipOnPlatform(t, "windows")
274+
workers.CheckFeatureCompat(t, sb, workers.FeatureFrontendOutline)
275+
f := getFrontend(t, sb)
276+
if _, ok := f.(*clientFrontend); !ok {
277+
t.Skip("only test with client frontend")
278+
}
279+
280+
dockerfile := []byte(`
281+
ARG FOO=123
282+
ARG ABC=abc
283+
ARG DEF=def
284+
ARG FOO=${FOO}${ABC}
285+
ARG BAR=${FOO}456
286+
ARG FOO=${FOO}456${BAR}
287+
FROM scratch
288+
ARG FOO
289+
ARG INFOO=123
290+
ARG INBAR=${INFOO}456
291+
ARG INFOO=${INFOO}456${INBAR}
292+
`)
293+
294+
dir := integration.Tmpdir(
295+
t,
296+
fstest.CreateFile("Dockerfile", []byte(dockerfile), 0600),
297+
)
298+
299+
c, err := client.New(sb.Context(), sb.Address())
300+
require.NoError(t, err)
301+
defer c.Close()
302+
303+
destDir, err := os.MkdirTemp("", "buildkit")
304+
require.NoError(t, err)
305+
defer os.RemoveAll(destDir)
306+
307+
called := false
308+
frontend := func(ctx context.Context, c gateway.Client) (*gateway.Result, error) {
309+
res, err := c.Solve(ctx, gateway.SolveRequest{
310+
FrontendOpt: map[string]string{
311+
"frontend.caps": "moby.buildkit.frontend.subrequests",
312+
"requestid": "frontend.outline",
313+
},
314+
Frontend: "dockerfile.v0",
315+
})
316+
require.NoError(t, err)
317+
318+
outline, err := unmarshalOutline(res)
319+
require.NoError(t, err)
320+
321+
require.Len(t, outline.Args, 5)
322+
323+
require.Equal(t, "ABC", outline.Args[0].Name)
324+
require.Equal(t, "abc", outline.Args[0].Value)
325+
326+
require.Equal(t, "BAR", outline.Args[1].Name)
327+
require.Equal(t, "123abc456", outline.Args[1].Value)
328+
329+
require.Equal(t, "FOO", outline.Args[2].Name)
330+
require.Equal(t, "123abc456123abc456", outline.Args[2].Value)
331+
332+
require.Equal(t, "INBAR", outline.Args[3].Name)
333+
require.Equal(t, "123456", outline.Args[3].Value)
334+
335+
require.Equal(t, "INFOO", outline.Args[4].Name)
336+
require.Equal(t, "123456123456", outline.Args[4].Value)
337+
338+
called = true
339+
return nil, nil
340+
}
341+
342+
_, err = c.Build(sb.Context(), client.SolveOpt{
343+
LocalMounts: map[string]fsutil.FS{
344+
dockerui.DefaultLocalNameDockerfile: dir,
345+
},
346+
}, "", frontend, nil)
347+
require.NoError(t, err)
348+
349+
require.True(t, called)
350+
}
351+
271352
func testOutlineDescribeDefinition(t *testing.T, sb integration.Sandbox) {
272353
workers.CheckFeatureCompat(t, sb, workers.FeatureFrontendOutline)
273354
f := getFrontend(t, sb)

0 commit comments

Comments
 (0)