diff --git a/README.md b/README.md index 66831d0d..789822cf 100644 --- a/README.md +++ b/README.md @@ -296,8 +296,8 @@ flavor: | ``` * `latest=`: Handle [latest tag](#latest-tag) (default `auto`) -* `prefix=`: A global prefix for each generated tag -* `suffix=`: A global suffix for each generated tag +* `prefix=,onlatest=`: A global prefix for each generated tag and optionnally for `latest` +* `suffix=,onlatest=`: A global suffix for each generated tag and optionnally for `latest` ## `tags` input diff --git a/__tests__/flavor.test.ts b/__tests__/flavor.test.ts index fcd6642d..373ab4b1 100644 --- a/__tests__/flavor.test.ts +++ b/__tests__/flavor.test.ts @@ -32,7 +32,9 @@ describe('transform', () => { { latest: "true", prefix: "", - suffix: "" + prefixLatest: false, + suffix: "", + suffixLatest: false, } as Flavor, false ], @@ -43,7 +45,9 @@ describe('transform', () => { { latest: "false", prefix: "", - suffix: "" + prefixLatest: false, + suffix: "", + suffixLatest: false, } as Flavor, false ], @@ -54,7 +58,9 @@ describe('transform', () => { { latest: "auto", prefix: "", - suffix: "" + prefixLatest: false, + suffix: "", + suffixLatest: false, } as Flavor, false ], @@ -72,7 +78,9 @@ describe('transform', () => { { latest: "auto", prefix: "sha-", - suffix: "" + prefixLatest: false, + suffix: "", + suffixLatest: false, } as Flavor, false ], @@ -83,7 +91,9 @@ describe('transform', () => { { latest: "auto", prefix: "", - suffix: "-alpine" + prefixLatest: false, + suffix: "-alpine", + suffixLatest: false, } as Flavor, false ], @@ -96,7 +106,49 @@ describe('transform', () => { { latest: "false", prefix: "dev-", - suffix: "-alpine" + prefixLatest: false, + suffix: "-alpine", + suffixLatest: false, + } as Flavor, + false + ], + [ + [ + `prefix=dev-,onlatest=true`, + ], + { + latest: "auto", + prefix: "dev-", + prefixLatest: true, + suffix: "", + suffixLatest: false, + } as Flavor, + false + ], + [ + [ + `suffix=-alpine,onlatest=true`, + ], + { + latest: "auto", + prefix: "", + prefixLatest: false, + suffix: "-alpine", + suffixLatest: true, + } as Flavor, + false + ], + [ + [ + `prefix=dev-,onlatest=true`, + `suffix=-alpine,onlatest=true`, + ], + { + latest: "auto", + prefix: "dev-", + prefixLatest: true, + suffix: "-alpine", + suffixLatest: true, } as Flavor, false ], diff --git a/__tests__/meta.test.ts b/__tests__/meta.test.ts index 2e0c3e9f..a8ee660a 100644 --- a/__tests__/meta.test.ts +++ b/__tests__/meta.test.ts @@ -1301,7 +1301,78 @@ describe('tag', () => { "org.opencontainers.image.revision=90dd6032fac8bda1b6c4436a2e65de27961ed071", "org.opencontainers.image.licenses=MIT" ] - ] + ], + [ + 'tag21', + 'event_tag_v1.1.1.env', + { + images: ['org/app', 'ghcr.io/user/app'], + tags: [ + `type=semver,pattern={{version}}`, + `type=semver,pattern={{major}}.{{minor}}.{{patch}}` + ], + flavor: [ + `suffix=-dev,onlatest=true` + ] + } as Inputs, + { + main: '1.1.1-dev', + partial: [], + latest: true + } as Version, + [ + 'org/app:1.1.1-dev', + 'org/app:latest-dev', + 'ghcr.io/user/app:1.1.1-dev', + 'ghcr.io/user/app:latest-dev' + ], + [ + "org.opencontainers.image.title=Hello-World", + "org.opencontainers.image.description=This your first repo!", + "org.opencontainers.image.url=https://github.com/octocat/Hello-World", + "org.opencontainers.image.source=https://github.com/octocat/Hello-World", + "org.opencontainers.image.version=1.1.1-dev", + "org.opencontainers.image.created=2020-01-10T00:30:00.000Z", + "org.opencontainers.image.revision=90dd6032fac8bda1b6c4436a2e65de27961ed071", + "org.opencontainers.image.licenses=MIT" + ] + ], + [ + 'tag22', + 'event_tag_v1.1.1.env', + { + images: ['org/app', 'ghcr.io/user/app'], + tags: [ + `type=semver,pattern={{version}}`, + `type=semver,pattern={{major}}.{{minor}}.{{patch}}` + ], + flavor: [ + `prefix=foo-,onlatest=true`, + `suffix=-dev,onlatest=true` + ] + } as Inputs, + { + main: 'foo-1.1.1-dev', + partial: [], + latest: true + } as Version, + [ + 'org/app:foo-1.1.1-dev', + 'org/app:foo-latest-dev', + 'ghcr.io/user/app:foo-1.1.1-dev', + 'ghcr.io/user/app:foo-latest-dev' + ], + [ + "org.opencontainers.image.title=Hello-World", + "org.opencontainers.image.description=This your first repo!", + "org.opencontainers.image.url=https://github.com/octocat/Hello-World", + "org.opencontainers.image.source=https://github.com/octocat/Hello-World", + "org.opencontainers.image.version=foo-1.1.1-dev", + "org.opencontainers.image.created=2020-01-10T00:30:00.000Z", + "org.opencontainers.image.revision=90dd6032fac8bda1b6c4436a2e65de27961ed071", + "org.opencontainers.image.licenses=MIT" + ] + ], ])('given %p with %p event', tagsLabelsTest); }); diff --git a/dist/index.js b/dist/index.js index b68e1a58..6a4a7f60 100644 --- a/dist/index.js +++ b/dist/index.js @@ -129,45 +129,78 @@ var __importStar = (this && this.__importStar) || function (mod) { __setModuleDefault(result, mod); return result; }; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.Transform = void 0; const core = __importStar(__webpack_require__(2186)); +const sync_1 = __importDefault(__webpack_require__(8750)); function Transform(inputs) { const flavor = { latest: 'auto', prefix: '', - suffix: '' + prefixLatest: false, + suffix: '', + suffixLatest: false }; for (const input of inputs) { - const parts = input.split('=', 2); - if (parts.length == 1) { - throw new Error(`Invalid entry: ${input}`); - } - switch (parts[0]) { - case 'latest': { - flavor.latest = parts[1]; - if (!['auto', 'true', 'false'].includes(flavor.latest)) { - throw new Error(`Invalid latest flavor entry: ${input}`); - } - break; - } - case 'prefix': { - flavor.prefix = parts[1]; - break; + const fields = sync_1.default(input, { + relaxColumnCount: true, + skipLinesWithEmptyValues: true + })[0]; + let onlatestfor = ''; + for (const field of fields) { + const parts = field.toString().split('=', 2); + if (parts.length == 1) { + throw new Error(`Invalid flavor entry: ${input}`); } - case 'suffix': { - flavor.suffix = parts[1]; - break; - } - default: { - throw new Error(`Unknown entry: ${input}`); + switch (parts[0]) { + case 'latest': { + flavor.latest = parts[1]; + if (!['auto', 'true', 'false'].includes(flavor.latest)) { + throw new Error(`Invalid latest flavor entry: ${input}`); + } + break; + } + case 'prefix': { + flavor.prefix = parts[1]; + onlatestfor = 'prefix'; + break; + } + case 'suffix': { + flavor.suffix = parts[1]; + onlatestfor = 'suffix'; + break; + } + case 'onlatest': { + if (!['true', 'false'].includes(parts[1])) { + throw new Error(`Invalid value for onlatest attribute: ${parts[1]}`); + } + switch (onlatestfor) { + case 'prefix': { + flavor.prefixLatest = /true/i.test(parts[1]); + break; + } + case 'suffix': { + flavor.suffixLatest = /true/i.test(parts[1]); + break; + } + } + break; + } + default: { + throw new Error(`Unknown flavor entry: ${input}`); + } } } } core.startGroup(`Processing flavor input`); core.info(`latest=${flavor.latest}`); core.info(`prefix=${flavor.prefix}`); + core.info(`prefixLatest=${flavor.prefixLatest}`); core.info(`suffix=${flavor.suffix}`); + core.info(`suffixLatest=${flavor.suffixLatest}`); core.endGroup(); return flavor; } @@ -501,7 +534,6 @@ class Meta { else { vraw = this.context.ref.replace(/^refs\/tags\//g, '').replace(/\//g, '-'); } - let latest = false; let tmatch; const isRegEx = tag.attrs['pattern'].match(/^\/(.+)\/(.*)$/); if (isRegEx) { @@ -633,7 +665,7 @@ class Meta { tags.push(`${imageLc}:${partial}`); } if (this.version.latest) { - tags.push(`${imageLc}:latest`); + tags.push(`${imageLc}:${this.flavor.prefixLatest ? this.flavor.prefix : ''}latest${this.flavor.suffixLatest ? this.flavor.suffix : ''}`); } } return tags; @@ -817,7 +849,7 @@ function Parse(s) { switch (key) { case 'type': { if (!Object.values(Type).includes(value)) { - throw new Error(`Unknown type attribute: ${value}`); + throw new Error(`Unknown tag type attribute: ${value}`); } tag.type = value; break; diff --git a/src/flavor.ts b/src/flavor.ts index 40b02d3d..c5f96cf5 100644 --- a/src/flavor.ts +++ b/src/flavor.ts @@ -1,41 +1,71 @@ import * as core from '@actions/core'; +import csvparse from 'csv-parse/lib/sync'; export interface Flavor { latest: string; prefix: string; + prefixLatest: boolean; suffix: string; + suffixLatest: boolean; } export function Transform(inputs: string[]): Flavor { const flavor: Flavor = { latest: 'auto', prefix: '', - suffix: '' + prefixLatest: false, + suffix: '', + suffixLatest: false }; for (const input of inputs) { - const parts = input.split('=', 2); - if (parts.length == 1) { - throw new Error(`Invalid entry: ${input}`); - } - switch (parts[0]) { - case 'latest': { - flavor.latest = parts[1]; - if (!['auto', 'true', 'false'].includes(flavor.latest)) { - throw new Error(`Invalid latest flavor entry: ${input}`); - } - break; - } - case 'prefix': { - flavor.prefix = parts[1]; - break; + const fields = csvparse(input, { + relaxColumnCount: true, + skipLinesWithEmptyValues: true + })[0]; + let onlatestfor = ''; + for (const field of fields) { + const parts = field.toString().split('=', 2); + if (parts.length == 1) { + throw new Error(`Invalid flavor entry: ${input}`); } - case 'suffix': { - flavor.suffix = parts[1]; - break; - } - default: { - throw new Error(`Unknown entry: ${input}`); + switch (parts[0]) { + case 'latest': { + flavor.latest = parts[1]; + if (!['auto', 'true', 'false'].includes(flavor.latest)) { + throw new Error(`Invalid latest flavor entry: ${input}`); + } + break; + } + case 'prefix': { + flavor.prefix = parts[1]; + onlatestfor = 'prefix'; + break; + } + case 'suffix': { + flavor.suffix = parts[1]; + onlatestfor = 'suffix'; + break; + } + case 'onlatest': { + if (!['true', 'false'].includes(parts[1])) { + throw new Error(`Invalid value for onlatest attribute: ${parts[1]}`); + } + switch (onlatestfor) { + case 'prefix': { + flavor.prefixLatest = /true/i.test(parts[1]); + break; + } + case 'suffix': { + flavor.suffixLatest = /true/i.test(parts[1]); + break; + } + } + break; + } + default: { + throw new Error(`Unknown flavor entry: ${input}`); + } } } } @@ -43,7 +73,9 @@ export function Transform(inputs: string[]): Flavor { core.startGroup(`Processing flavor input`); core.info(`latest=${flavor.latest}`); core.info(`prefix=${flavor.prefix}`); + core.info(`prefixLatest=${flavor.prefixLatest}`); core.info(`suffix=${flavor.suffix}`); + core.info(`suffixLatest=${flavor.suffixLatest}`); core.endGroup(); return flavor; diff --git a/src/meta.ts b/src/meta.ts index ebcb7a6c..1c708931 100644 --- a/src/meta.ts +++ b/src/meta.ts @@ -159,7 +159,6 @@ export class Meta { vraw = this.context.ref.replace(/^refs\/tags\//g, '').replace(/\//g, '-'); } - let latest: boolean = false; let tmatch; const isRegEx = tag.attrs['pattern'].match(/^\/(.+)\/(.*)$/); if (isRegEx) { @@ -304,7 +303,7 @@ export class Meta { tags.push(`${imageLc}:${partial}`); } if (this.version.latest) { - tags.push(`${imageLc}:latest`); + tags.push(`${imageLc}:${this.flavor.prefixLatest ? this.flavor.prefix : ''}latest${this.flavor.suffixLatest ? this.flavor.suffix : ''}`); } } return tags; diff --git a/src/tag.ts b/src/tag.ts index b0c83df8..4ac9d15e 100644 --- a/src/tag.ts +++ b/src/tag.ts @@ -100,7 +100,7 @@ export function Parse(s: string): Tag { switch (key) { case 'type': { if (!Object.values(Type).includes(value)) { - throw new Error(`Unknown type attribute: ${value}`); + throw new Error(`Unknown tag type attribute: ${value}`); } tag.type = value; break;