build: gate local delete outputs

Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
This commit is contained in:
CrazyMax
2026-06-10 16:41:10 +02:00
parent 473c6ef306
commit b6e1b7328f
14 changed files with 380 additions and 148 deletions
+75
View File
@@ -45,6 +45,7 @@ var bakeTests = []func(t *testing.T, sb integration.Sandbox){
testBakePrintRemoteContextSubdir,
testBakeLocal,
testBakeLocalMulti,
testBakeLocalExportDeleteMode,
testBakeRemote,
testBakeRemoteAuth,
testBakeRemoteCmdContext,
@@ -673,6 +674,80 @@ services:
require.FileExists(t, filepath.Join(dirDest2, "foo"))
}
func testBakeLocalExportDeleteMode(t *testing.T, sb integration.Sandbox) {
dockerfile := []byte(`
FROM scratch
COPY foo /foo
`)
bakefile := []byte(`
target "default" {
output = ["type=local,dest=out,mode=delete"]
}
`)
t.Run("definition requires allow", func(t *testing.T) {
dir := tmpdir(
t,
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
)
cmd := buildxCmd(sb, withDir(dir), withArgs("bake", "--progress=rawjson"))
out, err := cmd.CombinedOutput()
require.Error(t, err, string(out))
require.Contains(t, string(out), "--allow=buildx.local.delete")
})
t.Run("set requires allow", func(t *testing.T) {
dir := tmpdir(
t,
fstest.CreateFile("docker-bake.hcl", []byte(`target "default" {}`), 0600),
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
)
cmd := buildxCmd(sb, withDir(dir), withArgs("bake", "--progress=rawjson", "--set", "*.output=type=local,dest=out,mode=delete"))
out, err := cmd.CombinedOutput()
require.Error(t, err, string(out))
require.Contains(t, string(out), "--allow=buildx.local.delete")
})
t.Run("allow does not accept value", func(t *testing.T) {
dir := tmpdir(
t,
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
)
out, err := bakeCmd(sb, withDir(dir), withArgs("--allow=buildx.local.delete=true"))
require.Error(t, err, out)
require.Contains(t, out, "buildx.local.delete does not accept a value")
})
t.Run("allow deletes stale files", func(t *testing.T) {
skipNoCompatBuildKit(t, sb, ">= 0.31.0-0", "local exporter mode=delete")
dir := tmpdir(
t,
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
)
dest := filepath.Join(dir, "out")
stale := filepath.Join(dest, "stale")
require.NoError(t, os.MkdirAll(dest, 0o755))
require.NoError(t, os.WriteFile(stale, []byte("stale"), 0o600))
out, err := bakeCmd(sb, withDir(dir), withArgs("--allow=buildx.local.delete"))
require.NoError(t, err, out)
require.FileExists(t, filepath.Join(dest, "foo"))
_, err = os.Stat(stale)
require.ErrorIs(t, err, os.ErrNotExist)
})
}
func testBakeRemote(t *testing.T, sb integration.Sandbox) {
bakefile := []byte(`
target "default" {
+64
View File
@@ -63,6 +63,7 @@ var buildTests = []func(t *testing.T, sb integration.Sandbox){
testBuildLocalStateRemote,
testImageIDOutput,
testBuildLocalExport,
testBuildLocalExportDeleteMode,
testBuildRegistryExport,
testBuildRegistryExportAttestations,
testBuildTarExport,
@@ -491,6 +492,69 @@ func testBuildLocalExport(t *testing.T, sb integration.Sandbox) {
require.Equal(t, "foo", string(dt))
}
func testBuildLocalExportDeleteMode(t *testing.T, sb integration.Sandbox) {
t.Run("requires allow for current working directory", func(t *testing.T) {
dir := createTestProject(t)
out, err := buildCmd(sb, withDir(dir), withArgs("--output=type=local,dest=.,mode=delete", "."))
require.Error(t, err, out)
require.Contains(t, out, "--allow=buildx.local.delete")
})
t.Run("requires allow for outside destination", func(t *testing.T) {
dir := createTestProject(t)
dest := filepath.Join(t.TempDir(), "out")
out, err := buildCmd(sb, withDir(dir), withArgs("--output=type=local,dest="+dest+",mode=delete", "."))
require.Error(t, err, out)
require.Contains(t, out, "--allow=buildx.local.delete")
})
t.Run("requires allow for symlink outside destination", func(t *testing.T) {
dir := createTestProject(t)
require.NoError(t, os.Symlink(t.TempDir(), filepath.Join(dir, "out")))
out, err := buildCmd(sb, withDir(dir), withArgs("--output=type=local,dest=out,mode=delete", "."))
require.Error(t, err, out)
require.Contains(t, out, "--allow=buildx.local.delete")
})
t.Run("allow does not accept value", func(t *testing.T) {
dir := createTestProject(t)
out, err := buildCmd(sb, withDir(dir), withArgs("--allow=buildx.local.delete=true", "--output=type=local,dest=out", "."))
require.Error(t, err, out)
require.Contains(t, out, "buildx.local.delete does not accept a value")
})
t.Run("deletes stale files in subdirectory without allow", func(t *testing.T) {
skipNoCompatBuildKit(t, sb, ">= 0.31.0-0", "local exporter mode=delete")
dir := createTestProject(t)
dest := filepath.Join(dir, "out")
stale := filepath.Join(dest, "stale")
require.NoError(t, os.MkdirAll(dest, 0o755))
require.NoError(t, os.WriteFile(stale, []byte("stale"), 0o600))
out, err := buildCmd(sb, withDir(dir), withArgs("--output=type=local,dest=out,mode=delete", "."))
require.NoError(t, err, out)
require.FileExists(t, filepath.Join(dest, "bar"))
_, err = os.Stat(stale)
require.ErrorIs(t, err, os.ErrNotExist)
})
t.Run("allow permits current working directory", func(t *testing.T) {
skipNoCompatBuildKit(t, sb, ">= 0.31.0-0", "local exporter mode=delete")
dir := createTestProject(t)
stale := filepath.Join(dir, "stale")
require.NoError(t, os.WriteFile(stale, []byte("stale"), 0o600))
out, err := buildCmd(sb, withDir(dir), withArgs("--allow=buildx.local.delete", "--output=type=local,dest=.,mode=delete", "."))
require.NoError(t, err, out)
require.FileExists(t, filepath.Join(dir, "bar"))
_, err = os.Stat(stale)
require.ErrorIs(t, err, os.ErrNotExist)
})
}
func testBuildTarExport(t *testing.T, sb integration.Sandbox) {
dir := createTestProject(t)
outdir := path.Join(dir, "out")