From c0b121fe44084a47a0893d4bad2947650b8ad82d Mon Sep 17 00:00:00 2001
From: CrazyMax <crazy-max@users.noreply.github.com>
Date: Mon, 16 Aug 2021 23:44:13 +0200
Subject: [PATCH] Add `metadata` output

Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
---
 .github/workflows/ci.yml  | 24 -----------------------
 README.md                 | 10 ++++------
 __tests__/buildx.test.ts  | 15 +++++++++++++++
 __tests__/context.test.ts | 25 +++++++++++++++++++++++-
 action.yml                |  2 ++
 dist/index.js             | 40 +++++++++++++++++++++++++++++++--------
 src/buildx.ts             | 12 ++++++++++++
 src/context.ts            |  4 +++-
 src/main.ts               | 19 ++++++++++++-------
 9 files changed, 104 insertions(+), 47 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index bd30bed..3ba5de2 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -70,9 +70,6 @@ jobs:
         name: Inspect
         run: |
           docker buildx imagetools inspect localhost:5000/name/app:1.0.0
-      -
-        name: Image digest
-        run: echo ${{ steps.docker_build.outputs.digest }}
       -
         name: Check digest
         run: |
@@ -133,9 +130,6 @@ jobs:
         name: Inspect
         run: |
           docker buildx imagetools inspect localhost:5000/name/app:1.0.0
-      -
-        name: Image digest
-        run: echo ${{ steps.docker_build.outputs.digest }}
       -
         name: Check digest
         run: |
@@ -191,9 +185,6 @@ jobs:
         name: Inspect
         run: |
           docker buildx imagetools inspect localhost:5000/name/app:1.0.0
-      -
-        name: Image digest
-        run: echo ${{ steps.docker_build.outputs.digest }}
       -
         name: Check digest
         run: |
@@ -392,9 +383,6 @@ jobs:
         name: Inspect
         run: |
           docker buildx imagetools inspect localhost:5000/name/app:1.0.0
-      -
-        name: Image digest
-        run: echo ${{ steps.docker_build.outputs.digest }}
       -
         name: Check digest
         run: |
@@ -447,9 +435,6 @@ jobs:
         name: Inspect (1)
         run: |
           docker buildx imagetools inspect localhost:5000/name/app:latest
-      -
-        name: Image digest (1)
-        run: echo ${{ steps.docker_build.outputs.digest }}
       -
         name: Check digest (1)
         run: |
@@ -480,9 +465,6 @@ jobs:
         name: Inspect (2)
         run: |
           docker buildx imagetools inspect localhost:5000/name/app:latest
-      -
-        name: Image digest (2)
-        run: echo ${{ steps.docker_build2.outputs.digest }}
       -
         name: Check digest (2)
         run: |
@@ -557,9 +539,6 @@ jobs:
         name: Inspect
         run: |
           docker buildx imagetools inspect localhost:5000/name/app:1.0.0
-      -
-        name: Image digest
-        run: echo ${{ steps.docker_build.outputs.digest }}
       -
         name: Check digest
         run: |
@@ -622,9 +601,6 @@ jobs:
         name: Inspect
         run: |
           docker buildx imagetools inspect localhost:5000/name/app:1.0.0
-      -
-        name: Image digest
-        run: echo ${{ steps.docker_build.outputs.digest }}
       -
         name: Check digest
         run: |
diff --git a/README.md b/README.md
index 5bdb96e..f72ad4a 100644
--- a/README.md
+++ b/README.md
@@ -100,9 +100,6 @@ jobs:
         with:
           push: true
           tags: user/app:latest
-      -
-        name: Image digest
-        run: echo ${{ steps.docker_build.outputs.digest }}
 ```
 
 Building from the current repository automatically uses the [GitHub Token](https://help.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token)
@@ -223,9 +220,10 @@ Following inputs can be used as `step.with` keys
 
 Following outputs are available
 
-| Name          | Type    | Description                           |
-|---------------|---------|---------------------------------------|
-| `digest`      | String  | Image content-addressable identifier also called a digest |
+| Name              | Type    | Description                           |
+|-------------------|---------|---------------------------------------|
+| `digest`          | String  | Image content-addressable identifier also called a digest |
+| `metadata`        | JSON    | Build result metadata |
 
 ## Troubleshooting
 
diff --git a/__tests__/buildx.test.ts b/__tests__/buildx.test.ts
index 8e65651..e64ac90 100644
--- a/__tests__/buildx.test.ts
+++ b/__tests__/buildx.test.ts
@@ -8,6 +8,10 @@ import * as context from '../src/context';
 
 const tmpNameSync = path.join('/tmp/.docker-build-push-jest', '.tmpname-jest').split(path.sep).join(path.posix.sep);
 const digest = 'sha256:bfb45ab72e46908183546477a08f8867fc40cebadd00af54b071b097aed127a9';
+const metadata = `{
+  "containerimage.config.digest": "sha256:059b68a595b22564a1cbc167af369349fdc2ecc1f7bc092c2235cbf601a795fd",
+  "containerimage.digest": "sha256:b09b9482c72371486bb2c1d2c2a2633ed1d0b8389e12c8d52b9e052725c0c83c"
+}`;
 
 jest.spyOn(context, 'tmpDir').mockImplementation((): string => {
   const tmpDir = path.join('/tmp/.docker-build-push-jest').split(path.sep).join(path.posix.sep);
@@ -32,6 +36,17 @@ describe('getImageID', () => {
   });
 });
 
+describe('getMetadata', () => {
+  it('matches', async () => {
+    const metadataFile = await buildx.getMetadataFile();
+    console.log(`metadataFile: ${metadataFile}`);
+    await fs.writeFileSync(metadataFile, metadata);
+    const expected = await buildx.getMetadata();
+    console.log(`metadata: ${expected}`);
+    expect(expected).toEqual(metadata);
+  });
+});
+
 describe('isLocalOrTarExporter', () => {
   // prettier-ignore
   test.each([
diff --git a/__tests__/context.test.ts b/__tests__/context.test.ts
index 3fc2cfe..3435701 100644
--- a/__tests__/context.test.ts
+++ b/__tests__/context.test.ts
@@ -425,7 +425,30 @@ ccc`],
         '--output', 'type=local,dest=./release-out',
         '.'
       ]
-    ]
+    ],
+    [
+      '0.6.0',
+      new Map<string, string>([
+        ['context', '.'],
+        ['tag', 'localhost:5000/name/app:latest'],
+        ['file', './test/Dockerfile'],
+        ['network', 'host'],
+        ['load', 'false'],
+        ['no-cache', 'false'],
+        ['push', 'true'],
+        ['pull', 'false']
+      ]),
+      [
+        'buildx',
+        'build',
+        '--iidfile', '/tmp/.docker-build-push-jest/iidfile',
+        '--metadata-file', '/tmp/.docker-build-push-jest/metadata-file',
+        '--file', './test/Dockerfile',
+        '--network', 'host',
+        '--push',
+        '.'
+      ]
+    ],
   ])(
     'given %p with %p as inputs, returns %p',
     async (buildxVersion: string, inputs: Map<string, any>, expected: Array<string>) => {
diff --git a/action.yml b/action.yml
index 49f08fb..abcfe2f 100644
--- a/action.yml
+++ b/action.yml
@@ -79,6 +79,8 @@ inputs:
 outputs:
   digest:
     description: 'Image content-addressable identifier also called a digest'
+  metadata:
+    description: 'Build result metadata'
 
 runs:
   using: 'node12'
diff --git a/dist/index.js b/dist/index.js
index ead03d6..8a233d7 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -38,7 +38,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
     return (mod && mod.__esModule) ? mod : { "default": mod };
 };
 Object.defineProperty(exports, "__esModule", ({ value: true }));
-exports.satisfies = exports.parseVersion = exports.getVersion = exports.isAvailable = exports.hasGitAuthToken = exports.isLocalOrTarExporter = exports.getSecret = exports.getSecretFile = exports.getSecretString = exports.getImageID = exports.getImageIDFile = void 0;
+exports.satisfies = exports.parseVersion = exports.getVersion = exports.isAvailable = exports.hasGitAuthToken = exports.isLocalOrTarExporter = exports.getSecret = exports.getSecretFile = exports.getSecretString = exports.getMetadata = exports.getMetadataFile = exports.getImageID = exports.getImageIDFile = void 0;
 const sync_1 = __importDefault(__nccwpck_require__(8750));
 const fs_1 = __importDefault(__nccwpck_require__(5747));
 const path_1 = __importDefault(__nccwpck_require__(5622));
@@ -61,6 +61,22 @@ function getImageID() {
     });
 }
 exports.getImageID = getImageID;
+function getMetadataFile() {
+    return __awaiter(this, void 0, void 0, function* () {
+        return path_1.default.join(context.tmpDir(), 'metadata-file').split(path_1.default.sep).join(path_1.default.posix.sep);
+    });
+}
+exports.getMetadataFile = getMetadataFile;
+function getMetadata() {
+    return __awaiter(this, void 0, void 0, function* () {
+        const metadataFile = yield getMetadataFile();
+        if (!fs_1.default.existsSync(metadataFile)) {
+            return undefined;
+        }
+        return fs_1.default.readFileSync(metadataFile, { encoding: 'utf-8' });
+    });
+}
+exports.getMetadata = getMetadata;
 function getSecretString(kvp) {
     return __awaiter(this, void 0, void 0, function* () {
         return getSecret(kvp, false);
@@ -311,6 +327,9 @@ function getBuildArgs(inputs, defaultContext, buildxVersion) {
         if (!buildx.isLocalOrTarExporter(inputs.outputs) && (inputs.platforms.length == 0 || buildx.satisfies(buildxVersion, '>=0.4.2'))) {
             args.push('--iidfile', yield buildx.getImageIDFile());
         }
+        if (buildx.satisfies(buildxVersion, '>=0.6.0')) {
+            args.push('--metadata-file', yield buildx.getMetadataFile());
+        }
         yield exports.asyncForEach(inputs.cacheFrom, (cacheFrom) => __awaiter(this, void 0, void 0, function* () {
             args.push('--cache-from', cacheFrom);
         }));
@@ -476,13 +495,18 @@ function run() {
                     throw new Error(`buildx failed with: ${res.stderr.match(/(.*)\s*$/)[0].trim()}`);
                 }
             });
-            const imageID = yield buildx.getImageID();
-            if (imageID) {
-                core.startGroup(`Extracting digest`);
-                core.info(`${imageID}`);
-                context.setOutput('digest', imageID);
-                core.endGroup();
-            }
+            yield core.group(`Setting outputs`, () => __awaiter(this, void 0, void 0, function* () {
+                const imageID = yield buildx.getImageID();
+                const metadata = yield buildx.getMetadata();
+                if (imageID) {
+                    core.info(`digest=${imageID}`);
+                    context.setOutput('digest', imageID);
+                }
+                if (metadata) {
+                    core.info(`metadata=${metadata}`);
+                    context.setOutput('metadata', metadata);
+                }
+            }));
         }
         catch (error) {
             core.setFailed(error.message);
diff --git a/src/buildx.ts b/src/buildx.ts
index 116fd2a..77b450b 100644
--- a/src/buildx.ts
+++ b/src/buildx.ts
@@ -18,6 +18,18 @@ export async function getImageID(): Promise<string | undefined> {
   return fs.readFileSync(iidFile, {encoding: 'utf-8'});
 }
 
+export async function getMetadataFile(): Promise<string> {
+  return path.join(context.tmpDir(), 'metadata-file').split(path.sep).join(path.posix.sep);
+}
+
+export async function getMetadata(): Promise<string | undefined> {
+  const metadataFile = await getMetadataFile();
+  if (!fs.existsSync(metadataFile)) {
+    return undefined;
+  }
+  return fs.readFileSync(metadataFile, {encoding: 'utf-8'});
+}
+
 export async function getSecretString(kvp: string): Promise<string> {
   return getSecret(kvp, false);
 }
diff --git a/src/context.ts b/src/context.ts
index 552b0fe..b951a9b 100644
--- a/src/context.ts
+++ b/src/context.ts
@@ -2,7 +2,6 @@ import csvparse from 'csv-parse/lib/sync';
 import * as fs from 'fs';
 import * as os from 'os';
 import * as path from 'path';
-import * as semver from 'semver';
 import * as tmp from 'tmp';
 
 import * as core from '@actions/core';
@@ -122,6 +121,9 @@ async function getBuildArgs(inputs: Inputs, defaultContext: string, buildxVersio
   if (!buildx.isLocalOrTarExporter(inputs.outputs) && (inputs.platforms.length == 0 || buildx.satisfies(buildxVersion, '>=0.4.2'))) {
     args.push('--iidfile', await buildx.getImageIDFile());
   }
+  if (buildx.satisfies(buildxVersion, '>=0.6.0')) {
+    args.push('--metadata-file', await buildx.getMetadataFile());
+  }
   await asyncForEach(inputs.cacheFrom, async cacheFrom => {
     args.push('--cache-from', cacheFrom);
   });
diff --git a/src/main.ts b/src/main.ts
index 4b6af66..d1ce06d 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -33,13 +33,18 @@ async function run(): Promise<void> {
         }
       });
 
-    const imageID = await buildx.getImageID();
-    if (imageID) {
-      core.startGroup(`Extracting digest`);
-      core.info(`${imageID}`);
-      context.setOutput('digest', imageID);
-      core.endGroup();
-    }
+    await core.group(`Setting outputs`, async () => {
+      const imageID = await buildx.getImageID();
+      const metadata = await buildx.getMetadata();
+      if (imageID) {
+        core.info(`digest=${imageID}`);
+        context.setOutput('digest', imageID);
+      }
+      if (metadata) {
+        core.info(`metadata=${metadata}`);
+        context.setOutput('metadata', metadata);
+      }
+    });
   } catch (error) {
     core.setFailed(error.message);
   }