From a459c7c911d8894666252eca1eb46102741047c4 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Fri, 26 Apr 2024 13:26:38 +0200 Subject: [PATCH 1/4] buildx: ensure consistent metadata filename for bake and build Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- src/buildx/bake.ts | 2 +- src/buildx/build.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/buildx/bake.ts b/src/buildx/bake.ts index 3b86fe1..44fa8ad 100644 --- a/src/buildx/bake.ts +++ b/src/buildx/bake.ts @@ -52,7 +52,7 @@ export class Bake { } public static getMetadataFilePath(): string { - return path.join(Context.tmpDir(), 'metadata-file'); + return path.join(Context.tmpDir(), 'bake-metadata.json'); } public static resolveMetadata(): BakeMetadata | undefined { diff --git a/src/buildx/build.ts b/src/buildx/build.ts index 4352a7a..2f7a11c 100644 --- a/src/buildx/build.ts +++ b/src/buildx/build.ts @@ -27,11 +27,7 @@ import {BuildMetadata} from '../types/build'; export class Build { public static getImageIDFilePath(): string { - return path.join(Context.tmpDir(), 'iidfile'); - } - - public static getMetadataFilePath(): string { - return path.join(Context.tmpDir(), 'metadata-file'); + return path.join(Context.tmpDir(), 'build-iidfile.txt'); } public static resolveImageID(): string | undefined { @@ -42,6 +38,10 @@ export class Build { return fs.readFileSync(iidFile, {encoding: 'utf-8'}).trim(); } + public static getMetadataFilePath(): string { + return path.join(Context.tmpDir(), 'build-metadata.json'); + } + public static resolveMetadata(): BuildMetadata | undefined { const metadataFile = Build.getMetadataFilePath(); if (!fs.existsSync(metadataFile)) { From 62e3923775286de83889b07df40834b6e0655197 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Fri, 26 Apr 2024 13:54:45 +0200 Subject: [PATCH 2/4] util: generateRandomString func Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- __tests__/util.test.ts | 16 ++++++++++++++++ src/util.ts | 5 +++++ 2 files changed, 21 insertions(+) diff --git a/__tests__/util.test.ts b/__tests__/util.test.ts index 745ec98..aa3d941 100644 --- a/__tests__/util.test.ts +++ b/__tests__/util.test.ts @@ -335,6 +335,22 @@ describe('formatFileSize', () => { }); }); +describe('generateRandomString', () => { + it('should generate a random string of default length 10', async () => { + const res = Util.generateRandomString(); + expect(typeof res).toBe('string'); + expect(res.length).toBe(10); + expect(/^[0-9a-f]+$/i.test(res)).toBe(true); + }); + it('should generate a random string of specified length', async () => { + const length = 15; + const res = Util.generateRandomString(length); + expect(typeof res).toBe('string'); + expect(res.length).toBe(15); + expect(/^[0-9a-f]+$/i.test(res)).toBe(true); + }); +}); + // See: https://github.com/actions/toolkit/blob/a1b068ec31a042ff1e10a522d8fdf0b8869d53ca/packages/core/src/core.ts#L89 function getInputName(name: string): string { return `INPUT_${name.replace(/ /g, '_').toUpperCase()}`; diff --git a/src/util.ts b/src/util.ts index 14a7da4..4fe0bd2 100644 --- a/src/util.ts +++ b/src/util.ts @@ -174,4 +174,9 @@ export class Util { const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } + + public static generateRandomString(length = 10) { + const bytes = crypto.randomBytes(Math.ceil(length / 2)); + return bytes.toString('hex').slice(0, length); + } } From 5820a0ba86f3eb8fb1a4589a1dd6c35a4a0bd174 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Fri, 26 Apr 2024 13:41:03 +0200 Subject: [PATCH 3/4] buildx: generate random metadata filename Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- __tests__/buildx/bake.test.ts | 14 ++++++------ __tests__/buildx/build.test.ts | 28 +++++++++++------------- src/buildx/bake.ts | 14 ++++++------ src/buildx/build.ts | 39 +++++++++++++++++++++++----------- src/toolkit.ts | 9 +++++--- 5 files changed, 59 insertions(+), 45 deletions(-) diff --git a/__tests__/buildx/bake.test.ts b/__tests__/buildx/bake.test.ts index 1b7cc85..d0c9c67 100644 --- a/__tests__/buildx/bake.test.ts +++ b/__tests__/buildx/bake.test.ts @@ -59,19 +59,17 @@ afterEach(() => { describe('resolveMetadata', () => { it('matches', async () => { - const metadataFile = Bake.getMetadataFilePath(); - await fs.writeFileSync(metadataFile, JSON.stringify(metadata)); - const expected = Bake.resolveMetadata(); - expect(expected).toEqual(metadata as BakeMetadata); + const bake = new Bake(); + await fs.writeFileSync(bake.getMetadataFilePath(), JSON.stringify(metadata)); + expect(bake.resolveMetadata()).toEqual(metadata as BakeMetadata); }); }); describe('resolveRefs', () => { it('matches', async () => { - const metadataFile = Bake.getMetadataFilePath(); - await fs.writeFileSync(metadataFile, JSON.stringify(metadata)); - const expected = Bake.resolveRefs(); - expect(expected).toEqual(['default/default/7frbdw1fmfozgtqavghowsepk', 'default/default/onic7g2axylf56rxetob7qruy']); + const bake = new Bake(); + await fs.writeFileSync(bake.getMetadataFilePath(), JSON.stringify(metadata)); + expect(bake.resolveRefs()).toEqual(['default/default/7frbdw1fmfozgtqavghowsepk', 'default/default/onic7g2axylf56rxetob7qruy']); }); }); diff --git a/__tests__/buildx/build.test.ts b/__tests__/buildx/build.test.ts index e258b27..ec15286 100644 --- a/__tests__/buildx/build.test.ts +++ b/__tests__/buildx/build.test.ts @@ -56,37 +56,33 @@ afterEach(() => { describe('resolveImageID', () => { it('matches', async () => { const imageID = 'sha256:bfb45ab72e46908183546477a08f8867fc40cebadd00af54b071b097aed127a9'; - const imageIDFile = Build.getImageIDFilePath(); - await fs.writeFileSync(imageIDFile, imageID); - const expected = Build.resolveImageID(); - expect(expected).toEqual(imageID); + const build = new Build(); + await fs.writeFileSync(build.getImageIDFilePath(), imageID); + expect(build.resolveImageID()).toEqual(imageID); }); }); describe('resolveMetadata', () => { it('matches', async () => { - const metadataFile = Build.getMetadataFilePath(); - await fs.writeFileSync(metadataFile, JSON.stringify(metadata)); - const expected = Build.resolveMetadata(); - expect(expected).toEqual(metadata); + const build = new Build(); + await fs.writeFileSync(build.getMetadataFilePath(), JSON.stringify(metadata)); + expect(build.resolveMetadata()).toEqual(metadata); }); }); describe('resolveRef', () => { it('matches', async () => { - const metadataFile = Build.getMetadataFilePath(); - await fs.writeFileSync(metadataFile, JSON.stringify(metadata)); - const expected = Build.resolveRef(); - expect(expected).toEqual('default/default/n6ibcp9b2pw108rrz7ywdznvo'); + const build = new Build(); + await fs.writeFileSync(build.getMetadataFilePath(), JSON.stringify(metadata)); + expect(build.resolveRef()).toEqual('default/default/n6ibcp9b2pw108rrz7ywdznvo'); }); }); describe('resolveDigest', () => { it('matches', async () => { - const metadataFile = Build.getMetadataFilePath(); - await fs.writeFileSync(metadataFile, JSON.stringify(metadata)); - const expected = Build.resolveDigest(); - expect(expected).toEqual('sha256:b09b9482c72371486bb2c1d2c2a2633ed1d0b8389e12c8d52b9e052725c0c83c'); + const build = new Build(); + await fs.writeFileSync(build.getMetadataFilePath(), JSON.stringify(metadata)); + expect(build.resolveDigest()).toEqual('sha256:b09b9482c72371486bb2c1d2c2a2633ed1d0b8389e12c8d52b9e052725c0c83c'); }); }); diff --git a/src/buildx/bake.ts b/src/buildx/bake.ts index 44fa8ad..60dec38 100644 --- a/src/buildx/bake.ts +++ b/src/buildx/bake.ts @@ -46,17 +46,19 @@ export interface BakeCmdOpts { export class Bake { private readonly buildx: Buildx; + private readonly metadataFilename: string; constructor(opts?: BakeOpts) { this.buildx = opts?.buildx || new Buildx(); + this.metadataFilename = `bake-metadata-${Util.generateRandomString()}.json`; } - public static getMetadataFilePath(): string { - return path.join(Context.tmpDir(), 'bake-metadata.json'); + public getMetadataFilePath(): string { + return path.join(Context.tmpDir(), this.metadataFilename); } - public static resolveMetadata(): BakeMetadata | undefined { - const metadataFile = Bake.getMetadataFilePath(); + public resolveMetadata(): BakeMetadata | undefined { + const metadataFile = this.getMetadataFilePath(); if (!fs.existsSync(metadataFile)) { return undefined; } @@ -67,8 +69,8 @@ export class Bake { return JSON.parse(content); } - public static resolveRefs(): Array | undefined { - const metadata = Bake.resolveMetadata(); + public resolveRefs(): Array | undefined { + const metadata = this.resolveMetadata(); if (!metadata) { return undefined; } diff --git a/src/buildx/build.ts b/src/buildx/build.ts index 2f7a11c..6ce292d 100644 --- a/src/buildx/build.ts +++ b/src/buildx/build.ts @@ -19,31 +19,46 @@ import path from 'path'; import * as core from '@actions/core'; import {parse} from 'csv-parse/sync'; +import {Buildx} from './buildx'; import {Context} from '../context'; import {GitHub} from '../github'; import {Util} from '../util'; import {BuildMetadata} from '../types/build'; +export interface BuildOpts { + buildx?: Buildx; +} + export class Build { - public static getImageIDFilePath(): string { - return path.join(Context.tmpDir(), 'build-iidfile.txt'); + private readonly buildx: Buildx; + private readonly iidFilename: string; + private readonly metadataFilename: string; + + constructor(opts?: BuildOpts) { + this.buildx = opts?.buildx || new Buildx(); + this.iidFilename = `build-iidfile-${Util.generateRandomString()}.txt`; + this.metadataFilename = `build-metadata-${Util.generateRandomString()}.json`; } - public static resolveImageID(): string | undefined { - const iidFile = Build.getImageIDFilePath(); + public getImageIDFilePath(): string { + return path.join(Context.tmpDir(), this.iidFilename); + } + + public resolveImageID(): string | undefined { + const iidFile = this.getImageIDFilePath(); if (!fs.existsSync(iidFile)) { return undefined; } return fs.readFileSync(iidFile, {encoding: 'utf-8'}).trim(); } - public static getMetadataFilePath(): string { - return path.join(Context.tmpDir(), 'build-metadata.json'); + public getMetadataFilePath(): string { + return path.join(Context.tmpDir(), this.metadataFilename); } - public static resolveMetadata(): BuildMetadata | undefined { - const metadataFile = Build.getMetadataFilePath(); + public resolveMetadata(): BuildMetadata | undefined { + const metadataFile = this.getMetadataFilePath(); if (!fs.existsSync(metadataFile)) { return undefined; } @@ -54,8 +69,8 @@ export class Build { return JSON.parse(content); } - public static resolveRef(): string | undefined { - const metadata = Build.resolveMetadata(); + public resolveRef(): string | undefined { + const metadata = this.resolveMetadata(); if (!metadata) { return undefined; } @@ -65,8 +80,8 @@ export class Build { return undefined; } - public static resolveDigest(): string | undefined { - const metadata = Build.resolveMetadata(); + public resolveDigest(): string | undefined { + const metadata = this.resolveMetadata(); if (!metadata) { return undefined; } diff --git a/src/toolkit.ts b/src/toolkit.ts index dc6ffec..2566ea5 100644 --- a/src/toolkit.ts +++ b/src/toolkit.ts @@ -15,8 +15,9 @@ */ import {Buildx} from './buildx/buildx'; +import {Build as BuildxBuild} from './buildx/build'; +import {Bake as BuildxBake} from './buildx/bake'; import {Install as BuildxInstall} from './buildx/install'; -import {Bake} from './buildx/bake'; import {Builder} from './buildx/builder'; import {BuildKit} from './buildkit/buildkit'; import {GitHub} from './github'; @@ -32,16 +33,18 @@ export interface ToolkitOpts { export class Toolkit { public github: GitHub; public buildx: Buildx; + public buildxBuild: BuildxBuild; + public buildxBake: BuildxBake; public buildxInstall: BuildxInstall; - public bake: Bake; public builder: Builder; public buildkit: BuildKit; constructor(opts: ToolkitOpts = {}) { this.github = new GitHub({token: opts.githubToken}); this.buildx = new Buildx(); + this.buildxBuild = new BuildxBuild({buildx: this.buildx}); + this.buildxBake = new BuildxBake({buildx: this.buildx}); this.buildxInstall = new BuildxInstall(); - this.bake = new Bake({buildx: this.buildx}); this.builder = new Builder({buildx: this.buildx}); this.buildkit = new BuildKit({buildx: this.buildx}); } From a99525f2f1c3671dc81b4a4fc60a48d8073b069b Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Fri, 26 Apr 2024 14:04:58 +0200 Subject: [PATCH 4/4] buildx: allow passing metadata file when resolving digest or refs Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- src/buildx/bake.ts | 8 +++++--- src/buildx/build.ts | 16 ++++++++++------ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/buildx/bake.ts b/src/buildx/bake.ts index 60dec38..d296eaf 100644 --- a/src/buildx/bake.ts +++ b/src/buildx/bake.ts @@ -69,10 +69,12 @@ export class Bake { return JSON.parse(content); } - public resolveRefs(): Array | undefined { - const metadata = this.resolveMetadata(); + public resolveRefs(metadata?: BakeMetadata): Array | undefined { if (!metadata) { - return undefined; + metadata = this.resolveMetadata(); + if (!metadata) { + return undefined; + } } const refs = new Array(); for (const key in metadata) { diff --git a/src/buildx/build.ts b/src/buildx/build.ts index 6ce292d..8b4bc86 100644 --- a/src/buildx/build.ts +++ b/src/buildx/build.ts @@ -69,10 +69,12 @@ export class Build { return JSON.parse(content); } - public resolveRef(): string | undefined { - const metadata = this.resolveMetadata(); + public resolveRef(metadata?: BuildMetadata): string | undefined { if (!metadata) { - return undefined; + metadata = this.resolveMetadata(); + if (!metadata) { + return undefined; + } } if ('buildx.build.ref' in metadata) { return metadata['buildx.build.ref']; @@ -80,10 +82,12 @@ export class Build { return undefined; } - public resolveDigest(): string | undefined { - const metadata = this.resolveMetadata(); + public resolveDigest(metadata?: BuildMetadata): string | undefined { if (!metadata) { - return undefined; + metadata = this.resolveMetadata(); + if (!metadata) { + return undefined; + } } if ('containerimage.digest' in metadata) { return metadata['containerimage.digest'];