@sonar/scan
Version:
SonarQube/SonarCloud Scanner for the JavaScript world
134 lines (133 loc) • 6.08 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.fetchScannerEngine = fetchScannerEngine;
exports.runScannerEngine = runScannerEngine;
/*
* sonar-scanner-npm
* Copyright (C) 2022-2025 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
const fs_extra_1 = __importDefault(require("fs-extra"));
const child_process_1 = require("child_process");
const constants_1 = require("./constants");
const file_1 = require("./file");
const logging_1 = require("./logging");
const proxy_1 = require("./proxy");
const request_1 = require("./request");
const types_1 = require("./types");
async function fetchScannerEngine(properties) {
(0, logging_1.log)(logging_1.LogLevel.DEBUG, `Detecting latest version of ${constants_1.SONAR_SCANNER_ALIAS}`);
const { data } = await (0, request_1.fetch)({ url: constants_1.API_V2_SCANNER_ENGINE_ENDPOINT });
const { sha256: checksum, filename, downloadUrl } = data;
(0, logging_1.log)(logging_1.LogLevel.DEBUG, `Latest ${constants_1.SONAR_SCANNER_ALIAS} version:`, filename);
(0, logging_1.log)(logging_1.LogLevel.DEBUG, `Looking for Cached ${constants_1.SONAR_SCANNER_ALIAS}`);
const cachedScannerEngine = await (0, file_1.getCacheFileLocation)(properties, {
checksum,
filename,
alias: constants_1.SONAR_SCANNER_ALIAS,
});
if (cachedScannerEngine) {
(0, logging_1.log)(logging_1.LogLevel.DEBUG, `Using ${constants_1.SONAR_SCANNER_ALIAS} from the cache`);
properties[types_1.ScannerProperty.SonarScannerWasEngineCacheHit] = 'true';
return cachedScannerEngine;
}
properties[types_1.ScannerProperty.SonarScannerWasEngineCacheHit] = 'false';
const { archivePath } = await (0, file_1.getCacheDirectories)(properties, {
checksum,
filename,
alias: constants_1.SONAR_SCANNER_ALIAS,
});
const url = downloadUrl ?? constants_1.API_V2_SCANNER_ENGINE_ENDPOINT;
(0, logging_1.log)(logging_1.LogLevel.DEBUG, `Starting download of ${constants_1.SONAR_SCANNER_ALIAS}`);
await (0, request_1.download)(url, archivePath);
(0, logging_1.log)(logging_1.LogLevel.INFO, `Downloaded ${constants_1.SONAR_SCANNER_ALIAS} to ${archivePath}`);
try {
await (0, file_1.validateChecksum)(archivePath, checksum);
}
catch (error) {
await fs_extra_1.default.remove(archivePath);
throw error;
}
return archivePath;
}
async function logOutput(message) {
try {
// Try and assume the log comes from the scanner engine
const parsed = JSON.parse(message);
(0, logging_1.logWithPrefix)(parsed.level, 'ScannerEngine', parsed.message);
if (parsed.stacktrace) {
// Console.log without newline
process.stdout.write(parsed.stacktrace);
}
}
catch (e) {
process.stdout.write(message);
}
}
function runScannerEngine(javaBinPath, scannerEnginePath, scanOptions, properties) {
(0, logging_1.log)(logging_1.LogLevel.DEBUG, `Running the ${constants_1.SONAR_SCANNER_ALIAS}`);
// The scanner engine expects a JSON object of properties attached to a key name "scannerProperties"
const propertiesJSON = JSON.stringify({
scannerProperties: Object.entries(properties).map(([key, value]) => ({
key,
value,
})),
});
// Run the scanner-engine
const args = [
...(0, proxy_1.proxyUrlToJavaOptions)(properties),
...(scanOptions.jvmOptions ?? []),
...(properties[types_1.ScannerProperty.SonarScannerJavaOptions]
? properties[types_1.ScannerProperty.SonarScannerJavaOptions].split(' ')
: []),
'-jar',
scannerEnginePath,
];
// If debugging with dumpToFile, write the properties to a file and exit
const dumpToFile = properties[types_1.ScannerProperty.SonarScannerInternalDumpToFile];
if (dumpToFile) {
const data = {
propertiesJSON,
javaBinPath,
scannerEnginePath,
args,
};
(0, logging_1.log)(logging_1.LogLevel.INFO, 'Dumping data to file and exiting');
return fs_extra_1.default.promises.writeFile(dumpToFile, JSON.stringify(data, null, 2));
}
(0, logging_1.log)(logging_1.LogLevel.DEBUG, `Running ${constants_1.SONAR_SCANNER_ALIAS}`, javaBinPath, ...args);
const child = (0, child_process_1.spawn)(javaBinPath, args);
(0, logging_1.log)(logging_1.LogLevel.DEBUG, `Writing properties to ${constants_1.SONAR_SCANNER_ALIAS}`, propertiesJSON);
child.stdin.write(propertiesJSON);
child.stdin.end();
child.stdout.on('data', buffer => buffer.toString().trim().split('\n').forEach(logOutput));
child.stderr.on('data', buffer => (0, logging_1.log)(logging_1.LogLevel.ERROR, buffer.toString()));
return new Promise((resolve, reject) => {
child.on('exit', code => {
if (code === 0) {
(0, logging_1.log)(logging_1.LogLevel.DEBUG, 'Scanner engine finished successfully');
resolve();
}
else {
reject(new Error(`Scanner engine failed with code ${code}`));
}
});
});
}