mirror of
https://github.com/SonarSource/sonarqube-scan-action.git
synced 2026-06-04 16:47:57 +03:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d92c61d794 | |||
| 375c3f5c03 | |||
| 9c783232fe |
@@ -88,3 +88,17 @@ jobs:
|
||||
BINARY: ${{ steps.run-action.outputs.build-wrapper-binary }}
|
||||
run: |
|
||||
("$BINARY" || true) | grep "build-wrapper, version "
|
||||
|
||||
qa-gate:
|
||||
name: QA Deprecated C and C++ - gate
|
||||
runs-on: ubuntu-latest
|
||||
needs: [output-test]
|
||||
if: always()
|
||||
steps:
|
||||
- name: Check all jobs passed
|
||||
run: |
|
||||
if [[ "${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}" == "true" ]]; then
|
||||
echo "One or more required jobs failed or were cancelled."
|
||||
exit 1
|
||||
fi
|
||||
echo "All checks passed."
|
||||
|
||||
@@ -70,3 +70,17 @@ jobs:
|
||||
BINARY: ${{ steps.run-action.outputs.build-wrapper-binary }}
|
||||
run: |
|
||||
("$BINARY" || true) | grep "build-wrapper, version "
|
||||
|
||||
qa-gate:
|
||||
name: QA Install Build Wrapper - gate
|
||||
runs-on: ubuntu-latest
|
||||
needs: [output-test]
|
||||
if: always()
|
||||
steps:
|
||||
- name: Check all jobs passed
|
||||
run: |
|
||||
if [[ "${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}" == "true" ]]; then
|
||||
echo "One or more required jobs failed or were cancelled."
|
||||
exit 1
|
||||
fi
|
||||
echo "All checks passed."
|
||||
|
||||
@@ -451,7 +451,7 @@ jobs:
|
||||
./test/assertFileExists ./test/example-project/.scannerwork/report-task.txt
|
||||
overrideSonarcloudUrlTest:
|
||||
name: >
|
||||
'SONARCLOUD_URL' is used
|
||||
Deprecated 'SONARCLOUD_URL' still works and emits a deprecation warning
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -461,7 +461,7 @@ jobs:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Run action with SONARCLOUD_URL
|
||||
- name: Run action with deprecated SONARCLOUD_URL
|
||||
uses: ./
|
||||
with:
|
||||
args: -Dsonar.scanner.apiBaseUrl=api.mirror.sonarcloud.io -Dsonar.scanner.internal.dumpToFile=./output.properties
|
||||
@@ -827,3 +827,40 @@ jobs:
|
||||
run: |
|
||||
echo "Action with invalid scannerVersion should have failed but succeeded"
|
||||
exit 1
|
||||
|
||||
qa-gate:
|
||||
name: QA Main - gate
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- noInputsTest
|
||||
- argsInputTest
|
||||
- argsInputInjectionTest
|
||||
- backtickCommandInjectionTest
|
||||
- dollarSymbolCommandInjectionTest
|
||||
- otherCommandInjectionVariantsTest
|
||||
- projectBaseDirInputTest
|
||||
- scannerVersionTest
|
||||
- scannerBinariesUrlTest
|
||||
- scannerBinariesUrlIsEscapedWithWget
|
||||
- scannerBinariesUrlIsEscapedWithCurl
|
||||
- dontFailGradleTest
|
||||
- dontFailGradleKotlinTest
|
||||
- dontFailMavenTest
|
||||
- runAnalysisTest
|
||||
- runnerDebugUsedTest
|
||||
- runAnalysisWithCacheTest
|
||||
- overrideSonarcloudUrlTest
|
||||
- curlPerformsRedirect
|
||||
- useSslCertificate
|
||||
- analysisWithSslCertificate
|
||||
- updateTruststoreWhenPresent
|
||||
- scannerVersionValidationTest
|
||||
if: always()
|
||||
steps:
|
||||
- name: Check all jobs passed
|
||||
run: |
|
||||
if [[ "${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}" == "true" ]]; then
|
||||
echo "One or more required jobs failed or were cancelled."
|
||||
exit 1
|
||||
fi
|
||||
echo "All checks passed."
|
||||
|
||||
@@ -200,6 +200,20 @@ 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/
|
||||
```
|
||||
|
||||
#### `scannerBinariesAuthHeader`
|
||||
|
||||
If the server specified by `scannerBinariesUrl` requires authentication, you can provide an `Authorization` header value using the `scannerBinariesAuthHeader` option.
|
||||
The value is passed directly as the `Authorization` HTTP header, so you must include the scheme (e.g. `Bearer`, `Basic`):
|
||||
|
||||
```yaml
|
||||
- uses: SonarSource/sonarqube-scan-action@<action version>
|
||||
with:
|
||||
scannerBinariesUrl: https://my.custom.binaries.url.com/Distribution/sonar-scanner-cli/
|
||||
scannerBinariesAuthHeader: ${{ secrets.BINARIES_AUTH_HEADER }}
|
||||
```
|
||||
|
||||
Store the full header value (e.g. `Bearer mytoken`) in the GitHub secret to avoid exposing credentials.
|
||||
|
||||
#### `skipSignatureVerification`
|
||||
|
||||
By default, the action verifies the OpenPGP signature of the SonarScanner CLI binary before executing it. You can disable this verification using the `skipSignatureVerification` option:
|
||||
|
||||
@@ -28,6 +28,13 @@ inputs:
|
||||
description: Skip GPG signature verification (not recommended for security)
|
||||
required: false
|
||||
default: "false"
|
||||
scannerBinariesAuthHeader:
|
||||
description: >
|
||||
Authorization header value to use when downloading the SonarScanner CLI binaries
|
||||
(e.g. 'Bearer mytoken' or 'Basic base64creds'). Use this when scannerBinariesUrl
|
||||
points to a private server that requires authentication.
|
||||
required: false
|
||||
default: ""
|
||||
runs:
|
||||
using: node24
|
||||
main: dist/index.js
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+26
-5
@@ -3503,6 +3503,13 @@ function downloadToolAttempt(url, dest, auth, headers) {
|
||||
const http = new HttpClient(userAgent, [], {
|
||||
allowRetries: false
|
||||
});
|
||||
if (auth) {
|
||||
debug('set auth');
|
||||
if (headers === undefined) {
|
||||
headers = {};
|
||||
}
|
||||
headers.authorization = auth;
|
||||
}
|
||||
const response = yield http.get(url, headers);
|
||||
if (response.message.statusCode !== 200) {
|
||||
const err = new HTTPError(response.message.statusCode);
|
||||
@@ -4140,6 +4147,7 @@ const TOOLNAME = "sonar-scanner-cli";
|
||||
async function installSonarScanner({
|
||||
scannerVersion,
|
||||
scannerBinariesUrl,
|
||||
scannerBinariesAuthHeader,
|
||||
skipSignatureVerification = false,
|
||||
}) {
|
||||
const flavor = getPlatformFlavor(os$1.platform(), os$1.arch());
|
||||
@@ -4160,7 +4168,7 @@ async function installSonarScanner({
|
||||
|
||||
info(`Downloading from: ${downloadUrl}`);
|
||||
|
||||
const downloadPath = await downloadTool(downloadUrl);
|
||||
const downloadPath = await downloadTool(downloadUrl, undefined, scannerBinariesAuthHeader);
|
||||
|
||||
if (skipSignatureVerification) {
|
||||
warning("⚠ Skipping GPG signature verification (not recommended)");
|
||||
@@ -4170,7 +4178,7 @@ async function installSonarScanner({
|
||||
|
||||
let signaturePath;
|
||||
try {
|
||||
signaturePath = await downloadTool(signatureUrl);
|
||||
signaturePath = await downloadTool(signatureUrl, undefined, scannerBinariesAuthHeader);
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Failed to download signature file from ${signatureUrl}: ${error.message}`
|
||||
@@ -4489,10 +4497,14 @@ function getInputs() {
|
||||
const args = getInput("args");
|
||||
const projectBaseDir = getInput("projectBaseDir");
|
||||
const scannerBinariesUrl = getInput("scannerBinariesUrl");
|
||||
const scannerBinariesAuthHeader = getInput("scannerBinariesAuthHeader") || undefined;
|
||||
if (scannerBinariesAuthHeader) {
|
||||
setSecret(scannerBinariesAuthHeader);
|
||||
}
|
||||
const scannerVersion = getInput("scannerVersion");
|
||||
const skipSignatureVerification = getBooleanInput("skipSignatureVerification");
|
||||
|
||||
return { args, projectBaseDir, scannerBinariesUrl, scannerVersion, skipSignatureVerification };
|
||||
return { args, projectBaseDir, scannerBinariesUrl, scannerBinariesAuthHeader, scannerVersion, skipSignatureVerification };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4528,16 +4540,25 @@ function runSanityChecks(inputs) {
|
||||
|
||||
async function run() {
|
||||
try {
|
||||
const { args, projectBaseDir, scannerVersion, scannerBinariesUrl, skipSignatureVerification } =
|
||||
const { args, projectBaseDir, scannerVersion, scannerBinariesUrl, scannerBinariesAuthHeader, skipSignatureVerification } =
|
||||
getInputs();
|
||||
const runnerEnv = getEnvVariables();
|
||||
const { sonarToken } = runnerEnv;
|
||||
const { sonarToken, sonarcloudUrl } = runnerEnv;
|
||||
|
||||
if (sonarcloudUrl) {
|
||||
warning(
|
||||
"The SONARCLOUD_URL environment variable is deprecated and will be removed in a future version. " +
|
||||
"Regular users should not set it; use SONAR_REGION=us for the US region. " +
|
||||
"For advanced needs, pass -Dsonar.scanner.sonarcloudUrl and -Dsonar.scanner.apiBaseUrl via the args input."
|
||||
);
|
||||
}
|
||||
|
||||
runSanityChecks({ projectBaseDir, scannerVersion, sonarToken });
|
||||
|
||||
const scannerDir = await installSonarScanner({
|
||||
scannerVersion,
|
||||
scannerBinariesUrl,
|
||||
scannerBinariesAuthHeader,
|
||||
skipSignatureVerification,
|
||||
});
|
||||
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* sonarqube-scan-action
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
import assert from "node:assert/strict";
|
||||
import { describe, it, mock } from "node:test";
|
||||
|
||||
function mockDependencies(t, { getInputFn, setSecretFn }) {
|
||||
t.mock.module("@actions/core", {
|
||||
namedExports: {
|
||||
getInput: getInputFn,
|
||||
getBooleanInput: mock.fn(() => false),
|
||||
setSecret: setSecretFn,
|
||||
setFailed: mock.fn(),
|
||||
info: mock.fn(),
|
||||
warning: mock.fn(),
|
||||
},
|
||||
});
|
||||
t.mock.module("../install-sonar-scanner.js", {
|
||||
namedExports: { installSonarScanner: mock.fn(async () => "/scanner") },
|
||||
});
|
||||
t.mock.module("../run-sonar-scanner.js", {
|
||||
namedExports: { runSonarScanner: mock.fn(async () => {}) },
|
||||
});
|
||||
t.mock.module("../sanity-checks.js", {
|
||||
namedExports: {
|
||||
validateScannerVersion: mock.fn(),
|
||||
checkSonarToken: mock.fn(),
|
||||
checkMavenProject: mock.fn(),
|
||||
checkGradleProject: mock.fn(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
describe("SONARCLOUD_URL deprecation", () => {
|
||||
it("should warn when SONARCLOUD_URL is set", async (t) => {
|
||||
const warningFn = mock.fn();
|
||||
const getInputFn = mock.fn(() => "");
|
||||
|
||||
t.mock.module("@actions/core", {
|
||||
namedExports: {
|
||||
getInput: getInputFn,
|
||||
getBooleanInput: mock.fn(() => false),
|
||||
setSecret: mock.fn(),
|
||||
setFailed: mock.fn(),
|
||||
info: mock.fn(),
|
||||
warning: warningFn,
|
||||
},
|
||||
});
|
||||
t.mock.module("../install-sonar-scanner.js", {
|
||||
namedExports: { installSonarScanner: mock.fn(async () => "/scanner") },
|
||||
});
|
||||
t.mock.module("../run-sonar-scanner.js", {
|
||||
namedExports: { runSonarScanner: mock.fn(async () => {}) },
|
||||
});
|
||||
t.mock.module("../sanity-checks.js", {
|
||||
namedExports: {
|
||||
validateScannerVersion: mock.fn(),
|
||||
checkSonarToken: mock.fn(),
|
||||
checkMavenProject: mock.fn(),
|
||||
checkGradleProject: mock.fn(),
|
||||
},
|
||||
});
|
||||
|
||||
process.env.SONARCLOUD_URL = "mirror.sonarcloud.io";
|
||||
t.after(() => delete process.env.SONARCLOUD_URL);
|
||||
|
||||
await import("../index.js?test=deprecation-warning");
|
||||
|
||||
assert.equal(warningFn.mock.calls.length, 1);
|
||||
assert.match(
|
||||
warningFn.mock.calls[0].arguments[0],
|
||||
/SONARCLOUD_URL.*deprecated/
|
||||
);
|
||||
});
|
||||
|
||||
it("should not warn when SONARCLOUD_URL is not set", async (t) => {
|
||||
const warningFn = mock.fn();
|
||||
const getInputFn = mock.fn(() => "");
|
||||
|
||||
t.mock.module("@actions/core", {
|
||||
namedExports: {
|
||||
getInput: getInputFn,
|
||||
getBooleanInput: mock.fn(() => false),
|
||||
setSecret: mock.fn(),
|
||||
setFailed: mock.fn(),
|
||||
info: mock.fn(),
|
||||
warning: warningFn,
|
||||
},
|
||||
});
|
||||
t.mock.module("../install-sonar-scanner.js", {
|
||||
namedExports: { installSonarScanner: mock.fn(async () => "/scanner") },
|
||||
});
|
||||
t.mock.module("../run-sonar-scanner.js", {
|
||||
namedExports: { runSonarScanner: mock.fn(async () => {}) },
|
||||
});
|
||||
t.mock.module("../sanity-checks.js", {
|
||||
namedExports: {
|
||||
validateScannerVersion: mock.fn(),
|
||||
checkSonarToken: mock.fn(),
|
||||
checkMavenProject: mock.fn(),
|
||||
checkGradleProject: mock.fn(),
|
||||
},
|
||||
});
|
||||
|
||||
delete process.env.SONARCLOUD_URL;
|
||||
|
||||
await import("../index.js?test=no-deprecation-warning");
|
||||
|
||||
assert.equal(warningFn.mock.calls.length, 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getInputs", () => {
|
||||
it("should mask scannerBinariesAuthHeader using setSecret when provided", async (t) => {
|
||||
const setSecretFn = mock.fn();
|
||||
const getInputFn = mock.fn((name) => name === "scannerBinariesAuthHeader" ? "Bearer mytoken" : "");
|
||||
|
||||
mockDependencies(t, { getInputFn, setSecretFn });
|
||||
|
||||
await import("../index.js?test=set-secret");
|
||||
|
||||
assert.equal(setSecretFn.mock.calls.length, 1);
|
||||
assert.equal(setSecretFn.mock.calls[0].arguments[0], "Bearer mytoken");
|
||||
});
|
||||
|
||||
it("should not call setSecret when scannerBinariesAuthHeader is not provided", async (t) => {
|
||||
const setSecretFn = mock.fn();
|
||||
const getInputFn = mock.fn(() => "");
|
||||
|
||||
mockDependencies(t, { getInputFn, setSecretFn });
|
||||
|
||||
await import("../index.js?test=no-set-secret");
|
||||
|
||||
assert.equal(setSecretFn.mock.calls.length, 0);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* sonarqube-scan-action
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
import assert from "node:assert/strict";
|
||||
import { describe, it, mock } from "node:test";
|
||||
|
||||
const SCANNER_VERSION = "6.2.0.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`;
|
||||
|
||||
function mockUtils(t) {
|
||||
t.mock.module("../utils.js", {
|
||||
namedExports: {
|
||||
getPlatformFlavor: mock.fn(() => "linux-x64"),
|
||||
getScannerDownloadURL: mock.fn(() => BINARY_DOWNLOAD_URL),
|
||||
scannerDirName: mock.fn(() => `sonar-scanner-${SCANNER_VERSION}-linux-x64`),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
describe("installSonarScanner", () => {
|
||||
it("should forward scannerBinariesAuthHeader to both binary and signature downloads", async (t) => {
|
||||
const downloadCalls = [];
|
||||
const downloadToolFn = mock.fn(async (url, dest, auth) => {
|
||||
downloadCalls.push({ url, auth });
|
||||
return `/tmp/downloaded-${downloadCalls.length}`;
|
||||
});
|
||||
|
||||
mockUtils(t);
|
||||
|
||||
t.mock.module("@actions/tool-cache", {
|
||||
namedExports: {
|
||||
find: mock.fn(() => null),
|
||||
downloadTool: downloadToolFn,
|
||||
extractZip: mock.fn(async () => "/tmp/extracted"),
|
||||
cacheDir: mock.fn(async () => "/tmp/cached"),
|
||||
},
|
||||
});
|
||||
|
||||
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=auth-header`
|
||||
);
|
||||
|
||||
await installSonarScanner({
|
||||
scannerVersion: SCANNER_VERSION,
|
||||
scannerBinariesUrl: BINARIES_URL,
|
||||
scannerBinariesAuthHeader: "Bearer mytoken",
|
||||
});
|
||||
|
||||
assert.equal(downloadCalls.length, 2, "Should download binary and signature");
|
||||
assert.equal(downloadCalls[0].auth, "Bearer mytoken", "Binary download should use auth header");
|
||||
assert.equal(downloadCalls[1].auth, "Bearer mytoken", "Signature download should use auth header");
|
||||
assert.ok(downloadCalls[1].url.endsWith(".asc"), "Second download should be the signature");
|
||||
});
|
||||
|
||||
it("should not set auth header when scannerBinariesAuthHeader is not provided", async (t) => {
|
||||
const downloadCalls = [];
|
||||
const downloadToolFn = mock.fn(async (url, dest, auth) => {
|
||||
downloadCalls.push({ url, auth });
|
||||
return `/tmp/downloaded-${downloadCalls.length}`;
|
||||
});
|
||||
|
||||
mockUtils(t);
|
||||
|
||||
t.mock.module("@actions/tool-cache", {
|
||||
namedExports: {
|
||||
find: mock.fn(() => null),
|
||||
downloadTool: downloadToolFn,
|
||||
extractZip: mock.fn(async () => "/tmp/extracted"),
|
||||
cacheDir: mock.fn(async () => "/tmp/cached"),
|
||||
},
|
||||
});
|
||||
|
||||
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=no-auth-header`
|
||||
);
|
||||
|
||||
await installSonarScanner({
|
||||
scannerVersion: SCANNER_VERSION,
|
||||
scannerBinariesUrl: BINARIES_URL,
|
||||
});
|
||||
|
||||
assert.equal(downloadCalls.length, 2);
|
||||
assert.equal(downloadCalls[0].auth, undefined, "Binary download should have no auth header");
|
||||
assert.equal(downloadCalls[1].auth, undefined, "Signature download should have no auth header");
|
||||
});
|
||||
|
||||
it("should skip signature download when skipSignatureVerification is true", async (t) => {
|
||||
const downloadCalls = [];
|
||||
const downloadToolFn = mock.fn(async (url, dest, auth) => {
|
||||
downloadCalls.push({ url, auth });
|
||||
return `/tmp/downloaded-${downloadCalls.length}`;
|
||||
});
|
||||
|
||||
mockUtils(t);
|
||||
|
||||
t.mock.module("@actions/tool-cache", {
|
||||
namedExports: {
|
||||
find: mock.fn(() => null),
|
||||
downloadTool: downloadToolFn,
|
||||
extractZip: mock.fn(async () => "/tmp/extracted"),
|
||||
cacheDir: mock.fn(async () => "/tmp/cached"),
|
||||
},
|
||||
});
|
||||
|
||||
t.mock.module("@actions/core", {
|
||||
namedExports: {
|
||||
info: mock.fn(),
|
||||
warning: mock.fn(),
|
||||
addPath: mock.fn(),
|
||||
},
|
||||
});
|
||||
|
||||
const { installSonarScanner } = await import(
|
||||
`../install-sonar-scanner.js?test=skip-sig`
|
||||
);
|
||||
|
||||
await installSonarScanner({
|
||||
scannerVersion: SCANNER_VERSION,
|
||||
scannerBinariesUrl: BINARIES_URL,
|
||||
scannerBinariesAuthHeader: "Bearer mytoken",
|
||||
skipSignatureVerification: true,
|
||||
});
|
||||
|
||||
assert.equal(downloadCalls.length, 1, "Should only download binary, not signature");
|
||||
assert.equal(downloadCalls[0].auth, "Bearer mytoken");
|
||||
});
|
||||
|
||||
it("should use cached tool when available and skip download", async (t) => {
|
||||
const downloadToolFn = mock.fn();
|
||||
|
||||
mockUtils(t);
|
||||
|
||||
t.mock.module("@actions/tool-cache", {
|
||||
namedExports: {
|
||||
find: mock.fn(() => "/tmp/cached-tool"),
|
||||
downloadTool: downloadToolFn,
|
||||
extractZip: mock.fn(),
|
||||
cacheDir: mock.fn(),
|
||||
},
|
||||
});
|
||||
|
||||
t.mock.module("@actions/core", {
|
||||
namedExports: {
|
||||
info: mock.fn(),
|
||||
warning: mock.fn(),
|
||||
addPath: mock.fn(),
|
||||
},
|
||||
});
|
||||
|
||||
const { installSonarScanner } = await import(
|
||||
`../install-sonar-scanner.js?test=cached`
|
||||
);
|
||||
|
||||
await installSonarScanner({
|
||||
scannerVersion: SCANNER_VERSION,
|
||||
scannerBinariesUrl: BINARIES_URL,
|
||||
});
|
||||
|
||||
assert.equal(downloadToolFn.mock.calls.length, 0, "Should not download when cached");
|
||||
});
|
||||
});
|
||||
+19
-6
@@ -17,14 +17,14 @@
|
||||
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
import * as core from "@actions/core";
|
||||
import { installSonarScanner } from "./install-sonar-scanner";
|
||||
import { runSonarScanner } from "./run-sonar-scanner";
|
||||
import { installSonarScanner } from "./install-sonar-scanner.js";
|
||||
import { runSonarScanner } from "./run-sonar-scanner.js";
|
||||
import {
|
||||
checkGradleProject,
|
||||
checkMavenProject,
|
||||
checkSonarToken,
|
||||
validateScannerVersion,
|
||||
} from "./sanity-checks";
|
||||
} from "./sanity-checks.js";
|
||||
|
||||
/**
|
||||
* Inputs are defined in action.yml
|
||||
@@ -33,10 +33,14 @@ function getInputs() {
|
||||
const args = core.getInput("args");
|
||||
const projectBaseDir = core.getInput("projectBaseDir");
|
||||
const scannerBinariesUrl = core.getInput("scannerBinariesUrl");
|
||||
const scannerBinariesAuthHeader = core.getInput("scannerBinariesAuthHeader") || undefined;
|
||||
if (scannerBinariesAuthHeader) {
|
||||
core.setSecret(scannerBinariesAuthHeader);
|
||||
}
|
||||
const scannerVersion = core.getInput("scannerVersion");
|
||||
const skipSignatureVerification = core.getBooleanInput("skipSignatureVerification");
|
||||
|
||||
return { args, projectBaseDir, scannerBinariesUrl, scannerVersion, skipSignatureVerification };
|
||||
return { args, projectBaseDir, scannerBinariesUrl, scannerBinariesAuthHeader, scannerVersion, skipSignatureVerification };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,16 +76,25 @@ function runSanityChecks(inputs) {
|
||||
|
||||
async function run() {
|
||||
try {
|
||||
const { args, projectBaseDir, scannerVersion, scannerBinariesUrl, skipSignatureVerification } =
|
||||
const { args, projectBaseDir, scannerVersion, scannerBinariesUrl, scannerBinariesAuthHeader, skipSignatureVerification } =
|
||||
getInputs();
|
||||
const runnerEnv = getEnvVariables();
|
||||
const { sonarToken } = runnerEnv;
|
||||
const { sonarToken, sonarcloudUrl } = runnerEnv;
|
||||
|
||||
if (sonarcloudUrl) {
|
||||
core.warning(
|
||||
"The SONARCLOUD_URL environment variable is deprecated and will be removed in a future version. " +
|
||||
"Regular users should not set it; use SONAR_REGION=us for the US region. " +
|
||||
"For advanced needs, pass -Dsonar.scanner.sonarcloudUrl and -Dsonar.scanner.apiBaseUrl via the args input."
|
||||
);
|
||||
}
|
||||
|
||||
runSanityChecks({ projectBaseDir, scannerVersion, sonarToken });
|
||||
|
||||
const scannerDir = await installSonarScanner({
|
||||
scannerVersion,
|
||||
scannerBinariesUrl,
|
||||
scannerBinariesAuthHeader,
|
||||
skipSignatureVerification,
|
||||
});
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@ import {
|
||||
getPlatformFlavor,
|
||||
getScannerDownloadURL,
|
||||
scannerDirName,
|
||||
} from "./utils";
|
||||
import { verifySignature } from "./gpg-verification";
|
||||
} from "./utils.js";
|
||||
import { verifySignature } from "./gpg-verification.js";
|
||||
|
||||
const TOOLNAME = "sonar-scanner-cli";
|
||||
|
||||
@@ -35,6 +35,7 @@ const TOOLNAME = "sonar-scanner-cli";
|
||||
export async function installSonarScanner({
|
||||
scannerVersion,
|
||||
scannerBinariesUrl,
|
||||
scannerBinariesAuthHeader,
|
||||
skipSignatureVerification = false,
|
||||
}) {
|
||||
const flavor = getPlatformFlavor(os.platform(), os.arch());
|
||||
@@ -55,7 +56,7 @@ export async function installSonarScanner({
|
||||
|
||||
core.info(`Downloading from: ${downloadUrl}`);
|
||||
|
||||
const downloadPath = await tc.downloadTool(downloadUrl);
|
||||
const downloadPath = await tc.downloadTool(downloadUrl, undefined, scannerBinariesAuthHeader);
|
||||
|
||||
if (skipSignatureVerification) {
|
||||
core.warning("⚠ Skipping GPG signature verification (not recommended)");
|
||||
@@ -65,7 +66,7 @@ export async function installSonarScanner({
|
||||
|
||||
let signaturePath;
|
||||
try {
|
||||
signaturePath = await tc.downloadTool(signatureUrl);
|
||||
signaturePath = await tc.downloadTool(signatureUrl, undefined, scannerBinariesAuthHeader);
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Failed to download signature file from ${signatureUrl}: ${error.message}`
|
||||
|
||||
Reference in New Issue
Block a user