From 50fa0058d9f8c431884235ef44c496ce90806fcf Mon Sep 17 00:00:00 2001 From: CrazyMax Date: Mon, 14 Mar 2022 19:30:50 +0100 Subject: [PATCH] add imageid output and use metadata to set digest output Signed-off-by: CrazyMax --- .github/workflows/ci.yml | 158 +++++++++++++++++++++------------------ README.md | 3 +- __tests__/buildx.test.ts | 17 ++++- action.yml | 4 +- dist/index.js | 30 ++++++-- src/buildx.ts | 11 +++ src/main.ts | 17 +++-- 7 files changed, 153 insertions(+), 87 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5b1febc..815eab2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,10 +26,6 @@ jobs: uses: ./action with: file: ./test/Dockerfile - - - name: Dump context - if: always() - uses: crazy-max/ghaction-dump-context@v1 git-context: runs-on: ubuntu-latest @@ -77,10 +73,6 @@ jobs: echo "::error::Digest should not be empty" exit 1 fi - - - name: Dump context - if: always() - uses: crazy-max/ghaction-dump-context@v1 git-context-secret: runs-on: ubuntu-latest @@ -137,10 +129,6 @@ jobs: echo "::error::Digest should not be empty" exit 1 fi - - - name: Dump context - if: always() - uses: crazy-max/ghaction-dump-context@v1 path-context: runs-on: ubuntu-latest @@ -192,10 +180,6 @@ jobs: echo "::error::Digest should not be empty" exit 1 fi - - - name: Dump context - if: always() - uses: crazy-max/ghaction-dump-context@v1 error: runs-on: ubuntu-latest @@ -223,10 +207,6 @@ jobs: echo "::error::Should have failed" exit 1 fi - - - name: Dump context - if: always() - uses: crazy-max/ghaction-dump-context@v1 error-buildx: runs-on: ubuntu-latest @@ -259,10 +239,6 @@ jobs: echo "::error::Should have failed" exit 1 fi - - - name: Dump context - if: always() - uses: crazy-max/ghaction-dump-context@v1 docker-driver: runs-on: ubuntu-latest @@ -284,10 +260,6 @@ jobs: file: ./test/Dockerfile push: true tags: localhost:5000/name/app:latest - - - name: Dump context - if: always() - uses: crazy-max/ghaction-dump-context@v1 export-docker: runs-on: ubuntu-latest @@ -307,10 +279,6 @@ jobs: name: Inspect run: | docker image inspect myimage:latest - - - name: Dump context - if: always() - uses: crazy-max/ghaction-dump-context@v1 network: runs-on: ubuntu-latest @@ -331,10 +299,6 @@ jobs: context: ./test tags: name/app:latest network: host - - - name: Dump context - if: always() - uses: crazy-max/ghaction-dump-context@v1 shm-size: runs-on: ubuntu-latest @@ -357,10 +321,6 @@ jobs: file: ./test/shmsize.Dockerfile tags: name/app:latest shm-size: 2g - - - name: Dump context - if: always() - uses: crazy-max/ghaction-dump-context@v1 ulimit: runs-on: ubuntu-latest @@ -385,10 +345,6 @@ jobs: ulimit: | nofile=1024:1024 nproc=3 - - - name: Dump context - if: always() - uses: crazy-max/ghaction-dump-context@v1 cgroup-parent: runs-on: ubuntu-latest @@ -411,10 +367,6 @@ jobs: file: ./test/cgroup.Dockerfile tags: name/app:latest cgroup-parent: foo - - - name: Dump context - if: always() - uses: crazy-max/ghaction-dump-context@v1 add-hosts: runs-on: ubuntu-latest @@ -435,10 +387,6 @@ jobs: add-hosts: | docker:10.180.0.1 foo:10.0.0.1 - - - name: Dump context - if: always() - uses: crazy-max/ghaction-dump-context@v1 multi: runs-on: ubuntu-latest @@ -494,10 +442,94 @@ jobs: echo "::error::Digest should not be empty" exit 1 fi + + digest: + runs-on: ubuntu-latest + env: + DOCKER_IMAGE: localhost:5000/name/app + strategy: + fail-fast: false + matrix: + driver: + - docker + - docker-container + load: + - true + - false + push: + - true + - false + exclude: + - driver: docker + load: true + push: true + - driver: docker-container + load: true + push: true + - driver: docker + load: false + push: false + - driver: docker-container + load: false + push: false + services: + registry: + image: registry:2 + ports: + - 5000:5000 + steps: - - name: Dump context - if: always() - uses: crazy-max/ghaction-dump-context@v1 + name: Checkout + uses: actions/checkout@v2 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + with: + version: v0.8.0 + driver: ${{ matrix.driver }} + driver-opts: | + network=host + - + name: Build + id: docker_build + uses: ./ + with: + context: ./test + load: ${{ matrix.load }} + push: ${{ matrix.push }} + tags: ${{ env.DOCKER_IMAGE }}:latest + platforms: ${{ matrix.platforms }} + - + name: Docker images + run: | + docker image ls --no-trunc + - + name: Check digest + if: ${{ matrix.push }} + run: | + if [ -z "${{ steps.docker_build.outputs.digest }}" ]; then + echo "::error::Digest should not be empty" + exit 1 + fi + - + name: Check manifest + if: ${{ matrix.push }} + run: | + set -x + docker buildx imagetools inspect ${{ env.DOCKER_IMAGE }}@${{ steps.docker_build.outputs.digest }} --format '{{json .}}' + - + name: Check image ID + run: | + if [ -z "${{ steps.docker_build.outputs.imageid }}" ]; then + echo "::error::Image ID should not be empty" + exit 1 + fi + - + name: Inspect image + if: ${{ matrix.load }} + run: | + set -x + docker image inspect ${{ steps.docker_build.outputs.imageid }} registry-cache: runs-on: ubuntu-latest @@ -584,10 +616,6 @@ jobs: echo "::error::Digests should be identical" exit 1 fi - - - name: Dump context - if: always() - uses: crazy-max/ghaction-dump-context@v1 local-cache-first: runs-on: ubuntu-latest @@ -650,10 +678,6 @@ jobs: echo "::error::Digest should not be empty" exit 1 fi - - - name: Dump context - if: always() - uses: crazy-max/ghaction-dump-context@v1 local-cache-hit: runs-on: ubuntu-latest @@ -723,10 +747,6 @@ jobs: - name: Cache hit run: echo ${{ steps.cache.outputs.cache-hit }} - - - name: Dump context - if: always() - uses: crazy-max/ghaction-dump-context@v1 github-cache: runs-on: ubuntu-latest @@ -773,7 +793,3 @@ jobs: name: Inspect run: | docker buildx imagetools inspect localhost:5000/name/app:1.0.0 - - - name: Dump context - if: always() - uses: crazy-max/ghaction-dump-context@v1 diff --git a/README.md b/README.md index a053736..8be7ccf 100644 --- a/README.md +++ b/README.md @@ -241,7 +241,8 @@ Following outputs are available | Name | Type | Description | |-------------------|---------|---------------------------------------| -| `digest` | String | Image content-addressable identifier also called a digest | +| `imageid` | String | Image ID | +| `digest` | String | Image digest | | `metadata` | JSON | Build result metadata | ## Troubleshooting diff --git a/__tests__/buildx.test.ts b/__tests__/buildx.test.ts index 028b001..d8855f6 100644 --- a/__tests__/buildx.test.ts +++ b/__tests__/buildx.test.ts @@ -7,7 +7,7 @@ import * as buildx from '../src/buildx'; 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 imageID = 'sha256:bfb45ab72e46908183546477a08f8867fc40cebadd00af54b071b097aed127a9'; const metadata = `{ "containerimage.config.digest": "sha256:059b68a595b22564a1cbc167af369349fdc2ecc1f7bc092c2235cbf601a795fd", "containerimage.digest": "sha256:b09b9482c72371486bb2c1d2c2a2633ed1d0b8389e12c8d52b9e052725c0c83c" @@ -28,9 +28,9 @@ jest.spyOn(context, 'tmpNameSync').mockImplementation((): string => { describe('getImageID', () => { it('matches', async () => { const imageIDFile = await buildx.getImageIDFile(); - await fs.writeFileSync(imageIDFile, digest); - const imageID = await buildx.getImageID(); - expect(imageID).toEqual(digest); + await fs.writeFileSync(imageIDFile, imageID); + const expected = await buildx.getImageID(); + expect(expected).toEqual(imageID); }); }); @@ -43,6 +43,15 @@ describe('getMetadata', () => { }); }); +describe('getDigest', () => { + it('matches', async () => { + const metadataFile = await buildx.getMetadataFile(); + await fs.writeFileSync(metadataFile, metadata); + const expected = await buildx.getDigest(metadata); + expect(expected).toEqual('sha256:b09b9482c72371486bb2c1d2c2a2633ed1d0b8389e12c8d52b9e052725c0c83c'); + }); +}); + describe('isLocalOrTarExporter', () => { // prettier-ignore test.each([ diff --git a/action.yml b/action.yml index d6f5818..a7c9177 100644 --- a/action.yml +++ b/action.yml @@ -89,8 +89,10 @@ inputs: required: false outputs: + imageid: + description: 'Image ID' digest: - description: 'Image content-addressable identifier also called a digest' + description: 'Image digest' metadata: description: 'Build result metadata' diff --git a/dist/index.js b/dist/index.js index 9340fde..14484e4 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.getMetadata = exports.getMetadataFile = 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.getDigest = 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)); @@ -81,6 +81,19 @@ function getMetadata() { }); } exports.getMetadata = getMetadata; +function getDigest(metadata) { + return __awaiter(this, void 0, void 0, function* () { + if (metadata === undefined) { + return undefined; + } + const metadataJSON = JSON.parse(metadata); + if (metadataJSON['containerimage.digest']) { + return metadataJSON['containerimage.digest']; + } + return undefined; + }); +} +exports.getDigest = getDigest; function getSecretString(kvp) { return __awaiter(this, void 0, void 0, function* () { return getSecret(kvp, false); @@ -517,15 +530,22 @@ function run() { } }); const imageID = yield buildx.getImageID(); + const metadata = yield buildx.getMetadata(); + const digest = yield buildx.getDigest(metadata); if (imageID) { - yield core.group(`Digest output`, () => __awaiter(this, void 0, void 0, function* () { + yield core.group(`ImageID`, () => __awaiter(this, void 0, void 0, function* () { core.info(imageID); - context.setOutput('digest', imageID); + context.setOutput('imageid', imageID); + })); + } + if (digest) { + yield core.group(`Digest`, () => __awaiter(this, void 0, void 0, function* () { + core.info(digest); + context.setOutput('digest', digest); })); } - const metadata = yield buildx.getMetadata(); if (metadata) { - yield core.group(`Metadata output`, () => __awaiter(this, void 0, void 0, function* () { + yield core.group(`Metadata`, () => __awaiter(this, void 0, void 0, function* () { core.info(metadata); context.setOutput('metadata', metadata); })); diff --git a/src/buildx.ts b/src/buildx.ts index 157f21c..6d065a7 100644 --- a/src/buildx.ts +++ b/src/buildx.ts @@ -34,6 +34,17 @@ export async function getMetadata(): Promise { return content; } +export async function getDigest(metadata: string | undefined): Promise { + if (metadata === undefined) { + return undefined; + } + const metadataJSON = JSON.parse(metadata); + if (metadataJSON['containerimage.digest']) { + return metadataJSON['containerimage.digest']; + } + return undefined; +} + export async function getSecretString(kvp: string): Promise { return getSecret(kvp, false); } diff --git a/src/main.ts b/src/main.ts index ceb4b36..3c98e3d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -34,16 +34,23 @@ async function run(): Promise { }); const imageID = await buildx.getImageID(); + const metadata = await buildx.getMetadata(); + const digest = await buildx.getDigest(metadata); + if (imageID) { - await core.group(`Digest output`, async () => { + await core.group(`ImageID`, async () => { core.info(imageID); - context.setOutput('digest', imageID); + context.setOutput('imageid', imageID); + }); + } + if (digest) { + await core.group(`Digest`, async () => { + core.info(digest); + context.setOutput('digest', digest); }); } - - const metadata = await buildx.getMetadata(); if (metadata) { - await core.group(`Metadata output`, async () => { + await core.group(`Metadata`, async () => { core.info(metadata); context.setOutput('metadata', metadata); });