Compare commits

...

23 commits
v5.3 ... master

Author SHA1 Message Date
github-actions[bot]
a31c9398be
SQSCANGHA-126 Update SonarScanner CLI to 8.0.1.6346 (#218)
Some checks failed
QA Deprecated C and C++ action / Action outputs (push) Has been cancelled
Unit tests / test (push) Has been cancelled
QA Install Build Wrapper action / Action outputs (push) Has been cancelled
QA Main action / No inputs (push) Has been cancelled
QA Main action / 'args' input (push) Has been cancelled
QA Main action / 'args' input with command injection will fail (push) Has been cancelled
QA Main action / 'args' input with backticks injection does not execute command (push) Has been cancelled
QA Main action / 'args' input with dollar command injection does not execute command (push) Has been cancelled
QA Main action / 'args' input with other command injection variants does not execute command (push) Has been cancelled
QA Main action / 'projectBaseDir' input (push) Has been cancelled
QA Main action / 'scannerVersion' input (push) Has been cancelled
QA Main action / 'scannerBinariesUrl' input with invalid URL (push) Has been cancelled
QA Main action / 'scannerBinariesUrl' is escaped with wget so special chars are not injected in the download command (push) Has been cancelled
QA Main action / 'scannerBinariesUrl' is escaped with curl so special chars are not injected in the download command (push) Has been cancelled
QA Main action / Don't fail on Gradle project (push) Has been cancelled
QA Main action / Don't fail on Kotlin Gradle project (push) Has been cancelled
QA Main action / Don't fail on Maven project (push) Has been cancelled
QA Main action / runAnalysisTest (push) Has been cancelled
QA Main action / 'RUNNER_DEBUG' is used (push) Has been cancelled
QA Main action / runAnalysisWithCacheTest (push) Has been cancelled
QA Main action / 'SONARCLOUD_URL' is used (push) Has been cancelled
QA Main action / curl performs redirect when scannerBinariesUrl returns 3xx (push) Has been cancelled
QA Main action / 'SONAR_ROOT_CERT' is converted to truststore (push) Has been cancelled
QA Main action / Analysis takes into account 'SONAR_ROOT_CERT' (push) Has been cancelled
QA Main action / truststore.p12 is updated when present (push) Has been cancelled
QA Main action / 'scannerVersion' input validation (push) Has been cancelled
QA Scripts / create_install_path.sh (push) Has been cancelled
QA Scripts / configure_paths.sh (push) Has been cancelled
QA Scripts / download.sh (push) Has been cancelled
QA Scripts / fetch_latest_version.sh (push) Has been cancelled
2025-12-09 09:53:51 +01:00
dependabot[bot]
40f5b61913
SQSCANGHA-123 NO-JIRA Bump actions/setup-node from 5 to 6 (#214) 2025-10-15 15:09:18 +02:00
Brandon Davis
9bf7c126a1
SQSCANGHA-122 Include caveats for running SCA (#213) 2025-10-09 06:21:35 -05:00
github-actions[bot]
ba6563cca7
Update SonarScanner CLI to 7.3.0.5189 (#212) 2025-10-06 09:29:17 +02:00
dependabot[bot]
5ffbad4454
SQSCANGHA-120 Bump actions/setup-node from 4 to 5 (#211) 2025-09-22 07:47:48 +02:00
Joan Biel
fd88b7d7cc SQSCANGHA-119 New Readme structure
Add quick start section

Increase visibility of special cases and alternatives

Prioritize SQC examples over SQS
2025-09-18 10:38:53 +02:00
Julien HENRY
27a157d234 SQSCANGHA-118 Update the README to document the breaking change for args parsing 2025-09-18 10:38:53 +02:00
Jeremy Davis
e327da8e78 NO-JIRA Add documentation for contribution 2025-09-18 10:38:53 +02:00
Jeremy Davis
ff001fd600 SQSCANGHA-107 Migrate install-build-wrapper 2025-09-18 10:38:53 +02:00
Jeremy Davis
a88c96d7e4 SQSCANGHA-107 Make room for install-build-wrapper action 2025-09-18 10:38:53 +02:00
Jeremy Davis
a64281002c SQSCANGHA-112 SQSCANGHA-113 Fixes from review and keytool refactor 2025-09-18 10:38:53 +02:00
Julien HENRY
60aee7033b NO-JIRA Disable fail fast on matrix jobs 2025-09-18 10:38:53 +02:00
Julien HENRY
502204eab4 NO-JIRA Fix test assertion 2025-09-18 10:38:53 +02:00
Jeremy Davis
0b794a06fa SQSCANGHA-112 Delete legacy shell script 2025-09-18 10:38:53 +02:00
Jeremy Davis
ece10df5d7 SQSCANGHA-112 Extract installation step and other fixes 2025-09-18 10:38:53 +02:00
Jeremy Davis
ee80e84272 SQSCANGHA-112 Fix redirect test to deal with TLS 2025-09-18 10:38:53 +02:00
Jeremy Davis
cbabf0572a SQSCANGHA-113 Delete legacy shell scripts 2025-09-18 10:38:53 +02:00
Jeremy Davis
16df975da5 SQSCANGHA-113 Migrate scanner run step 2025-09-18 10:38:53 +02:00
Jeremy Davis
ed9f3aad50 SQSCANGHA-112 Migrate installation step 2025-09-18 10:38:53 +02:00
Jeremy Davis
8f448484d9 SQSCANGHA-115 Delete legacy shell script 2025-09-18 10:38:53 +02:00
Jeremy Davis
6a808e9a20 SQSCANGHA-115 Migrate sanity checks 2025-09-18 10:38:53 +02:00
Jeremy Davis
9db61695c9 SQSCANGHA-117 Set up js build 2025-09-18 10:38:53 +02:00
SonarTech
5837ebfcca
BUILD-8875: Migrate to standardized GitHub runner names
Co-authored-by: Julien HENRY <julien.henry@sonarsource.com>
2025-09-02 10:10:38 +02:00
44 changed files with 32869 additions and 661 deletions

View file

@ -5,9 +5,11 @@ services:
- 8080:8080
volumes:
- $GITHUB_WORKSPACE/.github/qa-nginx-redirecting/nginx.conf:/etc/nginx/nginx.conf:ro
- $GITHUB_WORKSPACE/.github/qa-nginx-redirecting/nginx.crt:/etc/nginx/nginx.crt:ro
- $GITHUB_WORKSPACE/.github/qa-nginx-redirecting/nginx.key:/etc/nginx/nginx.key:ro
healthcheck:
test: ["CMD", "curl", "--fail", "localhost:8080/health"]
test: ["CMD", "curl", "--fail", "--insecure", "https://localhost:8080/health"]
interval: 10s
timeout: 5s
retries: 20
start_period: 2m
start_period: 2m

10
.github/qa-nginx-redirecting/generate-ssl.sh vendored Executable file
View file

@ -0,0 +1,10 @@
#!/bin/bash
# Generate self-signed SSL certificate for localhost with 1-day expiry
openssl req -x509 -nodes -days 1 -newkey rsa:2048 \
-keyout nginx.key \
-out nginx.crt \
-subj "/C=US/ST=CA/L=Local/O=Test/CN=localhost" \
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1"
echo "SSL certificates generated with 1-day expiry: nginx.crt and nginx.key"

View file

@ -18,7 +18,9 @@ http {
include /etc/nginx/conf.d/*.conf;
server {
listen 8080;
listen 8080 ssl;
ssl_certificate /etc/nginx/nginx.crt;
ssl_certificate_key /etc/nginx/nginx.key;
location /health {
add_header 'Content-Type' 'text/plain';

View file

@ -7,7 +7,7 @@ on:
jobs:
PullRequestClosed_job:
name: Pull Request Closed
runs-on: ubuntu-latest-large
runs-on: github-ubuntu-latest-s
permissions:
id-token: write
pull-requests: read

View file

@ -7,7 +7,7 @@ on:
jobs:
PullRequestCreated_job:
name: Pull Request Created
runs-on: ubuntu-latest-large
runs-on: github-ubuntu-latest-s
permissions:
id-token: write
# For external PR, ticket should be created manually

View file

@ -7,7 +7,7 @@ on:
jobs:
RequestReview_job:
name: Request review
runs-on: ubuntu-latest-large
runs-on: github-ubuntu-latest-s
permissions:
id-token: write
# For external PR, ticket should be moved manually

View file

@ -7,7 +7,7 @@ on:
jobs:
SubmitReview_job:
name: Submit Review
runs-on: ubuntu-latest-large
runs-on: github-ubuntu-latest-s
permissions:
id-token: write
pull-requests: read

View file

@ -11,13 +11,16 @@ jobs:
output-test:
name: Action outputs
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest-large, windows-latest-large, macos-latest, macos-13]
os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest, macos-14]
cache: [true, false]
include:
- arch: X64
- os: macos-latest
arch: ARM64
- os: macos-14
arch: ARM64
runs-on: ${{ matrix.os }}
steps:
# Specifying a specific architecture of the runner is not possible for Github hosted runners

View file

@ -11,13 +11,16 @@ jobs:
output-test:
name: Action outputs
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest-large, windows-latest-large, macos-latest, macos-13]
os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest, macos-14]
cache: [true, false]
include:
- arch: X64
- os: macos-latest
arch: ARM64
- os: macos-14
arch: ARM64
runs-on: ${{ matrix.os }}
steps:
# Specifying a specific architecture of the runner is not possible for Github hosted runners

View file

@ -12,8 +12,9 @@ jobs:
name: >
No inputs
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest-large, macos-latest ]
os: [github-ubuntu-latest-s, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
@ -31,8 +32,9 @@ jobs:
name: >
'args' input
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest-large, windows-latest-large, macos-latest ]
os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
@ -54,15 +56,21 @@ jobs:
name: >
'args' input with command injection will fail
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest-large, windows-latest-large, macos-latest ]
args: [ -Dsonar.someArg=aValue && echo "Injection", -Dsonar.someArg="value\"; whoami; echo \"" ]
os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
args:
[
-Dsonar.someArg=aValue && echo "Injection",
-Dsonar.someArg="value\"; whoami; echo \"",
]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Run action with args
id: runTest
uses: ./
continue-on-error: true
with:
@ -80,8 +88,9 @@ jobs:
name: >
'args' input with backticks injection does not execute command
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest-large, windows-latest-large, macos-latest ]
os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
@ -109,7 +118,7 @@ jobs:
'args' input with dollar command injection does not execute command
strategy:
matrix:
os: [ ubuntu-latest-large, windows-latest-large, macos-latest ]
os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
@ -136,7 +145,7 @@ jobs:
'args' input with other command injection variants does not execute command
strategy:
matrix:
os: [ ubuntu-latest-large, windows-latest-large, macos-latest ]
os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
@ -166,7 +175,7 @@ jobs:
'projectBaseDir' input
strategy:
matrix:
os: [ ubuntu-latest-large, windows-latest-large, macos-latest ]
os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
@ -187,7 +196,7 @@ jobs:
scannerVersionTest:
name: >
'scannerVersion' input
runs-on: ubuntu-latest-large # assumes default RUNNER_ARCH for linux is X64
runs-on: github-ubuntu-latest-s # assumes default RUNNER_ARCH for linux is X64
steps:
- uses: actions/checkout@v5
with:
@ -203,11 +212,15 @@ jobs:
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
- name: Assert
run: |
./test/assertFileExists "$RUNNER_TEMP/sonarscanner/sonar-scanner-cli-6.1.0.4477-linux-x64.zip"
# 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:
name: >
'scannerBinariesUrl' input with invalid URL
runs-on: ubuntu-latest-large # assumes default RUNNER_ARCH for linux is X64
runs-on: github-ubuntu-latest-s # assumes default RUNNER_ARCH for linux is X64
steps:
- uses: actions/checkout@v5
with:
@ -235,7 +248,7 @@ jobs:
scannerBinariesUrlIsEscapedWithWget:
name: >
'scannerBinariesUrl' is escaped with wget so special chars are not injected in the download command
runs-on: ubuntu-latest-large
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@v5
with:
@ -245,7 +258,7 @@ jobs:
uses: ./
continue-on-error: true
with:
scannerBinariesUrl: 'http://some_uri;touch file.txt;'
scannerBinariesUrl: "http://some_uri;touch file.txt;"
env:
NO_CACHE: true
SONAR_HOST_URL: http://not_actually_used
@ -256,7 +269,7 @@ jobs:
scannerBinariesUrlIsEscapedWithCurl:
name: >
'scannerBinariesUrl' is escaped with curl so special chars are not injected in the download command
runs-on: ubuntu-latest-large
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@v5
with:
@ -274,7 +287,7 @@ jobs:
uses: ./
continue-on-error: true
with:
scannerBinariesUrl: 'http://some_uri http://another_uri''; touch file.txt;'
scannerBinariesUrl: "http://some_uri http://another_uri'; touch file.txt;"
env:
NO_CACHE: true
SONAR_HOST_URL: http://not_actually_used
@ -285,7 +298,7 @@ jobs:
dontFailGradleTest:
name: >
Don't fail on Gradle project
runs-on: ubuntu-latest-large
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@v5
with:
@ -306,7 +319,7 @@ jobs:
dontFailGradleKotlinTest:
name: >
Don't fail on Kotlin Gradle project
runs-on: ubuntu-latest-large
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@v5
with:
@ -327,7 +340,7 @@ jobs:
dontFailMavenTest:
name: >
Don't fail on Maven project
runs-on: ubuntu-latest-large
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@v5
with:
@ -346,7 +359,7 @@ jobs:
run: |
./test/assertFileExists ./output.properties
runAnalysisTest:
runs-on: ubuntu-latest-large
runs-on: github-ubuntu-latest-s
services:
sonarqube:
image: sonarqube:lts-community
@ -380,8 +393,9 @@ jobs:
name: >
'RUNNER_DEBUG' is used
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest-large, windows-latest-large, macos-latest ]
os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
@ -399,7 +413,7 @@ jobs:
run: |
./test/assertFileContains ./output.properties "sonar.verbose=true"
runAnalysisWithCacheTest:
runs-on: ubuntu-latest-large
runs-on: github-ubuntu-latest-s
services:
sonarqube:
image: sonarqube:lts-community
@ -434,13 +448,14 @@ jobs:
projectBaseDir: ./test/example-project
- name: Assert
run: |
./test/assertFileExists ./test/example-project/.scannerwork/report-task.txt
./test/assertFileExists ./test/example-project/.scannerwork/report-task.txt
overrideSonarcloudUrlTest:
name: >
'SONARCLOUD_URL' is used
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest-large, windows-latest-large, macos-latest ]
os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
@ -456,96 +471,11 @@ jobs:
- name: Assert
run: |
./test/assertFileContains ./output.properties "sonar.host.url=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: ubuntu-latest-large
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: ubuntu-latest-large
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: ubuntu-latest-large
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
./test/assertFileContains ./output.properties "sonar.scanner.sonarcloudUrl=mirror.sonarcloud.io"
curlPerformsRedirect:
name: >
curl performs redirect when scannerBinariesUrl returns 3xx
runs-on: ubuntu-latest-large
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@v5
with:
@ -558,6 +488,9 @@ jobs:
then
exit 1
fi
- name: Generate SSL certificates for nginx
run: ./generate-ssl.sh
working-directory: .github/qa-nginx-redirecting
- name: Start nginx via Docker Compose
run: docker compose up -d --wait
working-directory: .github/qa-nginx-redirecting
@ -566,20 +499,26 @@ jobs:
uses: ./
with:
scannerVersion: 6.2.1.4610
scannerBinariesUrl: http://localhost:8080/clientRedirectToSonarBinaries
scannerBinariesUrl: https://localhost:8080/clientRedirectToSonarBinaries
env:
NO_CACHE: true
NODE_TLS_REJECT_UNAUTHORIZED: 0
SONAR_HOST_URL: http://not_actually_used
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output1.properties"}'
- name: Assert Sonar Scanner CLI was downloaded
run: |
./test/assertFileExists "$RUNNER_TEMP/sonarscanner/sonar-scanner-cli-6.2.1.4610-linux-x64.zip"
# 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
useSslCertificate:
name: >
'SONAR_ROOT_CERT' is converted to truststore
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest-large, windows-latest-large, macos-latest ]
os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
@ -631,7 +570,7 @@ jobs:
analysisWithSslCertificate:
name: >
Analysis takes into account 'SONAR_ROOT_CERT'
runs-on: ubuntu-latest-large
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@v5
with:
@ -652,7 +591,7 @@ jobs:
- name: Start nginx and SonarQube via Docker Compose
run: docker compose up -d --wait
working-directory: .github/qa-sq-behind-ngix
- name: Read correct server certificate
- name: Read correct server certificate
run: |
# 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
@ -736,44 +675,10 @@ 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: ubuntu-latest-large
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
runs-on: ubuntu-latest-large
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@v5
with:
@ -791,7 +696,7 @@ jobs:
- name: Run action with SONAR_ROOT_CERT
uses: ./
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_ROOT_CERT: |
-----BEGIN CERTIFICATE-----
@ -840,7 +745,7 @@ jobs:
- name: Run action a second time with a different SONAR_ROOT_CERT
uses: ./
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_ROOT_CERT: |
-----BEGIN CERTIFICATE-----
@ -873,7 +778,7 @@ jobs:
- name: Run action a third time
uses: ./
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_ROOT_CERT: |
-----BEGIN CERTIFICATE-----
@ -902,7 +807,7 @@ jobs:
scannerVersionValidationTest:
name: >
'scannerVersion' input validation
runs-on: ubuntu-latest-large
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@v5
with:

View file

@ -10,7 +10,7 @@ on:
jobs:
create-install-dir-test:
name: create_install_path.sh
runs-on: ubuntu-latest-large
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@v5
with:
@ -107,7 +107,7 @@ jobs:
grep "=== Script failed ===" output
setup-script-test:
name: configure_paths.sh
runs-on: ubuntu-latest-large
runs-on: github-ubuntu-latest-s
env:
INSTALL_PATH: 'install-directory'
SONAR_HOST_URL: 'http://sonar-host.com'
@ -250,7 +250,7 @@ jobs:
grep "=== Script failed ===" output
download-script-test:
name: download.sh
runs-on: ubuntu-latest-large
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@v5
with:
@ -319,7 +319,7 @@ jobs:
grep "=== Script failed ===" output
fetch-latest-version-test:
name: fetch_latest_version.sh
runs-on: ubuntu-latest-large
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@v5
with:

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@v6
with:
node-version: "20"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test

View file

@ -7,7 +7,7 @@ on:
jobs:
generate:
runs-on: ubuntu-latest-large
runs-on: github-ubuntu-latest-s
permissions:
contents: write

View file

@ -7,7 +7,7 @@ on:
jobs:
check-version:
name: Check for sonar-scanner version update
runs-on: ubuntu-latest-large
runs-on: github-ubuntu-latest-s
outputs:
should_update: ${{ steps.version-check.outputs.should_update }}
new-version: ${{ steps.latest-version.outputs.sonar-scanner-version }}
@ -43,7 +43,7 @@ jobs:
update-version:
name: Prepare pull request for sonar-scanner version update
needs: check-version
runs-on: ubuntu-latest-large
runs-on: github-ubuntu-latest-s
permissions:
contents: write
pull-requests: write

3
.gitignore vendored
View file

@ -1,2 +1,5 @@
.idea
.DS_Store
# Node
node_modules/

554
README.md
View file

@ -1,53 +1,42 @@
# Scan your code with SonarQube [![QA Main](https://github.com/SonarSource/sonarqube-scan-action/actions/workflows/qa-main.yml/badge.svg)](https://github.com/SonarSource/sonarqube-scan-action/actions/workflows/qa-main.yml) [![QA Install Build Wrapper](https://github.com/SonarSource/sonarqube-scan-action/actions/workflows/qa-install-build-wrapper.yml/badge.svg)](https://github.com/SonarSource/sonarqube-scan-action/actions/workflows/qa-install-build-wrapper.yml) [![QA Scripts](https://github.com/SonarSource/sonarqube-scan-action/actions/workflows/qa-scripts.yml/badge.svg)](https://github.com/SonarSource/sonarqube-scan-action/actions/workflows/qa-scripts.yml) [![QA Deprecated C and C++ Action](https://github.com/SonarSource/sonarqube-scan-action/actions/workflows/qa-deprecated-c-cpp.yml/badge.svg)](https://github.com/SonarSource/sonarqube-scan-action/actions/workflows/qa-deprecated-c-cpp.yml)
This SonarSource project, available as a GitHub Action, scans your projects with SonarQube [Server](https://www.sonarsource.com/products/sonarqube/) or [Cloud](https://www.sonarsource.com/products/sonarcloud/).
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./images/SQ_Logo_Server_Cloud_Dark_Backgrounds.png">
<img alt="SonarQube Logo" src="./images/SQ_Logo_Server_Cloud_Light_Backgrounds.png">
</picture>
SonarQube [Server](https://www.sonarsource.com/products/sonarqube/) and [Cloud](https://www.sonarsource.com/products/sonarcloud/) (formerly SonarQube and SonarCloud) is a widely used static analysis solution for continuous code quality and security inspection.
It helps developers detect coding issues in 30+ languages, frameworks, and IaC platforms, including Java, JavaScript, TypeScript, C#, Python, C, C++, and [many more](https://www.sonarsource.com/knowledge/languages/).
## A GitHub Action for SonarQube
This GitHub Action integrates continuous code quality and security analysis directly into your workflow. It scans your project with either [SonarQube Server](https://www.sonarsource.com/products/sonarqube/) or [SonarQube Cloud](https://www.sonarsource.com/products/sonarcloud/), helping you catch bugs, security vulnerabilities, and code smells automatically within your CI/CD pipeline. **This action is the official method for scanning C, C++, Objective-C, and Dart projects via GitHub Actions.**
The solution also provides fix recommendations leveraging AI with Sonar's AI CodeFix capability.
> [!NOTE]
> This action now supports and is the official entrypoint for scanning C, C++, Objective-C and Dart projects via GitHub actions.
### What is SonarQube?
[SonarQube Server](https://www.sonarsource.com/products/sonarqube/) and [SonarQube Cloud](https://www.sonarsource.com/products/sonarcloud/) are widely used static analysis solutions for continuous code quality, security inspection, and fix remediation.
The platform supports over in 30+ languages, frameworks, and IaC platforms, including Java, JavaScript, TypeScript, C#, Python, C, C++, and [many more](https://www.sonarsource.com/knowledge/languages/).
## Requirements
## Quick Start
### Server
### 1. Prerequisites:
To run an analysis on your code, you first need to set up your project on SonarQube Server. Your SonarQube Server instance must be accessible from GitHub, and you will need an access token to run the analysis (more information below under **Environment variables**).
You must have a project already set up on SonarQube Cloud or SonarQube Server. This action performs the analysis, but the project must exist on the platform to receive the results.
Read more information on how to analyze your code [here](https://docs.sonarsource.com/sonarqube-server/latest/devops-platform-integration/github-integration/introduction/).
For more information, see [Key Requirements](#key-requirements).
### Cloud
* Create your account on SonarQube Cloud. [Sign up for free](https://www.sonarsource.com/products/sonarcloud/signup/?utm_medium=referral&utm_source=github&utm_campaign=sc-signup&utm_content=signup-sonarcloud-listing-x-x&utm_term=ww-psp-x) now if it's not already the case!
* The repository to analyze is set up on SonarQube Cloud. [Set it up](https://sonarcloud.io/projects/create) in just one click.
### 2. Required variables:
## Usage
The action needs two key variables to connect to the SonarQube instance and run the analysis. These should be stored as GitHub secrets or variables for security.
Project metadata, including the location of the sources to be analyzed, must be declared in the file `sonar-project.properties` in the base directory:
• `SONAR_TOKEN` : The authentication token required to access the SonarQube instance. This is a mandatory secret for all use cases.
### Server
• `SONAR_HOST_URL` : The URL of the SonarQube Server. This is required for self-hosted SonarQube Server but not needed for SonarQube Cloud.
```properties
sonar.projectKey=<replace with the key generated when setting up the project on SonarQube Server>
For more information, see [Configuration](#configuration).
# relative paths to source directories. More details and properties are described
# at https://docs.sonarsource.com/sonarqube-server/latest/project-administration/analysis-scope/
sonar.sources=.
```
### 3. Quick Start Workflow Example (for SonarQube Cloud)
In the following cases:
- for projects that don't have C, C++, or Objective-C in them
- for C, C++, Objective-C projects that don't use [Build Wrapper](https://docs.sonarsource.com/sonarqube-server/latest/analyzing-source-code/languages/c-family/prerequisites/#using-buildwrapper)
Create or update your CI pipeline to run the scan action:
the workflow, usually declared under `.github/workflows`, looks like the following:
```yaml
on:
@ -71,12 +60,332 @@ jobs:
# Disabling shallow clones is recommended for improving the relevancy of reporting
fetch-depth: 0
- name: SonarQube Scan
uses: SonarSource/sonarqube-scan-action@<action version> # Ex: v4.1.0, See the latest version at https://github.com/marketplace/actions/official-sonarqube-scan
uses: SonarSource/sonarqube-scan-action@<action version or sha1> # Ex: v4.1.0 or sha1, See the latest version at https://github.com/marketplace/actions/official-sonarqube-scan
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
```
Create a configuration file in the root directory of the project and name it `sonar-project.properties`:
```properties
sonar.organization=<replace with your SonarQube Cloud organization key>
sonar.projectKey=<replace with the key generated when setting up the project on SonarQube Cloud>
# relative paths to source directories. More details and properties are described
# at https://docs.sonarsource.com/sonarqube-cloud/advanced-setup/analysis-scope/
sonar.sources=src
```
For other workflows, see [Workflow Examples](#workflow-examples).
## Important: Special Cases and alternatives
This GitHub Action will not work for all technologies. If you are in one of the following situations, you should use the following alternatives:
* **Your code is built with Maven**. Read the documentation about our SonarScanner for Maven in SonarQube [Server](https://docs.sonarsource.com/sonarqube-server/latest/analyzing-source-code/scanners/sonarscanner-for-maven/) and [Cloud](https://docs.sonarsource.com/sonarqube-cloud/advanced-setup/ci-based-analysis/sonarscanner-for-maven/).
* **Your code is built with Gradle**. Read the documentation about our SonarScanner for Gradle in SonarQube [Server](https://docs.sonarsource.com/sonarqube-server/latest/analyzing-source-code/scanners/sonarscanner-for-gradle/) and [Cloud](https://docs.sonarsource.com/sonarqube-cloud/advanced-setup/ci-based-analysis/sonarscanner-for-gradle/).
* **You want to analyze a .NET solution**. Read the documentation about our SonarScanner for .NET in SonarQube [Server](https://docs.sonarsource.com/sonarqube-server/latest/analyzing-source-code/scanners/dotnet/introduction/) and [Cloud](https://docs.sonarsource.com/sonarqube-cloud/advanced-setup/ci-based-analysis/sonarscanner-for-dotnet/introduction/).
**Do not use this GitHub action if:**
* You want to run the action on C, C++, or Objective-C projects on a 32-bits system - build wrappers support only 64-bits OS.
**If you want to use Software Composition Analysis (SCA)**
Dependency scanning with SonarQube Advanced Security SCA may not work correctly if scanning requires on-the-fly manifest file generation. See the SCA analysis environment requirement documentation for [Cloud](https://docs.sonarsource.com/sonarqube-cloud/advanced-security/analyzing-projects-for-dependencies-sca#appropriate-environment) or [Server](https://docs.sonarsource.com/sonarqube-server/advanced-security/analyzing-projects-for-dependencies#appropriate-environment).
## Key requirements
To use this GitHub Action you need to meet the following prerequisites for your choosen SonarQube platform.
### For SonarQube Cloud
* Create your account on SonarQube Cloud. [Sign up for free](https://www.sonarsource.com/products/sonarcloud/signup/?utm_medium=referral&utm_source=github&utm_campaign=sc-signup&utm_content=signup-sonarcloud-listing-x-x&utm_term=ww-psp-x) now if it's not already the case!
* [Set up a repository to be analyzed](https://sonarcloud.io/projects/create) in just one click.
### For SonarQube Server
* Your SonarQube Server instance must be accessible from GitHub, and you will need an access token to run the analysis (more information below under **Environment variables**).
* To run an analysis on your code, you first need to set up your project on SonarQube Server.
Read more information on how to analyze your code [here](https://docs.sonarsource.com/sonarqube-server/latest/devops-platform-integration/github-integration/introduction/).
## Configuration
### Action parameters
#### `projectBaseDir`
You can change the analysis base directory by using the optional input `projectBaseDir` like this:
```yaml
- uses: SonarSource/sonarqube-scan-action@<action version or sha1>
with:
projectBaseDir: app/src
```
#### `scannerVersion`
In case you need to specify the version of the Sonar Scanner, you can use the `scannerVersion` option:
```yaml
- uses: SonarSource/sonarqube-scan-action@<action version or sha1>
with:
scannerVersion: 6.2.0.4584
```
#### `args`
In case you need to add additional analysis parameters, and you do not wish to set them in the `sonar-project.properties` file, you can use the `args` option:
```yaml
- uses: SonarSource/sonarqube-scan-action@<action version>
with:
projectBaseDir: app/src
args: >
-Dsonar.organization=my-organization # For SonarQube Cloud only
"-Dsonar.projectName=My Project"
-Dsonar.projectKey=my-projectkey
-Dsonar.python.coverage.reportPaths=coverage.xml
-Dsonar.sources=lib/
-Dsonar.tests=tests/
-Dsonar.test.exclusions=tests/**
-Dsonar.verbose=true
```
> [!NOTE]
> In version 6, the way the `args` option is handled has been changed to prevent command injection.
> As a result, we no longer support the full bash syntax.
> This means there is now a much more restricted use of quoting and escaping compared to older versions of the action.
> Example:
> ```yaml
> with:
> args: >
> -testing test
> -valid=true
> --quotes "test quotes" "nested \'quotes\'"
> -Dsonar.property="some value"
> "-Dsonar.property=some value"
> ```
> will be parsed as the following array of strings:
> ```
> [
> '-testing',
> 'test',
> '-valid=true',
> '--quotes',
> 'test quotes', # Surrounding quotes are removed
> 'nested \'quotes\'',
> '-Dsonar.property="some value"', # Internal quotes are NOT removed, contrary to the bash syntax
> '-Dsonar.property=some value', # This is the proper way to pass scanner arguments with spaces
> ]
> ```
#### `scannerBinariesUrl`
You can also specify the URL where to retrieve the SonarScanner CLI from.
The specified URL overrides the default address: `https://binaries.sonarsource.com/Distribution/sonar-scanner-cli`.
This can be useful when the runner executing the action is self-hosted and has regulated or no access to the Internet:
```yaml
- uses: SonarSource/sonarqube-scan-action@<action version>
with:
scannerBinariesUrl: https://my.custom.binaries.url.com/Distribution/sonar-scanner-cli/
```
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
### Environment variables
- `SONAR_TOKEN` – **Required** this is the token used to authenticate access to SonarQube. You can read more about security tokens in the documentation of SonarQube [Server](https://docs.sonarsource.com/sonarqube-server/latest/user-guide/managing-tokens/) and [Cloud](https://docs.sonarsource.com/sonarqube-cloud/managing-your-account/managing-tokens/). You can set the `SONAR_TOKEN` environment variable in the "Secrets" settings page of your repository, or you can add them at the level of your GitHub organization (recommended).
- `SONAR_HOST_URL` – this tells the scanner where SonarQube Server is hosted. You can set the `SONAR_HOST_URL` environment variable in the "Variables" settings page of your repository, or you can add them at the level of your GitHub organization (recommended). Not needed for SonarQube Cloud.
- `SONAR_ROOT_CERT` – Holds an additional certificate (in PEM format) that is used to validate the certificate of SonarQube Server or of a secured proxy to SonarQube (Server or Cloud). You can set the `SONAR_ROOT_CERT` environment variable in the "Secrets" settings page of your repository, or you can add them at the level of your GitHub organization (recommended).
Here is an example of how you can pass a certificate (in PEM format) to the Scanner truststore:
```yaml
- uses: SonarSource/sonarqube-scan-action@<action version>
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }}
SONAR_ROOT_CERT: ${{ secrets.SONAR_ROOT_CERT }}
```
If your source code file names contain special characters that are not covered by the locale range of `en_US.UTF-8`, you can configure your desired locale like this:
```yaml
- uses: SonarSource/sonarqube-scan-action@<action version>
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }} # or https://sonarcloud.io
LC_ALL: "ru_RU.UTF-8"
```
## Workflow Examples
### For SonarQube Cloud
Project metadata, including the location of the sources to be analyzed, must be declared in the file sonar-project.properties in the base directory:
```properties
sonar.organization=<replace with your SonarQube Cloud organization key>
sonar.projectKey=<replace with the key generated when setting up the project on SonarQube Cloud>
# relative paths to source directories. More details and properties are described
# at https://docs.sonarsource.com/sonarqube-cloud/advanced-setup/analysis-scope/
sonar.sources=src
```
#### Standard Projects
For projects that:
- do not contain C, C++, or Objective-C, and
- for C, C++, Objective-C projects that don't use [Build Wrapper](https://docs.sonarsource.com/sonarqube-cloud/advanced-setup/languages/c-family/prerequisites/#using-build-wrapper)
the workflow, usually declared under `.github/workflows/build.yml`, looks like the following:
```yaml
on:
# Trigger analysis when pushing to your main branches, and when creating a pull request.
push:
branches:
- main
- master
- develop
- 'releases/**'
pull_request:
types: [opened, synchronize, reopened]
name: Main Workflow
jobs:
sonarqube:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
# Disabling shallow clones is recommended for improving the relevancy of reporting
fetch-depth: 0
- name: SonarQube Scan
uses: SonarSource/sonarqube-scan-action@<action version or sha1> # Ex: v4.1.0 or sha1, See the latest version at https://github.com/marketplace/actions/official-sonarqube-scan
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
```
#### C/C++/Objective-C with Build Wrapper
For C, C++, and Objective-C projects relying on [Build Wrapper](https://docs.sonarsource.com/sonarqube-cloud/advanced-setup/languages/c-family/prerequisites/#using-build-wrapper) to generate the compilation database, the workflow requires additional steps to download the Build Wrapper and invoke it:
```yaml
# Trigger analysis when pushing to your main branches, and when creating a pull request.
push:
branches:
- main
- master
- develop
- 'releases/**'
pull_request:
types: [opened, synchronize, reopened]
name: Main Workflow
jobs:
sonarqube:
runs-on: ubuntu-latest
env:
BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed
steps:
- uses: actions/checkout@v4
with:
# Disabling shallow clone is recommended for improving relevancy of reporting
fetch-depth: 0
- name: Install Build Wrapper
uses: SonarSource/sonarqube-scan-action/install-build-wrapper@<action version>
- name: Run Build Wrapper
run: |
# Here goes your compilation wrapped with Build Wrapper
# For more information, see https://docs.sonarsource.com/sonarqube-cloud/advanced-setup/languages/c-family/prerequisites/#using-build-wrapper
# build-preparation steps
# build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} build-command
- name: SonarQube Scan
uses: SonarSource/sonarqube-scan-action@<action version or sha1>
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_ROOT_CERT: ${{ secrets.SONAR_ROOT_CERT }}
with:
# Consult https://docs.sonarsource.com/sonarqube-server/latest/analyzing-source-code/scanners/sonarscanner/ for more information and options
args: >
--define "sonar.cfamily.compile-commands=${{ env.BUILD_WRAPPER_OUT_DIR }}/compile_commands.json"
```
See also [example configurations of C++ projects for SonarQube Cloud](https://github.com/search?q=org%3Asonarsource-cfamily-examples+gh-actions-sc&type=repositories).
### For SonarQube Server
Project metadata, including the location of the sources to be analyzed, can be declared in the file `sonar-project.properties` in the base directory:
```properties
sonar.projectKey=<replace with the key generated when setting up the project on SonarQube Server>
# relative paths to source directories. More details and properties are described
# at https://docs.sonarsource.com/sonarqube-server/latest/project-administration/analysis-scope/
sonar.sources=src
```
#### Standard Projects
For projects that:
- do not contain C, C++, or Objective-C, and
- for C, C++, Objective-C projects that don't use [Build Wrapper](https://docs.sonarsource.com/sonarqube-server/latest/analyzing-source-code/languages/c-family/prerequisites/#using-buildwrapper)
the workflow, usually declared under `.github/workflows/build.yml`, looks like the following:
```yaml
on:
# Trigger analysis when pushing to your main branches, and when creating a pull request.
push:
branches:
- main
- master
- develop
- 'releases/**'
pull_request:
types: [opened, synchronize, reopened]
name: Main Workflow
jobs:
sonarqube:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
# Disabling shallow clones is recommended for improving the relevancy of reporting
fetch-depth: 0
- name: SonarQube Scan
uses: SonarSource/sonarqube-scan-action@<action version or sha1> # Ex: v4.1.0, or sha1, See the latest version at https://github.com/marketplace/actions/official-sonarqube-scan
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }}
```
#### C/C++/Objective-C with Build Wrapper
This subsection would contain the more complex YAML configuration for projects that require the
build wrapper to generate a compilation database. The example would detail the three-step
process: checking out the code, installing the build wrapper, and then running the SonarQube
scan with the appropriate parameters.
For C, C++, and Objective-C projects relying on [Build Wrapper](https://docs.sonarsource.com/sonarqube-server/latest/analyzing-source-code/languages/c-family/prerequisites/#using-buildwrapper) to generate the compilation database, the workflow requires additional steps to download the Build Wrapper and invoke it:
```yaml
@ -112,7 +421,7 @@ jobs:
# build-preparation steps
# build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} build-command
- name: SonarQube Scan
uses: SonarSource/sonarqube-scan-action@<action version>
uses: SonarSource/sonarqube-scan-action@<action version or sha1>
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }}
@ -130,201 +439,28 @@ It should look like this:
```yaml
with:
args: >
--define sonar.cfamily.build-wrapper-output="${{ env.BUILD_WRAPPER_OUT_DIR }}"
--define "sonar.cfamily.build-wrapper-output=${{ env.BUILD_WRAPPER_OUT_DIR }}"
```
See also [example configurations of C++ projects for SonarQube Server](https://github.com/search?q=org%3Asonarsource-cfamily-examples+gh-actions-sq&type=repositories).
### Cloud
## Advanced Settings
```properties
sonar.organization=<replace with your SonarQube Cloud organization key>
sonar.projectKey=<replace with the key generated when setting up the project on SonarQube Cloud>
# relative paths to source directories. More details and properties are described
# at https://docs.sonarsource.com/sonarqube-cloud/advanced-setup/analysis-scope/
sonar.sources=.
```
In the following cases:
- for projects that don't have C, C++, or Objective-C in them
- for C, C++, Objective-C projects that don't use [Build Wrapper](https://docs.sonarsource.com/sonarqube-cloud/advanced-setup/languages/c-family/prerequisites/#using-build-wrapper)
the workflow, usually declared under `.github/workflows`, looks like the following:
```yaml
on:
# Trigger analysis when pushing to your main branches, and when creating a pull request.
push:
branches:
- main
- master
- develop
- 'releases/**'
pull_request:
types: [opened, synchronize, reopened]
name: Main Workflow
jobs:
sonarqube:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
# Disabling shallow clones is recommended for improving the relevancy of reporting
fetch-depth: 0
- name: SonarQube Scan
uses: SonarSource/sonarqube-scan-action@<action version> # Ex: v4.1.0, See the latest version at https://github.com/marketplace/actions/official-sonarqube-scan
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
```
For C, C++, and Objective-C projects relying on [Build Wrapper](https://docs.sonarsource.com/sonarqube-cloud/advanced-setup/languages/c-family/prerequisites/#using-build-wrapper) to generate the compilation database, the workflow requires additional steps to download the Build Wrapper and invoke it:
```yaml
# Trigger analysis when pushing to your main branches, and when creating a pull request.
push:
branches:
- main
- master
- develop
- 'releases/**'
pull_request:
types: [opened, synchronize, reopened]
name: Main Workflow
jobs:
sonarqube:
runs-on: ubuntu-latest
env:
BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed
steps:
- uses: actions/checkout@v4
with:
# Disabling shallow clone is recommended for improving relevancy of reporting
fetch-depth: 0
- name: Install Build Wrapper
uses: SonarSource/sonarqube-scan-action/install-build-wrapper@<action version>
- name: Run Build Wrapper
run: |
# Here goes your compilation wrapped with Build Wrapper
# For more information, see https://docs.sonarsource.com/sonarqube-cloud/advanced-setup/languages/c-family/prerequisites/#using-build-wrapper
# build-preparation steps
# build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} build-command
- name: SonarQube Scan
uses: SonarSource/sonarqube-scan-action@<action version>
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_ROOT_CERT: ${{ secrets.SONAR_ROOT_CERT }}
with:
# Consult https://docs.sonarsource.com/sonarqube-server/latest/analyzing-source-code/scanners/sonarscanner/ for more information and options
args: >
--define sonar.cfamily.compile-commands="${{ env.BUILD_WRAPPER_OUT_DIR }}/compile_commands.json"
```
See also [example configurations of C++ projects for SonarQube Cloud](https://github.com/search?q=org%3Asonarsource-cfamily-examples+gh-actions-sc&type=repositories).
## Action parameters
You can change the analysis base directory by using the optional input `projectBaseDir` like this:
```yaml
- uses: SonarSource/sonarqube-scan-action@<action version>
with:
projectBaseDir: app/src
```
In case you need to specify the version of the Sonar Scanner, you can use the `scannerVersion` option:
```yaml
- uses: SonarSource/sonarqube-scan-action@<action version>
with:
scannerVersion: 6.2.0.4584
```
In case you need to add additional analysis parameters, and you do not wish to set them in the `sonar-project.properties` file, you can use the `args` option:
```yaml
- uses: SonarSource/sonarqube-scan-action@<action version>
with:
projectBaseDir: app/src
args: >
-Dsonar.organization=my-organization # For SonarQube Cloud only
-Dsonar.projectKey=my-projectkey
-Dsonar.python.coverage.reportPaths=coverage.xml
-Dsonar.sources=lib/
-Dsonar.tests=tests/
-Dsonar.test.exclusions=tests/**
-Dsonar.verbose=true
```
You can also specify the URL where to retrieve the SonarScanner CLI from.
The specified URL overrides the default address: `https://binaries.sonarsource.com/Distribution/sonar-scanner-cli`.
This can be useful when the runner executing the action is self-hosted and has regulated or no access to the Internet:
```yaml
- uses: SonarSource/sonarqube-scan-action@<action version>
with:
scannerBinariesUrl: https://my.custom.binaries.url.com/Distribution/sonar-scanner-cli/
```
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
### Environment variables
- `SONAR_TOKEN` – **Required** this is the token used to authenticate access to SonarQube. You can read more about security tokens in the documentation of SonarQube [Server](https://docs.sonarsource.com/sonarqube-server/latest/user-guide/managing-tokens/) and [Cloud](https://docs.sonarsource.com/sonarqube-cloud/managing-your-account/managing-tokens/). You can set the `SONAR_TOKEN` environment variable in the "Secrets" settings page of your repository, or you can add them at the level of your GitHub organization (recommended).
- `SONAR_HOST_URL` – this tells the scanner where SonarQube Server is hosted. You can set the `SONAR_HOST_URL` environment variable in the "Variables" settings page of your repository, or you can add them at the level of your GitHub organization (recommended). Not needed for SonarQube Cloud.
- `SONAR_ROOT_CERT` – Holds an additional certificate (in PEM format) that is used to validate the certificate of SonarQube Server or of a secured proxy to SonarQube (Server or Cloud). You can set the `SONAR_ROOT_CERT` environment variable in the "Secrets" settings page of your repository, or you can add them at the level of your GitHub organization (recommended).
Here is an example of how you can pass a certificate (in PEM format) to the Scanner truststore:
```yaml
- uses: SonarSource/sonarqube-scan-action@<action version>
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }}
SONAR_ROOT_CERT: ${{ secrets.SONAR_ROOT_CERT }}
```
If your source code file names contain special characters that are not covered by the locale range of `en_US.UTF-8`, you can configure your desired locale like this:
```yaml
- uses: SonarSource/sonarqube-scan-action@<action version>
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }} # or https://sonarcloud.io
LC_ALL: "ru_RU.UTF-8"
```
## Alternatives for Java and .NET
This GitHub Action will not work for all technologies. If you are in one of the following situations, you should use the following alternatives:
* Your code is built with Maven. Read the documentation about our SonarScanner for Maven in SonarQube [Server](https://docs.sonarsource.com/sonarqube-server/latest/analyzing-source-code/scanners/sonarscanner-for-maven/) and [Cloud](https://docs.sonarsource.com/sonarqube-cloud/advanced-setup/ci-based-analysis/sonarscanner-for-maven/).
* Your code is built with Gradle. Read the documentation about our SonarScanner for Gradle in SonarQube [Server](https://docs.sonarsource.com/sonarqube-server/latest/analyzing-source-code/scanners/sonarscanner-for-gradle/) and [Cloud](https://docs.sonarsource.com/sonarqube-cloud/advanced-setup/ci-based-analysis/sonarscanner-for-gradle/).
* You want to analyze a .NET solution. Read the documentation about our SonarScanner for .NET in SonarQube [Server](https://docs.sonarsource.com/sonarqube-server/latest/analyzing-source-code/scanners/dotnet/introduction/) and [Cloud](https://docs.sonarsource.com/sonarqube-cloud/advanced-setup/ci-based-analysis/sonarscanner-for-dotnet/introduction/).
## Do not use this GitHub action if you are in the following situations
* You want to run the action on C, C++, or Objective-C projects on a 32-bits system - build wrappers support only 64-bits OS.
## Self-hosted runner or container
### Self-hosted runner or container
When running the action in a self-hosted runner or container, please ensure that the following programs are installed:
* **curl** or **wget**
* **unzip**
## Additional information
### Additional information
The `sonarqube-scan-action/install-build-wrapper` action installs `coreutils` if run on macOS.
## Have questions or feedback?
## Support & Community
To provide feedback (requesting a feature or reporting a bug) please post on the SonarSource Community Forum page for SonarQube [Server](https://community.sonarsource.com/tags/c/help/sq/github-actions) or [Cloud](https://community.sonarsource.com/tags/c/help/sc/9/github-actions).
To provide feedback (requesting a feature or reporting a bug) please post on the SonarSource Community Forum page for [SonarQube Server](https://community.sonarsource.com/tags/c/help/sq/github-actions) or [SonarQube Cloud](https://community.sonarsource.com/tags/c/help/sc/9/github-actions).
## License
### License
Container images built with this project include third-party materials.

View file

@ -10,50 +10,20 @@ 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
# to be kept in sync with sonar-scanner-version
default: 7.2.0.5079
default: 8.0.1.6346
scannerBinariesUrl:
description: URL to download the Sonar Scanner CLI binaries from
required: false
default: https://binaries.sonarsource.com/Distribution/sonar-scanner-cli
runs:
using: "composite"
steps:
- name: Sanity checks
run: ${GITHUB_ACTION_PATH}/scripts/sanity-checks.sh
shell: bash
env:
INPUT_PROJECTBASEDIR: ${{ inputs.projectBaseDir }}
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 }}
- 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
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

View file

@ -1,17 +1,18 @@
Contributing
============
# SonarQube Scan Action
## Contributing
If you would like to see a new feature, please create a new thread in the forum ["Suggest new features"](https://community.sonarsource.com/c/suggestions/features).
Please be aware that we are not actively looking for feature contributions. The truth is that it's extremely difficult for someone outside SonarSource to comply with our roadmap and expectations. Therefore, we typically only accept minor cosmetic changes and typo fixes.
## Submitting a pull request
### Submitting a pull request
With that in mind, if you would like to submit a code contribution, please create a pull request for this repository. Please explain your motives to contribute this change: what problem you are trying to fix, what improvement you are trying to make.
Make sure that you follow our [code style](https://github.com/SonarSource/sonar-developer-toolset#code-style) and all tests are passing (Travis build is executed for each pull request).
## Next steps
### Next steps
One of the members of our team will carefully review your pull request. You might be asked at this point for clarifications or your pull request might be rejected if we decide that it doesn't fit our roadmap and vision for the product.
If your contribution looks promising then either we will decide:
@ -24,3 +25,34 @@ or
Thank You!
The SonarSource Team
## Development
Both the main action and the secondary _install-build-wrapper_ action are [Javascript actions](https://docs.github.com/en/actions/tutorials/create-actions/create-a-javascript-action). They need to be packaged to work properly. We follow the official guidelines and rely on rollup for that.
### Requirements
Make sure you have node 20 & npm installed. We recommend using [nvm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm#using-a-node-version-manager-to-install-nodejs-and-npm) for that.
### Building & testing
You'll first need to install dependencies:
```sh
npm install
```
To use rollup to bundle the scripts, run the `build` command:
```sh
npm run build
```
> ⚠️ Since the action uses the code in the repository, it is necessary to commit the bundled code! ⚠️
To run the js unit tests, run the `test` command:
```sh
npm run test
```

27277
dist/exec-BTlTa8sL.js vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/exec-BTlTa8sL.js.map vendored Normal file

File diff suppressed because one or more lines are too long

2979
dist/index.js vendored Normal file

File diff suppressed because it is too large Load diff

1
dist/index.js.map vendored Normal file

File diff suppressed because one or more lines are too long

207
dist/install-build-wrapper.js vendored Normal file
View file

@ -0,0 +1,207 @@
import { f as execExports, e as coreExports } from './exec-BTlTa8sL.js';
import * as fs from 'fs';
import * as path from 'path';
import 'os';
import 'crypto';
import 'http';
import 'https';
import 'net';
import 'tls';
import 'events';
import 'assert';
import 'util';
import 'stream';
import 'buffer';
import 'querystring';
import 'stream/web';
import 'node:stream';
import 'node:util';
import 'node:events';
import 'worker_threads';
import 'perf_hooks';
import 'util/types';
import 'async_hooks';
import 'console';
import 'url';
import 'zlib';
import 'string_decoder';
import 'diagnostics_channel';
import 'child_process';
import 'timers';
/**
* Compute all names and paths related to the build wrapper
* based on the runner environment
*/
function getBuildWrapperInfo({
runnerOS,
runnerArch,
runnerTemp,
sonarHostUrl,
}) {
const { buildWrapperSuffix, buildWrapperName } = getSuffixAndName(
runnerOS,
runnerArch
);
const buildWrapperDir = `${runnerTemp}/build-wrapper-${buildWrapperSuffix}`;
const buildWrapperUrl = `${sonarHostUrl}/static/cpp/build-wrapper-${buildWrapperSuffix}.zip`;
const buildWrapperBin = `${buildWrapperDir}/${buildWrapperName}`;
return {
buildWrapperUrl,
buildWrapperDir,
buildWrapperBin,
};
}
function getSuffixAndName(runnerOS, runnerArch) {
if (
runnerArch !== "X64" &&
!(runnerArch === "ARM64" && (runnerOS === "macOS" || runnerOS === "Linux"))
) {
throw new Error(
`Architecture '${runnerArch}' is unsupported by build-wrapper`
);
}
switch (runnerOS) {
case "Windows":
return {
buildWrapperSuffix: "win-x86",
buildWrapperName: "build-wrapper-win-x86-64.exe",
};
case "Linux":
switch (runnerArch) {
case "X64":
return {
buildWrapperSuffix: "linux-x86",
buildWrapperName: "build-wrapper-linux-x86-64",
};
case "ARM64":
return {
buildWrapperSuffix: "linux-aarch64",
buildWrapperName: "build-wrapper-linux-aarch64",
};
}
break; // handled before the switch
case "macOS":
return {
buildWrapperSuffix: "macosx-x86",
buildWrapperName: "build-wrapper-macosx-x86",
};
default:
throw new Error(`Unsupported runner OS '${runnerOS}'`);
}
}
async function getRealPath(filePath, runnerOS) {
switch (runnerOS) {
case "Windows": {
const windowsResult = await execExports.getExecOutput("cygpath", [
"--absolute",
"--windows",
filePath,
]);
return windowsResult.stdout.trim();
}
case "Linux": {
const linuxResult = await execExports.getExecOutput("readlink", [
"-f",
filePath,
]);
return linuxResult.stdout.trim();
}
case "macOS": {
const macResult = await execExports.getExecOutput("greadlink", ["-f", filePath]);
return macResult.stdout.trim();
}
default:
return path.resolve(filePath);
}
}
async function installMacOSPackages() {
if (process.platform === "darwin") {
coreExports.info("Installing required packages for macOS");
await execExports.exec("brew", ["install", "coreutils"]);
}
}
/**
* These RUNNER_XX env variables come from GitHub by default.
* See https://docs.github.com/en/actions/reference/workflows-and-actions/variables#default-environment-variables
*
* If SONAR_HOST_URL is omitted, we assume sonarcloud.io
*/
function getEnvVariables() {
const sonarHostUrl = process.env.SONAR_HOST_URL
? process.env.SONAR_HOST_URL.replace(/\/$/, "")
: "https://sonarcloud.io";
return {
runnerOS: process.env.RUNNER_OS,
runnerArch: process.env.RUNNER_ARCH,
runnerTemp: process.env.RUNNER_TEMP,
sonarHostUrl,
};
}
async function downloadAndInstallBuildWrapper(downloadUrl, runnerEnv) {
const { runnerArch, runnerOS, runnerTemp } = runnerEnv;
const tmpZipPath = path.join(
runnerTemp,
`build-wrapper-${runnerOS}-${runnerArch}.zip`
);
coreExports.startGroup(`Download ${downloadUrl}`);
coreExports.info(`Downloading '${downloadUrl}'`);
if (!fs.existsSync(runnerTemp)) {
fs.mkdirSync(runnerTemp, { recursive: true });
}
await execExports.exec("curl", ["-sSLo", tmpZipPath, downloadUrl]);
coreExports.info("Decompressing");
await execExports.exec("unzip", ["-o", "-d", runnerTemp, tmpZipPath]);
coreExports.endGroup();
}
async function run() {
try {
await installMacOSPackages();
const envVariables = getEnvVariables();
const { buildWrapperBin, buildWrapperDir, buildWrapperUrl } =
getBuildWrapperInfo(envVariables);
await downloadAndInstallBuildWrapper(buildWrapperUrl, envVariables);
const buildWrapperBinDir = await getRealPath(
buildWrapperDir,
envVariables.runnerOS
);
coreExports.addPath(buildWrapperBinDir);
coreExports.info(`'${buildWrapperBinDir}' added to the path`);
const buildWrapperBinPath = await getRealPath(
buildWrapperBin,
envVariables.runnerOS
);
coreExports.setOutput("build-wrapper-binary", buildWrapperBinPath);
coreExports.info(`'build-wrapper-binary' output set to '${buildWrapperBinPath}'`);
} catch (error) {
coreExports.setFailed(error.message);
}
}
run();
//# sourceMappingURL=install-build-wrapper.js.map

1
dist/install-build-wrapper.js.map vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -1,4 +1,4 @@
name: 'Install Build Wrapper for C and C++'
name: "Install Build Wrapper for C and C++"
description: >
Download and install the Build Wrapper for C, C++, and Objective-C
projects analyzed with manual config.
@ -8,55 +8,6 @@ branding:
outputs:
build-wrapper-binary:
description: "Absolute path to Build Wrapper binary."
value: ${{ steps.setup-outputs.outputs.build-wrapper-binary }}
runs:
using: "composite"
steps:
# install packaged required for greadlink and sha256sum command on macOS
- name: Install required packages for macOS
if: runner.os == 'macOS'
shell: bash
run: brew install coreutils
- name: Set SONAR_HOST_URL to 'https://sonarcloud.io'
if: env.SONAR_HOST_URL == ''
shell: bash
run: |
echo "Setting SONAR_HOST_URL to 'https://sonarcloud.io'"
echo "SONAR_HOST_URL=https://sonarcloud.io" >> $GITHUB_ENV
- name: Configure paths
id: configure_paths
shell: bash
env:
OS: ${{ runner.os }}
ARCH: ${{ runner.arch }}
INSTALL_PATH: ${{ runner.temp }} # TODO: or .sonar, if RUNNER_TEMP creates problem with caching and self-hosted runners
run: ${GITHUB_ACTION_PATH}/../scripts/configure_paths.sh >> $GITHUB_OUTPUT
- name: Download and install Build Wrapper
shell: bash
env:
DOWNLOAD_URL: ${{ steps.configure_paths.outputs.build-wrapper-url }}
TMP_ZIP_PATH: ${{ runner.temp }}/build-wrapper-${{ inputs.configure_paths.sonar-scanner-version }}-${{ runner.os }}-${{ runner.arch }}.zip
INSTALL_PATH: ${{ runner.temp }} # TODO: or .sonar, if RUNNER_TEMP creates problem with caching and self-hosted runners
run: ${GITHUB_ACTION_PATH}/../scripts/download.sh
- name: Setup action outputs
id: setup-outputs
shell: bash
env:
BUILD_WRAPPER_DIR: ${{ steps.configure_paths.outputs.build-wrapper-dir }}
BUILD_WRAPPER_BIN: ${{ steps.configure_paths.outputs.build-wrapper-bin }}
run: |
source ${GITHUB_ACTION_PATH}/../scripts/utils.sh
BUILD_WRAPPER_BIN_DIR=$(realpath "${BUILD_WRAPPER_DIR}")
echo "${BUILD_WRAPPER_BIN_DIR}" >> $GITHUB_PATH
echo "'${BUILD_WRAPPER_BIN_DIR}' added to the path"
BUILD_WRAPPER_BIN=$(realpath "${BUILD_WRAPPER_BIN}")
echo "build-wrapper-binary=${BUILD_WRAPPER_BIN}" >> $GITHUB_OUTPUT
echo "'build-wrapper-binary' output set to '${BUILD_WRAPPER_BIN}'"
echo "::endgroup::"
using: node20
main: ../dist/install-build-wrapper.js

945
package-lock.json generated Normal file
View file

@ -0,0 +1,945 @@
{
"name": "sonarqube-scan-action",
"version": "6.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "sonarqube-scan-action",
"version": "6.0.0",
"license": "LGPL-3.0-only",
"dependencies": {
"@actions/core": "1.11.1",
"@actions/github": "6.0.1",
"@actions/tool-cache": "2.0.2",
"string-argv": "0.3.2"
},
"devDependencies": {
"@rollup/plugin-commonjs": "28.0.6",
"@rollup/plugin-node-resolve": "16.0.1",
"mock-fs": "5.5.0",
"rollup": "4.50.1"
}
},
"node_modules/@actions/core": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.11.1.tgz",
"integrity": "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==",
"license": "MIT",
"dependencies": {
"@actions/exec": "^1.1.1",
"@actions/http-client": "^2.0.1"
}
},
"node_modules/@actions/exec": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz",
"integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==",
"license": "MIT",
"dependencies": {
"@actions/io": "^1.0.1"
}
},
"node_modules/@actions/github": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/@actions/github/-/github-6.0.1.tgz",
"integrity": "sha512-xbZVcaqD4XnQAe35qSQqskb3SqIAfRyLBrHMd/8TuL7hJSz2QtbDwnNM8zWx4zO5l2fnGtseNE3MbEvD7BxVMw==",
"license": "MIT",
"dependencies": {
"@actions/http-client": "^2.2.0",
"@octokit/core": "^5.0.1",
"@octokit/plugin-paginate-rest": "^9.2.2",
"@octokit/plugin-rest-endpoint-methods": "^10.4.0",
"@octokit/request": "^8.4.1",
"@octokit/request-error": "^5.1.1",
"undici": "^5.28.5"
}
},
"node_modules/@actions/http-client": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz",
"integrity": "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==",
"license": "MIT",
"dependencies": {
"tunnel": "^0.0.6",
"undici": "^5.25.4"
}
},
"node_modules/@actions/io": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz",
"integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==",
"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==",
"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": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz",
"integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==",
"license": "MIT",
"engines": {
"node": ">=14"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"dev": true,
"license": "MIT"
},
"node_modules/@octokit/auth-token": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz",
"integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==",
"license": "MIT",
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/core": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.2.tgz",
"integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==",
"license": "MIT",
"dependencies": {
"@octokit/auth-token": "^4.0.0",
"@octokit/graphql": "^7.1.0",
"@octokit/request": "^8.4.1",
"@octokit/request-error": "^5.1.1",
"@octokit/types": "^13.0.0",
"before-after-hook": "^2.2.0",
"universal-user-agent": "^6.0.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/endpoint": {
"version": "9.0.6",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz",
"integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==",
"license": "MIT",
"dependencies": {
"@octokit/types": "^13.1.0",
"universal-user-agent": "^6.0.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/graphql": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz",
"integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==",
"license": "MIT",
"dependencies": {
"@octokit/request": "^8.4.1",
"@octokit/types": "^13.0.0",
"universal-user-agent": "^6.0.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/openapi-types": {
"version": "24.2.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
"integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
"license": "MIT"
},
"node_modules/@octokit/plugin-paginate-rest": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.2.tgz",
"integrity": "sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==",
"license": "MIT",
"dependencies": {
"@octokit/types": "^12.6.0"
},
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"@octokit/core": "5"
}
},
"node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": {
"version": "20.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
"integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==",
"license": "MIT"
},
"node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": {
"version": "12.6.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
"integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
"license": "MIT",
"dependencies": {
"@octokit/openapi-types": "^20.0.0"
}
},
"node_modules/@octokit/plugin-rest-endpoint-methods": {
"version": "10.4.1",
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.4.1.tgz",
"integrity": "sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg==",
"license": "MIT",
"dependencies": {
"@octokit/types": "^12.6.0"
},
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"@octokit/core": "5"
}
},
"node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/openapi-types": {
"version": "20.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
"integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==",
"license": "MIT"
},
"node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": {
"version": "12.6.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
"integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
"license": "MIT",
"dependencies": {
"@octokit/openapi-types": "^20.0.0"
}
},
"node_modules/@octokit/request": {
"version": "8.4.1",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz",
"integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==",
"license": "MIT",
"dependencies": {
"@octokit/endpoint": "^9.0.6",
"@octokit/request-error": "^5.1.1",
"@octokit/types": "^13.1.0",
"universal-user-agent": "^6.0.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/request-error": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz",
"integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==",
"license": "MIT",
"dependencies": {
"@octokit/types": "^13.1.0",
"deprecation": "^2.0.0",
"once": "^1.4.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/types": {
"version": "13.10.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
"integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
"license": "MIT",
"dependencies": {
"@octokit/openapi-types": "^24.2.0"
}
},
"node_modules/@rollup/plugin-commonjs": {
"version": "28.0.6",
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.6.tgz",
"integrity": "sha512-XSQB1K7FUU5QP+3lOQmVCE3I0FcbbNvmNT4VJSj93iUjayaARrTQeoRdiYQoftAJBLrR9t2agwAd3ekaTgHNlw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rollup/pluginutils": "^5.0.1",
"commondir": "^1.0.1",
"estree-walker": "^2.0.2",
"fdir": "^6.2.0",
"is-reference": "1.2.1",
"magic-string": "^0.30.3",
"picomatch": "^4.0.2"
},
"engines": {
"node": ">=16.0.0 || 14 >= 14.17"
},
"peerDependencies": {
"rollup": "^2.68.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/plugin-node-resolve": {
"version": "16.0.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.1.tgz",
"integrity": "sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rollup/pluginutils": "^5.0.1",
"@types/resolve": "1.20.2",
"deepmerge": "^4.2.2",
"is-module": "^1.0.0",
"resolve": "^1.22.1"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^2.78.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/pluginutils": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz",
"integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "^1.0.0",
"estree-walker": "^2.0.2",
"picomatch": "^4.0.2"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.50.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.1.tgz",
"integrity": "sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.50.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.1.tgz",
"integrity": "sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.50.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.1.tgz",
"integrity": "sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.50.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.1.tgz",
"integrity": "sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.50.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.1.tgz",
"integrity": "sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.50.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.1.tgz",
"integrity": "sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.50.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.1.tgz",
"integrity": "sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.50.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.1.tgz",
"integrity": "sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.50.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.1.tgz",
"integrity": "sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.50.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.1.tgz",
"integrity": "sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
"version": "4.50.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.50.1.tgz",
"integrity": "sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
"version": "4.50.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.1.tgz",
"integrity": "sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.50.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.1.tgz",
"integrity": "sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.50.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.1.tgz",
"integrity": "sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.50.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.1.tgz",
"integrity": "sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.50.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.1.tgz",
"integrity": "sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.50.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.1.tgz",
"integrity": "sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-openharmony-arm64": {
"version": "4.50.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.1.tgz",
"integrity": "sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.50.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.1.tgz",
"integrity": "sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.50.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.1.tgz",
"integrity": "sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.50.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.1.tgz",
"integrity": "sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/resolve": {
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
"dev": true,
"license": "MIT"
},
"node_modules/before-after-hook": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
"integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==",
"license": "Apache-2.0"
},
"node_modules/commondir": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
"integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
"dev": true,
"license": "MIT"
},
"node_modules/deepmerge": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/deprecation": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
"integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==",
"license": "ISC"
},
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"dev": true,
"license": "MIT"
},
"node_modules/fdir": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/is-core-module": {
"version": "2.16.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
"dev": true,
"license": "MIT",
"dependencies": {
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-module": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
"integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==",
"dev": true,
"license": "MIT"
},
"node_modules/is-reference": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
"integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "*"
}
},
"node_modules/magic-string": {
"version": "0.30.18",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz",
"integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
"node_modules/mock-fs": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-5.5.0.tgz",
"integrity": "sha512-d/P1M/RacgM3dB0sJ8rjeRNXxtapkPCUnMGmIN0ixJ16F/E4GUZCvWcSGfWGz8eaXYvn1s9baUwNjI4LOPEjiA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"license": "ISC",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true,
"license": "MIT"
},
"node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/resolve": {
"version": "1.22.10",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
"integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-core-module": "^2.16.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
"bin": {
"resolve": "bin/resolve"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/rollup": {
"version": "4.50.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.1.tgz",
"integrity": "sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "1.0.8"
},
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=18.0.0",
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.50.1",
"@rollup/rollup-android-arm64": "4.50.1",
"@rollup/rollup-darwin-arm64": "4.50.1",
"@rollup/rollup-darwin-x64": "4.50.1",
"@rollup/rollup-freebsd-arm64": "4.50.1",
"@rollup/rollup-freebsd-x64": "4.50.1",
"@rollup/rollup-linux-arm-gnueabihf": "4.50.1",
"@rollup/rollup-linux-arm-musleabihf": "4.50.1",
"@rollup/rollup-linux-arm64-gnu": "4.50.1",
"@rollup/rollup-linux-arm64-musl": "4.50.1",
"@rollup/rollup-linux-loongarch64-gnu": "4.50.1",
"@rollup/rollup-linux-ppc64-gnu": "4.50.1",
"@rollup/rollup-linux-riscv64-gnu": "4.50.1",
"@rollup/rollup-linux-riscv64-musl": "4.50.1",
"@rollup/rollup-linux-s390x-gnu": "4.50.1",
"@rollup/rollup-linux-x64-gnu": "4.50.1",
"@rollup/rollup-linux-x64-musl": "4.50.1",
"@rollup/rollup-openharmony-arm64": "4.50.1",
"@rollup/rollup-win32-arm64-msvc": "4.50.1",
"@rollup/rollup-win32-ia32-msvc": "4.50.1",
"@rollup/rollup-win32-x64-msvc": "4.50.1",
"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==",
"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",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/tunnel": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
"license": "MIT",
"engines": {
"node": ">=0.6.11 <=0.7.0 || >=0.7.3"
}
},
"node_modules/undici": {
"version": "5.29.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz",
"integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==",
"license": "MIT",
"dependencies": {
"@fastify/busboy": "^2.0.0"
},
"engines": {
"node": ">=14.0"
}
},
"node_modules/universal-user-agent": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
"integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==",
"license": "ISC"
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"license": "ISC"
}
}
}

24
package.json Normal file
View file

@ -0,0 +1,24 @@
{
"name": "sonarqube-scan-action",
"version": "6.0.0",
"description": "This SonarSource project, available as a GitHub Action, scans your projects with SonarQube [Server](https://www.sonarsource.com/products/sonarqube/) or [Cloud](https://www.sonarsource.com/products/sonarcloud/).",
"type": "module",
"main": "src/main/index.js",
"scripts": {
"build": "rollup --config rollup.config.js",
"test": "node --test"
},
"license": "LGPL-3.0-only",
"dependencies": {
"@actions/core": "1.11.1",
"@actions/github": "6.0.1",
"@actions/tool-cache": "2.0.2",
"string-argv": "0.3.2"
},
"devDependencies": {
"@rollup/plugin-commonjs": "28.0.6",
"@rollup/plugin-node-resolve": "16.0.1",
"mock-fs": "5.5.0",
"rollup": "4.50.1"
}
}

18
rollup.config.js Normal file
View file

@ -0,0 +1,18 @@
import commonjs from "@rollup/plugin-commonjs";
import { nodeResolve } from "@rollup/plugin-node-resolve";
const config = {
input: [
"src/main/index.js",
"src/install-build-wrapper/install-build-wrapper.js",
],
output: {
esModule: true,
dir: "dist",
format: "es",
sourcemap: true,
},
plugins: [commonjs(), nodeResolve({ preferBuiltins: true })],
};
export default config;

View file

@ -1,60 +0,0 @@
#!/usr/bin/env bash
set -eou pipefail
# See https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#default-environment-variables
#
# Script-specific variables required:
# - INPUT_SCANNERVERSION: e.g. 6.2.1.4610
# - INPUT_SCANNERBINARIESURL: e.g. https://github.com/me/my-repo/raw/refs/heads/main/binaries
if [[ "$RUNNER_OS" == "Linux" && "$RUNNER_ARCH" == "X64" ]]; then
FLAVOR="linux-x64"
elif [[ "$RUNNER_OS" == "Linux" && "$RUNNER_ARCH" == "ARM64" ]]; then
FLAVOR="linux-aarch64"
elif [[ "$RUNNER_OS" == "Windows" && "$RUNNER_ARCH" == "X64" ]]; then
FLAVOR="windows-x64"
elif [[ "$RUNNER_OS" == "macOS" && "$RUNNER_ARCH" == "X64" ]]; then
FLAVOR="macosx-x64"
elif [[ "$RUNNER_OS" == "macOS" && "$RUNNER_ARCH" == "ARM64" ]]; then
FLAVOR="macosx-aarch64"
else
echo "::error title=SonarScanner::$RUNNER_OS $RUNNER_ARCH not supported"
exit 1
fi
set -x
mkdir -p $RUNNER_TEMP/sonarscanner
cd $RUNNER_TEMP/sonarscanner
SCANNER_FILE_NAME="sonar-scanner-cli-$INPUT_SCANNERVERSION-$FLAVOR.zip"
SCANNER_URI="${INPUT_SCANNERBINARIESURL%/}/$SCANNER_FILE_NAME"
if command -v wget &> /dev/null; then
wget --no-verbose --user-agent=sonarqube-scan-action "$SCANNER_URI"
elif command -v curl &> /dev/null; then
curl --fail --silent --show-error --user-agent sonarqube-scan-action \
--location --output "$SCANNER_FILE_NAME" "$SCANNER_URI"
elif [ "$RUNNER_OS" == "Windows" ] && [ -t "C:\\msys64\\usr\\bin\\wget.exe" ]; then
"C:\\msys64\\usr\\bin\\wget.exe" --no-verbose --user-agent=sonarqube-scan-action "$SCANNER_URI"
elif [ "$RUNNER_OS" == "Windows" ] && [ -t "C:\\msys64\\usr\\bin\\curl.exe" ]; then
"C:\\msys64\\usr\\bin\\curl.exe" --fail --silent --show-error --user-agent sonarqube-scan-action \
--location --output "$SCANNER_FILE_NAME" "$SCANNER_URI"
else
echo "::error title=SonarScanner::Neither wget nor curl found on the machine"
exit 1
fi
unzip -q -o $SCANNER_FILE_NAME
SCANNER_UNZIP_FOLDER="sonar-scanner-$INPUT_SCANNERVERSION-$FLAVOR"
# Folder name should correspond to the directory cached by the actions/cache
SCANNER_LOCAL_FOLDER="$RUNNER_TEMP/sonar-scanner-cli-$INPUT_SCANNERVERSION-$RUNNER_OS-$RUNNER_ARCH"
if [ -d "$SCANNER_LOCAL_FOLDER" ]; then
echo "::warning title=SonarScanner::Cleaning existing scanner folder: $SCANNER_LOCAL_FOLDER"
rm -rf "$SCANNER_LOCAL_FOLDER"
fi
mv -f "$SCANNER_UNZIP_FOLDER" "$SCANNER_LOCAL_FOLDER"

View file

@ -1,91 +0,0 @@
#!/usr/bin/env bash
set -eo pipefail
if [[ "$RUNNER_OS" == "Windows" ]]; then
SCANNER_BIN="sonar-scanner.bat"
else
SCANNER_BIN="sonar-scanner"
fi
scanner_args=()
if [[ ${SONARCLOUD_URL} ]]; then
scanner_args+=("-Dsonar.scanner.sonarcloudUrl=${SONARCLOUD_URL}")
fi
if [[ "$RUNNER_DEBUG" == '1' ]]; then
scanner_args+=('--debug')
fi
if [[ -n "${INPUT_PROJECTBASEDIR}" ]]; then
scanner_args+=("-Dsonar.projectBaseDir=${INPUT_PROJECTBASEDIR}")
fi
# The SSL folder may exist on an uncleaned self-hosted runner
SONAR_SSL_FOLDER=~/.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'
KEYTOOL_MAIN_CLASS=sun.security.tools.keytool.Main
SONAR_SSL_TRUSTSTORE_FILE="$SONAR_SSL_FOLDER/truststore.p12"
SONAR_SSL_TRUSTSTORE_PASSWORD=changeit
if [ -f "$SONAR_SSL_TRUSTSTORE_FILE" ]; then
ALIAS_SONAR_IS_PRESENT=true
"$SONAR_SCANNER_JRE/bin/java" "$KEYTOOL_MAIN_CLASS" \
-storetype PKCS12 \
-keystore "$SONAR_SSL_TRUSTSTORE_FILE" \
-storepass "$SONAR_SSL_TRUSTSTORE_PASSWORD" \
-noprompt \
-trustcacerts \
-list -v -alias sonar > /dev/null 2>&1 || {
ALIAS_SONAR_IS_PRESENT=false
echo "Existing Scanner truststore $SONAR_SSL_TRUSTSTORE_FILE does not contain 'sonar' alias"
}
if [[ $ALIAS_SONAR_IS_PRESENT == "true" ]]; then
echo "Removing 'sonar' alias from already existing Scanner truststore: $SONAR_SSL_TRUSTSTORE_FILE"
"$SONAR_SCANNER_JRE/bin/java" "$KEYTOOL_MAIN_CLASS" \
-storetype PKCS12 \
-keystore "$SONAR_SSL_TRUSTSTORE_FILE" \
-storepass "$SONAR_SSL_TRUSTSTORE_PASSWORD" \
-noprompt \
-trustcacerts \
-delete \
-alias sonar
fi
fi
if [[ -n "${SONAR_ROOT_CERT}" ]]; then
echo "Adding SSL certificate to the Scanner truststore"
rm -f $RUNNER_TEMP/tmpcert.pem
echo "${SONAR_ROOT_CERT}" > $RUNNER_TEMP/tmpcert.pem
mkdir -p "$SONAR_SSL_FOLDER"
"$SONAR_SCANNER_JRE/bin/java" "$KEYTOOL_MAIN_CLASS" \
-storetype PKCS12 \
-keystore "$SONAR_SSL_TRUSTSTORE_FILE" \
-storepass "$SONAR_SSL_TRUSTSTORE_PASSWORD" \
-noprompt \
-trustcacerts \
-importcert \
-alias sonar \
-file "$RUNNER_TEMP/tmpcert.pem"
scanner_args+=("-Dsonar.scanner.truststorePassword=$SONAR_SSL_TRUSTSTORE_PASSWORD")
fi
# split input args correctly (passed through INPUT_ARGS env var to avoid execution of injected command)
args=()
if [[ -n "${INPUT_ARGS}" ]]; then
# the regex recognizes args with values in single or double quotes (without character escaping), and args without quotes as well
# more specifically, the following patterns: -Darg="value", -Darg='value', -Darg=value, "-Darg=value" and '-Darg=value'
IFS=$'\n'; args=($(echo ${INPUT_ARGS} | egrep -o '[^" '\'']+="[^"]*"|[^" '\'']+='\''[^'\'']*'\''|[^" '\'']+|"[^"]+"|'\''[^'\'']+'\'''))
fi
for arg in "${args[@]}"; do
scanner_args+=("$arg")
done
set -ux
$SCANNER_BIN ${scanner_args[@]+"${scanner_args[@]}"}

View file

@ -1,6 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
# run the sonar scanner cli
cmd=(${GITHUB_ACTION_PATH}/scripts/run-sonar-scanner-cli.sh "${INPUT_ARGS}")
"${cmd[@]}"

View file

@ -1,23 +0,0 @@
#!/usr/bin/env bash
set -eo pipefail
if [[ ! "${INPUT_SCANNERVERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "::error title=SonarScanner::Invalid scannerVersion format. Expected format: x.y.z.w (e.g., 7.1.0.4889)"
exit 1
fi
if [[ -z "${SONAR_TOKEN}" ]]; then
echo "::warning title=SonarScanner::Running this GitHub Action without SONAR_TOKEN is not recommended"
fi
if [[ -f "${INPUT_PROJECTBASEDIR%/}/pom.xml" ]]; then
echo "::warning title=SonarScanner::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."
fi
if [[ -f "${INPUT_PROJECTBASEDIR%/}/build.gradle" || -f "${INPUT_PROJECTBASEDIR%/}/build.gradle.kts" ]]; then
echo "::warning title=SonarScanner::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."
fi

View file

@ -1,11 +1,11 @@
sonar-scanner-version=7.2.0.5079
sonar-scanner-url-windows-x64=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-7.2.0.5079-windows-x64.zip
sonar-scanner-sha-windows-x64=71936f352206b63cb05ffbcd68e366e52d22916148cf4a2418789bc776f733ea
sonar-scanner-url-linux-x64=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-7.2.0.5079-linux-x64.zip
sonar-scanner-sha-linux-x64=da9f4e64a3d555f08ce38b5469ebd91fe2b311af473f7001a5ee5c1fd58b004b
sonar-scanner-url-linux-aarch64=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-7.2.0.5079-linux-aarch64.zip
sonar-scanner-sha-linux-aarch64=803ca725d463e95eeb7537515706367bb8e52bf05ac32174daf9773bdb36d1e2
sonar-scanner-url-macosx-x64=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-7.2.0.5079-macosx-x64.zip
sonar-scanner-sha-macosx-x64=7b9e92248ca740fff41503bfe5459c460bac43c501d80043cc4fbebb72dfc5fa
sonar-scanner-url-macosx-aarch64=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-7.2.0.5079-macosx-aarch64.zip
sonar-scanner-sha-macosx-aarch64=c8adb3fbfe5485c17de193a217be765b66cbc10d6540057655afa3c3b5be6f61
sonar-scanner-version=8.0.1.6346
sonar-scanner-url-windows-x64=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-8.0.1.6346-windows-x64.zip
sonar-scanner-sha-windows-x64=52b35b24be4ce5ec2e2933b32683db45db139581c46945546d9739b0c8866231
sonar-scanner-url-linux-x64=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-8.0.1.6346-linux-x64.zip
sonar-scanner-sha-linux-x64=4bd40bf8411ed104853e94a3746ec92bc92845fde2b27dbf5c33fb5cfa8ecbe9
sonar-scanner-url-linux-aarch64=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-8.0.1.6346-linux-aarch64.zip
sonar-scanner-sha-linux-aarch64=ae2b062ed6d640ab9014ab576042385d54c910857de952f5cb2592d2a2d7c8d8
sonar-scanner-url-macosx-x64=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-8.0.1.6346-macosx-x64.zip
sonar-scanner-sha-macosx-x64=aa9065347ba834ff6f3d461183eb40a67a321e6996206875fd257e8e7d5745b2
sonar-scanner-url-macosx-aarch64=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-8.0.1.6346-macosx-aarch64.zip
sonar-scanner-sha-macosx-aarch64=2d65d49c327ec8ca5ec7c6dc2af17749f5b43c596fd906501bba5a0b09edc5e2

View file

@ -0,0 +1,80 @@
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { getBuildWrapperInfo } from "../utils.js";
describe("getBuildWrapperInfo", () => {
const supportedPlatforms = [
{
platform: "Linux",
arch: "X64",
expectedSuffix: "linux-x86",
expectedName: "build-wrapper-linux-x86-64",
},
{
platform: "Linux",
arch: "ARM64",
expectedSuffix: "linux-aarch64",
expectedName: "build-wrapper-linux-aarch64",
},
{
platform: "Windows",
arch: "X64",
expectedSuffix: "win-x86",
expectedName: "build-wrapper-win-x86-64.exe",
},
{
platform: "macOS",
arch: "X64",
expectedSuffix: "macosx-x86",
expectedName: "build-wrapper-macosx-x86",
},
{
platform: "macOS",
arch: "ARM64",
expectedSuffix: "macosx-x86",
expectedName: "build-wrapper-macosx-x86",
},
];
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, expectedSuffix, expectedName }) => {
it(`returns ${expectedName} for ${platform} ${arch}`, () => {
const result = getBuildWrapperInfo({
runnerOS: platform,
runnerArch: arch,
runnerTemp: "/tmp",
sonarHostUrl: "https://sonarcloud.io"
});
assert.equal(result.buildWrapperUrl, `https://sonarcloud.io/static/cpp/build-wrapper-${expectedSuffix}.zip`);
assert.equal(result.buildWrapperDir, `/tmp/build-wrapper-${expectedSuffix}`);
assert.equal(result.buildWrapperBin, `/tmp/build-wrapper-${expectedSuffix}/${expectedName}`);
});
}
);
unsupportedPlatforms.forEach(({ platform, arch }) => {
it(`throws for unsupported platform ${platform} ${arch}`, () => {
assert.throws(
() => getBuildWrapperInfo({
runnerOS: platform,
runnerArch: arch,
runnerTemp: "/tmp",
sonarHostUrl: "https://sonarcloud.io"
}),
(error) => {
return error.message.includes('unsupported') || error.message.includes('Unsupported');
},
`should have thrown for ${platform} ${arch}`
);
});
});
});

View file

@ -0,0 +1,85 @@
import * as core from "@actions/core";
import * as exec from "@actions/exec";
import * as fs from "fs";
import * as path from "path";
import { getBuildWrapperInfo, getRealPath } from "./utils";
async function installMacOSPackages() {
if (process.platform === "darwin") {
core.info("Installing required packages for macOS");
await exec.exec("brew", ["install", "coreutils"]);
}
}
/**
* These RUNNER_XX env variables come from GitHub by default.
* See https://docs.github.com/en/actions/reference/workflows-and-actions/variables#default-environment-variables
*
* If SONAR_HOST_URL is omitted, we assume sonarcloud.io
*/
function getEnvVariables() {
const sonarHostUrl = process.env.SONAR_HOST_URL
? process.env.SONAR_HOST_URL.replace(/\/$/, "")
: "https://sonarcloud.io";
return {
runnerOS: process.env.RUNNER_OS,
runnerArch: process.env.RUNNER_ARCH,
runnerTemp: process.env.RUNNER_TEMP,
sonarHostUrl,
};
}
async function downloadAndInstallBuildWrapper(downloadUrl, runnerEnv) {
const { runnerArch, runnerOS, runnerTemp } = runnerEnv;
const tmpZipPath = path.join(
runnerTemp,
`build-wrapper-${runnerOS}-${runnerArch}.zip`
);
core.startGroup(`Download ${downloadUrl}`);
core.info(`Downloading '${downloadUrl}'`);
if (!fs.existsSync(runnerTemp)) {
fs.mkdirSync(runnerTemp, { recursive: true });
}
await exec.exec("curl", ["-sSLo", tmpZipPath, downloadUrl]);
core.info("Decompressing");
await exec.exec("unzip", ["-o", "-d", runnerTemp, tmpZipPath]);
core.endGroup();
}
async function run() {
try {
await installMacOSPackages();
const envVariables = getEnvVariables();
const { buildWrapperBin, buildWrapperDir, buildWrapperUrl } =
getBuildWrapperInfo(envVariables);
await downloadAndInstallBuildWrapper(buildWrapperUrl, envVariables);
const buildWrapperBinDir = await getRealPath(
buildWrapperDir,
envVariables.runnerOS
);
core.addPath(buildWrapperBinDir);
core.info(`'${buildWrapperBinDir}' added to the path`);
const buildWrapperBinPath = await getRealPath(
buildWrapperBin,
envVariables.runnerOS
);
core.setOutput("build-wrapper-binary", buildWrapperBinPath);
core.info(`'build-wrapper-binary' output set to '${buildWrapperBinPath}'`);
} catch (error) {
core.setFailed(error.message);
}
}
run();

View file

@ -0,0 +1,98 @@
import * as exec from "@actions/exec";
import * as path from "path";
/**
* Compute all names and paths related to the build wrapper
* based on the runner environment
*/
export function getBuildWrapperInfo({
runnerOS,
runnerArch,
runnerTemp,
sonarHostUrl,
}) {
const { buildWrapperSuffix, buildWrapperName } = getSuffixAndName(
runnerOS,
runnerArch
);
const buildWrapperDir = `${runnerTemp}/build-wrapper-${buildWrapperSuffix}`;
const buildWrapperUrl = `${sonarHostUrl}/static/cpp/build-wrapper-${buildWrapperSuffix}.zip`;
const buildWrapperBin = `${buildWrapperDir}/${buildWrapperName}`;
return {
buildWrapperUrl,
buildWrapperDir,
buildWrapperBin,
};
}
function getSuffixAndName(runnerOS, runnerArch) {
if (
runnerArch !== "X64" &&
!(runnerArch === "ARM64" && (runnerOS === "macOS" || runnerOS === "Linux"))
) {
throw new Error(
`Architecture '${runnerArch}' is unsupported by build-wrapper`
);
}
switch (runnerOS) {
case "Windows":
return {
buildWrapperSuffix: "win-x86",
buildWrapperName: "build-wrapper-win-x86-64.exe",
};
case "Linux":
switch (runnerArch) {
case "X64":
return {
buildWrapperSuffix: "linux-x86",
buildWrapperName: "build-wrapper-linux-x86-64",
};
case "ARM64":
return {
buildWrapperSuffix: "linux-aarch64",
buildWrapperName: "build-wrapper-linux-aarch64",
};
}
break; // handled before the switch
case "macOS":
return {
buildWrapperSuffix: "macosx-x86",
buildWrapperName: "build-wrapper-macosx-x86",
};
default:
throw new Error(`Unsupported runner OS '${runnerOS}'`);
}
}
export async function getRealPath(filePath, runnerOS) {
switch (runnerOS) {
case "Windows": {
const windowsResult = await exec.getExecOutput("cygpath", [
"--absolute",
"--windows",
filePath,
]);
return windowsResult.stdout.trim();
}
case "Linux": {
const linuxResult = await exec.getExecOutput("readlink", [
"-f",
filePath,
]);
return linuxResult.stdout.trim();
}
case "macOS": {
const macResult = await exec.getExecOutput("greadlink", ["-f", filePath]);
return macResult.stdout.trim();
}
default:
return path.resolve(filePath);
}
}

View file

@ -0,0 +1,7 @@
export function mockCore(overrides = {}) {
return {
setFailed: (msg) => console.error(msg),
warning: (msg) => console.log(msg),
...overrides,
};
}

View file

@ -0,0 +1,177 @@
import mockfs from "mock-fs";
import assert from "node:assert/strict";
import { describe, it, mock } from "node:test";
import {
checkGradleProject,
checkMavenProject,
checkSonarToken,
validateScannerVersion,
} from "../sanity-checks.js";
import { mockCore } from "./mocks.js";
describe("validateScannerVersion", () => {
const expected =
"Invalid scannerVersion format. Expected format: x.y.z.w (e.g., 7.1.0.4889)";
const validVersions = [undefined, "", "7.1.0.4889", "1.2.3.4"];
const invalidVersions = [
"wrong",
"4.2.",
"7.1.0",
"7.1.0.abc",
"7.1.0.4889.5",
"7.1",
"7",
"7.1.0.",
".7.1.0.4889",
"7..1.0.4889",
"7.1..0.4889",
"7.1.0..4889",
"a.b.c.d",
"7.1.0.4889-SNAPSHOT",
"v7.1.0.4889",
"7.1.0.4889.0.0",
"-7.1.0.4889",
"7.-1.0.4889",
"7.1.-0.4889",
"7.1.0.-4889",
"7.1.0.4889 ",
" 7.1.0.4889",
"7.1.0.4889\n",
"7,1,0,4889",
];
validVersions.forEach((version) => {
it(`accepts ${version}`, () => {
assert.equal(validateScannerVersion(version), undefined);
});
});
invalidVersions.forEach((version) =>
it(`throws for ${version}`, () => {
assert.throws(
() => validateScannerVersion(version),
{
message: expected,
},
`should have thrown for ${version}`
);
})
);
});
describe("checkSonarToken", () => {
it("calls core.warning when SONAR_TOKEN is not set", () => {
const warning = mock.fn();
checkSonarToken(mockCore({ warning }));
assert.equal(warning.mock.calls.length, 1);
assert.equal(
warning.mock.calls[0].arguments[0],
"Running this GitHub Action without SONAR_TOKEN is not recommended"
);
});
it("does not call core.warning when SONAR_TOKEN is set", () => {
const warning = mock.fn();
checkSonarToken(mockCore({ warning }), "test-token");
assert.equal(warning.mock.calls.length, 0);
});
});
describe("checkMavenProject", () => {
it("calls core.warning when pom.xml exists", async () => {
mockfs({ "/test/project/": { "pom.xml": "" } });
const warning = mock.fn();
checkMavenProject({ warning }, "/test/project");
assert.equal(warning.mock.calls.length, 1);
assert.equal(
warning.mock.calls[0].arguments[0],
"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."
);
mockfs.restore();
});
it("does not call core.warning when pom.xml does not exist", async () => {
mockfs({ "/test/project/": {} });
const warning = mock.fn();
checkMavenProject(mockCore({ warning }), "/test/project");
assert.equal(warning.mock.calls.length, 0);
mockfs.restore();
});
it("handles project base dir with trailing slash", async () => {
mockfs({ "/test/project/": { "pom.xml": "" } });
const warning = mock.fn();
checkMavenProject(mockCore({ warning }), "/test/project/");
assert.equal(warning.mock.calls.length, 1);
mockfs.restore();
});
});
describe("checkGradleProject", () => {
it("calls core.warning when build.gradle exists", async () => {
mockfs({ "/test/project/": { "build.gradle": "" } });
const warning = mock.fn();
checkGradleProject(mockCore({ warning }), "/test/project");
assert.equal(warning.mock.calls.length, 1);
assert.equal(
warning.mock.calls[0].arguments[0],
"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."
);
mockfs.restore();
});
it("calls core.warning when build.gradle.kts exists", async () => {
mockfs({ "/test/project/": { "build.gradle.kts": "" } });
const warning = mock.fn();
checkGradleProject(mockCore({ warning }), "/test/project");
assert.equal(warning.mock.calls.length, 1);
assert.equal(
warning.mock.calls[0].arguments[0],
"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."
);
mockfs.restore();
});
it("does not call core.warning when neither gradle file exists", async () => {
mockfs({ "/test/project/": {} });
const warning = mock.fn();
checkGradleProject(mockCore({ warning }), "/test/project");
assert.equal(warning.mock.calls.length, 0);
mockfs.restore();
});
it("handles project base dir with trailing slash", async () => {
mockfs({ "/test/project/": { "build.gradle": "" } });
const warning = mock.fn();
checkGradleProject(mockCore({ warning }), "/test/project/");
assert.equal(warning.mock.calls.length, 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"
);
});
});

75
src/main/index.js Normal file
View file

@ -0,0 +1,75 @@
import * as core from "@actions/core";
import { installSonarScanner } from "./install-sonar-scanner";
import { runSonarScanner } from "./run-sonar-scanner";
import {
checkGradleProject,
checkMavenProject,
checkSonarToken,
validateScannerVersion,
} from "./sanity-checks";
/**
* Inputs are defined in action.yml
*/
function getInputs() {
const args = core.getInput("args");
const projectBaseDir = core.getInput("projectBaseDir");
const scannerBinariesUrl = core.getInput("scannerBinariesUrl");
const scannerVersion = core.getInput("scannerVersion");
return { args, projectBaseDir, scannerBinariesUrl, scannerVersion };
}
/**
* These RUNNER env variables come from GitHub by default.
* See https://docs.github.com/en/actions/reference/workflows-and-actions/variables#default-environment-variables
*
* The others are optional env variables provided by the user of the action
*/
function getEnvVariables() {
return {
runnerDebug: process.env.RUNNER_DEBUG,
runnerOs: process.env.RUNNER_OS,
runnerTemp: process.env.RUNNER_TEMP,
sonarRootCert: process.env.SONAR_ROOT_CERT,
sonarcloudUrl: process.env.SONARCLOUD_URL,
sonarToken: process.env.SONAR_TOKEN,
};
}
function runSanityChecks(inputs) {
try {
const { projectBaseDir, scannerVersion, sonarToken } = inputs;
validateScannerVersion(scannerVersion);
checkSonarToken(core, sonarToken);
checkMavenProject(core, projectBaseDir);
checkGradleProject(core, projectBaseDir);
} catch (error) {
core.setFailed(`Sanity checks failed: ${error.message}`);
process.exit(1);
}
}
async function run() {
try {
const { args, projectBaseDir, scannerVersion, scannerBinariesUrl } =
getInputs();
const runnerEnv = getEnvVariables();
const { sonarToken } = runnerEnv;
runSanityChecks({ projectBaseDir, scannerVersion, sonarToken });
const scannerDir = await installSonarScanner({
scannerVersion,
scannerBinariesUrl,
});
await runSonarScanner(args, projectBaseDir, scannerDir, runnerEnv);
} catch (error) {
core.setFailed(`Action failed: ${error.message}`);
process.exit(1);
}
}
run();

View file

@ -0,0 +1,59 @@
import * as core from "@actions/core";
import * as tc from "@actions/tool-cache";
import * as os from "os";
import * as path from "path";
import {
getPlatformFlavor,
getScannerDownloadURL,
scannerDirName,
} from "./utils";
const TOOLNAME = "sonar-scanner-cli";
/**
* Download the Sonar Scanner CLI for the current environment and cache it.
*/
export async function installSonarScanner({
scannerVersion,
scannerBinariesUrl,
}) {
const flavor = getPlatformFlavor(os.platform(), os.arch());
// Check if tool is already cached
let toolDir = tc.find(TOOLNAME, scannerVersion, flavor);
if (!toolDir) {
core.info(
`Installing Sonar Scanner CLI ${scannerVersion} for ${flavor}...`
);
const downloadUrl = getScannerDownloadURL({
scannerBinariesUrl,
scannerVersion,
flavor,
});
core.info(`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);
core.info(`Sonar Scanner CLI cached to: ${toolDir}`);
} else {
core.info(`Using cached Sonar Scanner CLI from: ${toolDir}`);
}
// Add the bin directory to PATH
const binDir = path.join(toolDir, "bin");
core.addPath(binDir);
return toolDir;
}

View file

@ -0,0 +1,152 @@
import * as core from "@actions/core";
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";
const KEYTOOL_MAIN_CLASS = "sun.security.tools.keytool.Main";
const TRUSTSTORE_PASSWORD = "changeit"; // default password of the Java truststore!
export async function runSonarScanner(
inputArgs,
projectBaseDir,
scannerDir,
runnerEnv = {}
) {
const { runnerDebug, runnerOs, runnerTemp, sonarRootCert, sonarcloudUrl } =
runnerEnv;
const scannerBin =
runnerOs === "Windows" ? "sonar-scanner.bat" : "sonar-scanner";
const scannerArgs = [];
/**
* Not sanitization is needed when populating scannerArgs.
* @actions/exec will take care of sanitizing the args it receives.
*/
if (sonarcloudUrl) {
scannerArgs.push(`-Dsonar.scanner.sonarcloudUrl=${sonarcloudUrl}`);
}
if (runnerDebug === "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");
const truststoreFile = path.join(sslFolder, "truststore.p12");
const keytoolParams = {
scannerDir,
truststoreFile,
};
if (fs.existsSync(truststoreFile)) {
let aliasSonarIsPresent = true;
try {
await checkSonarAliasInTruststore(keytoolParams);
} catch (_) {
aliasSonarIsPresent = false;
core.info(
`Existing Scanner truststore ${truststoreFile} does not contain 'sonar' alias`
);
}
if (aliasSonarIsPresent) {
core.info(
`Removing 'sonar' alias from already existing Scanner truststore: ${truststoreFile}`
);
await deleteSonarAliasFromTruststore(keytoolParams);
}
}
if (sonarRootCert) {
core.info("Adding SSL certificate to the Scanner truststore");
const tempCertPath = path.join(runnerTemp, "tmpcert.pem");
try {
fs.unlinkSync(tempCertPath);
} catch (_) {
// File doesn't exist, ignore
}
fs.writeFileSync(tempCertPath, sonarRootCert);
fs.mkdirSync(sslFolder, { recursive: true });
await importCertificateToTruststore(keytoolParams, tempCertPath);
scannerArgs.push(
`-Dsonar.scanner.truststorePassword=${TRUSTSTORE_PASSWORD}`
);
}
if (inputArgs) {
/**
* No sanitization, but it is parsing a string into an array of arguments in a safe way (= no command execution),
* and with good enough support of quotes to support arguments containing spaces.
*/
const args = parseArgsStringToArgv(inputArgs);
scannerArgs.push(...args);
}
/**
* Arguments are sanitized by `exec`
*/
await exec.exec(scannerBin, scannerArgs);
}
/**
* 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'
*/
function executeKeytoolCommand({
scannerDir,
truststoreFile,
extraArgs,
options = {},
}) {
const baseArgs = [
KEYTOOL_MAIN_CLASS,
"-storetype",
"PKCS12",
"-keystore",
truststoreFile,
"-storepass",
TRUSTSTORE_PASSWORD,
"-noprompt",
"-trustcacerts",
...extraArgs,
];
return exec.exec(`${scannerDir}/jre/bin/java`, baseArgs, options);
}
function importCertificateToTruststore(keytoolParams, certPath) {
return executeKeytoolCommand({
...keytoolParams,
extraArgs: ["-importcert", "-alias", "sonar", "-file", certPath],
});
}
function checkSonarAliasInTruststore(keytoolParams) {
return executeKeytoolCommand({
...keytoolParams,
extraArgs: ["-list", "-v", "-alias", "sonar"],
options: { silent: true },
});
}
function deleteSonarAliasFromTruststore(keytoolParams) {
return executeKeytoolCommand({
...keytoolParams,
extraArgs: ["-delete", "-alias", "sonar"],
});
}

44
src/main/sanity-checks.js Normal file
View file

@ -0,0 +1,44 @@
import fs from "fs";
import { join } from "path";
export 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)"
);
}
}
export function checkSonarToken(core, sonarToken) {
if (!sonarToken) {
core.warning(
"Running this GitHub Action without SONAR_TOKEN is not recommended"
);
}
}
export 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."
);
}
}
export 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."
);
}
}

35
src/main/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}`;