From dd060cbd1c6fcce877405cf5acc2fdf6b40849a9 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Thu, 16 Nov 2023 15:42:03 +0100 Subject: [PATCH] bake: split definition into two files Allows to either include tags or labels or both definitions. Keep bake-file output for backward compatiblity. Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- .github/workflows/ci.yml | 3 +- README.md | 25 +++--- UPGRADE.md | 17 ++++ __tests__/meta.test.ts | 173 +++++++++++++++++++++++++++++++-------- action.yml | 10 ++- src/main.ts | 18 ++-- src/meta.ts | 34 +++++++- 7 files changed, 224 insertions(+), 56 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6d0328b58..d1076473f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -323,7 +323,8 @@ jobs: with: files: | ./test/docker-bake.hcl - ${{ steps.docker_meta.outputs.bake-file }} + ${{ steps.docker_meta.outputs.bake-file-tags }} + ${{ steps.docker_meta.outputs.bake-file-labels }} targets: | release diff --git a/README.md b/README.md index e2fc5f338..57710ebe3 100644 --- a/README.md +++ b/README.md @@ -226,12 +226,13 @@ jobs: with: files: | ./docker-bake.hcl - ${{ steps.meta.outputs.bake-file }} + ${{ steps.meta.outputs.bake-file-tags }} + ${{ steps.meta.outputs.bake-file-labels }} targets: build ``` -Content of `${{ steps.meta.outputs.bake-file }}` file will look like this with -`refs/tags/v1.2.3` ref: +Content of `${{ steps.meta.outputs.bake-file-tags }}` and `${{ steps.meta.outputs.bake-file-labels }}` +files merged will look like this with `refs/tags/v1.2.3` ref: ```json { @@ -291,13 +292,14 @@ The following inputs can be used as `step.with` keys: The following outputs are available: -| Name | Type | Description | -|-------------|--------|----------------------------------------------------------------------------| -| `version` | String | Docker image version | -| `tags` | String | Docker tags | -| `labels` | String | Docker labels | -| `json` | String | JSON output of tags and labels | -| `bake-file` | File | [Bake file definition](https://docs.docker.com/build/bake/reference/) path | +| Name | Type | Description | +|--------------------|--------|----------------------------------------------------------------------------------------| +| `version` | String | Docker image version | +| `tags` | String | Docker tags | +| `labels` | String | Docker labels | +| `json` | String | JSON output of tags and labels | +| `bake-file-tags` | File | [Bake file definition](https://docs.docker.com/build/bake/reference/) path with tags | +| `bake-file-labels` | File | [Bake file definition](https://docs.docker.com/build/bake/reference/) path with labels | Alternatively, each output is also exported as an environment variable: @@ -305,7 +307,8 @@ Alternatively, each output is also exported as an environment variable: * `DOCKER_METADATA_OUTPUT_TAGS` * `DOCKER_METADATA_OUTPUT_LABELS` * `DOCKER_METADATA_OUTPUT_JSON` -* `DOCKER_METADATA_OUTPUT_BAKE_FILE` +* `DOCKER_METADATA_OUTPUT_BAKE_FILE_TAGS` +* `DOCKER_METADATA_OUTPUT_BAKE_FILE_LABELS` So it can be used with our [Docker Build Push action](https://github.com/docker/build-push-action/): diff --git a/UPGRADE.md b/UPGRADE.md index ee44179d6..0364f7819 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,5 +1,22 @@ # Upgrade notes +## v5 to v6 + +Bake definition has been split into two files, so you can include either tags +or labels or both: + +```yaml + - + name: Build + uses: docker/bake-action@v3 + with: + files: | + ./docker-bake.hcl + ${{ steps.meta.outputs.bake-file-tags }} + ${{ steps.meta.outputs.bake-file-labels }} + targets: build +``` + ## v2 to v3 * Repository has been moved to docker org. Replace `crazy-max/ghaction-docker-meta@v2` diff --git a/__tests__/meta.test.ts b/__tests__/meta.test.ts index 052ecfc5d..e5033a4c4 100644 --- a/__tests__/meta.test.ts +++ b/__tests__/meta.test.ts @@ -3719,12 +3719,12 @@ describe('json', () => { }); }); -describe('bake', () => { +describe('bakeFile', () => { // prettier-ignore // eslint-disable-next-line jest/expect-expect test.each([ [ - 'bake01', + 'bakeFile01', 'event_push_dev.env', { images: ['user/app'], @@ -3747,6 +3747,16 @@ describe('bake', () => { "user/app:custom", "user/app:tags" ], + "args": { + "DOCKER_META_IMAGES": "user/app", + "DOCKER_META_VERSION": "dev", + } + } + } + }, + { + "target": { + "docker-metadata-action": { "labels": { "org.opencontainers.image.created": "2020-01-10T00:30:00.000Z", "org.opencontainers.image.description": "This your first repo!", @@ -3756,17 +3766,13 @@ describe('bake', () => { "org.opencontainers.image.title": "Hello-World", "org.opencontainers.image.url": "https://github.com/octocat/Hello-World", "org.opencontainers.image.version": "dev" - }, - "args": { - "DOCKER_META_IMAGES": "user/app", - "DOCKER_META_VERSION": "dev", } } } } ], [ - 'bake02', + 'bakeFile02', 'event_push_dev.env', { images: ['user/app'], @@ -3782,6 +3788,16 @@ describe('bake', () => { "user/app:dev", "user/app:my", ], + "args": { + "DOCKER_META_IMAGES": "user/app", + "DOCKER_META_VERSION": "dev", + } + } + } + }, + { + "target": { + "docker-metadata-action": { "labels": { "org.opencontainers.image.created": "2020-01-10T00:30:00.000Z", "org.opencontainers.image.description": "This your first repo!", @@ -3791,17 +3807,13 @@ describe('bake', () => { "org.opencontainers.image.title": "Hello-World", "org.opencontainers.image.url": "https://github.com/octocat/Hello-World", "org.opencontainers.image.version": "dev" - }, - "args": { - "DOCKER_META_IMAGES": "user/app", - "DOCKER_META_VERSION": "dev", } } } } ], [ - 'bake03', + 'bakeFile03', 'event_tag_release1.env', { images: ['user/app'], @@ -3823,6 +3835,16 @@ describe('bake', () => { "user/app:tags", "user/app:latest" ], + "args": { + "DOCKER_META_IMAGES": "user/app", + "DOCKER_META_VERSION": "release1", + } + } + } + }, + { + "target": { + "meta": { "labels": { "org.opencontainers.image.created": "2020-01-10T00:30:00.000Z", "org.opencontainers.image.description": "This your first repo!", @@ -3832,17 +3854,13 @@ describe('bake', () => { "org.opencontainers.image.title": "Hello-World", "org.opencontainers.image.url": "https://github.com/octocat/Hello-World", "org.opencontainers.image.version": "release1" - }, - "args": { - "DOCKER_META_IMAGES": "user/app", - "DOCKER_META_VERSION": "release1", } } } } ], [ - 'bake04', + 'bakeFile04', 'event_tag_20200110-RC2.env', { images: ['user/app'], @@ -3865,6 +3883,16 @@ describe('bake', () => { "user/app:custom", "user/app:tags" ], + "args": { + "DOCKER_META_IMAGES": "user/app", + "DOCKER_META_VERSION": "20200110", + } + } + } + }, + { + "target": { + "docker-metadata-action": { "labels": { "org.opencontainers.image.created": "2020-01-10T00:30:00.000Z", "org.opencontainers.image.description": "This your first repo!", @@ -3874,17 +3902,13 @@ describe('bake', () => { "org.opencontainers.image.title": "Hello-World", "org.opencontainers.image.url": "https://github.com/octocat/Hello-World", "org.opencontainers.image.version": "20200110" - }, - "args": { - "DOCKER_META_IMAGES": "user/app", - "DOCKER_META_VERSION": "20200110", } } } } ], [ - 'bake05', + 'bakeFile05', 'event_tag_v1.1.1.env', { images: ['org/app', 'ghcr.io/user/app'], @@ -3916,6 +3940,16 @@ describe('bake', () => { "ghcr.io/user/app:tags", "ghcr.io/user/app:latest" ], + "args": { + "DOCKER_META_IMAGES": "org/app,ghcr.io/user/app", + "DOCKER_META_VERSION": "1.1.1", + } + } + } + }, + { + "target": { + "docker-metadata-action": { "labels": { "org.opencontainers.image.created": "2020-01-10T00:30:00.000Z", "org.opencontainers.image.description": "This your first repo!", @@ -3925,17 +3959,13 @@ describe('bake', () => { "org.opencontainers.image.title": "Hello-World", "org.opencontainers.image.url": "https://github.com/octocat/Hello-World", "org.opencontainers.image.version": "1.1.1" - }, - "args": { - "DOCKER_META_IMAGES": "org/app,ghcr.io/user/app", - "DOCKER_META_VERSION": "1.1.1", } } } } ], [ - 'bake06', + 'bakeFile06', 'event_tag_v1.1.1.env', { images: ['org/app', 'ghcr.io/user/app'], @@ -3956,6 +3986,16 @@ describe('bake', () => { "ghcr.io/user/app:custom", "ghcr.io/user/app:tags" ], + "args": { + "DOCKER_META_IMAGES": "org/app,ghcr.io/user/app", + "DOCKER_META_VERSION": "my", + } + } + } + }, + { + "target": { + "docker-metadata-action": { "labels": { "org.opencontainers.image.created": "2020-01-10T00:30:00.000Z", "org.opencontainers.image.description": "This your first repo!", @@ -3965,17 +4005,13 @@ describe('bake', () => { "org.opencontainers.image.title": "Hello-World", "org.opencontainers.image.url": "https://github.com/octocat/Hello-World", "org.opencontainers.image.version": "my" - }, - "args": { - "DOCKER_META_IMAGES": "org/app,ghcr.io/user/app", - "DOCKER_META_VERSION": "my", } } } } ], [ - 'bake07', + 'bakeFile07', 'event_tag_v1.1.1.env', { images: ['org/app'], @@ -3993,6 +4029,16 @@ describe('bake', () => { "org/app:v1.1.1", "org/app:latest" ], + "args": { + "DOCKER_META_IMAGES": "org/app", + "DOCKER_META_VERSION": "v1.1.1", + } + } + } + }, + { + "target": { + "docker-metadata-action": { "labels": { "maintainer": "CrazyMax", "org.opencontainers.image.created": "2020-01-10T00:30:00.000Z", @@ -4004,10 +4050,67 @@ describe('bake', () => { "org.opencontainers.image.url": "https://github.com/octocat/Hello-World", "org.opencontainers.image.vendor": "MyCompany", "org.opencontainers.image.version": "v1.1.1" + } + } + } + } + ] + ])('given %p with %p event', async (name: string, envFile: string, inputs: Inputs, exBakeTags: unknown, exBakeLabels: unknown) => { + process.env = dotenv.parse(fs.readFileSync(path.join(__dirname, 'fixtures', envFile))); + + const toolkit = new Toolkit(); + const repo = await toolkit.github.repoData(); + const meta = new Meta({...getInputs(), ...inputs}, await getContext(ContextSource.workflow), repo); + + const bakeFileTags = meta.getBakeFile('tags'); + expect(JSON.parse(fs.readFileSync(bakeFileTags, 'utf8'))).toEqual(exBakeTags); + + const bakeFileLabels = meta.getBakeFile('labels'); + expect(JSON.parse(fs.readFileSync(bakeFileLabels, 'utf8'))).toEqual(exBakeLabels); + }); +}); + +describe('bakeFileTagsLabels', () => { + // prettier-ignore + // eslint-disable-next-line jest/expect-expect + test.each([ + [ + 'bakeFileTagsLabels01', + 'event_push_dev.env', + { + images: ['user/app'], + tags: [ + `type=ref,event=branch`, + `type=raw,my`, + `type=raw,custom`, + `type=raw,tags` + ], + labels: [ + "invalid" + ] + } as Inputs, + { + "target": { + "docker-metadata-action": { + "tags": [ + "user/app:dev", + "user/app:my", + "user/app:custom", + "user/app:tags" + ], + "labels": { + "org.opencontainers.image.created": "2020-01-10T00:30:00.000Z", + "org.opencontainers.image.description": "This your first repo!", + "org.opencontainers.image.licenses": "MIT", + "org.opencontainers.image.revision": "860c1904a1ce19322e91ac35af1ab07466440c37", + "org.opencontainers.image.source": "https://github.com/octocat/Hello-World", + "org.opencontainers.image.title": "Hello-World", + "org.opencontainers.image.url": "https://github.com/octocat/Hello-World", + "org.opencontainers.image.version": "dev" }, "args": { - "DOCKER_META_IMAGES": "org/app", - "DOCKER_META_VERSION": "v1.1.1", + "DOCKER_META_IMAGES": "user/app", + "DOCKER_META_VERSION": "dev", } } } @@ -4020,7 +4123,7 @@ describe('bake', () => { const repo = await toolkit.github.repoData(); const meta = new Meta({...getInputs(), ...inputs}, await getContext(ContextSource.workflow), repo); - const bakeFile = meta.getBakeFile(); + const bakeFile = meta.getBakeFileTagsLabels(); expect(JSON.parse(fs.readFileSync(bakeFile, 'utf8'))).toEqual(exBakeDefinition); }); }); diff --git a/action.yml b/action.yml index 238c58806..fb3f9e3eb 100644 --- a/action.yml +++ b/action.yml @@ -44,10 +44,16 @@ outputs: description: 'Generated Docker tags' labels: description: 'Generated Docker labels' - bake-file: - description: 'Bake definiton file' json: description: 'JSON output of tags and labels' + bake-file-tags: + description: 'Bake definition file with tags' + bake-file-labels: + description: 'Bake definition file with labels' + bake-file: + # We keep this output for backward compatibility. Please use bake-file-tags + # and bake-file-labels outputs instead. + description: 'Bake definition file with tags and labels' runs: using: 'node20' diff --git a/src/main.ts b/src/main.ts index 4d36144e6..5ab44aec9 100644 --- a/src/main.ts +++ b/src/main.ts @@ -81,11 +81,17 @@ actionsToolkit.run( setOutput('json', JSON.stringify(jsonOutput)); }); - // Bake file definition - const bakeFile: string = meta.getBakeFile(); - await core.group(`Bake file definition`, async () => { - core.info(fs.readFileSync(bakeFile, 'utf8')); - setOutput('bake-file', bakeFile); - }); + // Bake files + for (const kind of ['tags', 'labels']) { + const bakeFile: string = meta.getBakeFile(kind); + await core.group(`Bake file definition (${kind})`, async () => { + core.info(fs.readFileSync(bakeFile, 'utf8')); + setOutput(`bake-file-${kind}`, bakeFile); + }); + } + + // Bake file with tags and labels for backward compatibility + // Use bake-file-tags and bake-file-labels outputs instead + setOutput(`bake-file`, meta.getBakeFileTagsLabels()); } ); diff --git a/src/meta.ts b/src/meta.ts index 2e3d110d4..15ee1da3e 100644 --- a/src/meta.ts +++ b/src/meta.ts @@ -494,7 +494,34 @@ export class Meta { }; } - public getBakeFile(): string { + public getBakeFile(kind: string): string { + switch (kind) { + case 'tags': + return this.generateBakeFile(kind, { + tags: this.getTags(), + args: { + DOCKER_META_IMAGES: this.getImageNames().join(','), + DOCKER_META_VERSION: this.version.main + } + }); + case 'labels': + return this.generateBakeFile(kind, { + labels: this.getLabels().reduce((res, label) => { + const matches = label.match(/([^=]*)=(.*)/); + if (!matches) { + return res; + } + res[matches[1]] = matches[2]; + return res; + }, {}) + }); + default: + throw new Error(`Unknown bake file type: ${kind}`); + } + } + + // use getBakeFile instead + public getBakeFileTagsLabels(): string { const bakeFile = path.join(ToolkitContext.tmpDir(), 'docker-metadata-action-bake.json'); fs.writeFileSync( bakeFile, @@ -522,7 +549,12 @@ export class Meta { 2 ) ); + return bakeFile; + } + private generateBakeFile(name: string, dt): string { + const bakeFile = path.join(ToolkitContext.tmpDir(), `docker-metadata-action-bake-${name}.json`); + fs.writeFileSync(bakeFile, JSON.stringify({target: {[this.inputs.bakeTarget]: dt}}, null, 2)); return bakeFile; }