diff --git a/__tests__/buildx.test.ts b/__tests__/buildx.test.ts index 7d76677..7dd2e33 100644 --- a/__tests__/buildx.test.ts +++ b/__tests__/buildx.test.ts @@ -177,6 +177,86 @@ describe('getDigest', () => { }); }); +describe('getProvenanceInput', () => { + beforeEach(() => { + process.env = Object.keys(process.env).reduce((object, key) => { + if (!key.startsWith('INPUT_')) { + object[key] = process.env[key]; + } + return object; + }, {}); + }); + + // prettier-ignore + test.each([ + [ + 'true', + 'builder-id=https://github.com/docker/actions-toolkit/actions/runs/123' + ], + [ + 'false', + 'false' + ], + [ + 'mode=min', + 'mode=min,builder-id=https://github.com/docker/actions-toolkit/actions/runs/123' + ], + [ + 'mode=max', + 'mode=max,builder-id=https://github.com/docker/actions-toolkit/actions/runs/123' + ], + [ + 'builder-id=foo', + 'builder-id=foo' + ], + [ + 'mode=max,builder-id=foo', + 'mode=max,builder-id=foo' + ], + [ + '', + '' + ], + ])('given input %p', async (input: string, expected: string) => { + await setInput('provenance', input); + const buildx = new Buildx({ + context: new Context() + }); + expect(buildx.getProvenanceInput('provenance')).toEqual(expected); + }); +}); + +describe('getProvenanceAttrs', () => { + // prettier-ignore + test.each([ + [ + 'mode=min', + 'mode=min,builder-id=https://github.com/docker/actions-toolkit/actions/runs/123' + ], + [ + 'mode=max', + 'mode=max,builder-id=https://github.com/docker/actions-toolkit/actions/runs/123' + ], + [ + 'builder-id=foo', + 'builder-id=foo' + ], + [ + 'mode=max,builder-id=foo', + 'mode=max,builder-id=foo' + ], + [ + '', + 'builder-id=https://github.com/docker/actions-toolkit/actions/runs/123' + ], + ])('given %p', async (input: string, expected: string) => { + const buildx = new Buildx({ + context: new Context() + }); + expect(buildx.getProvenanceAttrs(input)).toEqual(expected); + }); +}); + describe('generateBuildSecret', () => { test.each([ ['A_SECRET=abcdef0123456789', false, 'A_SECRET', 'abcdef0123456789', null], @@ -264,3 +344,12 @@ describe('hasGitAuthTokenSecret', () => { expect(Buildx.hasGitAuthTokenSecret(kvp)).toBe(expected); }); }); + +// 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()}`; +} + +function setInput(name: string, value: string): void { + process.env[getInputName(name)] = value; +} diff --git a/src/buildx.ts b/src/buildx.ts index 7223745..6e006fb 100644 --- a/src/buildx.ts +++ b/src/buildx.ts @@ -1,5 +1,6 @@ import fs from 'fs'; import path from 'path'; +import * as core from '@actions/core'; import * as exec from '@actions/exec'; import {parse} from 'csv-parse/sync'; import * as semver from 'semver'; @@ -157,6 +158,44 @@ export class Buildx { return `id=${key},src=${secretFile}`; } + public getProvenanceInput(name: string): string { + const input = core.getInput(name); + if (!input) { + // if input is not set returns empty string + return input; + } + const builderID = this.context.provenanceBuilderID; + try { + return core.getBooleanInput(name) ? `builder-id=${builderID}` : 'false'; + } catch (err) { + // not a valid boolean, so we assume it's a string + return this.getProvenanceAttrs(input); + } + } + + public getProvenanceAttrs(input: string): string { + if (!input) { + return `builder-id=${this.context.provenanceBuilderID}`; + } + // parse attributes from input + const fields = parse(input, { + relaxColumnCount: true, + skipEmptyLines: true + })[0]; + // check if builder-id attribute exists in the input + for (const field of fields) { + const parts = field + .toString() + .split(/(?<=^[^=]+?)=/) + .map(item => item.trim()); + if (parts[0] == 'builder-id') { + return input; + } + } + // if not add builder-id attribute + return `${input},builder-id=${this.context.provenanceBuilderID}`; + } + public static hasLocalExporter(exporters: string[]): boolean { return Buildx.hasExporterType('local', exporters); }