diff --git a/src/main/__tests__/install-sonar-scanner.test.js b/src/main/__tests__/install-sonar-scanner.test.js index 78e2582..43105ef 100644 --- a/src/main/__tests__/install-sonar-scanner.test.js +++ b/src/main/__tests__/install-sonar-scanner.test.js @@ -22,6 +22,7 @@ import assert from "node:assert/strict"; import { describe, it, mock } from "node:test"; const SCANNER_VERSION = "6.2.0.4584"; +const SCANNER_SEMVER_VERSION = "6.2.0-build.4584"; const BINARIES_URL = "https://my.artifactory.example.com/sonar-scanner-cli"; const BINARY_DOWNLOAD_URL = `${BINARIES_URL}/sonar-scanner-cli-${SCANNER_VERSION}-linux-x64.zip`; @@ -31,6 +32,7 @@ function mockUtils(t) { getPlatformFlavor: mock.fn(() => "linux-x64"), getScannerDownloadURL: mock.fn(() => BINARY_DOWNLOAD_URL), scannerDirName: mock.fn(() => `sonar-scanner-${SCANNER_VERSION}-linux-x64`), + toSemVer: mock.fn(() => SCANNER_SEMVER_VERSION), }, }); } @@ -171,6 +173,50 @@ describe("installSonarScanner", () => { assert.equal(downloadCalls[0].auth, "Bearer mytoken"); }); + it("should use semver-compatible version for tool-cache find and cacheDir", async (t) => { + const findFn = mock.fn(() => null); + const cacheDirFn = mock.fn(async () => "/tmp/cached"); + + mockUtils(t); + + t.mock.module("@actions/tool-cache", { + namedExports: { + find: findFn, + downloadTool: mock.fn(async () => "/tmp/downloaded"), + extractZip: mock.fn(async () => "/tmp/extracted"), + cacheDir: cacheDirFn, + }, + }); + + t.mock.module("@actions/core", { + namedExports: { + info: mock.fn(), + warning: mock.fn(), + addPath: mock.fn(), + }, + }); + + t.mock.module("../gpg-verification.js", { + namedExports: { + verifySignature: mock.fn(async () => {}), + }, + }); + + const { installSonarScanner } = await import( + `../install-sonar-scanner.js?test=semver-version` + ); + + await installSonarScanner({ + scannerVersion: SCANNER_VERSION, + scannerBinariesUrl: BINARIES_URL, + }); + + assert.equal(findFn.mock.calls[0].arguments[1], SCANNER_SEMVER_VERSION, + "tc.find should be called with semver-compatible version"); + assert.equal(cacheDirFn.mock.calls[0].arguments[2], SCANNER_SEMVER_VERSION, + "tc.cacheDir should be called with semver-compatible version"); + }); + it("should use cached tool when available and skip download", async (t) => { const downloadToolFn = mock.fn(); diff --git a/src/main/__tests__/utils.test.js b/src/main/__tests__/utils.test.js index cc88650..b379131 100644 --- a/src/main/__tests__/utils.test.js +++ b/src/main/__tests__/utils.test.js @@ -22,6 +22,7 @@ import { getPlatformFlavor, getScannerDownloadURL, scannerDirName, + toSemVer, } from "../utils.js"; describe("getPlatformFlavor", () => { @@ -97,3 +98,22 @@ describe("scannerDirName", () => { ); }); }); + +describe("toSemVer", () => { + it("converts 4-part version to semver pre-release format", () => { + assert.equal(toSemVer("8.0.1.6346"), "8.0.1-build.6346"); + }); + + it("leaves 3-part semver version unchanged", () => { + assert.equal(toSemVer("8.0.1"), "8.0.1"); + }); + + it("leaves version with pre-release identifier unchanged", () => { + assert.equal(toSemVer("7.2.0-SNAPSHOT"), "7.2.0-SNAPSHOT"); + }); + + it("converts different 4-part versions correctly", () => { + assert.equal(toSemVer("6.2.0.4584"), "6.2.0-build.4584"); + assert.equal(toSemVer("8.1.0.6389"), "8.1.0-build.6389"); + }); +}); diff --git a/src/main/install-sonar-scanner.js b/src/main/install-sonar-scanner.js index 1d74798..13dd179 100644 --- a/src/main/install-sonar-scanner.js +++ b/src/main/install-sonar-scanner.js @@ -24,6 +24,7 @@ import { getPlatformFlavor, getScannerDownloadURL, scannerDirName, + toSemVer, } from "./utils.js"; import { verifySignature } from "./gpg-verification.js"; @@ -39,9 +40,10 @@ export async function installSonarScanner({ skipSignatureVerification = false, }) { const flavor = getPlatformFlavor(os.platform(), os.arch()); + const semVerVersion = toSemVer(scannerVersion); // Check if tool is already cached - let toolDir = tc.find(TOOLNAME, scannerVersion, flavor); + let toolDir = tc.find(TOOLNAME, semVerVersion, flavor); if (!toolDir) { core.info( @@ -84,7 +86,7 @@ export async function installSonarScanner({ scannerDirName(scannerVersion, flavor) ); - toolDir = await tc.cacheDir(scannerPath, TOOLNAME, scannerVersion, flavor); + toolDir = await tc.cacheDir(scannerPath, TOOLNAME, semVerVersion, flavor); core.info(`Sonar Scanner CLI cached to: ${toolDir}`); } else { diff --git a/src/main/utils.js b/src/main/utils.js index 87ba8b9..a53e653 100644 --- a/src/main/utils.js +++ b/src/main/utils.js @@ -51,3 +51,16 @@ export function getScannerDownloadURL({ export const scannerDirName = (version, flavor) => `sonar-scanner-${version}-${flavor}`; + +/** + * Converts a 4-part version string (e.g. "8.0.1.6346") to a SemVer 2.0 compatible + * string (e.g. "8.0.1-build.6346") for use with GitHub's tool-cache library, + * which requires SemVer-compliant version strings. + */ +export function toSemVer(version) { + const parts = version.split("."); + if (parts.length === 4) { + return `${parts[0]}.${parts[1]}.${parts[2]}-build.${parts[3]}`; + } + return version; +}