feat: Enhance Sonar Scanner installation with authentication support

- Added a new input parameter `scannerBinariesAuth` to allow passing an authentication token for downloading the Sonar Scanner CLI.
- Implemented a `downloadWithFetch` function to handle downloads with authorization headers.
- Updated the `installSonarScanner` function to use the new download method when an auth token is provided.
- Included the `fs` module in the project dependencies to manage file system operations.
- Updated package.json and package-lock.json to include the new `fs` dependency.
- Modified the `getInputs` function to return the new `scannerBinariesAuth` input.
This commit is contained in:
“Krishna 2025-10-22 14:18:56 +05:30
parent 40f5b61913
commit fcd9e5e893
9 changed files with 155 additions and 26 deletions

View file

@ -200,6 +200,15 @@ This can be useful when the runner executing the action is self-hosted and has r
scannerBinariesUrl: https://my.custom.binaries.url.com/Distribution/sonar-scanner-cli/
```
If the specified URL requires authorization then use the `scannerBinariesAuth` input to provide the necessary token:
```yaml
- uses: SonarSource/sonarqube-scan-action@<action version>
with:
scannerBinariesUrl: https://my.custom.binaries.url.com/Distribution/sonar-scanner-cli/
scannerBinariesAuth: ${{ secrets.SCANNER_BINARIES_AUTH_TOKEN }}
```
More information about possible analysis parameters can be found:
* in the [Analysis parameters page](https://docs.sonarsource.com/sonarqube-server/latest/analyzing-source-code/analysis-parameters/) of the SonarQube Server documentation
* in the [Analysis parameters page](https://docs.sonarsource.com/sonarqube-cloud/advanced-setup/analysis-parameters/) of the SonarQube Cloud documentation

View file

@ -24,6 +24,10 @@ inputs:
description: URL to download the Sonar Scanner CLI binaries from
required: false
default: https://binaries.sonarsource.com/Distribution/sonar-scanner-cli
scannerBinariesAuth:
description: Authentication token to download the Sonar Scanner CLI binary from customBinariesUrl
required: false
default: ""
runs:
using: node20
main: dist/index.js

78
dist/index.js vendored
View file

@ -2640,12 +2640,53 @@ const scannerDirName = (version, flavor) =>
const TOOLNAME = "sonar-scanner-cli";
/**
* Download the sonar-scanner-cli from a internal url along with authorization token
*/
async function downloadWithFetch(url, outputPath, authToken) {
coreExports.info(`Downloading sonar-scanner-cli from: ${url}`);
//create output directory if it doesn't exist
const outputDir = path.dirname(outputPath);
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
//prepare headers
const headers = {};
if (authToken) {
headers["Authorization"] = `Bearer ${authToken}`;
coreExports.info("Using auth token for download");
}
try {
const response = await fetch(url, { headers });
if (!response.ok) {
throw new Error(
`Failed to download sonar-scanner-cli from ${url}: ${response.statusText}`
);
}
const arrayBuffer = await response.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
fs.writeFileSync(outputPath, buffer);
coreExports.info(`Successfully Downloaded sonar-scanner-cli to: ${outputPath}`);
return outputPath;
}
catch (error) {
coreExports.setFailed(error.message);
}
}
/**
* Download the Sonar Scanner CLI for the current environment and cache it.
*/
async function installSonarScanner({
scannerVersion,
scannerBinariesUrl,
scannerBinariesAuth,
}) {
const flavor = getPlatformFlavor(require$$0.platform(), require$$0.arch());
@ -2665,28 +2706,39 @@ async function installSonarScanner({
coreExports.info(`Downloading from: ${downloadUrl}`);
const downloadPath = await toolCacheExports.downloadTool(downloadUrl);
const extractedPath = await toolCacheExports.extractZip(downloadPath);
let downloadPath;
if (scannerBinariesAuth) {
// If auth token is provided
const tempDir = process.env.RUNNER_TEMP || require$$0.tmpdir();
const fileName = path.basename(downloadUrl);
const tempFile = path.join(tempDir, fileName);
downloadPath = await downloadWithFetch(downloadUrl, tempFile, scannerBinariesAuth);
} else {
// use tool-cache without auth token
coreExports.info("Using tool-cache to run sonar-scanner-cli");
downloadPath = await toolCacheExports.downloadTool(downloadUrl);
}
const extractPath = await toolCacheExports.extractZip(downloadPath);
// Find the actual scanner directory inside the extracted folder
const scannerPath = path.join(
extractedPath,
scannerDirName(scannerVersion, flavor)
);
const scannerPath = path.join(extractPath, scannerDirName(scannerVersion, flavor));
toolDir = await toolCacheExports.cacheDir(scannerPath, TOOLNAME, scannerVersion, flavor);
coreExports.info(`Sonar Scanner CLI cached to: ${toolDir}`);
} else {
coreExports.info(`Using cached Sonar Scanner CLI from: ${toolDir}`);
coreExports.info(`Using Sonar Scanner CLI from cache: ${toolDir}`);
}
// Add the bin directory to PATH
// Add the tool to the path
const binDir = path.join(toolDir, "bin");
coreExports.addPath(binDir);
return toolDir;
}
}
function parseArgsStringToArgv(value, env, file) {
// ([^\s'"]([^\s'"]*(['"])([^\3]*?)\3)+[^\s'"]*) Matches nested quotes until the first space outside of quotes
@ -2918,9 +2970,10 @@ function getInputs() {
const args = coreExports.getInput("args");
const projectBaseDir = coreExports.getInput("projectBaseDir");
const scannerBinariesUrl = coreExports.getInput("scannerBinariesUrl");
const scannerBinariesAuth = coreExports.getInput("scannerBinariesAuth");
const scannerVersion = coreExports.getInput("scannerVersion");
return { args, projectBaseDir, scannerBinariesUrl, scannerVersion };
return { args, projectBaseDir, scannerBinariesUrl, scannerBinariesAuth, scannerVersion };
}
/**
@ -2956,7 +3009,7 @@ function runSanityChecks(inputs) {
async function run() {
try {
const { args, projectBaseDir, scannerVersion, scannerBinariesUrl } =
const { args, projectBaseDir, scannerVersion, scannerBinariesUrl, scannerBinariesAuth } =
getInputs();
const runnerEnv = getEnvVariables();
const { sonarToken } = runnerEnv;
@ -2966,6 +3019,7 @@ async function run() {
const scannerDir = await installSonarScanner({
scannerVersion,
scannerBinariesUrl,
scannerBinariesAuth,
});
await runSonarScanner(args, projectBaseDir, scannerDir, runnerEnv);

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

6
package-lock.json generated
View file

@ -12,6 +12,7 @@
"@actions/core": "1.11.1",
"@actions/github": "6.0.1",
"@actions/tool-cache": "2.0.2",
"fs": "^0.0.1-security",
"string-argv": "0.3.2"
},
"devDependencies": {
@ -695,6 +696,11 @@
}
}
},
"node_modules/fs": {
"version": "0.0.1-security",
"resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz",
"integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w=="
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",

View file

@ -13,6 +13,7 @@
"@actions/core": "1.11.1",
"@actions/github": "6.0.1",
"@actions/tool-cache": "2.0.2",
"fs": "^0.0.1-security",
"string-argv": "0.3.2"
},
"devDependencies": {

View file

@ -15,9 +15,10 @@ function getInputs() {
const args = core.getInput("args");
const projectBaseDir = core.getInput("projectBaseDir");
const scannerBinariesUrl = core.getInput("scannerBinariesUrl");
const scannerBinariesAuth = core.getInput("scannerBinariesAuth");
const scannerVersion = core.getInput("scannerVersion");
return { args, projectBaseDir, scannerBinariesUrl, scannerVersion };
return { args, projectBaseDir, scannerBinariesUrl, scannerBinariesAuth, scannerVersion };
}
/**
@ -53,7 +54,7 @@ function runSanityChecks(inputs) {
async function run() {
try {
const { args, projectBaseDir, scannerVersion, scannerBinariesUrl } =
const { args, projectBaseDir, scannerVersion, scannerBinariesUrl, scannerBinariesAuth } =
getInputs();
const runnerEnv = getEnvVariables();
const { sonarToken } = runnerEnv;
@ -63,6 +64,7 @@ async function run() {
const scannerDir = await installSonarScanner({
scannerVersion,
scannerBinariesUrl,
scannerBinariesAuth,
});
await runSonarScanner(args, projectBaseDir, scannerDir, runnerEnv);

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 * as fs from "fs";
import {
getPlatformFlavor,
getScannerDownloadURL,
@ -10,12 +11,53 @@ import {
const TOOLNAME = "sonar-scanner-cli";
/**
* Download the sonar-scanner-cli from a internal url along with authorization token
*/
async function downloadWithFetch(url, outputPath, authToken) {
core.info(`Downloading sonar-scanner-cli from: ${url}`);
//create output directory if it doesn't exist
const outputDir = path.dirname(outputPath);
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
//prepare headers
const headers = {};
if (authToken) {
headers["Authorization"] = `Bearer ${authToken}`;
core.info("Using auth token for download");
}
try {
const response = await fetch(url, { headers });
if (!response.ok) {
throw new Error(
`Failed to download sonar-scanner-cli from ${url}: ${response.statusText}`
);
}
const arrayBuffer = await response.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
fs.writeFileSync(outputPath, buffer);
core.info(`Successfully Downloaded sonar-scanner-cli to: ${outputPath}`);
return outputPath;
}
catch (error) {
core.setFailed(error.message);
}
}
/**
* Download the Sonar Scanner CLI for the current environment and cache it.
*/
export async function installSonarScanner({
scannerVersion,
scannerBinariesUrl,
scannerBinariesAuth,
}) {
const flavor = getPlatformFlavor(os.platform(), os.arch());
@ -35,25 +77,36 @@ export async function installSonarScanner({
core.info(`Downloading from: ${downloadUrl}`);
const downloadPath = await tc.downloadTool(downloadUrl);
const extractedPath = await tc.extractZip(downloadPath);
let downloadPath;
if (scannerBinariesAuth) {
// If auth token is provided
const tempDir = process.env.RUNNER_TEMP || os.tmpdir();
const fileName = path.basename(downloadUrl);
const tempFile = path.join(tempDir, fileName);
downloadPath = await downloadWithFetch(downloadUrl, tempFile, scannerBinariesAuth);
} else {
// use tool-cache without auth token
core.info("Using tool-cache to run sonar-scanner-cli");
downloadPath = await tc.downloadTool(downloadUrl);
}
const extractPath = await tc.extractZip(downloadPath);
// Find the actual scanner directory inside the extracted folder
const scannerPath = path.join(
extractedPath,
scannerDirName(scannerVersion, flavor)
);
const scannerPath = path.join(extractPath, scannerDirName(scannerVersion, flavor));
toolDir = await tc.cacheDir(scannerPath, TOOLNAME, scannerVersion, flavor);
core.info(`Sonar Scanner CLI cached to: ${toolDir}`);
} else {
core.info(`Using cached Sonar Scanner CLI from: ${toolDir}`);
core.info(`Using Sonar Scanner CLI from cache: ${toolDir}`);
}
// Add the bin directory to PATH
// Add the tool to the path
const binDir = path.join(toolDir, "bin");
core.addPath(binDir);
return toolDir;
}
}