diff --git a/__tests__/setup-go.test.ts b/__tests__/setup-go.test.ts index 8cefcce..27c6c7e 100644 --- a/__tests__/setup-go.test.ts +++ b/__tests__/setup-go.test.ts @@ -265,6 +265,33 @@ describe('setup-go', () => { expect(fileName).toBe('go1.14rc1.linux-amd64.tar.gz'); }); + it('finds unstable pre-release version using Go-style version format', async () => { + os.platform = 'linux'; + os.arch = 'x64'; + + // User specifies "1.14rc1" (Go-style) instead of "1.14.0-rc.1" (semver-style) + // This should match go1.14rc1 + const match: im.IGoVersion | undefined = await im.findMatch('1.14rc1'); + expect(match).toBeDefined(); + const version: string = match ? match.version : ''; + expect(version).toBe('go1.14rc1'); + const fileName = match ? match.files[0].filename : ''; + expect(fileName).toBe('go1.14rc1.linux-amd64.tar.gz'); + }); + + it('finds latest version matching caret range with Go-style prerelease', async () => { + os.platform = 'linux'; + os.arch = 'x64'; + + // spec: ^1.13beta1 should match go1.13.7 (latest 1.13.x) + const match: im.IGoVersion | undefined = await im.findMatch('^1.13beta1'); + expect(match).toBeDefined(); + const version: string = match ? match.version : ''; + expect(version).toBe('go1.13.7'); + const fileName = match ? match.files[0].filename : ''; + expect(fileName).toBe('go1.13.7.linux-amd64.tar.gz'); + }); + it('evaluates to stable with input as true', async () => { inputs['go-version'] = '1.13.0'; inputs.stable = 'true'; @@ -683,6 +710,41 @@ describe('setup-go', () => { expect(im.makeSemver('1.13.1')).toBe('1.13.1'); }); + describe('normalizeVersionSpec', () => { + it('converts Go-style prerelease to semver format', () => { + expect(im.normalizeVersionSpec('1.14rc1')).toBe('1.14.0-rc.1'); + expect(im.normalizeVersionSpec('1.14beta1')).toBe('1.14.0-beta.1'); + expect(im.normalizeVersionSpec('1.21rc2')).toBe('1.21.0-rc.2'); + }); + + it('preserves range prefixes when converting', () => { + expect(im.normalizeVersionSpec('^1.14rc1')).toBe('^1.14.0-rc.1'); + expect(im.normalizeVersionSpec('~1.14beta1')).toBe('~1.14.0-beta.1'); + expect(im.normalizeVersionSpec('>=1.14rc1')).toBe('>=1.14.0-rc.1'); + expect(im.normalizeVersionSpec('>1.14rc1')).toBe('>1.14.0-rc.1'); + expect(im.normalizeVersionSpec('<=1.14rc1')).toBe('<=1.14.0-rc.1'); + expect(im.normalizeVersionSpec('<1.14rc1')).toBe('<1.14.0-rc.1'); + expect(im.normalizeVersionSpec('=1.14rc1')).toBe('=1.14.0-rc.1'); + }); + + it('preserves versions without Go-style prerelease', () => { + expect(im.normalizeVersionSpec('1.13')).toBe('1.13'); + expect(im.normalizeVersionSpec('1.13.7')).toBe('1.13.7'); + expect(im.normalizeVersionSpec('^1.13.6')).toBe('^1.13.6'); + expect(im.normalizeVersionSpec('>=1.13')).toBe('>=1.13'); + }); + + it('preserves already valid semver prerelease format', () => { + expect(im.normalizeVersionSpec('1.14.0-rc.1')).toBe('1.14.0-rc.1'); + expect(im.normalizeVersionSpec('^1.14.0-beta.1')).toBe('^1.14.0-beta.1'); + }); + + it('does not match false positives like "traced"', () => { + // "traced" contains "rc" but should not be treated as prerelease + expect(im.normalizeVersionSpec('1.13traced')).toBe('1.13traced'); + }); + }); + describe('check-latest flag', () => { it("use local version and don't check manifest if check-latest is not specified", async () => { os.platform = 'linux'; diff --git a/dist/setup/index.js b/dist/setup/index.js index e35f338..0133c2f 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -94474,6 +94474,7 @@ exports.getManifest = getManifest; exports.getInfoFromManifest = getInfoFromManifest; exports.findMatch = findMatch; exports.getVersionsDist = getVersionsDist; +exports.normalizeVersionSpec = normalizeVersionSpec; exports.makeSemver = makeSemver; exports.parseGoVersionFile = parseGoVersionFile; exports.resolveStableVersionInput = resolveStableVersionInput; @@ -94758,12 +94759,13 @@ function findMatch(versionSpec_1) { if (!candidates) { throw new Error(`golang download url did not return results`); } + const normalizedVersionSpec = normalizeVersionSpec(versionSpec); let goFile; for (let i = 0; i < candidates.length; i++) { const candidate = candidates[i]; const version = makeSemver(candidate.version); - core.debug(`check ${version} satisfies ${versionSpec}`); - if (semver.satisfies(version, versionSpec)) { + core.debug(`check ${version} satisfies ${normalizedVersionSpec}`); + if (semver.satisfies(version, normalizedVersionSpec)) { goFile = candidate.files.find(file => { core.debug(`${file.arch}===${archFilter} && ${file.os}===${platFilter}`); return file.arch === archFilter && file.os === platFilter; @@ -94794,6 +94796,24 @@ function getVersionsDist(dlUrl) { }); } // +// Normalize user-provided version spec for semver matching +// Converts Go-style prerelease versions while preserving range semantics +// 1.13 => 1.13 (preserved for range matching) +// 1.14rc1 => 1.14.0-rc.1 +// ^1.14rc1 => ^1.14.0-rc.1 +// >=1.14beta1 => >=1.14.0-beta.1 +function normalizeVersionSpec(versionSpec) { + // Match semver range prefixes: ^, ~, >, >=, <, <=, = + const rangePrefixMatch = versionSpec.match(/^([~^]|[<>]=?|=)/); + const rangePrefix = (rangePrefixMatch === null || rangePrefixMatch === void 0 ? void 0 : rangePrefixMatch[0]) || ''; + const version = versionSpec.slice(rangePrefix.length); + // Only convert if it has Go-style prerelease (e.g., rc1, beta1) + if (!/(?:rc|beta)\d+/.test(version)) { + return versionSpec; + } + return rangePrefix + makeSemver(version); +} +// // Convert the go version syntax into semver for semver matching // 1.13.1 => 1.13.1 // 1.13 => 1.13.0 diff --git a/src/installer.ts b/src/installer.ts index 9402a83..2c57eb8 100644 --- a/src/installer.ts +++ b/src/installer.ts @@ -417,13 +417,15 @@ export async function findMatch( throw new Error(`golang download url did not return results`); } + const normalizedVersionSpec = normalizeVersionSpec(versionSpec); + let goFile: IGoVersionFile | undefined; for (let i = 0; i < candidates.length; i++) { const candidate: IGoVersion = candidates[i]; const version = makeSemver(candidate.version); - core.debug(`check ${version} satisfies ${versionSpec}`); - if (semver.satisfies(version, versionSpec)) { + core.debug(`check ${version} satisfies ${normalizedVersionSpec}`); + if (semver.satisfies(version, normalizedVersionSpec)) { goFile = candidate.files.find(file => { core.debug( `${file.arch}===${archFilter} && ${file.os}===${platFilter}` @@ -459,6 +461,27 @@ export async function getVersionsDist( return (await http.getJson(dlUrl)).result; } +// +// Normalize user-provided version spec for semver matching +// Converts Go-style prerelease versions while preserving range semantics +// 1.13 => 1.13 (preserved for range matching) +// 1.14rc1 => 1.14.0-rc.1 +// ^1.14rc1 => ^1.14.0-rc.1 +// >=1.14beta1 => >=1.14.0-beta.1 +export function normalizeVersionSpec(versionSpec: string): string { + // Match semver range prefixes: ^, ~, >, >=, <, <=, = + const rangePrefixMatch = versionSpec.match(/^([~^]|[<>]=?|=)/); + const rangePrefix = rangePrefixMatch?.[0] || ''; + const version = versionSpec.slice(rangePrefix.length); + + // Only convert if it has Go-style prerelease (e.g., rc1, beta1) + if (!/(?:rc|beta)\d+/.test(version)) { + return versionSpec; + } + + return rangePrefix + makeSemver(version); +} + // // Convert the go version syntax into semver for semver matching // 1.13.1 => 1.13.1