From c5714886ee41ece461169bb16e5f587bc9a0d2ff Mon Sep 17 00:00:00 2001
From: CrazyMax <1951866+crazy-max@users.noreply.github.com>
Date: Tue, 20 Feb 2024 16:46:22 +0100
Subject: [PATCH] build: set build ref in response

Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
---
 build/build.go                 |  1 +
 docs/reference/buildx_build.md |  1 +
 tests/bake.go                  | 45 ++++++++++++++++++++++++
 tests/build.go                 | 62 ++++++++++++++++++++++++----------
 4 files changed, 92 insertions(+), 17 deletions(-)

diff --git a/build/build.go b/build/build.go
index 24e7b376a345..d39338feb667 100644
--- a/build/build.go
+++ b/build/build.go
@@ -806,6 +806,7 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opt map[s
 					for k, v := range printRes {
 						rr.ExporterResponse[k] = string(v)
 					}
+					rr.ExporterResponse["buildx.build.ref"] = fmt.Sprintf("%s/%s/%s", node.Builder, node.Name, so.Ref)
 
 					node := dp.Node().Driver
 					if node.IsMobyDriver() {
diff --git a/docs/reference/buildx_build.md b/docs/reference/buildx_build.md
index a2cd99fbfb0e..b39741d5a60c 100644
--- a/docs/reference/buildx_build.md
+++ b/docs/reference/buildx_build.md
@@ -327,6 +327,7 @@ $ cat metadata.json
 
 ```json
 {
+  "buildx.build.ref": "mybuilder/mybuilder0/0fjb6ubs52xx3vygf6fgdl611",
   "containerimage.config.digest": "sha256:2937f66a9722f7f4a2df583de2f8cb97fc9196059a410e7f00072fc918930e66",
   "containerimage.descriptor": {
     "annotations": {
diff --git a/tests/bake.go b/tests/bake.go
index a2af62cf0f7d..1f6bd11c7c2c 100644
--- a/tests/bake.go
+++ b/tests/bake.go
@@ -1,6 +1,7 @@
 package tests
 
 import (
+	"encoding/json"
 	"os"
 	"path/filepath"
 	"testing"
@@ -32,6 +33,7 @@ var bakeTests = []func(t *testing.T, sb integration.Sandbox){
 	testBakeRemoteDockerfileCwd,
 	testBakeRemoteLocalContextRemoteDockerfile,
 	testBakeEmpty,
+	testBakeRefs,
 }
 
 func testBakeLocal(t *testing.T, sb integration.Sandbox) {
@@ -520,3 +522,46 @@ func testBakeEmpty(t *testing.T, sb integration.Sandbox) {
 	require.Error(t, err, out)
 	require.Contains(t, out, "couldn't find a bake definition")
 }
+
+func testBakeRefs(t *testing.T, sb integration.Sandbox) {
+	dockerfile := []byte(`
+FROM scratch
+COPY foo /foo
+	`)
+	bakefile := []byte(`
+target "default" {
+}
+`)
+	dir := tmpdir(
+		t,
+		fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
+		fstest.CreateFile("Dockerfile", dockerfile, 0600),
+		fstest.CreateFile("foo", []byte("foo"), 0600),
+	)
+
+	dirDest := t.TempDir()
+
+	outFlag := "default.output=type=docker"
+	if sb.DockerAddress() == "" {
+		// there is no Docker atm to load the image
+		outFlag += ",dest=" + dirDest + "/image.tar"
+	}
+
+	cmd := buildxCmd(sb, withDir(dir), withArgs("bake", "--metadata-file", filepath.Join(dirDest, "md.json"), "--set", outFlag))
+	out, err := cmd.CombinedOutput()
+	require.NoError(t, err, out)
+
+	dt, err := os.ReadFile(filepath.Join(dirDest, "md.json"))
+	require.NoError(t, err)
+
+	type mdT struct {
+		Default struct {
+			BuildRef string `json:"buildx.build.ref"`
+		} `json:"default"`
+	}
+	var md mdT
+	err = json.Unmarshal(dt, &md)
+	require.NoError(t, err)
+
+	require.NotEmpty(t, md.Default.BuildRef)
+}
diff --git a/tests/build.go b/tests/build.go
index 88796b15f801..7e2f222124a5 100644
--- a/tests/build.go
+++ b/tests/build.go
@@ -48,6 +48,7 @@ var buildTests = []func(t *testing.T, sb integration.Sandbox){
 	testBuildOCIExportNotSupported,
 	testBuildMultiPlatformNotSupported,
 	testDockerHostGateway,
+	testBuildRef,
 }
 
 func testBuild(t *testing.T, sb integration.Sandbox) {
@@ -279,23 +280,6 @@ RUN exit 1`)
 	require.True(t, buildDetailsPattern.MatchString(string(out)), fmt.Sprintf("expected build details link in output, got %q", out))
 }
 
-func createTestProject(t *testing.T) string {
-	dockerfile := []byte(`
-FROM busybox:latest AS base
-COPY foo /etc/foo
-RUN cp /etc/foo /etc/bar
-
-FROM scratch
-COPY --from=base /etc/bar /bar
-`)
-	dir := tmpdir(
-		t,
-		fstest.CreateFile("Dockerfile", dockerfile, 0600),
-		fstest.CreateFile("foo", []byte("foo"), 0600),
-	)
-	return dir
-}
-
 func testBuildProgress(t *testing.T, sb integration.Sandbox) {
 	dir := createTestProject(t)
 	driver, _, _ := strings.Cut(sb.Name(), "+")
@@ -432,3 +416,47 @@ RUN ping -c 1 buildx.host-gateway-ip.local
 		require.NoError(t, err, string(out))
 	}
 }
+
+func testBuildRef(t *testing.T, sb integration.Sandbox) {
+	dir := createTestProject(t)
+	dirDest := t.TempDir()
+
+	outFlag := "--output=type=docker"
+	if sb.DockerAddress() == "" {
+		// there is no Docker atm to load the image
+		outFlag += ",dest=" + dirDest + "/image.tar"
+	}
+
+	cmd := buildxCmd(sb, withArgs("build", outFlag, "--metadata-file", filepath.Join(dirDest, "md.json"), dir))
+	out, err := cmd.CombinedOutput()
+	require.NoError(t, err, string(out))
+
+	dt, err := os.ReadFile(filepath.Join(dirDest, "md.json"))
+	require.NoError(t, err)
+
+	type mdT struct {
+		BuildRef string `json:"buildx.build.ref"`
+	}
+	var md mdT
+	err = json.Unmarshal(dt, &md)
+	require.NoError(t, err)
+
+	require.NotEmpty(t, md.BuildRef)
+}
+
+func createTestProject(t *testing.T) string {
+	dockerfile := []byte(`
+FROM busybox:latest AS base
+COPY foo /etc/foo
+RUN cp /etc/foo /etc/bar
+
+FROM scratch
+COPY --from=base /etc/bar /bar
+`)
+	dir := tmpdir(
+		t,
+		fstest.CreateFile("Dockerfile", dockerfile, 0600),
+		fstest.CreateFile("foo", []byte("foo"), 0600),
+	)
+	return dir
+}