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/__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/buildx/bake.ts b/src/buildx/bake.ts index 3b86fe1..d296eaf 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(), 'metadata-file'); + 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,10 +69,12 @@ export class Bake { return JSON.parse(content); } - public static resolveRefs(): Array | undefined { - const metadata = Bake.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 4352a7a..8b4bc86 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(), 'iidfile'); + 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 getMetadataFilePath(): string { - return path.join(Context.tmpDir(), 'metadata-file'); + public getImageIDFilePath(): string { + return path.join(Context.tmpDir(), this.iidFilename); } - public static resolveImageID(): string | undefined { - const iidFile = Build.getImageIDFilePath(); + public resolveImageID(): string | undefined { + const iidFile = this.getImageIDFilePath(); if (!fs.existsSync(iidFile)) { return undefined; } return fs.readFileSync(iidFile, {encoding: 'utf-8'}).trim(); } - public static resolveMetadata(): BuildMetadata | undefined { - const metadataFile = Build.getMetadataFilePath(); + public getMetadataFilePath(): string { + return path.join(Context.tmpDir(), this.metadataFilename); + } + + public resolveMetadata(): BuildMetadata | undefined { + const metadataFile = this.getMetadataFilePath(); if (!fs.existsSync(metadataFile)) { return undefined; } @@ -54,10 +69,12 @@ export class Build { return JSON.parse(content); } - public static resolveRef(): string | undefined { - const metadata = Build.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']; @@ -65,10 +82,12 @@ export class Build { return undefined; } - public static resolveDigest(): string | undefined { - const metadata = Build.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']; 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}); } 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); + } }