diff --git a/setup-gcloud/test/setup-gcloud.test.js b/setup-gcloud/test/setup-gcloud.test.js index b64a3c521..f89b5ed8d 100644 --- a/setup-gcloud/test/setup-gcloud.test.js +++ b/setup-gcloud/test/setup-gcloud.test.js @@ -1,3 +1,5 @@ +const path = require('path'); + const mockFs = require('mock-fs'); // Mock out tools download @@ -10,7 +12,6 @@ jest.mock('@actions/core'); const core = require('@actions/core'); const exec = require('@actions/exec'); -const os = require('os'); const setupGcloud = require('../src/setup-gcloud'); const jsonKey = { @@ -72,10 +73,6 @@ describe('Setup Gcloud', () => { exec.exec.mockResolvedValueOnce(0); await setupGcloud(base64Key, 'latest', true); expect(core.exportVariable.mock.calls[0][0]).toEqual('GOOGLE_APPLICATION_CREDENTIALS'); - if (os.platform() === 'win32') { - expect(core.exportVariable.mock.calls[0][1]).toContain('\\workspace\\'); - } else { - expect(core.exportVariable.mock.calls[0][1]).toContain('/workspace/'); - } + expect(core.exportVariable.mock.calls[0][1]).toContain(`${path.sep}workspace${path.sep}`); }); }); diff --git a/sonar-scanner/action.yml b/sonar-scanner/action.yml index e7325200c..dc9e7568b 100644 --- a/sonar-scanner/action.yml +++ b/sonar-scanner/action.yml @@ -8,6 +8,8 @@ inputs: description: | The sonar scanner to use. Valid values are 'auto', 'maven', 'gradle', 'node' and 'dotnet'. The default value 'auto' will attempt to perform auto-discovery. Auto-discovery does not work for 'dotnet'. + For 'dotnet' it is possible to pin the scanner to a specific version, for example 'dotnet-5.6.0'. Only pin the + dotnet scanner version if you are using an old dotnet version that is no longer supported by the latest version. required: true default: auto main-branch: diff --git a/sonar-scanner/dist/index.js b/sonar-scanner/dist/index.js index 40f550e7a..a816689cb 100644 --- a/sonar-scanner/dist/index.js +++ b/sonar-scanner/dist/index.js @@ -60967,9 +60967,9 @@ const action = async () => { let waitForQualityGate = false; - if (sonarScanner === 'dotnet') { + if (sonarScanner.startsWith('dotnet')) { // MSBuild scanning - waitForQualityGate = await scanMsBuild(hostUrl, mainBranch, scanCommands.dotnet); + waitForQualityGate = await scanMsBuild(hostUrl, mainBranch, sonarScanner, scanCommands.dotnet); } else { // Perform the scanning for everything else. await core.group('Run Sonar analysis', async () => scan(hostUrl, mainBranch, sonarScanner, scanCommands)); @@ -61128,9 +61128,17 @@ const scanWithJavaHome = async (args) => { return exec.exec(scanner, args, { env }); }; -const beginScan = async (hostUrl, mainBranch, customArgs = '') => { +const beginScan = async (hostUrl, mainBranch, sonarScanner, customArgs = '') => { await core.group('Install dotnet-sonarscanner', async () => { - await exec.exec(`dotnet tool install -g dotnet-sonarscanner ${hostUrl.startsWith('https://sonar.extenda.io') ? '--version 4.10.0' : ''}`); + const [, scannerVersion = ''] = sonarScanner.split('-'); + let versionArg = ''; + if (scannerVersion !== '') { + core.warning(`Using pinned dotnet-scanner version ${scannerVersion}`); + versionArg = `--version ${scannerVersion}`; + } else if (hostUrl.startsWith('https://sonar.extenda.io')) { + versionArg = '--version 4.10.0'; + } + await exec.exec(`dotnet tool install -g dotnet-sonarscanner ${versionArg}`); }); const version = await getBuildVersion(`-${process.env.GITHUB_SHA}`); @@ -61154,11 +61162,11 @@ const finishScan = async (hostUrl) => { }); }; -const scanMsBuild = async (hostUrl, mainBranch, customArgs = '') => { +const scanMsBuild = async (hostUrl, mainBranch, sonarScanner, customArgs = '') => { if (!fs.existsSync(markerFile)) { // Create marker and begin scan fs.closeSync(fs.openSync(markerFile, 'w')); - await beginScan(hostUrl, mainBranch, customArgs); + await beginScan(hostUrl, mainBranch, sonarScanner, customArgs); return false; } diff --git a/sonar-scanner/src/index.js b/sonar-scanner/src/index.js index b239bae0b..c025b86cf 100644 --- a/sonar-scanner/src/index.js +++ b/sonar-scanner/src/index.js @@ -51,9 +51,9 @@ const action = async () => { let waitForQualityGate = false; - if (sonarScanner === 'dotnet') { + if (sonarScanner.startsWith('dotnet')) { // MSBuild scanning - waitForQualityGate = await scanMsBuild(hostUrl, mainBranch, scanCommands.dotnet); + waitForQualityGate = await scanMsBuild(hostUrl, mainBranch, sonarScanner, scanCommands.dotnet); } else { // Perform the scanning for everything else. await core.group('Run Sonar analysis', async () => scan(hostUrl, mainBranch, sonarScanner, scanCommands)); diff --git a/sonar-scanner/src/scan-msbuild.js b/sonar-scanner/src/scan-msbuild.js index 298b5c9a9..9063d9721 100644 --- a/sonar-scanner/src/scan-msbuild.js +++ b/sonar-scanner/src/scan-msbuild.js @@ -40,9 +40,17 @@ const scanWithJavaHome = async (args) => { return exec.exec(scanner, args, { env }); }; -const beginScan = async (hostUrl, mainBranch, customArgs = '') => { +const beginScan = async (hostUrl, mainBranch, sonarScanner, customArgs = '') => { await core.group('Install dotnet-sonarscanner', async () => { - await exec.exec(`dotnet tool install -g dotnet-sonarscanner ${hostUrl.startsWith('https://sonar.extenda.io') ? '--version 4.10.0' : ''}`); + const [, scannerVersion = ''] = sonarScanner.split('-'); + let versionArg = ''; + if (scannerVersion !== '') { + core.warning(`Using pinned dotnet-scanner version ${scannerVersion}`); + versionArg = `--version ${scannerVersion}`; + } else if (hostUrl.startsWith('https://sonar.extenda.io')) { + versionArg = '--version 4.10.0'; + } + await exec.exec(`dotnet tool install -g dotnet-sonarscanner ${versionArg}`); }); const version = await getBuildVersion(`-${process.env.GITHUB_SHA}`); @@ -66,11 +74,11 @@ const finishScan = async (hostUrl) => { }); }; -const scanMsBuild = async (hostUrl, mainBranch, customArgs = '') => { +const scanMsBuild = async (hostUrl, mainBranch, sonarScanner, customArgs = '') => { if (!fs.existsSync(markerFile)) { // Create marker and begin scan fs.closeSync(fs.openSync(markerFile, 'w')); - await beginScan(hostUrl, mainBranch, customArgs); + await beginScan(hostUrl, mainBranch, sonarScanner, customArgs); return false; } diff --git a/sonar-scanner/test/index.test.js b/sonar-scanner/test/index.test.js index b8265cd62..c55d70226 100644 --- a/sonar-scanner/test/index.test.js +++ b/sonar-scanner/test/index.test.js @@ -112,7 +112,7 @@ describe('Sonar-Scanner Action', () => { checkQualityGate.mockResolvedValueOnce(0); scanMsBuild.mockResolvedValueOnce(true); await action(); - expect(scanMsBuild).toHaveBeenCalledWith('https://sonarcloud.io', 'master', ''); + expect(scanMsBuild).toHaveBeenCalledWith('https://sonarcloud.io', 'master', 'dotnet', ''); expect(checkQualityGate).toHaveBeenCalled(); }); diff --git a/sonar-scanner/test/scan-msbuild.test.js b/sonar-scanner/test/scan-msbuild.test.js index cbc980141..ac48459fa 100644 --- a/sonar-scanner/test/scan-msbuild.test.js +++ b/sonar-scanner/test/scan-msbuild.test.js @@ -1,12 +1,14 @@ +const mockFs = require('mock-fs'); const exec = require('@actions/exec'); const path = require('path'); const fs = require('fs'); const os = require('os'); +const { getBuildVersion } = require('../../utils/src/versions'); jest.mock('@actions/exec'); - jest.mock('../src/sonar-credentials'); jest.mock('../../utils/src/pull-request-info'); +jest.mock('../../utils/src/versions'); const { scanMsBuild, markerFile } = require('../src/scan-msbuild'); @@ -20,18 +22,18 @@ describe('Scan MSBuild', () => { process.env.GITHUB_REPOSITORY = 'extenda/actions'; process.env.SONAR_TOKEN = 'x'; process.env.GITHUB_EVENT_PATH = path.join(__dirname, 'push-event.json'); + getBuildVersion.mockReturnValueOnce('0.0.1-local'); }); afterEach(() => { - if (fs.existsSync(markerFile)) { - fs.unlinkSync(markerFile); - } + mockFs.restore(); process.env = orgEnv; jest.resetAllMocks(); }); test('It begins to scan when marker file is missing', async () => { - const output = await scanMsBuild('https://sonar.extenda.io', 'master'); + mockFs({}); + const output = await scanMsBuild('https://sonar.extenda.io', 'master', 'dotnet'); expect(output).toEqual(false); expect(fs.existsSync(markerFile)).toEqual(true); expect(exec.exec.mock.calls.length).toEqual(2); @@ -40,11 +42,19 @@ describe('Scan MSBuild', () => { expect(exec.exec.mock.calls[1][0]).toContain('begin'); }); + test('It can install a pinned dotnet-scanner version', async () => { + mockFs({}); + await scanMsBuild('https://sonarcloud.io', 'master', 'dotnet-5.6.0'); + expect(exec.exec.mock.calls[0][0]).toEqual('dotnet tool install -g dotnet-sonarscanner --version 5.6.0'); + }); + test('It ends scan when marker file exists', async () => { // Create marker file - fs.closeSync(fs.openSync(markerFile, 'w')); + const files = {}; + files[markerFile] = ''; + mockFs(files); - const output = await scanMsBuild('https://sonar.extenda.io', 'master'); + const output = await scanMsBuild('https://sonar.extenda.io', 'master', 'dotnet'); expect(output).toEqual(true); expect(exec.exec.mock.calls.length).toEqual(1); @@ -54,35 +64,44 @@ describe('Scan MSBuild', () => { test('It will skip JAVA_HOME on Windows', async () => { // Create marker file - fs.closeSync(fs.openSync(markerFile, 'w')); + const files = {}; + files[markerFile] = ''; + mockFs(files); - const platformSpy = jest.spyOn(os, 'platform').mockReturnValue('win32'); - await scanMsBuild('https://sonar.extenda.io', 'master'); + const platformSpy = jest.spyOn(os, 'platform').mockReturnValueOnce('win32'); + await scanMsBuild('https://sonar.extenda.io', 'master', 'dotnet'); expect(Object.keys(exec.exec.mock.calls[0][2].env)).not.toContain('JAVA_HOME'); platformSpy.mockRestore(); }); test('It can handle missing JDK', async () => { - // Create marker fil - fs.closeSync(fs.openSync(markerFile, 'w')); + // Create marker file + const files = {}; + files[markerFile] = ''; + mockFs(files); + process.env.JDK_BASEDIR = '/tmp/missing'; - const platformSpy = jest.spyOn(os, 'platform').mockReturnValue('linux'); - await scanMsBuild('https://sonar.extenda.io', 'master'); + const platformSpy = jest.spyOn(os, 'platform').mockReturnValueOnce('linux'); + await scanMsBuild('https://sonar.extenda.io', 'master', 'dotnet'); expect(Object.keys(exec.exec.mock.calls[0][2].env)).not.toContain('JAVA_HOME'); platformSpy.mockRestore(); }); - if (fs.existsSync('/usr/lib/jvm')) { - test('It can set JAVA_HOME', async () => { - fs.closeSync(fs.openSync(markerFile, 'w')); - const platformSpy = jest.spyOn(os, 'platform').mockReturnValue('linux'); - await scanMsBuild('https://sonar.extenda.io', 'master'); - expect(Object.keys(exec.exec.mock.calls[0][2].env)).toContain('JAVA_HOME'); - expect(exec.exec.mock.calls[0][2].env).toMatchObject({ - JAVA_HOME: '/usr/lib/jvm/adoptopenjdk-11-hotspot-amd64', - }); - platformSpy.mockRestore(); + test('It can set JAVA_HOME', async () => { + // Create marker file + const files = { + '/usr/lib/jvm/adoptopenjdk-11-hotspot-amd64/bin': '', + }; + files[markerFile] = ''; + mockFs(files); + + const platformSpy = jest.spyOn(os, 'platform').mockReturnValueOnce('linux'); + await scanMsBuild('https://sonar.extenda.io', 'master', 'dotnet'); + expect(Object.keys(exec.exec.mock.calls[0][2].env)).toContain('JAVA_HOME'); + expect(exec.exec.mock.calls[0][2].env).toMatchObject({ + JAVA_HOME: '/usr/lib/jvm/adoptopenjdk-11-hotspot-amd64', }); - } + platformSpy.mockRestore(); + }); });