diff --git a/.github/tools/tag-selector/action.yml b/.github/tools/tag-selector/action.yml index f9adbd453..00330d67a 100644 --- a/.github/tools/tag-selector/action.yml +++ b/.github/tools/tag-selector/action.yml @@ -16,10 +16,14 @@ inputs: description: "Number of latest stable patches to return" required: false default: "2" + rc_count: + description: "Number of latest RC versions to return" + required: false + default: "2" rc_identifier: description: "RC prerelease identifier" required: false default: "rc" outputs: matrix_json: - description: "Output matrix" \ No newline at end of file + description: "Output matrix" diff --git a/.github/tools/tag-selector/dist/index.js b/.github/tools/tag-selector/dist/index.js index beacceaec..7bf2e591a 100644 --- a/.github/tools/tag-selector/dist/index.js +++ b/.github/tools/tag-selector/dist/index.js @@ -25431,12 +25431,10 @@ function stripPrefix(tag, prefix) { return tag; return tag.startsWith(prefix) ? tag.slice(prefix.length) : tag; } -function versionKey(major, minor) { - return `${major}.${minor}`; -} function computeFromTags(input) { const tagPrefix = input.tagPrefix ?? ""; const stableCount = input.stableCount ?? 2; + const rcCount = input.rcCount ?? 1; const rcIdent = input.rcIdent ?? "rc"; const versions = []; for (const t of input.tags) { @@ -25461,47 +25459,13 @@ function computeFromTags(input) { stableMinor = `${sMajor}.${sMinor}`; stablePatches = stableSorted.filter((v) => import_semver.default.major(v) === sMajor && import_semver.default.minor(v) === sMinor).slice(0, stableCount); } - const minorMap = /* @__PURE__ */ new Map(); - for (const v of stable) { - const key = versionKey(import_semver.default.major(v), import_semver.default.minor(v)); - const entry = minorMap.get(key) || { - hasStable: false, - rcVersions: [], - major: import_semver.default.major(v), - minor: import_semver.default.minor(v) - }; - entry.hasStable = true; - minorMap.set(key, entry); - } - for (const v of prerelease) { + const rcVersions = prerelease.filter((v) => { const pr = import_semver.default.prerelease(v) || []; - if (pr[0] === rcIdent) { - const key = versionKey(import_semver.default.major(v), import_semver.default.minor(v)); - const entry = minorMap.get(key) || { - hasStable: false, - rcVersions: [], - major: import_semver.default.major(v), - minor: import_semver.default.minor(v) - }; - entry.rcVersions.push(v); - minorMap.set(key, entry); - } - } - let rcMinor = ""; - let latestRc = ""; - const candidates = Array.from(minorMap.values()).filter( - (m) => !m.hasStable && m.rcVersions.length > 0 - ); - if (candidates.length > 0) { - candidates.sort( - (a, b) => a.major === b.major ? b.minor - a.minor : b.major - a.major - ); - const newest = candidates[0]; - rcMinor = `${newest.major}.${newest.minor}`; - latestRc = newest.rcVersions.sort(import_semver.default.rcompare)[0]; - } + return pr[0] === rcIdent; + }); + const latestRcs = rcCount > 0 ? [...rcVersions].sort(import_semver.default.rcompare).slice(0, rcCount) : []; const matrix = [ - ...latestRc ? [{ version: latestRc, channel: "rc" }] : [], + ...latestRcs.map((v) => ({ version: v, channel: "rc" })), ...stablePatches.map((v) => ({ version: v, channel: "stable" })) ]; return { @@ -25515,6 +25479,7 @@ async function run() { const token = getInput("github_token", { required: true }); const tagPrefix = getInput("tag_prefix") || ""; const stableCount = parseInt(getInput("stable_count") || "1", 10); + const rcCount = parseInt(getInput("rc_count") || "1", 10); const rcIdent = getInput("rc_identifier") || "rc"; const owner = getInput("owner") || "dapr"; const repo = getInput("repo") || "dapr"; @@ -25529,6 +25494,7 @@ async function run() { tags: tagNames, tagPrefix, stableCount, + rcCount, rcIdent }); setOutput("matrix_json", JSON.stringify(result.matrix_json)); diff --git a/.github/tools/tag-selector/src/index.ts b/.github/tools/tag-selector/src/index.ts index e283dc8d8..f9281803c 100644 --- a/.github/tools/tag-selector/src/index.ts +++ b/.github/tools/tag-selector/src/index.ts @@ -7,25 +7,27 @@ async function run() { const token = core.getInput("github_token", { required: true}); const tagPrefix = core.getInput("tag_prefix") || ""; const stableCount = parseInt(core.getInput("stable_count") || "1", 10); + const rcCount = parseInt(core.getInput("rc_count") || "1", 10); const rcIdent = core.getInput("rc_identifier") || "rc"; - + const owner = core.getInput("owner") || "dapr"; const repo = core.getInput("repo") || "dapr"; - + const octokit = github.getOctokit(token); - + // Paginate all tags const tags: Tag[] = await octokit.paginate(octokit.rest.repos.listTags, { owner, repo, per_page: 100 }); - + const tagNames = tags.map((t: { name: string }) => t.name); const result = computeFromTags({ tags: tagNames, tagPrefix, stableCount, + rcCount, rcIdent, }); @@ -35,4 +37,4 @@ async function run() { } } -run(); \ No newline at end of file +run(); diff --git a/.github/tools/tag-selector/src/lib.ts b/.github/tools/tag-selector/src/lib.ts index 5b4b736c1..a732014a4 100644 --- a/.github/tools/tag-selector/src/lib.ts +++ b/.github/tools/tag-selector/src/lib.ts @@ -4,6 +4,7 @@ export type ComputeInput = { tags: string[]; // raw tag names e.g., ["v1.16.8", "1.17.0-rc.3"] tagPrefix?: string; // e.g., "v" stableCount?: number; // default: 2 + rcCount?: number; // default: 1 rcIdent?: string; // default: "rc" } @@ -14,18 +15,15 @@ export type ComputeOutput = { export function stripPrefix(tag: string, prefix?: string): string { if (!prefix) return tag; - - return tag.startsWith(prefix) ? tag.slice(prefix.length) : tag; -} -function versionKey(major: number, minor: number) { - return `${major}.${minor}`; + return tag.startsWith(prefix) ? tag.slice(prefix.length) : tag; } /** Compute outputs from a set of tag names */ export function computeFromTags(input: ComputeInput): ComputeOutput { const tagPrefix = input.tagPrefix ?? ""; const stableCount = input.stableCount ?? 2; + const rcCount = input.rcCount ?? 1; const rcIdent = input.rcIdent ?? "rc"; // Normalize tags -> semver.valid versions @@ -59,58 +57,16 @@ export function computeFromTags(input: ComputeInput): ComputeOutput { .slice(0, stableCount); } - // Build minor map for RC-only minors - const minorMap = new Map< - string, - { hasStable: boolean; rcVersions: string[]; major: number; minor: number } - >(); - - for (const v of stable) { - const key = versionKey(semver.major(v), semver.minor(v)); - const entry = - minorMap.get(key) || { - hasStable: false, - rcVersions: [], - major: semver.major(v), - minor: semver.minor(v), - }; - entry.hasStable = true; - minorMap.set(key, entry); - } - - for (const v of prerelease) { + // Pick latest RC versions across all minors + const rcVersions = prerelease.filter((v) => { const pr = semver.prerelease(v) || []; - if (pr[0] === rcIdent) { - const key = versionKey(semver.major(v), semver.minor(v)); - const entry = - minorMap.get(key) || { - hasStable: false, - rcVersions: [], - major: semver.major(v), - minor: semver.minor(v), - }; - entry.rcVersions.push(v); - minorMap.set(key, entry); - } - } - - // Find newest (major.minor) with only RCs - let rcMinor = ""; - let latestRc = ""; - const candidates = Array.from(minorMap.values()).filter( - (m) => !m.hasStable && m.rcVersions.length > 0 - ); - if (candidates.length > 0) { - candidates.sort((a, b) => - a.major === b.major ? b.minor - a.minor : b.major - a.major - ); - const newest = candidates[0]; - rcMinor = `${newest.major}.${newest.minor}`; - latestRc = newest.rcVersions.sort(semver.rcompare)[0]; - } + return pr[0] === rcIdent; + }); + const latestRcs = + rcCount > 0 ? [...rcVersions].sort(semver.rcompare).slice(0, rcCount) : []; const matrix = [ - ...(latestRc ? [{ version: latestRc, channel: "rc" as const }] : []), + ...latestRcs.map((v) => ({ version: v, channel: "rc" as const })), ...stablePatches.map((v) => ({ version: v, channel: "stable" as const })), ]; diff --git a/.github/tools/tag-selector/test/lib.test.ts b/.github/tools/tag-selector/test/lib.test.ts index 80e309b71..60d59c83a 100644 --- a/.github/tools/tag-selector/test/lib.test.ts +++ b/.github/tools/tag-selector/test/lib.test.ts @@ -38,6 +38,70 @@ describe("computeFromTags - core scenarios", () => { ]); }); + test("rc_count returns latest N RCs from newest RC minor", () => { + const tags = [ + "v1.16.7", + "v1.16.8", + "v1.17.0-rc.1", + "v1.17.0-rc.2", + "v1.17.0-rc.3", + ]; + const out = computeFromTags({ + tags, + tagPrefix: "v", + stableCount: 2, + rcCount: 2, + rcIdent: "rc", + }); + + expect(out.matrix_json).toEqual([ + { version: "1.17.0-rc.3", channel: "rc" }, + { version: "1.17.0-rc.2", channel: "rc" }, + { version: "1.16.8", channel: "stable" }, + { version: "1.16.7", channel: "stable" }, + ]); + }); + + test("rc_count returns available RCs when fewer exist", () => { + const tags = [ + "v1.16.8", + "v1.17.0-rc.1", + ]; + const out = computeFromTags({ + tags, + tagPrefix: "v", + stableCount: 1, + rcCount: 2, + rcIdent: "rc", + }); + + expect(out.matrix_json).toEqual([ + { version: "1.17.0-rc.1", channel: "rc" }, + { version: "1.16.8", channel: "stable" }, + ]); + }); + + test("rc_count returns latest RCs regardless of stable availability", () => { + const tags = [ + "1.18.0", + "1.18.1-rc.1", + "1.17.0-rc.2", + "1.17.0-rc.1", + ]; + const out = computeFromTags({ + tags, + stableCount: 1, + rcCount: 2, + rcIdent: "rc", + }); + + expect(out.matrix_json).toEqual([ + { version: "1.18.1-rc.1", channel: "rc" }, + { version: "1.17.0-rc.2", channel: "rc" }, + { version: "1.18.0", channel: "stable" }, + ]); + }); + test("no RC-only newest minor -> only stable outputs", () => { const tags = ["1.16.7", "1.16.8", "1.17.0"]; // 1.17 has a stable, so not RC-only const out = computeFromTags({ tags, stableCount: 2 }); @@ -58,4 +122,4 @@ describe("computeFromTags - core scenarios", () => { matrix_json: [], }); }); -}); \ No newline at end of file +}); diff --git a/.github/workflows/sdk_build.yml b/.github/workflows/sdk_build.yml index 39a9d7583..433a0735a 100644 --- a/.github/workflows/sdk_build.yml +++ b/.github/workflows/sdk_build.yml @@ -107,6 +107,7 @@ jobs: INPUT_TAG_PREFIX: "v" INPUT_STABLE_COUNT: "2" INPUT_RC_IDENTIFIER: "rc" + INPUT_RC_COUNT: "2" run: | node .github/tools/tag-selector/dist/index.js - name: Show outputs