Skip to content

Commit 6a31a78

Browse files
committed
dockerfile: expose TARGETSTAGE as builtin argument
Allows capturing what build stage is currently being built for patterns with better base stage reuse. Signed-off-by: Tonis Tiigi <[email protected]>
1 parent f835dd4 commit 6a31a78

File tree

3 files changed

+128
-5
lines changed

3 files changed

+128
-5
lines changed

frontend/dockerfile/dockerfile2llb/convert.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -227,10 +227,6 @@ func toDispatchState(ctx context.Context, dt []byte, opt ConvertOpt) (*dispatchS
227227
opt.LLBCaps = &caps
228228
}
229229

230-
platformOpt := buildPlatformOpt(&opt)
231-
232-
globalArgs := platformArgs(platformOpt, opt.BuildArgs)
233-
234230
dockerfile, err := parser.Parse(bytes.NewReader(dt))
235231
if err != nil {
236232
return nil, err
@@ -261,6 +257,13 @@ func toDispatchState(ctx context.Context, dt []byte, opt ConvertOpt) (*dispatchS
261257
validateStageNames(stages, lint)
262258
validateCommandCasing(stages, lint)
263259

260+
platformOpt := buildPlatformOpt(&opt)
261+
targetName := opt.Target
262+
if targetName == "" {
263+
targetName = stages[len(stages)-1].Name
264+
}
265+
globalArgs := defaultArgs(platformOpt, opt.BuildArgs, targetName)
266+
264267
shlex := shell.NewLex(dockerfile.EscapeToken)
265268
outline := newOutlineCapture()
266269

frontend/dockerfile/dockerfile2llb/platform.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,12 @@ func buildPlatformOpt(opt *ConvertOpt) *platformOpt {
3636
}
3737
}
3838

39-
func platformArgs(po *platformOpt, overrides map[string]string) *llb.EnvList {
39+
func defaultArgs(po *platformOpt, overrides map[string]string, target string) *llb.EnvList {
4040
bp := po.buildPlatforms[0]
4141
tp := po.targetPlatform
42+
if target == "" {
43+
target = "default"
44+
}
4245
s := [...][2]string{
4346
{"BUILDPLATFORM", platforms.Format(bp)},
4447
{"BUILDOS", bp.OS},
@@ -48,6 +51,7 @@ func platformArgs(po *platformOpt, overrides map[string]string) *llb.EnvList {
4851
{"TARGETOS", tp.OS},
4952
{"TARGETARCH", tp.Architecture},
5053
{"TARGETVARIANT", tp.Variant},
54+
{"TARGETSTAGE", target},
5155
}
5256
env := &llb.EnvList{}
5357
for _, kv := range s {

frontend/dockerfile/dockerfile_test.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ var allTests = integration.TestFuncs(
207207
testHistoryFinalizeTrace,
208208
testEmptyStages,
209209
testLocalCustomSessionID,
210+
testTargetStageNameArg,
210211
)
211212

212213
// Tests that depend on the `security.*` entitlements
@@ -1791,6 +1792,121 @@ COPY Dockerfile .
17911792
}
17921793
}
17931794

1795+
func testTargetStageNameArg(t *testing.T, sb integration.Sandbox) {
1796+
integration.SkipOnPlatform(t, "windows")
1797+
f := getFrontend(t, sb)
1798+
1799+
dockerfile := []byte(`
1800+
FROM alpine AS base
1801+
WORKDIR /out
1802+
RUN echo -n "value:$TARGETSTAGE" > /out/first
1803+
ARG TARGETSTAGE
1804+
RUN echo -n "value:$TARGETSTAGE" > /out/second
1805+
1806+
FROM scratch AS foo
1807+
COPY --from=base /out/ /
1808+
1809+
FROM scratch
1810+
COPY --from=base /out/ /
1811+
`)
1812+
1813+
dir := integration.Tmpdir(
1814+
t,
1815+
fstest.CreateFile("Dockerfile", dockerfile, 0600),
1816+
)
1817+
1818+
destDir := t.TempDir()
1819+
1820+
c, err := client.New(sb.Context(), sb.Address())
1821+
require.NoError(t, err)
1822+
defer c.Close()
1823+
1824+
_, err = f.Solve(sb.Context(), c, client.SolveOpt{
1825+
LocalMounts: map[string]fsutil.FS{
1826+
dockerui.DefaultLocalNameDockerfile: dir,
1827+
dockerui.DefaultLocalNameContext: dir,
1828+
},
1829+
FrontendAttrs: map[string]string{
1830+
"target": "foo",
1831+
},
1832+
Exports: []client.ExportEntry{
1833+
{
1834+
Type: client.ExporterLocal,
1835+
OutputDir: destDir,
1836+
},
1837+
},
1838+
}, nil)
1839+
require.NoError(t, err)
1840+
1841+
dt, err := os.ReadFile(filepath.Join(destDir, "first"))
1842+
require.NoError(t, err)
1843+
require.Equal(t, "value:", string(dt))
1844+
1845+
dt, err = os.ReadFile(filepath.Join(destDir, "second"))
1846+
require.NoError(t, err)
1847+
require.Equal(t, "value:foo", string(dt))
1848+
1849+
destDir = t.TempDir()
1850+
1851+
_, err = f.Solve(sb.Context(), c, client.SolveOpt{
1852+
LocalMounts: map[string]fsutil.FS{
1853+
dockerui.DefaultLocalNameDockerfile: dir,
1854+
dockerui.DefaultLocalNameContext: dir,
1855+
},
1856+
Exports: []client.ExportEntry{
1857+
{
1858+
Type: client.ExporterLocal,
1859+
OutputDir: destDir,
1860+
},
1861+
},
1862+
}, nil)
1863+
require.NoError(t, err)
1864+
1865+
dt, err = os.ReadFile(filepath.Join(destDir, "first"))
1866+
require.NoError(t, err)
1867+
require.Equal(t, "value:", string(dt))
1868+
1869+
dt, err = os.ReadFile(filepath.Join(destDir, "second"))
1870+
require.NoError(t, err)
1871+
require.Equal(t, "value:default", string(dt))
1872+
1873+
// stage name defined in Dockerfile but not passed in request
1874+
dockerfile = append(dockerfile, []byte(`
1875+
1876+
FROM scratch AS final
1877+
COPY --from=base /out/ /
1878+
`)...)
1879+
1880+
dir = integration.Tmpdir(
1881+
t,
1882+
fstest.CreateFile("Dockerfile", dockerfile, 0600),
1883+
)
1884+
1885+
destDir = t.TempDir()
1886+
1887+
_, err = f.Solve(sb.Context(), c, client.SolveOpt{
1888+
LocalMounts: map[string]fsutil.FS{
1889+
dockerui.DefaultLocalNameDockerfile: dir,
1890+
dockerui.DefaultLocalNameContext: dir,
1891+
},
1892+
Exports: []client.ExportEntry{
1893+
{
1894+
Type: client.ExporterLocal,
1895+
OutputDir: destDir,
1896+
},
1897+
},
1898+
}, nil)
1899+
require.NoError(t, err)
1900+
1901+
dt, err = os.ReadFile(filepath.Join(destDir, "first"))
1902+
require.NoError(t, err)
1903+
require.Equal(t, "value:", string(dt))
1904+
1905+
dt, err = os.ReadFile(filepath.Join(destDir, "second"))
1906+
require.NoError(t, err)
1907+
require.Equal(t, "value:final", string(dt))
1908+
}
1909+
17941910
func testExportMultiPlatform(t *testing.T, sb integration.Sandbox) {
17951911
integration.SkipOnPlatform(t, "windows")
17961912
workers.CheckFeatureCompat(t, sb, workers.FeatureOCIExporter, workers.FeatureMultiPlatform)

0 commit comments

Comments
 (0)