SQSCANGHA-112 Migrate installation step

This commit is contained in:
Jeremy Davis 2025-09-09 17:00:04 +02:00 committed by Julien HENRY
parent 8f448484d9
commit ed9f3aad50
12 changed files with 2963 additions and 216 deletions

View file

@ -13,7 +13,7 @@ jobs:
No inputs No inputs
strategy: strategy:
matrix: matrix:
os: [ github-ubuntu-latest-s, macos-latest ] os: [github-ubuntu-latest-s, macos-latest]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
@ -32,7 +32,7 @@ jobs:
'args' input 'args' input
strategy: strategy:
matrix: matrix:
os: [ github-ubuntu-latest-s, github-windows-latest-s, macos-latest ] os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
@ -55,8 +55,12 @@ jobs:
'args' input with command injection will fail 'args' input with command injection will fail
strategy: strategy:
matrix: matrix:
os: [ github-ubuntu-latest-s, github-windows-latest-s, macos-latest ] os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
args: [ -Dsonar.someArg=aValue && echo "Injection", -Dsonar.someArg="value\"; whoami; echo \"" ] args:
[
-Dsonar.someArg=aValue && echo "Injection",
-Dsonar.someArg="value\"; whoami; echo \"",
]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
@ -81,7 +85,7 @@ jobs:
'args' input with backticks injection does not execute command 'args' input with backticks injection does not execute command
strategy: strategy:
matrix: matrix:
os: [ github-ubuntu-latest-s, github-windows-latest-s, macos-latest ] os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
@ -109,7 +113,7 @@ jobs:
'args' input with dollar command injection does not execute command 'args' input with dollar command injection does not execute command
strategy: strategy:
matrix: matrix:
os: [ github-ubuntu-latest-s, github-windows-latest-s, macos-latest ] os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
@ -136,7 +140,7 @@ jobs:
'args' input with other command injection variants does not execute command 'args' input with other command injection variants does not execute command
strategy: strategy:
matrix: matrix:
os: [ github-ubuntu-latest-s, github-windows-latest-s, macos-latest ] os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
@ -166,7 +170,7 @@ jobs:
'projectBaseDir' input 'projectBaseDir' input
strategy: strategy:
matrix: matrix:
os: [ github-ubuntu-latest-s, github-windows-latest-s, macos-latest ] os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
@ -203,7 +207,12 @@ jobs:
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}' SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
- name: Assert - name: Assert
run: | run: |
./test/assertFileExists "$RUNNER_TEMP/sonarscanner/sonar-scanner-cli-6.1.0.4477-linux-x64.zip" # The new JavaScript implementation uses @actions/tool-cache which caches tools differently
# Instead of checking for the zip file, verify the tool was installed by checking it's in PATH
if ! command -v sonar-scanner &> /dev/null; then
echo "Error: sonar-scanner not found in PATH"
exit 1
fi
scannerBinariesUrlTest: scannerBinariesUrlTest:
name: > name: >
'scannerBinariesUrl' input with invalid URL 'scannerBinariesUrl' input with invalid URL
@ -245,7 +254,7 @@ jobs:
uses: ./ uses: ./
continue-on-error: true continue-on-error: true
with: with:
scannerBinariesUrl: 'http://some_uri;touch file.txt;' scannerBinariesUrl: "http://some_uri;touch file.txt;"
env: env:
NO_CACHE: true NO_CACHE: true
SONAR_HOST_URL: http://not_actually_used SONAR_HOST_URL: http://not_actually_used
@ -274,7 +283,7 @@ jobs:
uses: ./ uses: ./
continue-on-error: true continue-on-error: true
with: with:
scannerBinariesUrl: 'http://some_uri http://another_uri''; touch file.txt;' scannerBinariesUrl: "http://some_uri http://another_uri'; touch file.txt;"
env: env:
NO_CACHE: true NO_CACHE: true
SONAR_HOST_URL: http://not_actually_used SONAR_HOST_URL: http://not_actually_used
@ -381,7 +390,7 @@ jobs:
'RUNNER_DEBUG' is used 'RUNNER_DEBUG' is used
strategy: strategy:
matrix: matrix:
os: [ github-ubuntu-latest-s, github-windows-latest-s, macos-latest ] os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
@ -434,13 +443,13 @@ jobs:
projectBaseDir: ./test/example-project projectBaseDir: ./test/example-project
- name: Assert - name: Assert
run: | run: |
./test/assertFileExists ./test/example-project/.scannerwork/report-task.txt ./test/assertFileExists ./test/example-project/.scannerwork/report-task.txt
overrideSonarcloudUrlTest: overrideSonarcloudUrlTest:
name: > name: >
'SONARCLOUD_URL' is used 'SONARCLOUD_URL' is used
strategy: strategy:
matrix: matrix:
os: [ github-ubuntu-latest-s, github-windows-latest-s, macos-latest ] os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
@ -456,92 +465,7 @@ jobs:
- name: Assert - name: Assert
run: | run: |
./test/assertFileContains ./output.properties "sonar.host.url=mirror.sonarcloud.io" ./test/assertFileContains ./output.properties "sonar.host.url=mirror.sonarcloud.io"
./test/assertFileContains ./output.properties "sonar.scanner.sonarcloudUrl=mirror.sonarcloud.io" ./test/assertFileContains ./output.properties "sonar.scanner.sonarcloudUrl=mirror.sonarcloud.io"
dontFailWhenMissingWgetButCurlAvailable:
name: Don't fail when missing wget but curl available
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Remove wget
run: sudo apt-get remove -y wget
- name: Assert wget is not available
run: |
if command -v wget 2>&1 >/dev/null
then
exit 1
fi
- name: Run action
uses: ./
env:
NO_CACHE: true
SONAR_HOST_URL: http://not_actually_used
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
with:
args: -Dsonar.scanner.internal.dumpToFile=./output.properties
- name: Assert
run: |
./test/assertFileExists ./output.properties
dontFailWhenMissingCurlButWgetAvailable:
name: Don't fail when missing curl but wget available
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Remove curl
run: sudo apt-get remove -y curl
- name: Assert curl is not available
run: |
if command -v curl 2>&1 >/dev/null
then
exit 1
fi
- name: Run action
id: runTest
uses: ./
env:
NO_CACHE: true
SONAR_HOST_URL: http://not_actually_used
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
with:
args: -Dsonar.scanner.internal.dumpToFile=./output.properties
- name: Assert
run: |
./test/assertFileExists ./output.properties
failWhenBothWgetAndCurlMissing:
name: Fail when both wget and curl are missing
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Remove wget and curl
run: sudo apt-get remove -y wget curl
- name: Assert wget and curl are not available
run: |
if command -v wget 2>&1 >/dev/null
then
exit 1
fi
if command -v curl 2>&1 >/dev/null
then
exit 1
fi
- name: Run action
id: runTest
uses: ./
continue-on-error: true
env:
NO_CACHE: true
SONAR_HOST_URL: http://not_actually_used
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
with:
args: -Dsonar.scanner.internal.dumpToFile=./output.properties
- name: Assert failure of previous step
if: steps.runTest.outcome == 'success'
run: exit 1
curlPerformsRedirect: curlPerformsRedirect:
name: > name: >
curl performs redirect when scannerBinariesUrl returns 3xx curl performs redirect when scannerBinariesUrl returns 3xx
@ -566,7 +490,7 @@ jobs:
uses: ./ uses: ./
with: with:
scannerVersion: 6.2.1.4610 scannerVersion: 6.2.1.4610
scannerBinariesUrl: http://localhost:8080/clientRedirectToSonarBinaries scannerBinariesUrl: https://localhost:8080/clientRedirectToSonarBinaries
env: env:
NO_CACHE: true NO_CACHE: true
SONAR_HOST_URL: http://not_actually_used SONAR_HOST_URL: http://not_actually_used
@ -579,7 +503,7 @@ jobs:
'SONAR_ROOT_CERT' is converted to truststore 'SONAR_ROOT_CERT' is converted to truststore
strategy: strategy:
matrix: matrix:
os: [ github-ubuntu-latest-s, github-windows-latest-s, macos-latest ] os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
@ -652,7 +576,7 @@ jobs:
- name: Start nginx and SonarQube via Docker Compose - name: Start nginx and SonarQube via Docker Compose
run: docker compose up -d --wait run: docker compose up -d --wait
working-directory: .github/qa-sq-behind-ngix working-directory: .github/qa-sq-behind-ngix
- name: Read correct server certificate - name: Read correct server certificate
run: | run: |
# read server.crt from .github/qa-sq-behind-ngix/ and store into the SONAR_ROOT_CERT_VALID # read server.crt from .github/qa-sq-behind-ngix/ and store into the SONAR_ROOT_CERT_VALID
# environment variable, to be able to read it in the next step # environment variable, to be able to read it in the next step
@ -791,7 +715,7 @@ jobs:
- name: Run action with SONAR_ROOT_CERT - name: Run action with SONAR_ROOT_CERT
uses: ./ uses: ./
env: env:
# NO_CACHE not needed, as SONAR_SSL_FOLDER is setup when the Sonar Scanner is run, not installed # NO_CACHE not needed, as SONAR_SSL_FOLDER is setup when the Sonar Scanner is run, not installed
SONAR_HOST_URL: http://not_actually_used SONAR_HOST_URL: http://not_actually_used
SONAR_ROOT_CERT: | SONAR_ROOT_CERT: |
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
@ -840,7 +764,7 @@ jobs:
- name: Run action a second time with a different SONAR_ROOT_CERT - name: Run action a second time with a different SONAR_ROOT_CERT
uses: ./ uses: ./
env: env:
# NO_CACHE not needed, as SONAR_SSL_FOLDER is setup when the Sonar Scanner is run, not installed # NO_CACHE not needed, as SONAR_SSL_FOLDER is setup when the Sonar Scanner is run, not installed
SONAR_HOST_URL: http://not_actually_used SONAR_HOST_URL: http://not_actually_used
SONAR_ROOT_CERT: | SONAR_ROOT_CERT: |
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
@ -873,7 +797,7 @@ jobs:
- name: Run action a third time - name: Run action a third time
uses: ./ uses: ./
env: env:
# NO_CACHE not needed, as SONAR_SSL_FOLDER is setup when the Sonar Scanner is run, not installed # NO_CACHE not needed, as SONAR_SSL_FOLDER is setup when the Sonar Scanner is run, not installed
SONAR_HOST_URL: http://not_actually_used SONAR_HOST_URL: http://not_actually_used
SONAR_ROOT_CERT: | SONAR_ROOT_CERT: |
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----

25
.github/workflows/unit-tests.yml vendored Normal file
View file

@ -0,0 +1,25 @@
name: Unit tests
on:
push:
branches: [master]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test

View file

@ -30,32 +30,14 @@ runs:
# with: # with:
# ref: "jay/js-rewrite" # ref: "jay/js-rewrite"
- name: Sanity checks - name: Sanity checks & setup
run: node ./dist/index.js run: node ./dist/index.js
shell: bash shell: bash
env: env:
INPUT_PROJECTBASEDIR: ${{ inputs.projectBaseDir }} INPUT_PROJECTBASEDIR: ${{ inputs.projectBaseDir }}
INPUT_SCANNERVERSION: ${{ inputs.scannerVersion }} INPUT_SCANNERVERSION: ${{ inputs.scannerVersion }}
- name: Load Sonar Scanner CLI from cache
id: sonar-scanner-cli
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 #v4.2.4
env:
# The default value is 60mins. Reaching timeout is treated the same as a cache miss.
SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1
with:
path: ${{ runner.temp }}/sonar-scanner-cli-${{ inputs.scannerVersion }}-${{ runner.os }}-${{ runner.arch }}
key: sonar-scanner-cli-${{ inputs.scannerVersion }}-${{ runner.os }}-${{ runner.arch }}
- name: Install Sonar Scanner CLI
if: ${{ env.NO_CACHE == 'true' || steps.sonar-scanner-cli.outputs.cache-hit != 'true' }}
run: ${GITHUB_ACTION_PATH}/scripts/install-sonar-scanner-cli.sh
shell: bash
env:
INPUT_SCANNERVERSION: ${{ inputs.scannerVersion }}
INPUT_SCANNERBINARIESURL: ${{ inputs.scannerBinariesUrl }} INPUT_SCANNERBINARIESURL: ${{ inputs.scannerBinariesUrl }}
- name: Add SonarScanner CLI to the PATH
run: echo "${RUNNER_TEMP}/sonar-scanner-cli-${{ inputs.scannerVersion }}-${{ runner.os }}-${{ runner.arch }}/bin" >> $GITHUB_PATH
shell: bash
- name: Run SonarScanner - name: Run SonarScanner
run: ${GITHUB_ACTION_PATH}/scripts/run-sonar-scanner.sh run: ${GITHUB_ACTION_PATH}/scripts/run-sonar-scanner.sh
shell: bash shell: bash

2727
dist/index.js vendored

File diff suppressed because it is too large Load diff

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

47
dist/sanity-checks.js vendored
View file

@ -1,47 +0,0 @@
import fs from 'fs';
import { join } from 'path';
function validateScannerVersion(version) {
if (!version) {
return;
}
const versionRegex = /^\d+\.\d+\.\d+\.\d+$/;
if (!versionRegex.test(version)) {
throw new Error(
"Invalid scannerVersion format. Expected format: x.y.z.w (e.g., 7.1.0.4889)"
);
}
}
function checkSonarToken(core) {
if (!process.env.SONAR_TOKEN) {
core.warning(
"Running this GitHub Action without SONAR_TOKEN is not recommended"
);
}
}
function checkMavenProject(core, projectBaseDir) {
const pomPath = join(projectBaseDir.replace(/\/$/, ""), "pom.xml");
if (fs.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."
);
}
}
function checkGradleProject(core, projectBaseDir) {
const baseDir = projectBaseDir.replace(/\/$/, "");
const gradlePath = join(baseDir, "build.gradle");
const gradleKtsPath = join(baseDir, "build.gradle.kts");
if (fs.existsSync(gradlePath) || fs.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."
);
}
}
export { checkGradleProject, checkMavenProject, checkSonarToken, validateScannerVersion };
//# sourceMappingURL=sanity-checks.js.map

View file

@ -1 +0,0 @@
{"version":3,"file":"sanity-checks.js","sources":["../src/sanity-checks.js"],"sourcesContent":["import fs from \"fs\";\nimport { join } from \"path\";\n\nexport function validateScannerVersion(version) {\n if (!version) {\n return;\n }\n\n const versionRegex = /^\\d+\\.\\d+\\.\\d+\\.\\d+$/;\n if (!versionRegex.test(version)) {\n throw new Error(\n \"Invalid scannerVersion format. Expected format: x.y.z.w (e.g., 7.1.0.4889)\"\n );\n }\n}\n\nexport function checkSonarToken(core) {\n if (!process.env.SONAR_TOKEN) {\n core.warning(\n \"Running this GitHub Action without SONAR_TOKEN is not recommended\"\n );\n }\n}\n\nexport function checkMavenProject(core, projectBaseDir) {\n const pomPath = join(projectBaseDir.replace(/\\/$/, \"\"), \"pom.xml\");\n if (fs.existsSync(pomPath)) {\n core.warning(\n \"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.\"\n );\n }\n}\n\nexport function checkGradleProject(core, projectBaseDir) {\n const baseDir = projectBaseDir.replace(/\\/$/, \"\");\n const gradlePath = join(baseDir, \"build.gradle\");\n const gradleKtsPath = join(baseDir, \"build.gradle.kts\");\n\n if (fs.existsSync(gradlePath) || fs.existsSync(gradleKtsPath)) {\n core.warning(\n \"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.\"\n );\n }\n}\n"],"names":[],"mappings":";;;AAGO,SAAS,sBAAsB,CAAC,OAAO,EAAE;AAChD,EAAE,IAAI,CAAC,OAAO,EAAE;AAChB,IAAI;AACJ,EAAE;;AAEF,EAAE,MAAM,YAAY,GAAG,sBAAsB;AAC7C,EAAE,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;AACnC,IAAI,MAAM,IAAI,KAAK;AACnB,MAAM;AACN,KAAK;AACL,EAAE;AACF;;AAEO,SAAS,eAAe,CAAC,IAAI,EAAE;AACtC,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE;AAChC,IAAI,IAAI,CAAC,OAAO;AAChB,MAAM;AACN,KAAK;AACL,EAAE;AACF;;AAEO,SAAS,iBAAiB,CAAC,IAAI,EAAE,cAAc,EAAE;AACxD,EAAE,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,SAAS,CAAC;AACpE,EAAE,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE;AAC9B,IAAI,IAAI,CAAC,OAAO;AAChB,MAAM;AACN,KAAK;AACL,EAAE;AACF;;AAEO,SAAS,kBAAkB,CAAC,IAAI,EAAE,cAAc,EAAE;AACzD,EAAE,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;AACnD,EAAE,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC;AAClD,EAAE,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,EAAE,kBAAkB,CAAC;;AAEzD,EAAE,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE;AACjE,IAAI,IAAI,CAAC,OAAO;AAChB,MAAM;AACN,KAAK;AACL,EAAE;AACF;;;;"}

25
package-lock.json generated
View file

@ -13,6 +13,7 @@
"@actions/github": "6.0.1" "@actions/github": "6.0.1"
}, },
"devDependencies": { "devDependencies": {
"@actions/tool-cache": "^2.0.2",
"@rollup/plugin-commonjs": "28.0.6", "@rollup/plugin-commonjs": "28.0.6",
"@rollup/plugin-node-resolve": "16.0.1", "@rollup/plugin-node-resolve": "16.0.1",
"mock-fs": "^5.5.0", "mock-fs": "^5.5.0",
@ -69,6 +70,20 @@
"integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==", "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@actions/tool-cache": {
"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",
"@actions/exec": "^1.0.0",
"@actions/http-client": "^2.0.1",
"@actions/io": "^1.1.1",
"semver": "^6.1.0"
}
},
"node_modules/@fastify/busboy": { "node_modules/@fastify/busboy": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz",
@ -862,6 +877,16 @@
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
"node_modules/semver": {
"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/supports-preserve-symlinks-flag": { "node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",

View file

@ -11,12 +11,13 @@
"license": "LGPL-3.0-only", "license": "LGPL-3.0-only",
"dependencies": { "dependencies": {
"@actions/core": "1.11.1", "@actions/core": "1.11.1",
"@actions/github": "6.0.1" "@actions/github": "6.0.1",
"@actions/tool-cache": "2.0.2"
}, },
"devDependencies": { "devDependencies": {
"@rollup/plugin-commonjs": "28.0.6", "@rollup/plugin-commonjs": "28.0.6",
"@rollup/plugin-node-resolve": "16.0.1", "@rollup/plugin-node-resolve": "16.0.1",
"mock-fs": "^5.5.0", "mock-fs": "5.5.0",
"rollup": "4.50.1" "rollup": "4.50.1"
} }
} }

View file

@ -0,0 +1,81 @@
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import {
getPlatformFlavor,
getScannerDownloadURL,
scannerDirName,
} from "../utils.js";
describe("getPlatformFlavor", () => {
const supportedPlatforms = [
{ platform: "linux", arch: "x64", expected: "linux-x64" },
{ platform: "linux", arch: "arm64", expected: "linux-aarch64" },
{ platform: "win32", arch: "x64", expected: "windows-x64" },
{ platform: "darwin", arch: "x64", expected: "macosx-x64" },
{ platform: "darwin", arch: "arm64", expected: "macosx-aarch64" },
];
const unsupportedPlatforms = [
{ platform: "linux", arch: "arm" },
{ platform: "openbsd", arch: "x64" },
{ platform: undefined, arch: "x64" },
{ platform: "linux", arch: undefined },
{ platform: null, arch: "x64" },
{ platform: "linux", arch: null },
];
supportedPlatforms.forEach(({ platform, arch, expected }) => {
it(`returns ${expected} for ${platform} ${arch}`, () => {
assert.equal(getPlatformFlavor(platform, arch), expected);
});
});
unsupportedPlatforms.forEach(({ platform, arch }) => {
it(`throws for unsupported platform ${platform} ${arch}`, () => {
assert.throws(
() => getPlatformFlavor(platform, arch),
{
message: `Platform ${platform} ${arch} not supported`,
},
`should have thrown for ${platform} ${arch}`
);
});
});
});
describe("getScannerDownloadURL", () => {
it("generates correct URL without trailing slash", () => {
const result = getScannerDownloadURL({
scannerBinariesUrl:
"https://binaries.sonarsource.com/Distribution/sonar-scanner-cli",
scannerVersion: "7.2.0.5079",
flavor: "linux-x64",
});
assert.equal(
result,
"https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-7.2.0.5079-linux-x64.zip"
);
});
it("generates correct URL with trailing slash", () => {
const result = getScannerDownloadURL({
scannerBinariesUrl:
"https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/",
scannerVersion: "7.2.0.5079",
flavor: "linux-x64",
});
assert.equal(
result,
"https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-7.2.0.5079-linux-x64.zip"
);
});
});
describe("scannerDirName", () => {
it("handles special characters", () => {
assert.equal(
scannerDirName("7.2.0-SNAPSHOT", "linux_x64"),
"sonar-scanner-7.2.0-SNAPSHOT-linux_x64"
);
});
});

View file

@ -1,19 +1,28 @@
import * as core from "@actions/core"; import * as core from "@actions/core";
import * as tc from "@actions/tool-cache";
import * as os from "os";
import * as path from "path";
import { import {
checkGradleProject, checkGradleProject,
checkMavenProject, checkMavenProject,
checkSonarToken, checkSonarToken,
validateScannerVersion, validateScannerVersion,
} from "./sanity-checks"; } from "./sanity-checks";
import {
getPlatformFlavor,
getScannerDownloadURL,
scannerDirName,
} from "./utils";
const TOOLNAME = "sonar-scanner-cli";
function getInputs() { function getInputs() {
//FIXME: should not rely on ENV vars //FIXME: should not rely on ENV vars
const scannerVersion = process.env.INPUT_SCANNERVERSION; // core.getInput("scannerVersion"); const scannerVersion = process.env.INPUT_SCANNERVERSION; // core.getInput("scannerVersion");
const projectBaseDir = process.env.INPUT_PROJECTBASEDIR; // core.getInput("projectBaseDir") || "."; const projectBaseDir = process.env.INPUT_PROJECTBASEDIR; // core.getInput("projectBaseDir") || ".";
const scannerBinariesUrl = process.env.INPUT_SCANNERBINARIESURL; // core.getInput("scannerBinariesUrl");
console.log("scannerVersion: ", scannerVersion); return { scannerVersion, projectBaseDir, scannerBinariesUrl };
return { scannerVersion, projectBaseDir };
} }
function runSanityChecks(inputs) { function runSanityChecks(inputs) {
@ -30,10 +39,62 @@ function runSanityChecks(inputs) {
} }
} }
function run() { async function installSonarScannerCLI(scannerVersion, scannerBinariesUrl) {
const inputs = getInputs(); const flavor = getPlatformFlavor(os.platform(), os.arch());
runSanityChecks(inputs); // Check if tool is already cached
let toolDir = tc.find(TOOLNAME, scannerVersion, flavor);
if (!toolDir) {
console.log(
`Installing Sonar Scanner CLI ${scannerVersion} for ${flavor}...`
);
const downloadUrl = getScannerDownloadURL({
scannerBinariesUrl,
scannerVersion,
flavor,
});
console.log(`Downloading from: ${downloadUrl}`);
const downloadPath = await tc.downloadTool(downloadUrl);
const extractedPath = await tc.extractZip(downloadPath);
// Find the actual scanner directory inside the extracted folder
const scannerPath = path.join(
extractedPath,
scannerDirName(scannerVersion, flavor)
);
toolDir = await tc.cacheDir(scannerPath, TOOLNAME, scannerVersion, flavor);
console.log(`Sonar Scanner CLI cached to: ${toolDir}`);
} else {
console.log(`Using cached Sonar Scanner CLI from: ${toolDir}`);
}
// Add the bin directory to PATH
const binDir = path.join(toolDir, "bin");
core.addPath(binDir);
return toolDir;
}
async function run() {
try {
const inputs = getInputs();
const { scannerVersion, scannerBinariesUrl } = inputs;
// Run sanity checks first
runSanityChecks(inputs);
// Install Sonar Scanner CLI using @actions/tool-cache
await installSonarScannerCLI(scannerVersion, scannerBinariesUrl);
} catch (error) {
core.setFailed(`Action failed: ${error.message}`);
process.exit(1);
}
} }
run(); run();

35
src/utils.js Normal file
View file

@ -0,0 +1,35 @@
const platformFlavor = {
linux: {
x64: "linux-x64",
arm64: "linux-aarch64",
},
win32: {
x64: "windows-x64",
},
darwin: {
x64: "macosx-x64",
arm64: "macosx-aarch64",
},
};
export function getPlatformFlavor(platform, arch) {
const flavor = platformFlavor[platform]?.[arch];
if (!flavor) {
throw new Error(`Platform ${platform} ${arch} not supported`);
}
return flavor;
}
export function getScannerDownloadURL({
scannerBinariesUrl,
scannerVersion,
flavor,
}) {
const trimURL = scannerBinariesUrl.replace(/\/$/, "");
return `${trimURL}/sonar-scanner-cli-${scannerVersion}-${flavor}.zip`;
}
export const scannerDirName = (version, flavor) =>
`sonar-scanner-${version}-${flavor}`;