SQSCANGHA-113 Migrate scanner run step

This commit is contained in:
Jeremy Davis 2025-09-10 11:28:29 +02:00 committed by Julien HENRY
parent ed9f3aad50
commit 16df975da5
9 changed files with 448 additions and 137 deletions

View file

@ -490,7 +490,7 @@ jobs:
uses: ./
with:
scannerVersion: 6.2.1.4610
scannerBinariesUrl: https://localhost:8080/clientRedirectToSonarBinaries
scannerBinariesUrl: http://localhost:8080/clientRedirectToSonarBinaries
env:
NO_CACHE: true
SONAR_HOST_URL: http://not_actually_used
@ -660,40 +660,6 @@ jobs:
- name: Assert failure of previous step
if: steps.wrong_ssl_certificate.outcome == 'success'
run: exit 1
overridesScannerLocalFolderWhenPresent: # can happen in uncleaned self-hosted runners
name: >
'SCANNER_LOCAL_FOLDER' is cleaned with warning when present
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Create a dummy SCANNER_LOCAL_FOLDER with dummy content in it
run: |
SCANNER_VERSION="6.2.1.4610"
SCANNER_LOCAL_FOLDER="$RUNNER_TEMP/sonar-scanner-cli-$SCANNER_VERSION-$RUNNER_OS-$RUNNER_ARCH"
# emit SCANNER_VERSION and SCANNER_LOCAL_FOLDER to be able to read them in the next steps
echo "SCANNER_VERSION=$SCANNER_VERSION" >> $GITHUB_ENV
echo "SCANNER_LOCAL_FOLDER=$SCANNER_LOCAL_FOLDER" >> $GITHUB_ENV
mkdir -p "$SCANNER_LOCAL_FOLDER"
touch "$SCANNER_LOCAL_FOLDER/some_content.txt"
- name: Assert SCANNER_LOCAL_FOLDER exists and dummy file is in it
run: |
[ -d "$SCANNER_LOCAL_FOLDER" ] || exit 1
[ -f "$SCANNER_LOCAL_FOLDER/some_content.txt" ] || exit 1
- name: Run action with SONAR_SCANNER_TEMP
uses: ./
env:
NO_CACHE: true # force install-sonar-scanner-cli.sh execution
SONAR_SCANNER_TEMP: /tmp/sonar-scanner
SONAR_HOST_URL: http://not_actually_used
with:
args: -Dsonar.scanner.internal.dumpToFile=./output.properties
scannerVersion: ${{ env.SCANNER_VERSION }}
- name: Assert SCANNER_LOCAL_FOLDER exists and dummy file is not in it
run: |
[ -d "$SCANNER_LOCAL_FOLDER" ] || exit 1
[ ! -f "$SCANNER_LOCAL_FOLDER/some_content.txt" ] || exit 1
updateTruststoreWhenPresent: # can happen in uncleaned self-hosted runners
name: >
truststore.p12 is updated when present

View file

@ -10,9 +10,11 @@ inputs:
args:
description: Additional arguments to the Sonar Scanner CLI
required: false
default: ""
projectBaseDir:
description: Set the sonar.projectBaseDir analysis property
required: false
default: "."
scannerVersion:
description: Version of the Sonar Scanner CLI to use
required: false
@ -23,25 +25,5 @@ inputs:
required: false
default: https://binaries.sonarsource.com/Distribution/sonar-scanner-cli
runs:
using: "composite"
steps:
# - name: Checkout
# uses: actions/checkout@v5
# with:
# ref: "jay/js-rewrite"
- name: Sanity checks & setup
run: node ./dist/index.js
shell: bash
env:
INPUT_PROJECTBASEDIR: ${{ inputs.projectBaseDir }}
INPUT_SCANNERVERSION: ${{ inputs.scannerVersion }}
INPUT_SCANNERBINARIESURL: ${{ inputs.scannerBinariesUrl }}
- name: Run SonarScanner
run: ${GITHUB_ACTION_PATH}/scripts/run-sonar-scanner.sh
shell: bash
env:
INPUT_ARGS: ${{ inputs.args }}
INPUT_PROJECTBASEDIR: ${{ inputs.projectBaseDir }}
SONAR_SCANNER_JRE: ${{ runner.temp }}/sonar-scanner-cli-${{ inputs.scannerVersion }}-${{ runner.os }}-${{ runner.arch }}/jre
using: node20
main: dist/index.js

315
dist/index.js vendored
View file

@ -1,13 +1,14 @@
import * as require$$0 from 'os';
import require$$0__default from 'os';
import require$$0$1 from 'crypto';
import fs from 'fs';
import * as require$$1 from 'path';
import require$$1__default, { join } from 'path';
import * as fs from 'fs';
import fs__default from 'fs';
import * as path from 'path';
import path__default, { join } from 'path';
import require$$2 from 'http';
import require$$3 from 'https';
import require$$0$4 from 'net';
import require$$1$1 from 'tls';
import require$$1 from 'tls';
import require$$4 from 'events';
import require$$0$3 from 'assert';
import require$$0$2 from 'util';
@ -16,14 +17,14 @@ import require$$7 from 'buffer';
import require$$8 from 'querystring';
import require$$14 from 'stream/web';
import require$$0$7 from 'node:stream';
import require$$1$2 from 'node:util';
import require$$1$1 from 'node:util';
import require$$0$6 from 'node:events';
import require$$0$8 from 'worker_threads';
import require$$2$1 from 'perf_hooks';
import require$$5 from 'util/types';
import require$$4$1 from 'async_hooks';
import require$$1$3 from 'console';
import require$$1$4 from 'url';
import require$$1$2 from 'console';
import require$$1$3 from 'url';
import require$$3$1 from 'zlib';
import require$$6 from 'string_decoder';
import require$$0$9 from 'diagnostics_channel';
@ -243,7 +244,7 @@ function requireFileCommand () {
// We use any as a valid input type
/* eslint-disable @typescript-eslint/no-explicit-any */
const crypto = __importStar(require$$0$1);
const fs$1 = __importStar(fs);
const fs = __importStar(fs__default);
const os = __importStar(require$$0__default);
const utils_1 = requireUtils$1();
function issueFileCommand(command, message) {
@ -251,10 +252,10 @@ function requireFileCommand () {
if (!filePath) {
throw new Error(`Unable to find environment variable for file command ${command}`);
}
if (!fs$1.existsSync(filePath)) {
if (!fs.existsSync(filePath)) {
throw new Error(`Missing file at path: ${filePath}`);
}
fs$1.appendFileSync(filePath, `${(0, utils_1.toCommandValue)(message)}${os.EOL}`, {
fs.appendFileSync(filePath, `${(0, utils_1.toCommandValue)(message)}${os.EOL}`, {
encoding: 'utf8'
});
}
@ -393,7 +394,7 @@ var hasRequiredTunnel$1;
function requireTunnel$1 () {
if (hasRequiredTunnel$1) return tunnel$1;
hasRequiredTunnel$1 = 1;
var tls = require$$1$1;
var tls = require$$1;
var http = require$$2;
var https = require$$3;
var events = require$$4;
@ -1775,7 +1776,7 @@ function requireSbmh () {
* by Hongli Lai at: https://github.com/FooBarWidget/boyer-moore-horspool
*/
const EventEmitter = require$$0$6.EventEmitter;
const inherits = require$$1$2.inherits;
const inherits = require$$1$1.inherits;
function SBMH (needle) {
if (typeof needle === 'string') {
@ -1984,7 +1985,7 @@ function requirePartStream () {
if (hasRequiredPartStream) return PartStream_1;
hasRequiredPartStream = 1;
const inherits = require$$1$2.inherits;
const inherits = require$$1$1.inherits;
const ReadableStream = require$$0$7.Readable;
function PartStream (opts) {
@ -2030,7 +2031,7 @@ function requireHeaderParser () {
hasRequiredHeaderParser = 1;
const EventEmitter = require$$0$6.EventEmitter;
const inherits = require$$1$2.inherits;
const inherits = require$$1$1.inherits;
const getLimit = requireGetLimit();
const StreamSearch = requireSbmh();
@ -2138,7 +2139,7 @@ function requireDicer () {
hasRequiredDicer = 1;
const WritableStream = require$$0$7.Writable;
const inherits = require$$1$2.inherits;
const inherits = require$$1$1.inherits;
const StreamSearch = requireSbmh();
@ -2715,7 +2716,7 @@ function requireMultipart () {
// -- this will require modifications to utils.parseParams
const { Readable } = require$$0$7;
const { inherits } = require$$1$2;
const { inherits } = require$$1$1;
const Dicer = requireDicer();
@ -3281,7 +3282,7 @@ function requireMain () {
hasRequiredMain = 1;
const WritableStream = require$$0$7.Writable;
const { inherits } = require$$1$2;
const { inherits } = require$$1$1;
const Dicer = requireDicer();
const MultipartParser = requireMultipart();
@ -8105,7 +8106,7 @@ function requireConnect () {
let socket;
if (protocol === 'https:') {
if (!tls) {
tls = require$$1$1;
tls = require$$1;
}
servername = servername || options.servername || util.getServerName(host) || null;
@ -14129,7 +14130,7 @@ function requirePendingInterceptorsFormatter () {
hasRequiredPendingInterceptorsFormatter = 1;
const { Transform } = require$$0$5;
const { Console } = require$$1$3;
const { Console } = require$$1$2;
/**
* Gets the output of `console.table(…)` as a string.
@ -14356,7 +14357,7 @@ function requireProxyAgent () {
hasRequiredProxyAgent = 1;
const { kProxy, kClose, kDestroy, kInterceptors } = requireSymbols$4();
const { URL } = require$$1$4;
const { URL } = require$$1$3;
const Agent = requireAgent();
const Pool = requirePool();
const DispatcherBase = requireDispatcherBase();
@ -25221,7 +25222,7 @@ function requireSummary () {
Object.defineProperty(exports, "__esModule", { value: true });
exports.summary = exports.markdownSummary = exports.SUMMARY_DOCS_URL = exports.SUMMARY_ENV_VAR = void 0;
const os_1 = require$$0__default;
const fs_1 = fs;
const fs_1 = fs__default;
const { access, appendFile, writeFile } = fs_1.promises;
exports.SUMMARY_ENV_VAR = 'GITHUB_STEP_SUMMARY';
exports.SUMMARY_DOCS_URL = 'https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary';
@ -25527,7 +25528,7 @@ function requirePathUtils () {
};
Object.defineProperty(pathUtils, "__esModule", { value: true });
pathUtils.toPlatformPath = pathUtils.toWin32Path = pathUtils.toPosixPath = void 0;
const path = __importStar(require$$1__default);
const path = __importStar(path__default);
/**
* toPosixPath converts the given path to the posix form. On Windows, \\ will be
* replaced with /.
@ -25613,16 +25614,16 @@ function requireIoUtil () {
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getCmdPath = exports.tryGetExecutablePath = exports.isRooted = exports.isDirectory = exports.exists = exports.READONLY = exports.UV_FS_O_EXLOCK = exports.IS_WINDOWS = exports.unlink = exports.symlink = exports.stat = exports.rmdir = exports.rm = exports.rename = exports.readlink = exports.readdir = exports.open = exports.mkdir = exports.lstat = exports.copyFile = exports.chmod = void 0;
const fs$1 = __importStar(fs);
const path = __importStar(require$$1__default);
_a = fs$1.promises
const fs = __importStar(fs__default);
const path = __importStar(path__default);
_a = fs.promises
// export const {open} = 'fs'
, exports.chmod = _a.chmod, exports.copyFile = _a.copyFile, exports.lstat = _a.lstat, exports.mkdir = _a.mkdir, exports.open = _a.open, exports.readdir = _a.readdir, exports.readlink = _a.readlink, exports.rename = _a.rename, exports.rm = _a.rm, exports.rmdir = _a.rmdir, exports.stat = _a.stat, exports.symlink = _a.symlink, exports.unlink = _a.unlink;
// export const {open} = 'fs'
exports.IS_WINDOWS = process.platform === 'win32';
// See https://github.com/nodejs/node/blob/d0153aee367422d0858105abec186da4dff0a0c5/deps/uv/include/uv/win.h#L691
exports.UV_FS_O_EXLOCK = 0x10000000;
exports.READONLY = fs$1.constants.O_RDONLY;
exports.READONLY = fs.constants.O_RDONLY;
function exists(fsPath) {
return __awaiter(this, void 0, void 0, function* () {
try {
@ -25804,7 +25805,7 @@ function requireIo () {
Object.defineProperty(io, "__esModule", { value: true });
io.findInPath = io.which = io.mkdirP = io.rmRF = io.mv = io.cp = void 0;
const assert_1 = require$$0$3;
const path = __importStar(require$$1__default);
const path = __importStar(path__default);
const ioUtil = __importStar(requireIoUtil());
/**
* Copies a file or folder.
@ -26112,7 +26113,7 @@ function requireToolrunner () {
const os = __importStar(require$$0__default);
const events = __importStar(require$$4);
const child = __importStar(require$$2$2);
const path = __importStar(require$$1__default);
const path = __importStar(path__default);
const io = __importStar(requireIo());
const ioUtil = __importStar(requireIoUtil());
const timers_1 = require$$6$1;
@ -26956,7 +26957,7 @@ function requireCore () {
const file_command_1 = requireFileCommand();
const utils_1 = requireUtils$1();
const os = __importStar(require$$0__default);
const path = __importStar(require$$1__default);
const path = __importStar(path__default);
const oidc_utils_1 = requireOidcUtils();
/**
* The code to exit an action
@ -28980,7 +28981,7 @@ function requireManifest () {
/* eslint @typescript-eslint/no-require-imports: 0 */
const os = require$$0__default;
const cp = require$$2$2;
const fs$1 = fs;
const fs = fs__default;
function _findMatch(versionSpec, stable, candidates, archFilter) {
return __awaiter(this, void 0, void 0, function* () {
const platFilter = os.platform();
@ -29058,11 +29059,11 @@ function requireManifest () {
const lsbReleaseFile = '/etc/lsb-release';
const osReleaseFile = '/etc/os-release';
let contents = '';
if (fs$1.existsSync(lsbReleaseFile)) {
contents = fs$1.readFileSync(lsbReleaseFile).toString();
if (fs.existsSync(lsbReleaseFile)) {
contents = fs.readFileSync(lsbReleaseFile).toString();
}
else if (fs$1.existsSync(osReleaseFile)) {
contents = fs$1.readFileSync(osReleaseFile).toString();
else if (fs.existsSync(osReleaseFile)) {
contents = fs.readFileSync(osReleaseFile).toString();
}
return contents;
}
@ -29210,10 +29211,10 @@ function requireToolCache () {
const core = __importStar(requireCore());
const io = __importStar(requireIo());
const crypto = __importStar(require$$0$1);
const fs$1 = __importStar(fs);
const fs = __importStar(fs__default);
const mm = __importStar(requireManifest());
const os = __importStar(require$$0__default);
const path = __importStar(require$$1__default);
const path = __importStar(path__default);
const httpm = __importStar(requireLib());
const semver = __importStar(requireSemver());
const stream = __importStar(require$$0$5);
@ -29270,7 +29271,7 @@ function requireToolCache () {
toolCache.downloadTool = downloadTool;
function downloadToolAttempt(url, dest, auth, headers) {
return __awaiter(this, void 0, void 0, function* () {
if (fs$1.existsSync(dest)) {
if (fs.existsSync(dest)) {
throw new Error(`Destination file path ${dest} already exists`);
}
// Get the response headers
@ -29296,7 +29297,7 @@ function requireToolCache () {
const readStream = responseMessageFactory();
let succeeded = false;
try {
yield pipeline(readStream, fs$1.createWriteStream(dest));
yield pipeline(readStream, fs.createWriteStream(dest));
core.debug('download complete');
succeeded = true;
return dest;
@ -29577,14 +29578,14 @@ function requireToolCache () {
arch = arch || os.arch();
core.debug(`Caching tool ${tool} ${version} ${arch}`);
core.debug(`source dir: ${sourceDir}`);
if (!fs$1.statSync(sourceDir).isDirectory()) {
if (!fs.statSync(sourceDir).isDirectory()) {
throw new Error('sourceDir is not a directory');
}
// Create the tool dir
const destPath = yield _createToolPath(tool, version, arch);
// copy each child item. do not move. move can fail on Windows
// due to anti-virus software having an open handle on a file.
for (const itemName of fs$1.readdirSync(sourceDir)) {
for (const itemName of fs.readdirSync(sourceDir)) {
const s = path.join(sourceDir, itemName);
yield io.cp(s, destPath, { recursive: true });
}
@ -29610,7 +29611,7 @@ function requireToolCache () {
arch = arch || os.arch();
core.debug(`Caching tool ${tool} ${version} ${arch}`);
core.debug(`source file: ${sourceFile}`);
if (!fs$1.statSync(sourceFile).isFile()) {
if (!fs.statSync(sourceFile).isFile()) {
throw new Error('sourceFile is not a file');
}
// create the tool dir
@ -29653,7 +29654,7 @@ function requireToolCache () {
versionSpec = semver.clean(versionSpec) || '';
const cachePath = path.join(_getCacheDirectory(), toolName, versionSpec, arch);
core.debug(`checking cache: ${cachePath}`);
if (fs$1.existsSync(cachePath) && fs$1.existsSync(`${cachePath}.complete`)) {
if (fs.existsSync(cachePath) && fs.existsSync(`${cachePath}.complete`)) {
core.debug(`Found tool in cache ${toolName} ${versionSpec} ${arch}`);
toolPath = cachePath;
}
@ -29674,12 +29675,12 @@ function requireToolCache () {
const versions = [];
arch = arch || os.arch();
const toolPath = path.join(_getCacheDirectory(), toolName);
if (fs$1.existsSync(toolPath)) {
const children = fs$1.readdirSync(toolPath);
if (fs.existsSync(toolPath)) {
const children = fs.readdirSync(toolPath);
for (const child of children) {
if (isExplicitVersion(child)) {
const fullPath = path.join(toolPath, child, arch || '');
if (fs$1.existsSync(fullPath) && fs$1.existsSync(`${fullPath}.complete`)) {
if (fs.existsSync(fullPath) && fs.existsSync(`${fullPath}.complete`)) {
versions.push(child);
}
}
@ -29757,7 +29758,7 @@ function requireToolCache () {
function _completeToolPath(tool, version, arch) {
const folderPath = path.join(_getCacheDirectory(), tool, semver.clean(version) || version, arch || '');
const markerPath = `${folderPath}.complete`;
fs$1.writeFileSync(markerPath, '');
fs.writeFileSync(markerPath, '');
core.debug('finished caching tool');
}
/**
@ -29843,6 +29844,178 @@ function requireToolCache () {
var toolCacheExports = requireToolCache();
var execExports = requireExec();
function parseArgsStringToArgv(value, env, file) {
// ([^\s'"]([^\s'"]*(['"])([^\3]*?)\3)+[^\s'"]*) Matches nested quotes until the first space outside of quotes
// [^\s'"]+ or Match if not a space ' or "
// (['"])([^\5]*?)\5 or Match "quoted text" without quotes
// `\3` and `\5` are a backreference to the quote style (' or ") captured
var myRegexp = /([^\s'"]([^\s'"]*(['"])([^\3]*?)\3)+[^\s'"]*)|[^\s'"]+|(['"])([^\5]*?)\5/gi;
var myString = value;
var myArray = [];
var match;
do {
// Each call to exec returns the next regex match as an array
match = myRegexp.exec(myString);
if (match !== null) {
// Index 1 in the array is the captured group if it exists
// Index 0 is the matched text, which we use if no captured group exists
myArray.push(firstString(match[1], match[6], match[0]));
}
} while (match !== null);
return myArray;
}
// Accepts any number of arguments, and returns the first one that is a string
// (even an empty string)
function firstString() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
for (var i = 0; i < args.length; i++) {
var arg = args[i];
if (typeof arg === "string") {
return arg;
}
}
}
async function runSonarScanner(
inputArgs,
projectBaseDir,
scannerDir,
runnerEnv = {}
) {
const {
RUNNER_DEBUG,
RUNNER_OS,
RUNNER_TEMP,
SONAR_ROOT_CERT,
SONARCLOUD_URL,
} = runnerEnv;
const scannerBin =
RUNNER_OS === "Windows" ? "sonar-scanner.bat" : "sonar-scanner";
const scannerArgs = [];
if (SONARCLOUD_URL) {
scannerArgs.push(`-Dsonar.scanner.sonarcloudUrl=${SONARCLOUD_URL}`);
}
if (RUNNER_DEBUG === "1") {
scannerArgs.push("--debug");
}
if (projectBaseDir) {
scannerArgs.push(`-Dsonar.projectBaseDir=${projectBaseDir}`);
}
// The SSL folder may exist on an uncleaned self-hosted runner
const sslFolder = path.join(require$$0.homedir(), ".sonar", "ssl");
/**
* Use keytool for now, as SonarQube 10.6 and below doesn't support openssl generated keystores
* keytool requires a password > 6 characters, so we won't use the default password 'sonar'
*/
const keytoolMainClass = "sun.security.tools.keytool.Main";
const truststoreFile = path.join(sslFolder, "truststore.p12");
const truststorePassword = "changeit";
if (fs.existsSync(truststoreFile)) {
let aliasSonarIsPresent = true;
try {
await execExports.exec(
`${scannerDir}/jre/bin/java`,
[
keytoolMainClass,
"-storetype",
"PKCS12",
"-keystore",
truststoreFile,
"-storepass",
truststorePassword,
"-noprompt",
"-trustcacerts",
"-list",
"-v",
"-alias",
"sonar",
],
{ silent: true }
);
} catch (_) {
aliasSonarIsPresent = false;
console.log(
`Existing Scanner truststore ${truststoreFile} does not contain 'sonar' alias`
);
}
if (aliasSonarIsPresent) {
console.log(
`Removing 'sonar' alias from already existing Scanner truststore: ${truststoreFile}`
);
await execExports.exec(`${scannerDir}/jre/bin/java`, [
keytoolMainClass,
"-storetype",
"PKCS12",
"-keystore",
truststoreFile,
"-storepass",
truststorePassword,
"-noprompt",
"-trustcacerts",
"-delete",
"-alias",
"sonar",
]);
}
}
if (SONAR_ROOT_CERT) {
console.log("Adding SSL certificate to the Scanner truststore");
const tempCertPath = path.join(RUNNER_TEMP, "tmpcert.pem");
try {
fs.unlinkSync(tempCertPath);
} catch (_) {
// File doesn't exist, ignore
}
fs.writeFileSync(tempCertPath, SONAR_ROOT_CERT);
fs.mkdirSync(sslFolder, { recursive: true });
await execExports.exec(`${scannerDir}/jre/bin/java`, [
keytoolMainClass,
"-storetype",
"PKCS12",
"-keystore",
truststoreFile,
"-storepass",
truststorePassword,
"-noprompt",
"-trustcacerts",
"-importcert",
"-alias",
"sonar",
"-file",
tempCertPath,
]);
scannerArgs.push(
`-Dsonar.scanner.truststorePassword=${truststorePassword}`
);
}
if (inputArgs) {
const args = parseArgsStringToArgv(inputArgs);
scannerArgs.push(...args);
}
await execExports.exec(scannerBin, scannerArgs);
}
function validateScannerVersion(version) {
if (!version) {
return;
@ -29866,7 +30039,7 @@ function checkSonarToken(core) {
function checkMavenProject(core, projectBaseDir) {
const pomPath = join(projectBaseDir.replace(/\/$/, ""), "pom.xml");
if (fs.existsSync(pomPath)) {
if (fs__default.existsSync(pomPath)) {
core.warning(
"Maven project detected. Sonar recommends running the 'org.sonarsource.scanner.maven:sonar-maven-plugin:sonar' goal during the build process instead of using this GitHub Action to get more accurate results."
);
@ -29878,7 +30051,7 @@ function checkGradleProject(core, projectBaseDir) {
const gradlePath = join(baseDir, "build.gradle");
const gradleKtsPath = join(baseDir, "build.gradle.kts");
if (fs.existsSync(gradlePath) || fs.existsSync(gradleKtsPath)) {
if (fs__default.existsSync(gradlePath) || fs__default.existsSync(gradleKtsPath)) {
core.warning(
"Gradle project detected. Sonar recommends using the SonarQube plugin for Gradle during the build process instead of using this GitHub Action to get more accurate results."
);
@ -29923,18 +30096,31 @@ const scannerDirName = (version, flavor) =>
const TOOLNAME = "sonar-scanner-cli";
/**
* Inputs are defined in action.yml
*/
function getInputs() {
//FIXME: should not rely on ENV vars
const scannerVersion = process.env.INPUT_SCANNERVERSION; // core.getInput("scannerVersion");
const projectBaseDir = process.env.INPUT_PROJECTBASEDIR; // core.getInput("projectBaseDir") || ".";
const scannerBinariesUrl = process.env.INPUT_SCANNERBINARIESURL; // core.getInput("scannerBinariesUrl");
const args = coreExports.getInput("args");
const projectBaseDir = coreExports.getInput("projectBaseDir");
const scannerBinariesUrl = coreExports.getInput("scannerBinariesUrl");
const scannerVersion = coreExports.getInput("scannerVersion");
return { scannerVersion, projectBaseDir, scannerBinariesUrl };
return { args, projectBaseDir, scannerBinariesUrl, scannerVersion };
}
function getRunnerEnv() {
return {
RUNNER_OS: process.env.RUNNER_OS,
SONARCLOUD_URL: process.env.SONARCLOUD_URL,
RUNNER_DEBUG: process.env.RUNNER_DEBUG,
SONAR_ROOT_CERT: process.env.SONAR_ROOT_CERT,
RUNNER_TEMP: process.env.RUNNER_TEMP,
};
}
function runSanityChecks(inputs) {
try {
const { scannerVersion, projectBaseDir } = inputs;
const { projectBaseDir, scannerVersion } = inputs;
validateScannerVersion(scannerVersion);
checkSonarToken(core$1);
@ -29946,7 +30132,7 @@ function runSanityChecks(inputs) {
}
}
async function installSonarScannerCLI(scannerVersion, scannerBinariesUrl) {
async function installSonarScannerCLI({ scannerVersion, scannerBinariesUrl }) {
const flavor = getPlatformFlavor(require$$0.platform(), require$$0.arch());
// Check if tool is already cached
@ -29969,7 +30155,7 @@ async function installSonarScannerCLI(scannerVersion, scannerBinariesUrl) {
const extractedPath = await toolCacheExports.extractZip(downloadPath);
// Find the actual scanner directory inside the extracted folder
const scannerPath = require$$1.join(
const scannerPath = path.join(
extractedPath,
scannerDirName(scannerVersion, flavor)
);
@ -29982,7 +30168,7 @@ async function installSonarScannerCLI(scannerVersion, scannerBinariesUrl) {
}
// Add the bin directory to PATH
const binDir = require$$1.join(toolDir, "bin");
const binDir = path.join(toolDir, "bin");
coreExports.addPath(binDir);
return toolDir;
@ -29990,14 +30176,21 @@ async function installSonarScannerCLI(scannerVersion, scannerBinariesUrl) {
async function run() {
try {
const inputs = getInputs();
const { scannerVersion, scannerBinariesUrl } = inputs;
const { args, projectBaseDir, scannerVersion, scannerBinariesUrl } =
getInputs();
// Run sanity checks first
runSanityChecks(inputs);
runSanityChecks({ projectBaseDir, scannerVersion });
// Install Sonar Scanner CLI using @actions/tool-cache
await installSonarScannerCLI(scannerVersion, scannerBinariesUrl);
const scannerDir = await installSonarScannerCLI({
scannerVersion,
scannerBinariesUrl,
});
// Run the sonar scanner
const runnerEnv = getRunnerEnv();
await runSonarScanner(args, projectBaseDir, scannerDir, runnerEnv);
} catch (error) {
coreExports.setFailed(`Action failed: ${error.message}`);
process.exit(1);

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

18
package-lock.json generated
View file

@ -10,13 +10,14 @@
"license": "LGPL-3.0-only",
"dependencies": {
"@actions/core": "1.11.1",
"@actions/github": "6.0.1"
"@actions/github": "6.0.1",
"@actions/tool-cache": "2.0.2",
"string-argv": "0.3.2"
},
"devDependencies": {
"@actions/tool-cache": "^2.0.2",
"@rollup/plugin-commonjs": "28.0.6",
"@rollup/plugin-node-resolve": "16.0.1",
"mock-fs": "^5.5.0",
"mock-fs": "5.5.0",
"rollup": "4.50.1"
}
},
@ -74,7 +75,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-2.0.2.tgz",
"integrity": "sha512-fBhNNOWxuoLxztQebpOaWu6WeVmuwa77Z+DxIZ1B+OYvGkGQon6kTVg6Z32Cb13WCuw0szqonK+hh03mJV7Z6w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@actions/core": "^1.11.1",
@ -881,12 +881,20 @@
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/string-argv": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
"integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==",
"license": "MIT",
"engines": {
"node": ">=0.6.19"
}
},
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",

View file

@ -12,7 +12,8 @@
"dependencies": {
"@actions/core": "1.11.1",
"@actions/github": "6.0.1",
"@actions/tool-cache": "2.0.2"
"@actions/tool-cache": "2.0.2",
"string-argv": "0.3.2"
},
"devDependencies": {
"@rollup/plugin-commonjs": "28.0.6",

View file

@ -2,6 +2,7 @@ import * as core from "@actions/core";
import * as tc from "@actions/tool-cache";
import * as os from "os";
import * as path from "path";
import { runSonarScanner } from "./run-sonar-scanner";
import {
checkGradleProject,
checkMavenProject,
@ -16,18 +17,31 @@ import {
const TOOLNAME = "sonar-scanner-cli";
/**
* Inputs are defined in action.yml
*/
function getInputs() {
//FIXME: should not rely on ENV vars
const scannerVersion = process.env.INPUT_SCANNERVERSION; // core.getInput("scannerVersion");
const projectBaseDir = process.env.INPUT_PROJECTBASEDIR; // core.getInput("projectBaseDir") || ".";
const scannerBinariesUrl = process.env.INPUT_SCANNERBINARIESURL; // core.getInput("scannerBinariesUrl");
const args = core.getInput("args");
const projectBaseDir = core.getInput("projectBaseDir");
const scannerBinariesUrl = core.getInput("scannerBinariesUrl");
const scannerVersion = core.getInput("scannerVersion");
return { scannerVersion, projectBaseDir, scannerBinariesUrl };
return { args, projectBaseDir, scannerBinariesUrl, scannerVersion };
}
function getRunnerEnv() {
return {
RUNNER_OS: process.env.RUNNER_OS,
SONARCLOUD_URL: process.env.SONARCLOUD_URL,
RUNNER_DEBUG: process.env.RUNNER_DEBUG,
SONAR_ROOT_CERT: process.env.SONAR_ROOT_CERT,
RUNNER_TEMP: process.env.RUNNER_TEMP,
};
}
function runSanityChecks(inputs) {
try {
const { scannerVersion, projectBaseDir } = inputs;
const { projectBaseDir, scannerVersion } = inputs;
validateScannerVersion(scannerVersion);
checkSonarToken(core);
@ -39,7 +53,7 @@ function runSanityChecks(inputs) {
}
}
async function installSonarScannerCLI(scannerVersion, scannerBinariesUrl) {
async function installSonarScannerCLI({ scannerVersion, scannerBinariesUrl }) {
const flavor = getPlatformFlavor(os.platform(), os.arch());
// Check if tool is already cached
@ -83,14 +97,21 @@ async function installSonarScannerCLI(scannerVersion, scannerBinariesUrl) {
async function run() {
try {
const inputs = getInputs();
const { scannerVersion, scannerBinariesUrl } = inputs;
const { args, projectBaseDir, scannerVersion, scannerBinariesUrl } =
getInputs();
// Run sanity checks first
runSanityChecks(inputs);
runSanityChecks({ projectBaseDir, scannerVersion });
// Install Sonar Scanner CLI using @actions/tool-cache
await installSonarScannerCLI(scannerVersion, scannerBinariesUrl);
const scannerDir = await installSonarScannerCLI({
scannerVersion,
scannerBinariesUrl,
});
// Run the sonar scanner
const runnerEnv = getRunnerEnv();
await runSonarScanner(args, projectBaseDir, scannerDir, runnerEnv);
} catch (error) {
core.setFailed(`Action failed: ${error.message}`);
process.exit(1);

140
src/run-sonar-scanner.js Normal file
View file

@ -0,0 +1,140 @@
import * as exec from "@actions/exec";
import * as fs from "fs";
import * as os from "os";
import * as path from "path";
import { parseArgsStringToArgv } from "string-argv";
export async function runSonarScanner(
inputArgs,
projectBaseDir,
scannerDir,
runnerEnv = {}
) {
const {
RUNNER_DEBUG,
RUNNER_OS,
RUNNER_TEMP,
SONAR_ROOT_CERT,
SONARCLOUD_URL,
} = runnerEnv;
const scannerBin =
RUNNER_OS === "Windows" ? "sonar-scanner.bat" : "sonar-scanner";
const scannerArgs = [];
if (SONARCLOUD_URL) {
scannerArgs.push(`-Dsonar.scanner.sonarcloudUrl=${SONARCLOUD_URL}`);
}
if (RUNNER_DEBUG === "1") {
scannerArgs.push("--debug");
}
if (projectBaseDir) {
scannerArgs.push(`-Dsonar.projectBaseDir=${projectBaseDir}`);
}
// The SSL folder may exist on an uncleaned self-hosted runner
const sslFolder = path.join(os.homedir(), ".sonar", "ssl");
/**
* Use keytool for now, as SonarQube 10.6 and below doesn't support openssl generated keystores
* keytool requires a password > 6 characters, so we won't use the default password 'sonar'
*/
const keytoolMainClass = "sun.security.tools.keytool.Main";
const truststoreFile = path.join(sslFolder, "truststore.p12");
const truststorePassword = "changeit";
if (fs.existsSync(truststoreFile)) {
let aliasSonarIsPresent = true;
try {
await exec.exec(
`${scannerDir}/jre/bin/java`,
[
keytoolMainClass,
"-storetype",
"PKCS12",
"-keystore",
truststoreFile,
"-storepass",
truststorePassword,
"-noprompt",
"-trustcacerts",
"-list",
"-v",
"-alias",
"sonar",
],
{ silent: true }
);
} catch (_) {
aliasSonarIsPresent = false;
console.log(
`Existing Scanner truststore ${truststoreFile} does not contain 'sonar' alias`
);
}
if (aliasSonarIsPresent) {
console.log(
`Removing 'sonar' alias from already existing Scanner truststore: ${truststoreFile}`
);
await exec.exec(`${scannerDir}/jre/bin/java`, [
keytoolMainClass,
"-storetype",
"PKCS12",
"-keystore",
truststoreFile,
"-storepass",
truststorePassword,
"-noprompt",
"-trustcacerts",
"-delete",
"-alias",
"sonar",
]);
}
}
if (SONAR_ROOT_CERT) {
console.log("Adding SSL certificate to the Scanner truststore");
const tempCertPath = path.join(RUNNER_TEMP, "tmpcert.pem");
try {
fs.unlinkSync(tempCertPath);
} catch (_) {
// File doesn't exist, ignore
}
fs.writeFileSync(tempCertPath, SONAR_ROOT_CERT);
fs.mkdirSync(sslFolder, { recursive: true });
await exec.exec(`${scannerDir}/jre/bin/java`, [
keytoolMainClass,
"-storetype",
"PKCS12",
"-keystore",
truststoreFile,
"-storepass",
truststorePassword,
"-noprompt",
"-trustcacerts",
"-importcert",
"-alias",
"sonar",
"-file",
tempCertPath,
]);
scannerArgs.push(
`-Dsonar.scanner.truststorePassword=${truststorePassword}`
);
}
if (inputArgs) {
const args = parseArgsStringToArgv(inputArgs);
scannerArgs.push(...args);
}
await exec.exec(scannerBin, scannerArgs);
}