diff --git a/__tests__/main.test.js b/__tests__/main.test.js index b9cc28a..4a2eb02 100644 --- a/__tests__/main.test.js +++ b/__tests__/main.test.js @@ -291,7 +291,7 @@ test("Multiple prefix", async () => { repo: "repo", ref: "refs/tags/0.0.1", // test use of different whitespace separators and a combination of both - prefix: "prefix1:\tprefix2:\n, prefix3:", + prefix: "prefix1:\tprefix2:\n ,prefix3:", }); expect(tags).toEqual([ "prefix1:0.0.1", @@ -309,6 +309,97 @@ test("Multiple prefix", async () => { ]); }); +test("Single suffix", async () => { + tagInterceptor.reply(200, [ + { + name: "0.0.1", + }, + ]); + const tags = await calculateTags({ + token: "TOKEN", + owner: "owner", + repo: "repo", + ref: "refs/tags/0.0.1", + suffix: "-suffix", + }); + expect(tags).toEqual([ + "0.0.1-suffix", + "0.0-suffix", + "0-suffix", + "latest-suffix", + ]); +}); + +test("Multiple prefix and suffix", async () => { + tagInterceptor.reply(200, [ + { + name: "0.0.1", + }, + ]); + const tags = await calculateTags({ + token: "TOKEN", + owner: "owner", + repo: "repo", + ref: "refs/tags/0.0.1", + // test use of different whitespace separators and a combination of both + prefix: "prefix1:,prefix2:", + suffix: "-a -b", + }); + expect(tags).toEqual([ + "prefix1:0.0.1-a", + "prefix1:0.0.1-b", + "prefix2:0.0.1-a", + "prefix2:0.0.1-b", + "prefix1:0.0-a", + "prefix1:0.0-b", + "prefix2:0.0-a", + "prefix2:0.0-b", + "prefix1:0-a", + "prefix1:0-b", + "prefix2:0-a", + "prefix2:0-b", + "prefix1:latest-a", + "prefix1:latest-b", + "prefix2:latest-a", + "prefix2:latest-b", + ]); +}); + +test("Multiple prefix and suffix with empty string", async () => { + tagInterceptor.reply(200, [ + { + name: "0.0.1", + }, + ]); + const tags = await calculateTags({ + token: "TOKEN", + owner: "owner", + repo: "repo", + ref: "refs/tags/0.0.1", + // test use of + prefix: ",prefix2:", + suffix: ",-b", + }); + expect(tags).toEqual([ + "0.0.1", + "0.0.1-b", + "prefix2:0.0.1", + "prefix2:0.0.1-b", + "0.0", + "0.0-b", + "prefix2:0.0", + "prefix2:0.0-b", + "0", + "0-b", + "prefix2:0", + "prefix2:0-b", + "latest", + "latest-b", + "prefix2:latest", + "prefix2:latest-b", + ]); +}); + test("Branch", async () => { const tags = await calculateTags({ token: "TOKEN", diff --git a/action.yml b/action.yml index 5a82d4a..5b17df1 100644 --- a/action.yml +++ b/action.yml @@ -14,14 +14,23 @@ inputs: prefix: required: false description: |- - A string that each returned tag should be prefixed with, for example to tag a Docker container set this to `user/repository:`. + One or more whitespace or comma deliminated prefixes for returned tags. + + All permutations of prefixes and suffixes are considered. + default: "" + suffix: + required: false + description: |- + One or more whitespace or comma deliminated suffixes for returned tags. + + All permutations of prefixes and suffixes are considered. default: "" defaultTag: required: false description: |- If the tag output would be empty return this tag instead. This can be useful for running a workflow in pull requests where no suitable git references are present. - `prefix` is _not_ automatically added. + `prefix` or `suffix` are _not_ automatically added. default: "" branchRegex: required: false diff --git a/src/index.js b/src/index.js index 2e4f6d2..7d440c7 100644 --- a/src/index.js +++ b/src/index.js @@ -25,17 +25,25 @@ function checkAgainstRegex(name, regexAllowed) { return re.test(name); } -function expandPrefix(prefix, tag) { - // Adds one or more prefixes to a tag, where prefix could be a single prefix - // or a comma/whitespace separated list of prefixes. - if (!prefix) { - return [tag]; +function expandPrefixSuffix(prefix, suffix, tag) { + // Adds all permutations of prefixes and suffixes to a tag, where + // prefix/suffix could be a single prefix/suffix or a comma/whitespace + // separated list of prefixes/suffixes. If is observed, its + // replaced with an empty string. + let prefixes = [...new Set(prefix.split(/[\s,]/).filter(Boolean))]; + let suffixes = [...new Set(suffix.split(/[\s,]/).filter(Boolean))]; + prefixes = prefixes.map((p) => p.replaceAll(//g, "")); + suffixes = suffixes.map((p) => p.replaceAll(//g, "")); + + // the permutation logic requires at least one element in each list + if (prefixes.length == 0) { + prefixes.push(""); } - - let rv = []; - let prefixes = prefix.split(/\s|,/).filter(Boolean); - prefixes.forEach((p) => rv.push(`${p}${tag}`)); - return rv; + if (suffixes.length == 0) { + suffixes.push(""); + } + // permute + return prefixes.flatMap((p) => suffixes.map((s) => `${p}${tag}${s}`)); } async function calculateTags({ @@ -44,6 +52,7 @@ async function calculateTags({ repo, ref, prefix = "", + suffix = "", defaultTag = "", regexAllowed = "", }) { @@ -72,7 +81,7 @@ async function calculateTags({ } return []; } - return expandPrefix(prefix, branch); + return expandPrefixSuffix(prefix, suffix, branch); } if (!ref.startsWith("refs/tags/")) { throw new Error(`Not a tag or branch: ${ref}`); @@ -90,7 +99,7 @@ async function calculateTags({ }); if (!supportedPrerelease(current.prerelease)) { core.warning(`Tag prerelease ${currentTag} is not supported`); - return expandPrefix(prefix, currentTag); + return expandPrefixSuffix(prefix, suffix, currentTag); } const octokit = github.getOctokit(token); @@ -116,7 +125,7 @@ async function calculateTags({ let outputTags = []; if (current.prerelease.length) { - outputTags.push(...expandPrefix(prefix, current.version)); + outputTags.push(...expandPrefixSuffix(prefix, suffix, current.version)); // return without additional output tags if we got an outdated build number const similarTags = tags.filter( @@ -128,8 +137,9 @@ async function calculateTags({ } outputTags.push( - ...expandPrefix( + ...expandPrefixSuffix( prefix, + suffix, `${current.major}.${current.minor}.${current.patch}`, ), ); @@ -140,22 +150,34 @@ async function calculateTags({ semver.compare(current.toString().split("-")[0], tags[0]) >= 0 ) { outputTags.push( - ...expandPrefix(prefix, `${current.major}.${current.minor}`), + ...expandPrefixSuffix( + prefix, + suffix, + `${current.major}.${current.minor}`, + ), ); - outputTags.push(...expandPrefix(prefix, `${current.major}`)); - outputTags.push(...expandPrefix(prefix, "latest")); + outputTags.push(...expandPrefixSuffix(prefix, suffix, `${current.major}`)); + outputTags.push(...expandPrefixSuffix(prefix, suffix, "latest")); } else if ( semver.compare(current.toString().split("-")[0], majorTags[0]) >= 0 ) { outputTags.push( - ...expandPrefix(prefix, `${current.major}.${current.minor}`), + ...expandPrefixSuffix( + prefix, + suffix, + `${current.major}.${current.minor}`, + ), ); - outputTags.push(...expandPrefix(prefix, `${current.major}`)); + outputTags.push(...expandPrefixSuffix(prefix, suffix, `${current.major}`)); } else if ( semver.compare(current.toString().split("-")[0], minorTags[0]) >= 0 ) { outputTags.push( - ...expandPrefix(prefix, `${current.major}.${current.minor}`), + ...expandPrefixSuffix( + prefix, + suffix, + `${current.major}.${current.minor}`, + ), ); } core.debug(`outputTags: ${outputTags}`); @@ -168,6 +190,7 @@ async function run() { // githubToken: ${{ secrets.GITHUB_TOKEN }} const githubToken = core.getInput("githubToken"); const prefix = core.getInput("prefix"); + const suffix = core.getInput("suffix"); const defaultTag = core.getInput("defaultTag"); const branchRegex = core.getInput("branchRegex"); @@ -178,6 +201,7 @@ async function run() { repo: github.context.payload.repository.name, ref: github.context.payload.ref, prefix: prefix, + suffix: suffix, defaultTag: defaultTag, regexAllowed: branchRegex, });