Compare commits

..

No commits in common. "master" and "v3.1.0" have entirely different histories.

64 changed files with 476 additions and 34894 deletions

4
.cirrus.star Normal file
View file

@ -0,0 +1,4 @@
load("github.com/SonarSource/cirrus-modules@v3", "load_features")
def main(ctx):
return load_features(ctx)

37
.cirrus.yml Normal file
View file

@ -0,0 +1,37 @@
env:
CIRRUS_VAULT_URL: https://vault.sonar.build:8200
CIRRUS_VAULT_AUTH_PATH: jwt-cirrusci
CIRRUS_VAULT_ROLE: cirrusci-${CIRRUS_REPO_OWNER}-${CIRRUS_REPO_NAME}
# Mend scan global configuration
MEND_API_KEY: VAULT[development/kv/data/mend data.apikey]
# Staging image configuration
STAGING_IMAGE_NAME: sonarsource/sonarqube-scan-action
CURRENT_TAG: master
vm_instance_template: &VM_TEMPLATE
experimental: true # see https://github.com/cirruslabs/cirrus-ci-docs/issues/1051
image: docker-builder-v*
type: t2.small
region: eu-central-1
disk: 10
cpu: 4
memory: 16G
mend_task:
ec2_instance:
<<: *VM_TEMPLATE
# run only on master and long-term branches
only_if: $CIRRUS_USER_COLLABORATOR == 'true' && ($CIRRUS_BRANCH == "master" || $CIRRUS_BRANCH =~ "branch-.*")
setup_script:
- docker build --tag "${STAGING_IMAGE_NAME}:${CURRENT_TAG}" .
- apt-get remove -y unattended-upgrades
- apt-get update && apt-get install -y --no-install-recommends openjdk-17-jre
- curl -sSL https://unified-agent.s3.amazonaws.com/wss-unified-agent.jar -o wss-unified-agent.jar
- echo "docker.includes=${CURRENT_TAG}" >> .cirrus/wss-unified-agent.config
scan_script:
- echo "Scan the ${STAGING_IMAGE_NAME}:${CURRENT_TAG} image"
- java -jar wss-unified-agent.jar -c .cirrus/wss-unified-agent.config -apiKey $MEND_API_KEY

View file

@ -0,0 +1,4 @@
docker.projectNameFormat=repositoryNameAndTag
docker.scanImages=true
wss.url=https://saas-eu.whitesourcesoftware.com/agent
productName=GitHubAction/SonarQubeScanAction

2
.github/CODEOWNERS vendored
View file

@ -1 +1 @@
.github/* @sonarsource/orchestration-processing-squad
.github/CODEOWNERS @sonarsource/analysis-experience-squad

View file

@ -1,10 +1,3 @@
<!--
Only for standalone PRs without Jira issue in the PR title:
* Replace this comment with Epic ID to create a new Task in Jira
* Replace this comment with Issue ID to create a new Sub-Task in Jira
* Ignore or delete this note to create a new Task in Jira without a parent
-->
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. 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).
With that in mind, if you would like to submit a code contribution, make sure that you adhere to the following guidelines and all tests are passing:

View file

@ -1,15 +0,0 @@
services:
https-proxy:
image: nginx
ports:
- 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", "--insecure", "https://localhost:8080/health"]
interval: 10s
timeout: 5s
retries: 20
start_period: 2m

View file

@ -1,10 +0,0 @@
#!/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

@ -1,34 +0,0 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
include /etc/nginx/conf.d/*.conf;
server {
listen 8080 ssl;
ssl_certificate /etc/nginx/nginx.crt;
ssl_certificate_key /etc/nginx/nginx.key;
location /health {
add_header 'Content-Type' 'text/plain';
return 200 "healthy\n";
}
location ~ /clientRedirectToSonarBinaries/(.*) {
return 301 "https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/$1";
}
}
}

View file

@ -1,26 +0,0 @@
services:
sonarqube:
image: sonarqube:lts-community
ports:
- 9000:9000
healthcheck:
test: 'grep -Fq "SonarQube is operational" /opt/sonarqube/logs/sonar.log'
interval: 10s
timeout: 5s
retries: 20
start_period: 2m
https-proxy:
image: nginx
ports:
- 4443:4443
volumes:
- $GITHUB_WORKSPACE/.github/qa-sq-behind-ngix/nginx.conf:/etc/nginx/nginx.conf:ro
- $GITHUB_WORKSPACE/.github/qa-sq-behind-ngix/server.crt:/etc/nginx/server.crt:ro
- $GITHUB_WORKSPACE/.github/qa-sq-behind-ngix/server.key:/etc/nginx/server.key:ro
healthcheck:
test: ["CMD", "curl", "--fail", "localhost:8080/health"]
interval: 10s
timeout: 5s
retries: 20
start_period: 2m

View file

@ -1,43 +0,0 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
include /etc/nginx/conf.d/*.conf;
server {
listen 8080;
location /health {
add_header 'Content-Type' 'text/plain';
return 200 "healthy\n";
}
}
server {
listen 4443 ssl;
ssl_protocols TLSv1.1 TLSv1.2;
ssl_certificate /etc/nginx/server.crt;
ssl_certificate_key /etc/nginx/server.key;
location / {
proxy_pass http://sonarqube:9000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto https;
}
}
}

View file

@ -1,28 +0,0 @@
name: Pull Request Closed
on:
pull_request:
types: [closed]
jobs:
PullRequestClosed_job:
name: Pull Request Closed
runs-on: github-ubuntu-latest-s
permissions:
id-token: write
pull-requests: read
# For external PR, ticket should be moved manually
if: |
github.event.pull_request.head.repo.full_name == github.repository
steps:
- id: secrets
uses: SonarSource/vault-action-wrapper@v3
with:
secrets: |
development/kv/data/jira user | JIRA_USER;
development/kv/data/jira token | JIRA_TOKEN;
- uses: sonarsource/gh-action-lt-backlog/PullRequestClosed@v2
with:
github-token: ${{secrets.GITHUB_TOKEN}}
jira-user: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_USER }}
jira-token: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_TOKEN }}

View file

@ -1,29 +0,0 @@
name: Pull Request Created
on:
pull_request:
types: ["opened"]
jobs:
PullRequestCreated_job:
name: Pull Request Created
runs-on: github-ubuntu-latest-s
permissions:
id-token: write
# For external PR, ticket should be created manually
if: |
github.event.pull_request.head.repo.full_name == github.repository
steps:
- id: secrets
uses: SonarSource/vault-action-wrapper@v3
with:
secrets: |
development/github/token/{REPO_OWNER_NAME_DASH}-jira token | GITHUB_TOKEN;
development/kv/data/jira user | JIRA_USER;
development/kv/data/jira token | JIRA_TOKEN;
- uses: sonarsource/gh-action-lt-backlog/PullRequestCreated@v2
with:
github-token: ${{ fromJSON(steps.secrets.outputs.vault).GITHUB_TOKEN }}
jira-user: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_USER }}
jira-token: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_TOKEN }}
jira-project: SQSCANGHA

View file

@ -1,28 +0,0 @@
name: Request review
on:
pull_request:
types: ["review_requested"]
jobs:
RequestReview_job:
name: Request review
runs-on: github-ubuntu-latest-s
permissions:
id-token: write
# For external PR, ticket should be moved manually
if: |
github.event.pull_request.head.repo.full_name == github.repository
steps:
- id: secrets
uses: SonarSource/vault-action-wrapper@v3
with:
secrets: |
development/github/token/{REPO_OWNER_NAME_DASH}-jira token | GITHUB_TOKEN;
development/kv/data/jira user | JIRA_USER;
development/kv/data/jira token | JIRA_TOKEN;
- uses: sonarsource/gh-action-lt-backlog/RequestReview@v2
with:
github-token: ${{ fromJSON(steps.secrets.outputs.vault).GITHUB_TOKEN }}
jira-user: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_USER }}
jira-token: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_TOKEN }}

View file

@ -1,30 +0,0 @@
name: Submit Review
on:
pull_request_review:
types: [submitted]
jobs:
SubmitReview_job:
name: Submit Review
runs-on: github-ubuntu-latest-s
permissions:
id-token: write
pull-requests: read
# For external PR, ticket should be moved manually
if: |
github.event.pull_request.head.repo.full_name == github.repository
&& (github.event.review.state == 'changes_requested'
|| github.event.review.state == 'approved')
steps:
- id: secrets
uses: SonarSource/vault-action-wrapper@v3
with:
secrets: |
development/kv/data/jira user | JIRA_USER;
development/kv/data/jira token | JIRA_TOKEN;
- uses: sonarsource/gh-action-lt-backlog/SubmitReview@v2
with:
github-token: ${{secrets.GITHUB_TOKEN}}
jira-user: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_USER }}
jira-token: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_TOKEN }}

View file

@ -1,90 +0,0 @@
name: QA Deprecated C and C++ action
on:
push:
branches:
- master
pull_request:
types: [opened, synchronize, reopened]
jobs:
output-test:
name: Action outputs
strategy:
fail-fast: false
matrix:
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
# We can only check if the runner architecture matches the expected one
- name: check_runner_arch
shell: bash
run: |
echo "Runner architecture: ${{ runner.arch }}"
if [[ "${{ runner.arch }}" != "${{ matrix.arch }}" ]]; then
echo "##[error]Runner architecture does not match the expected one"
exit 1
fi
- uses: actions/checkout@v5
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Run SonarQube C/C++ action
id: run-action
uses: ./deprecated-c-cpp
env:
SONAR_HOST_URL: 'https://next.sonarqube.com/sonarqube/'
with:
cache-binaries: ${{ matrix.cache }}
- name: SONAR_HOST_URL is set
shell: bash
run: |
[[ $SONAR_HOST_URL == "https://next.sonarqube.com/sonarqube/" ]]
- name: sonar-scanner is installed and in PATH
run: |
sonar-scanner --help | grep "usage: sonar-scanner "
- name: sonar-scanner-binary output is correct
shell: bash
env:
BINARY: ${{ steps.run-action.outputs.sonar-scanner-binary }}
run: |
"$BINARY" --help | grep "usage: sonar-scanner "
# build-wrapper does not have --help or equivalent option.
# Pass to few arguments and ignore error code
- name: build-wrapper is installed and in PATH on Windows
if: runner.os == 'Windows'
shell: bash
run: |
(build-wrapper-win-x86-64.exe || true) | grep "build-wrapper, version "
- name: build-wrapper is installed and in PATH on Linux
if: runner.os == 'Linux'
shell: bash
run: |
(build-wrapper-linux-x86-64 || true) | grep "build-wrapper, version "
- name: build-wrapper is installed and in PATH on macOS
if: runner.os == 'macOs'
shell: bash
run: |
(build-wrapper-macosx-x86 || true) | grep "build-wrapper, version "
- name: build-wrapper-binary output is correct
shell: bash
env:
BINARY: ${{ steps.run-action.outputs.build-wrapper-binary }}
run: |
("$BINARY" || true) | grep "build-wrapper, version "

View file

@ -1,72 +0,0 @@
name: QA Install Build Wrapper action
on:
push:
branches:
- master
pull_request:
types: [opened, synchronize, reopened]
jobs:
output-test:
name: Action outputs
strategy:
fail-fast: false
matrix:
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
# We can only check if the runner architecture matches the expected one
- name: check_runner_arch
shell: bash
run: |
echo "Runner architecture: ${{ runner.arch }}"
if [[ "${{ runner.arch }}" != "${{ matrix.arch }}" ]]; then
echo "##[error]Runner architecture does not match the expected one"
exit 1
fi
- uses: actions/checkout@v5
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Run SonarQube C/C++ action
id: run-action
uses: ./install-build-wrapper
env:
SONAR_HOST_URL: 'https://next.sonarqube.com/sonarqube/'
# build-wrapper does not have --help or equivalent option.
# Pass to few arguments and ignore error code
- name: build-wrapper is installed and in PATH on Windows
if: runner.os == 'Windows'
shell: bash
run: |
(build-wrapper-win-x86-64.exe || true) | grep "build-wrapper, version "
- name: build-wrapper is installed and in PATH on Linux
if: runner.os == 'Linux'
shell: bash
run: |
(build-wrapper-linux-x86-64 || true) | grep "build-wrapper, version "
- name: build-wrapper is installed and in PATH on macOS
if: runner.os == 'macOs'
shell: bash
run: |
(build-wrapper-macosx-x86 || true) | grep "build-wrapper, version "
- name: build-wrapper-binary output is correct
shell: bash
env:
BINARY: ${{ steps.run-action.outputs.build-wrapper-binary }}
run: |
("$BINARY" || true) | grep "build-wrapper, version "

View file

@ -1,829 +0,0 @@
name: QA Main action
on:
push:
branches:
- master
pull_request:
types: [opened, synchronize, reopened]
jobs:
noInputsTest:
name: >
No inputs
strategy:
fail-fast: false
matrix:
os: [github-ubuntu-latest-s, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Run action without args
uses: ./
env:
SONAR_HOST_URL: http://not_actually_used
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
- name: Assert
run: |
./test/assertFileContains ./output.properties "sonar.projectBaseDir=."
argsInputTest:
name: >
'args' input
strategy:
fail-fast: false
matrix:
os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Run action with args
uses: ./
with:
args: -Dsonar.someArg=aValue -Dsonar.anotherArgWithSpaces="Another Value" -Dsonar.argWithSingleQuotes='Another Value'
env:
SONAR_HOST_URL: http://not_actually_used
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
- name: Assert
run: |
./test/assertFileContains ./output.properties "sonar.someArg=aValue"
./test/assertFileContains ./output.properties 'sonar.anotherArgWithSpaces="Another Value"'
./test/assertFileContains ./output.properties "sonar.argWithSingleQuotes='Another Value'"
argsInputInjectionTest:
name: >
'args' input with command injection will fail
strategy:
fail-fast: false
matrix:
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:
args: ${{ matrix.args }}
env:
SONAR_HOST_URL: http://not_actually_used
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
- name: Fail if action succeeded
if: steps.runTest.outcome == 'success'
run: exit 1
- name: Assert the scanner was not called
run: |
./test/assertFileDoesntExist ./output.properties
backtickCommandInjectionTest:
name: >
'args' input with backticks injection does not execute command
strategy:
fail-fast: false
matrix:
os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Run action with args
uses: ./
continue-on-error: true
with:
args: >
-Dsonar.arg1="refs/heads/branch: [workflows] Bump `actions/*`" -Dsonar.arg2="test `echo Command Injection`" -Dsonar.arg3="`id`" -Dsonar.arg4="test'; `echo injection`; echo '" -Dsonar.arg5=" `whoami` " -Dsonar.arg6="test\`echo injection\`test"
env:
SONAR_HOST_URL: http://not_actually_used
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
- name: Assert command in arg is not executed
run: |
./test/assertFileContains ./output.properties 'sonar.arg1="refs/heads/branch\\: \[workflows\] Bump `actions/\*`"'
./test/assertFileContains ./output.properties 'sonar.arg2="test `echo Command Injection`"'
./test/assertFileContains ./output.properties 'sonar.arg3="`id`"'
./test/assertFileContains ./output.properties "sonar.arg4=\"test'; \`echo injection\`; echo '\""
./test/assertFileContains ./output.properties 'sonar.arg5=" `whoami` "'
./test/assertFileContains ./output.properties 'sonar.arg6="test\\\\`echo injection\\\\`test"'
dollarSymbolCommandInjectionTest:
name: >
'args' input with dollar command injection does not execute command
strategy:
matrix:
os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Run action with args
uses: ./
continue-on-error: true
with:
args: -Dsonar.arg1="$(whoami)" -Dsonar.arg2="$GITHUB_TOKEN" -Dsonar.arg3="$(echo outer $(echo inner))" -Dsonar.arg4="value\$(whoami)end" -Dsonar.arg5="$(printf 'A%.0s' {1..10000})" -Dsonar.arg6='value"; $(whoami); echo "'
env:
SONAR_HOST_URL: http://not_actually_used
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
- name: Assert command in arg is not executed
run: |
./test/assertFileContains ./output.properties 'sonar.arg1="$(whoami)"'
./test/assertFileContains ./output.properties 'sonar.arg2="$GITHUB_TOKEN"'
./test/assertFileContains ./output.properties 'sonar.arg3="$(echo outer $(echo inner))"'
./test/assertFileContains ./output.properties 'sonar.arg4="value\\\\$(whoami)end"'
./test/assertFileContains ./output.properties 'sonar.arg5="$(printf '\''A%.0s'\'' {1..10000})"'
./test/assertFileContains ./output.properties 'sonar.arg6='\''value"; $(whoami); echo "'\'''
otherCommandInjectionVariantsTest:
name: >
'args' input with other command injection variants does not execute command
strategy:
matrix:
os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Run action with args
uses: ./
continue-on-error: true
with:
args: -Dsonar.arg1="test | base64" -Dsonar.arg2="value; whoami" -Dsonar.arg3="value && echo test" -Dsonar.arg4="value > /tmp/output.txt" -Dsonar.arg5="< /etc/passwd" -Dsonar.arg6="" -Dsonar.arg7="../../../*" -Dsonar.arg8="*.key" -Dsonar.arg9="test\u0027\u0060whoami\u0060"
env:
SONAR_HOST_URL: http://not_actually_used
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
- name: Assert command in arg is not executed
run: |
./test/assertFileContains ./output.properties 'sonar.arg1="test | base64"'
./test/assertFileContains ./output.properties 'sonar.arg2="value; whoami"'
./test/assertFileContains ./output.properties 'sonar.arg3="value && echo test"'
./test/assertFileContains ./output.properties 'sonar.arg4="value > /tmp/output.txt"'
./test/assertFileContains ./output.properties 'sonar.arg5="< /etc/passwd"'
./test/assertFileContains ./output.properties 'sonar.arg6=""'
./test/assertFileContains ./output.properties 'sonar.arg7="../../../\*"'
./test/assertFileContains ./output.properties 'sonar.arg8="\*.key"'
./test/assertFileContains ./output.properties 'sonar.arg9="test\\\\u0027\\\\u0060whoami\\\\u0060"'
projectBaseDirInputTest:
name: >
'projectBaseDir' input
strategy:
matrix:
os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- run: mkdir -p ./baseDir
- name: Run action with projectBaseDir
uses: ./
with:
args: -Dsonar.scanner.internal.dumpToFile=./output.properties
projectBaseDir: ./baseDir
env:
SONAR_HOST_URL: http://not_actually_used
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
- name: Assert
run: |
./test/assertFileContains ./output.properties "sonar.projectBaseDir=.*/baseDir"
scannerVersionTest:
name: >
'scannerVersion' input
runs-on: github-ubuntu-latest-s # assumes default RUNNER_ARCH for linux is X64
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Run action with scannerVersion
uses: ./
with:
scannerVersion: 6.1.0.4477
args: -Dsonar.scanner.internal.dumpToFile=./output.properties
env:
NO_CACHE: true # force install-sonar-scanner-cli.sh execution
SONAR_HOST_URL: http://not_actually_used
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
- name: Assert
run: |
# 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: github-ubuntu-latest-s # assumes default RUNNER_ARCH for linux is X64
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Run action with scannerBinariesUrl
id: runTest
uses: ./
continue-on-error: true
with:
scannerVersion: 6.2.1.4610
scannerBinariesUrl: https://invalid_uri/Distribution/sonar-scanner-cli
env:
NO_CACHE: true # force install-sonar-scanner-cli.sh execution
SONAR_HOST_URL: http://not_actually_used
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
- name: Fail if action succeeded
if: steps.runTest.outcome == 'success'
run: exit 1
- name: Assert Sonar Scanner CLI was not downloaded
run: |
./test/assertFileDoesntExist "$RUNNER_TEMP/sonarscanner/sonar-scanner-cli-6.2.1.4610-linux-x64.zip"
- name: Assert Sonar Scanner CLI was not executed
run: |
./test/assertFileDoesntExist ./output.properties
scannerBinariesUrlIsEscapedWithWget:
name: >
'scannerBinariesUrl' is escaped with wget so special chars are not injected in the download command
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Run action with scannerBinariesUrl
id: runTest
uses: ./
continue-on-error: true
with:
scannerBinariesUrl: "http://some_uri;touch file.txt;"
env:
NO_CACHE: true
SONAR_HOST_URL: http://not_actually_used
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output1.properties"}'
- name: Assert file.txt does not exist
run: |
./test/assertFileDoesntExist "$RUNNER_TEMP/sonarscanner/file.txt"
scannerBinariesUrlIsEscapedWithCurl:
name: >
'scannerBinariesUrl' is escaped with curl so special chars are not injected in the download command
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Remove wget
run: sudo apt-get remove -y wget
- name: Assert wget is not available
run: |
if command -v wget 2>&1 >/dev/null
then
exit 1
fi
- name: Run action with scannerBinariesUrl
id: runTest
uses: ./
continue-on-error: true
with:
scannerBinariesUrl: "http://some_uri http://another_uri'; touch file.txt;"
env:
NO_CACHE: true
SONAR_HOST_URL: http://not_actually_used
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output1.properties"}'
- name: Assert file.txt does not exist
run: |
./test/assertFileDoesntExist "$RUNNER_TEMP/sonarscanner/file.txt"
dontFailGradleTest:
name: >
Don't fail on Gradle project
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Run action on Gradle project
id: runTest
uses: ./
continue-on-error: true
env:
SONAR_HOST_URL: http://not_actually_used
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
with:
projectBaseDir: ./test/gradle-project
args: -Dsonar.scanner.internal.dumpToFile=./output.properties
- name: Assert
run: |
./test/assertFileExists ./output.properties
dontFailGradleKotlinTest:
name: >
Don't fail on Kotlin Gradle project
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Run action on Kotlin Gradle project
id: runTest
uses: ./
continue-on-error: true
env:
SONAR_HOST_URL: http://not_actually_used
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
with:
projectBaseDir: ./test/gradle-project
args: -Dsonar.scanner.internal.dumpToFile=./output.properties
- name: Assert
run: |
./test/assertFileExists ./output.properties
dontFailMavenTest:
name: >
Don't fail on Maven project
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Run action on Maven project
id: runTest
uses: ./
continue-on-error: true
env:
SONAR_HOST_URL: http://not_actually_used
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
with:
projectBaseDir: ./test/maven-project
args: -Dsonar.scanner.internal.dumpToFile=./output.properties
- name: Assert
run: |
./test/assertFileExists ./output.properties
runAnalysisTest:
runs-on: github-ubuntu-latest-s
services:
sonarqube:
image: sonarqube:lts-community
ports:
- 9000:9000
volumes:
- sonarqube_data:/opt/sonarqube/data
- sonarqube_logs:/opt/sonarqube/logs
- sonarqube_extensions:/opt/sonarqube/extensions
options: >-
--health-cmd "grep -Fq \"SonarQube is operational\" /opt/sonarqube/logs/sonar.log"
--health-interval 10s
--health-timeout 5s
--health-retries 10
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Run action on sample project
id: runTest
uses: ./
env:
SONAR_HOST_URL: http://localhost:9000
with:
args: -Dsonar.login=admin -Dsonar.password=admin
projectBaseDir: ./test/example-project
- name: Assert
run: |
./test/assertFileExists ./test/example-project/.scannerwork/report-task.txt
runnerDebugUsedTest:
name: >
'RUNNER_DEBUG' is used
strategy:
fail-fast: false
matrix:
os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Run action with debug mode
uses: ./
with:
args: -Dsonar.scanner.internal.dumpToFile=./output.properties
env:
RUNNER_DEBUG: 1
SONAR_HOST_URL: http://not_actually_used
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
- name: Assert
run: |
./test/assertFileContains ./output.properties "sonar.verbose=true"
runAnalysisWithCacheTest:
runs-on: github-ubuntu-latest-s
services:
sonarqube:
image: sonarqube:lts-community
ports:
- 9000:9000
volumes:
- sonarqube_data:/opt/sonarqube/data
- sonarqube_logs:/opt/sonarqube/logs
- sonarqube_extensions:/opt/sonarqube/extensions
options: >-
--health-cmd "grep -Fq \"SonarQube is operational\" /opt/sonarqube/logs/sonar.log"
--health-interval 10s
--health-timeout 5s
--health-retries 10
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: SonarQube Cache
uses: actions/cache@v4
with:
path: ${{ github.workspace }}/.sonar/cache
key: ${{ runner.os }}-${{ runner.arch }}-sonar
- name: Run action on sample project
id: runTest
uses: ./
env:
SONAR_HOST_URL: http://localhost:9000
SONAR_USER_HOME: ${{ github.workspace }}/.sonar
with:
args: -Dsonar.login=admin -Dsonar.password=admin
projectBaseDir: ./test/example-project
- name: Assert
run: |
./test/assertFileExists ./test/example-project/.scannerwork/report-task.txt
overrideSonarcloudUrlTest:
name: >
'SONARCLOUD_URL' is used
strategy:
fail-fast: false
matrix:
os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Run action with SONARCLOUD_URL
uses: ./
with:
args: -Dsonar.scanner.apiBaseUrl=api.mirror.sonarcloud.io -Dsonar.scanner.internal.dumpToFile=./output.properties
env:
SONARCLOUD_URL: mirror.sonarcloud.io
SONAR_TOKEN: FAKE_TOKEN
- name: Assert
run: |
./test/assertFileContains ./output.properties "sonar.host.url=mirror.sonarcloud.io"
./test/assertFileContains ./output.properties "sonar.scanner.sonarcloudUrl=mirror.sonarcloud.io"
curlPerformsRedirect:
name: >
curl performs redirect when scannerBinariesUrl returns 3xx
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Remove wget
run: sudo apt-get remove -y wget
- name: Assert wget is not available
run: |
if command -v wget 2>&1 >/dev/null
then
exit 1
fi
- name: 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
- name: Run action with scannerBinariesUrl
id: runTest
uses: ./
with:
scannerVersion: 6.2.1.4610
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: |
# 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: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Run action with SSL certificate
uses: ./
with:
args: -Dsonar.scanner.internal.dumpToFile=./output.properties
env:
SONAR_ROOT_CERT: |
-----BEGIN CERTIFICATE-----
MIIFtjCCA56gAwIBAgIULroxFuPWyNOiQtAVPS/XFFMXp6owDQYJKoZIhvcNAQEL
BQAwXDELMAkGA1UEBhMCQ0gxDzANBgNVBAgMBkdlbmV2YTEPMA0GA1UEBwwGR2Vu
ZXZhMRcwFQYDVQQKDA5Tb25hclNvdXJjZSBTQTESMBAGA1UEAwwJbG9jYWxob3N0
MB4XDTI0MDQxNjA4NDUyMVoXDTM0MDQxNDA4NDUyMVowXDELMAkGA1UEBhMCQ0gx
DzANBgNVBAgMBkdlbmV2YTEPMA0GA1UEBwwGR2VuZXZhMRcwFQYDVQQKDA5Tb25h
clNvdXJjZSBTQTESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEF
AAOCAg8AMIICCgKCAgEArRRQF25E5NCgXdoEBU2SWyAoyOWMGVT1Ioltnr3sJP6L
MjjfozK5YgaRn504291lwlG+k6tvzTSR9HB8q3ITa8AdnwMiL7jzbveYKWIlLQ7k
dHKXWbiaIjTaZCyfnWUlDFIuR7BHwOXVwyLrBQfhoyDVaaoyowQEsUro3okIR/kB
sqM+KH8bcdl06DMMppZ8Qy1DYvPodhnNRyOSSpfbIoodE1fju+5U0OKzvGIc9WpG
5pKIysaW3whOa/ieb02SXrgoiHnYPpmmGzm4u/Wn8jGwhYQJSQT10yjMacGHwmBE
q7FUr854cVd+eend056P6pwUukdNeVHCFjYRkmWCNzIxV+sS9PPtDs77/bLFIItr
nBMHVsId38tPoru/z1S1p2dzCX3Nq09aJFF/vH2u9Sg5aerHJ7xnRroR1jIrAZtc
jBkJHEiTlG+WaavP4j6oym+lvHvgHHL3Qwhh8emg0JiLYExVV7ma70aRDh8yoQtS
zAUDMVfhVPKd92MS+7DC2pv2KviUNKqbHDFadl01JN3t+17/gstUNSk1jpoUfUhK
BeUQxVEdVUy2p0HeD/TYpRvF2FEsWneq3+ZbnRp17I/uEQOck0LP2tkzAd4tmRgH
+95yyB8MgbAfvyKWkB4+3BhtdfoYDe1asqR6z43mejDHHqgBXn+u3UKjPypKfPEC
AwEAAaNwMG4wHwYDVR0jBBgwFoAUINXfg3fn6/RUenW3EobpMoP8wDQwCQYDVR0T
BAIwADALBgNVHQ8EBAMCBPAwFAYDVR0RBA0wC4IJbG9jYWxob3N0MB0GA1UdDgQW
BBRX4bsny+8GQcFpM10jtAfFxzNxzzANBgkqhkiG9w0BAQsFAAOCAgEAa+Myw6li
Fme95cPpINTite/9LXk+TlHHnXiV5Z+Um3NTLSllX3zPuRFiOE71OKFrWQPqH2N/
85l6h19G9xQsaqkkVFyQENkNzykZpJL/jU4+wgRtwcEDkaRGGURZacz3vfLTc1HX
tPDNv/JsZ5HE2d7cF5YhN4UahtxS2lvarrSujaOBpFZTT6PbEYX9EnwCdapORHOh
wKMc3OGGOiGWvRlVaWu/Huq2HvXXcK0pmaYWWKX3u21evthSYOu9U4Rk0z1y7m3/
CIYaIrvSbkzq2KKXMn7lr26bv2cthAQrPAjb2ILPUoyzKa3wEK3lkhanM6PN9CMH
y5KRTpqwV45Qr6BAVY1bP67pEkay2T31chIVKds6dkx9b2/bWpW9PWuymsbWX2vO
Q1MiaPkXKSTgCRwQUR0SNbPHw3X+VhrKKJB+beX8Bh2fcKw3jGGM8oHiA1hpdnbg
Y5fW7EupF5gabf2jNB1XJ4gowlpB3nTooKFgbcgsvi68MRdBno2TWUhsZ3zCVyaH
KFdDV0f78Fg7oL79K3kBL/iqr+jsb8sFHKIS4Dyyz2rDJrE0q0xAPes+Bu75R3/5
M/s2H7KuLqLdDYsCsMeMqOVuIcAyPp2MFWInYPyi0zY4fwKwm8f/Kv8Lzb+moxqI
Fct6d1S08JAosVnZcP2P7Yz+TbmDRtsqCgk=
-----END CERTIFICATE-----
SONAR_HOST_URL: http://not_actually_used
- name: Assert
run: |
./test/assertFileExists ~/.sonar/ssl/truststore.p12
analysisWithSslCertificate:
name: >
Analysis takes into account 'SONAR_ROOT_CERT'
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Generate server certificate
run: |
openssl req \
-newkey rsa:4096 \
-x509 \
-sha256 \
-addext "subjectAltName = DNS:localhost" \
-days 3650 \
-nodes \
-out server.crt \
-subj "/C=CH/ST=Geneva/L=Geneva/O=Server/OU=Dept" \
-keyout server.key
working-directory: .github/qa-sq-behind-ngix
- 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
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
{
echo 'SONAR_ROOT_CERT_VALID<<=========='
cat .github/qa-sq-behind-ngix/server.crt
echo ==========
} >> $GITHUB_ENV
- name: Run action with the correct SSL certificate
uses: ./
env:
SONAR_ROOT_CERT: ${{ env.SONAR_ROOT_CERT_VALID }}
SONAR_HOST_URL: https://localhost:4443
with:
args: -Dsonar.login=admin -Dsonar.password=admin
projectBaseDir: ./test/example-project
- name: Clear imported SSL certificates
run: |
rm -f ~/.sonar/ssl/truststore.p12
- name: Run action with an invalid SSL certificate
id: invalid_ssl_certificate
continue-on-error: true
uses: ./
env:
SONAR_ROOT_CERT: |
-----BEGIN CERTIFICATE-----
INVALID
-----END CERTIFICATE-----
SONAR_HOST_URL: https://localhost:4443
with:
args: -Dsonar.login=admin -Dsonar.password=admin
projectBaseDir: ./test/example-project
- name: Assert failure of previous step
if: steps.invalid_ssl_certificate.outcome == 'success'
run: exit 1
- name: Clear imported SSL certificates
run: |
rm -f ~/.sonar/ssl/truststore.p12
- name: Run action with the wrong SSL certificate
id: wrong_ssl_certificate
continue-on-error: true
uses: ./
env:
SONAR_ROOT_CERT: |
-----BEGIN CERTIFICATE-----
MIIFlTCCA32gAwIBAgIUXK4LyGUFe4ZVL93StPXCoJzmnLMwDQYJKoZIhvcNAQEL
BQAwTzELMAkGA1UEBhMCQ0gxDzANBgNVBAgMBkdlbmV2YTEPMA0GA1UEBwwGR2Vu
ZXZhMQ8wDQYDVQQKDAZTZXJ2ZXIxDTALBgNVBAsMBERlcHQwHhcNMjQxMTAxMDgx
MzM3WhcNMzQxMDMwMDgxMzM3WjBPMQswCQYDVQQGEwJDSDEPMA0GA1UECAwGR2Vu
ZXZhMQ8wDQYDVQQHDAZHZW5ldmExDzANBgNVBAoMBlNlcnZlcjENMAsGA1UECwwE
RGVwdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK5m0V6IFFykib77
nmlN7weS9q3D6YGEj+8hRNQViL9KduUoLjoKpONIihU5kfIg+5SkGygjHRkBvIp3
b0HQqhkwtGln3/FxxaSfGEguLHgzXR8JDQSyJ8UKIGOPCH93n1rUip5Ok1iExVup
HtkiVDRoCC9cRjZXbGOKrO6VBT4RvakpkaqCdXYikV244B5ElM7kdFdz8fso78Aq
xekb9dM0f21uUaDBKCIhRcxWeafp0CJIoejTq0+PF7qA2qIY5UHqWElWO5NsvQ8+
MqKkIdsOa1pYNuH/5eQ59k9KSE92ps1xTKweW000GfPqxx8IQ/e4aAd2SaMTKvN6
aac6piWBeJ7AssgWwkg/3rnZB5seQIrWjIUePmxJ4c0g0eL9cnVpYF0K/Dldle/G
wg0zi1g709rBI1TYj9xwrivxSwEQupz8OdKqOmgqrKHJJ/CCLl+JdFYjgwl3NWLH
wsU639H1bMXIJoQujg9U47e9fXbwiqdkMQzt7rPGkOBBaAkSctAReiXnWy+CbVEM
QFHDrnD5YUJRd5t/DUuWuqhR2QhfUvRClPUKoVqB/iOu2IumlgDEDA8jb1dxEW+W
iaYokQCS94OpxOJ8aeReSt9bghT0vc9ifCLWvuE1iBjujdK32ekKSY9DCZyBHXsG
J9N1nt1qd/k7QqWOkuPjr1JrTIMbAgMBAAGjaTBnMB0GA1UdDgQWBBQw4ESReEk+
AIxwjHRqPkESzMv1bTAfBgNVHSMEGDAWgBQw4ESReEk+AIxwjHRqPkESzMv1bTAP
BgNVHRMBAf8EBTADAQH/MBQGA1UdEQQNMAuCCWxvY2FsaG9zdDANBgkqhkiG9w0B
AQsFAAOCAgEAE8WefoZN23aOSe79ZN7zRBWP8DdPgFAqg5XUhfc9bCIVfJ4XMpEe
3lzRhgjwDm4naEs35QWOhPZH2vx8XrEKnZNI6vKO8JzaCsivgngk8bsWnvhwSXy5
eFdc99K+FOmOHevDmeiimoQnikffnSULRhQYzE2Qwyo9iky8703/+D3IKEC/8exC
rlyGMUV/Nqj+4M+57DiZ6OXeFuunfoFB7vmcDZygqDhKoHhVRyu8qN6PeK2fvUFK
EjeRtvA0GkdlOtLIF2g5yBTK2ykkt/oLUoAolfYUTKcoV2/FS0gVR5ovmEpKyBcP
H9hzr16a8dtrEqOf/oKHQSLwxn8afmS354HJ75sq9SujOtIWpHfyH5IgqtUpiBN/
bzvKs/QZjtGlqvquOTkdh9L4oxTXqG7zEStZyo/v9g5jf1Tq195b2DNFwVUZIcbb
u2d4CvAZ1yNr+8ax/kTwBSY8WU+mCtmvowFstdvsJXVXJKnUO6EZOdbg0GxTBVyE
zMsnPcnkOwV5TJIKKhonrgrwmPmQ9IOV9BrThVxujjjEbAdA6jM9PMiXzuDukldm
QBRwNbczGbdsHkMKHmQnrTqOyQyI4KCXF08kcOm4C1P+Whrvi0DXkqHnyKvBE0td
dciInBoeHwUs2eclz7gP7pMBJUlFUkKfQxwxGLIqZSXnlAFBfW6hHLI=
-----END CERTIFICATE-----
SONAR_HOST_URL: https://localhost:4443
with:
args: -Dsonar.login=admin -Dsonar.password=admin
projectBaseDir: ./test/example-project
- name: Assert failure of previous step
if: steps.wrong_ssl_certificate.outcome == 'success'
run: exit 1
updateTruststoreWhenPresent: # can happen in uncleaned self-hosted runners
name: >
truststore.p12 is updated when present
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Create SONAR_SSL_FOLDER with a file in it (not-truststore.p12)
run: |
SONAR_SSL_FOLDER=~/.sonar/ssl
mkdir -p "$SONAR_SSL_FOLDER"
touch "$SONAR_SSL_FOLDER/not-truststore.p12"
# emit SONAR_SSL_FOLDER to be able to read it in the next steps
echo "SONAR_SSL_FOLDER=$SONAR_SSL_FOLDER" >> $GITHUB_ENV
- name: Assert truststore.p12 does not file exists
run: |
[ ! -f "$SONAR_SSL_FOLDER/truststore.p12" ] || exit 1
- 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
SONAR_HOST_URL: http://not_actually_used
SONAR_ROOT_CERT: |
-----BEGIN CERTIFICATE-----
MIIFlTCCA32gAwIBAgIUXK4LyGUFe4ZVL93StPXCoJzmnLMwDQYJKoZIhvcNAQEL
BQAwTzELMAkGA1UEBhMCQ0gxDzANBgNVBAgMBkdlbmV2YTEPMA0GA1UEBwwGR2Vu
ZXZhMQ8wDQYDVQQKDAZTZXJ2ZXIxDTALBgNVBAsMBERlcHQwHhcNMjQxMTAxMDgx
MzM3WhcNMzQxMDMwMDgxMzM3WjBPMQswCQYDVQQGEwJDSDEPMA0GA1UECAwGR2Vu
ZXZhMQ8wDQYDVQQHDAZHZW5ldmExDzANBgNVBAoMBlNlcnZlcjENMAsGA1UECwwE
RGVwdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK5m0V6IFFykib77
nmlN7weS9q3D6YGEj+8hRNQViL9KduUoLjoKpONIihU5kfIg+5SkGygjHRkBvIp3
b0HQqhkwtGln3/FxxaSfGEguLHgzXR8JDQSyJ8UKIGOPCH93n1rUip5Ok1iExVup
HtkiVDRoCC9cRjZXbGOKrO6VBT4RvakpkaqCdXYikV244B5ElM7kdFdz8fso78Aq
xekb9dM0f21uUaDBKCIhRcxWeafp0CJIoejTq0+PF7qA2qIY5UHqWElWO5NsvQ8+
MqKkIdsOa1pYNuH/5eQ59k9KSE92ps1xTKweW000GfPqxx8IQ/e4aAd2SaMTKvN6
aac6piWBeJ7AssgWwkg/3rnZB5seQIrWjIUePmxJ4c0g0eL9cnVpYF0K/Dldle/G
wg0zi1g709rBI1TYj9xwrivxSwEQupz8OdKqOmgqrKHJJ/CCLl+JdFYjgwl3NWLH
wsU639H1bMXIJoQujg9U47e9fXbwiqdkMQzt7rPGkOBBaAkSctAReiXnWy+CbVEM
QFHDrnD5YUJRd5t/DUuWuqhR2QhfUvRClPUKoVqB/iOu2IumlgDEDA8jb1dxEW+W
iaYokQCS94OpxOJ8aeReSt9bghT0vc9ifCLWvuE1iBjujdK32ekKSY9DCZyBHXsG
J9N1nt1qd/k7QqWOkuPjr1JrTIMbAgMBAAGjaTBnMB0GA1UdDgQWBBQw4ESReEk+
AIxwjHRqPkESzMv1bTAfBgNVHSMEGDAWgBQw4ESReEk+AIxwjHRqPkESzMv1bTAP
BgNVHRMBAf8EBTADAQH/MBQGA1UdEQQNMAuCCWxvY2FsaG9zdDANBgkqhkiG9w0B
AQsFAAOCAgEAE8WefoZN23aOSe79ZN7zRBWP8DdPgFAqg5XUhfc9bCIVfJ4XMpEe
3lzRhgjwDm4naEs35QWOhPZH2vx8XrEKnZNI6vKO8JzaCsivgngk8bsWnvhwSXy5
eFdc99K+FOmOHevDmeiimoQnikffnSULRhQYzE2Qwyo9iky8703/+D3IKEC/8exC
rlyGMUV/Nqj+4M+57DiZ6OXeFuunfoFB7vmcDZygqDhKoHhVRyu8qN6PeK2fvUFK
EjeRtvA0GkdlOtLIF2g5yBTK2ykkt/oLUoAolfYUTKcoV2/FS0gVR5ovmEpKyBcP
H9hzr16a8dtrEqOf/oKHQSLwxn8afmS354HJ75sq9SujOtIWpHfyH5IgqtUpiBN/
bzvKs/QZjtGlqvquOTkdh9L4oxTXqG7zEStZyo/v9g5jf1Tq195b2DNFwVUZIcbb
u2d4CvAZ1yNr+8ax/kTwBSY8WU+mCtmvowFstdvsJXVXJKnUO6EZOdbg0GxTBVyE
zMsnPcnkOwV5TJIKKhonrgrwmPmQ9IOV9BrThVxujjjEbAdA6jM9PMiXzuDukldm
QBRwNbczGbdsHkMKHmQnrTqOyQyI4KCXF08kcOm4C1P+Whrvi0DXkqHnyKvBE0td
dciInBoeHwUs2eclz7gP7pMBJUlFUkKfQxwxGLIqZSXnlAFBfW6hHLI=
-----END CERTIFICATE-----
with:
args: -Dsonar.scanner.internal.dumpToFile=./output.properties
- name: Assert not-truststore.p12 file still exists
run: |
[ -f "$SONAR_SSL_FOLDER/not-truststore.p12" ] || exit 1
- name: Assert truststore.p12 file now exists and take note of modification time
run: |
[ -f "$SONAR_SSL_FOLDER/truststore.p12" ] || exit 1
# emit the modification time of the truststore.p12 file to be able to read it in the next steps
TRUSTSTORE_P12_MOD_TIME_T1=$(stat -c %Y "$SONAR_SSL_FOLDER/truststore.p12")
echo "TRUSTSTORE_P12_MOD_TIME_T1=$TRUSTSTORE_P12_MOD_TIME_T1" >> $GITHUB_ENV
- 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
SONAR_HOST_URL: http://not_actually_used
SONAR_ROOT_CERT: |
-----BEGIN CERTIFICATE-----
MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMC
Tk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYD
VQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG
9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4
MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xi
ZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2Zl
aWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5v
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LO
NoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHIS
KOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d
1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8
BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7n
bK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2Qar
Q4/67OZfHd7R+POBXhophSMv1ZOo
-----END CERTIFICATE-----
with:
args: -Dsonar.scanner.internal.dumpToFile=./output.properties
- name: Assert truststore.p12 still exists, but it has been updated, and take note of modification time
run: |
[ -f "$SONAR_SSL_FOLDER/truststore.p12" ] || exit 1
TRUSTSTORE_P12_MOD_TIME_T2=$(stat -c %Y "$SONAR_SSL_FOLDER/truststore.p12")
[ "$TRUSTSTORE_P12_MOD_TIME_T1" != "$TRUSTSTORE_P12_MOD_TIME_T2" ] || exit 1
# emit the modification time of the truststore.p12 file to be able to read it in the next steps
echo "TRUSTSTORE_P12_MOD_TIME_T2=$TRUSTSTORE_P12_MOD_TIME_T2" >> $GITHUB_ENV
- name: Remove sonar alias from truststore.p12
run: keytool -delete -alias sonar -keystore "$SONAR_SSL_FOLDER/truststore.p12" -storepass changeit
- 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
SONAR_HOST_URL: http://not_actually_used
SONAR_ROOT_CERT: |
-----BEGIN CERTIFICATE-----
MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMC
Tk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYD
VQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG
9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4
MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xi
ZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2Zl
aWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5v
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LO
NoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHIS
KOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d
1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8
BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7n
bK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2Qar
Q4/67OZfHd7R+POBXhophSMv1ZOo
-----END CERTIFICATE-----
with:
args: -Dsonar.scanner.internal.dumpToFile=./output.properties
- name: Assert truststore.p12 still exists, and it has been updated again
run: |
[ -f "$SONAR_SSL_FOLDER/truststore.p12" ] || exit 1
TRUSTSTORE_P12_MOD_TIME_T3=$(stat -c %Y "$SONAR_SSL_FOLDER/truststore.p12")
[ "$TRUSTSTORE_P12_MOD_TIME_T2" != "$TRUSTSTORE_P12_MOD_TIME_T3" ] || exit 1
scannerVersionValidationTest:
name: >
'scannerVersion' input validation
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Run action with invalid scannerVersion
id: invalid_version
uses: ./
continue-on-error: true
with:
scannerVersion: "7.1.0-SNAPSHOT"
args: -Dsonar.scanner.internal.dumpToFile=./output.properties
env:
NO_CACHE: true
SONAR_HOST_URL: http://not_actually_used
- name: Assert failure of previous step
if: steps.invalid_version.outcome == 'success'
run: |
echo "Action with invalid scannerVersion should have failed but succeeded"
exit 1

View file

@ -1,351 +0,0 @@
name: QA Scripts
on:
push:
branches:
- master
pull_request:
types: [opened, synchronize, reopened]
jobs:
create-install-dir-test:
name: create_install_path.sh
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Existing
shell: bash
env:
INSTALL_PATH: '.sonar'
run: |
echo "- Create dir"
mkdir -p "${INSTALL_PATH}"
echo "- Test script behavior"
./scripts/create_install_path.sh > output
grep -v "::error::" output
- name: Non-existing nested in current dir
shell: bash
env:
INSTALL_PATH: '.sonar'
run: |
./scripts/create_install_path.sh > output
grep -v "::error::" output
test -d "${INSTALL_PATH}"
- name: Nonexisting nested in home
shell: bash
env:
INSTALL_PATH: '~/third_party/.sonar'
run: |
./scripts/create_install_path.sh > output
grep -v "::error::" output
test -d "${INSTALL_PATH}"
- name: Empty install dir specified
shell: bash
env:
INSTALL_PATH: ''
run: |
(./scripts/create_install_path.sh || echo "=== Script failed ===") > output
grep "::error::Empty installation path specified" output
grep "=== Script failed ===" output
- name: No permission to create directory
shell: bash
env:
INSTALL_PATH: '/non_creatable'
run: |
(./scripts/create_install_path.sh || echo "=== Script failed ===") > output
grep "::error::Failed to create non-existing installation path '/non_creatable'" output
grep "=== Script failed ===" output
- name: Existing but not directory
shell: bash
env:
INSTALL_PATH: 'not_directory'
run: |
echo "- Create normal file"
echo "content" > "${INSTALL_PATH}"
echo "- Test script behavior"
(./scripts/create_install_path.sh || echo "=== Script failed ===") > output
grep "::error::Installation path 'not_directory' is not a directory" output
grep "=== Script failed ===" output
- name: Existing but not readable
shell: bash
env:
INSTALL_PATH: 'not_readable'
run: |
echo "- Create dir and make it not readable"
mkdir -p "${INSTALL_PATH}"
chmod -r "${INSTALL_PATH}"
echo "- Test script behavior"
(./scripts/create_install_path.sh || echo "=== Script failed ===") > output
grep "::error::Installation path 'not_readable' is not readable" output
grep "=== Script failed ===" output
- name: Existing but not writeable
shell: bash
env:
INSTALL_PATH: 'not_writeable'
run: |
echo "- Create dir and make it not writeable"
mkdir -p "${INSTALL_PATH}"
chmod -w "${INSTALL_PATH}"
echo "- Test script behavior"
(./scripts/create_install_path.sh || echo "=== Script failed ===") > output
grep "::error::Installation path 'not_writeable' is not writeable" output
grep "=== Script failed ===" output
setup-script-test:
name: configure_paths.sh
runs-on: github-ubuntu-latest-s
env:
INSTALL_PATH: 'install-directory'
SONAR_HOST_URL: 'http://sonar-host.com'
SONAR_SCANNER_VERSION: 'vX.Y.Z.MMMM'
SONAR_SCANNER_URL_WINDOWS_X64: 'https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-vX.Y.Z.MMMM-windows-x64.zip'
SONAR_SCANNER_SHA_WINDOWS_X64: 'DOWNLOAD-SHA-WINDOWS-X64'
SONAR_SCANNER_URL_LINUX_X64: 'https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-vX.Y.Z.MMMM-linux-x64.zip'
SONAR_SCANNER_SHA_LINUX_X64: 'DOWNLOAD-SHA-LINUX-X64'
SONAR_SCANNER_URL_LINUX_AARCH64: 'https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-vX.Y.Z.MMMM-linux-aarch64.zip'
SONAR_SCANNER_SHA_LINUX_AARCH64: 'DOWNLOAD-SHA-LINUX-AARCH64'
SONAR_SCANNER_URL_MACOSX_X64: 'https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-vX.Y.Z.MMMM-macosx-x64.zip'
SONAR_SCANNER_SHA_MACOSX_X64: 'DOWNLOAD-SHA-MACOSX-X64'
SONAR_SCANNER_URL_MACOSX_AARCH64: 'https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-vX.Y.Z.MMMM-macosx-aarch64.zip'
SONAR_SCANNER_SHA_MACOSX_AARCH64: 'DOWNLOAD-SHA-MACOSX-AARCH64'
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Windows
shell: bash
env:
OS: 'Windows'
ARCH: 'X64'
run: |
./scripts/configure_paths.sh > output
grep -v "::error::" output
echo "- Check sonar-scanner:"
grep "sonar-scanner-url=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-vX.Y.Z.MMMM-windows-x64.zip" output
grep "sonar-scanner-sha=DOWNLOAD-SHA-WINDOWS-X64" output
grep "sonar-scanner-dir=install-directory/sonar-scanner-vX.Y.Z.MMMM-windows-x64" output
grep "sonar-scanner-bin=install-directory/sonar-scanner-vX.Y.Z.MMMM-windows-x64/bin/sonar-scanner.bat" output
echo "- Check build-wrapper:"
grep "build-wrapper-url=http://sonar-host.com/static/cpp/build-wrapper-win-x86.zip" output
grep "build-wrapper-dir=install-directory/build-wrapper-win-x86" output
grep "build-wrapper-bin=install-directory/build-wrapper-win-x86/build-wrapper-win-x86-64.exe" output
- name: Linux X64
shell: bash
env:
OS: 'Linux'
ARCH: 'X64'
run: |
./scripts/configure_paths.sh > output
grep -v "::error::" output
echo "- Check sonar-scanner:"
grep "sonar-scanner-url=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-vX.Y.Z.MMMM-linux-x64.zip" output
grep "sonar-scanner-sha=DOWNLOAD-SHA-LINUX-X64" output
grep "sonar-scanner-dir=install-directory/sonar-scanner-vX.Y.Z.MMMM-linux-x64" output
grep "sonar-scanner-bin=install-directory/sonar-scanner-vX.Y.Z.MMMM-linux-x64/bin/sonar-scanner" output
echo "- Check build-wrapper:"
grep "build-wrapper-url=http://sonar-host.com/static/cpp/build-wrapper-linux-x86.zip" output
grep "build-wrapper-dir=install-directory/build-wrapper-linux-x86" output
grep "build-wrapper-bin=install-directory/build-wrapper-linux-x86/build-wrapper-linux-x86-64" output
- name: Linux ARM64
shell: bash
env:
OS: 'Linux'
ARCH: 'ARM64'
run: |
./scripts/configure_paths.sh > output
grep -v "::error::" output
echo "- Check sonar-scanner:"
grep "sonar-scanner-url=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-vX.Y.Z.MMMM-linux-aarch64.zip" output
grep "sonar-scanner-sha=DOWNLOAD-SHA-LINUX-AARCH64" output
grep "sonar-scanner-dir=install-directory/sonar-scanner-vX.Y.Z.MMMM-linux-aarch64" output
grep "sonar-scanner-bin=install-directory/sonar-scanner-vX.Y.Z.MMMM-linux-aarch64/bin/sonar-scanner" output
echo "- Check build-wrapper:"
grep "build-wrapper-url=http://sonar-host.com/static/cpp/build-wrapper-linux-aarch64.zip" output
grep "build-wrapper-dir=install-directory/build-wrapper-linux-aarch64" output
grep "build-wrapper-bin=install-directory/build-wrapper-linux-aarch64/build-wrapper-linux-aarch64" output
- name: macOSX_X64
shell: bash
env:
OS: 'macOS'
ARCH: 'X64'
run: |
./scripts/configure_paths.sh > output
grep -v "::error::" output
echo "- Check sonar-scanner:"
grep "sonar-scanner-url=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-vX.Y.Z.MMMM-macosx-x64.zip" output
grep "sonar-scanner-sha=DOWNLOAD-SHA-MACOSX-X64" output
grep "sonar-scanner-dir=install-directory/sonar-scanner-vX.Y.Z.MMMM-macosx-x64" output
grep "sonar-scanner-bin=install-directory/sonar-scanner-vX.Y.Z.MMMM-macosx-x64/bin/sonar-scanner" output
echo "- Check build-wrapper:"
grep "build-wrapper-url=http://sonar-host.com/static/cpp/build-wrapper-macosx-x86.zip" output
grep "build-wrapper-dir=install-directory/build-wrapper-macosx-x86" output
grep "build-wrapper-bin=install-directory/build-wrapper-macosx-x86/build-wrapper-macosx-x86" output
- name: macOSX_ARM64
shell: bash
env:
OS: 'macOS'
ARCH: 'ARM64'
run: |
./scripts/configure_paths.sh > output
grep -v "::error::" output
echo "- Check sonar-scanner:"
grep "sonar-scanner-url=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-vX.Y.Z.MMMM-macosx-aarch64.zip" output
grep "sonar-scanner-sha=DOWNLOAD-SHA-MACOSX-AARCH64" output
grep "sonar-scanner-dir=install-directory/sonar-scanner-vX.Y.Z.MMMM-macosx-aarch64" output
grep "sonar-scanner-bin=install-directory/sonar-scanner-vX.Y.Z.MMMM-macosx-aarch64/bin/sonar-scanner" output
echo "- Check build-wrapper:"
grep "build-wrapper-url=http://sonar-host.com/static/cpp/build-wrapper-macosx-x86.zip" output
grep "build-wrapper-dir=install-directory/build-wrapper-macosx-x86" output
grep "build-wrapper-bin=install-directory/build-wrapper-macosx-x86/build-wrapper-macosx-x86" output
- name: Unsupported OS
shell: bash
env:
OS: 'unsupportedOS'
ARCH: 'X64'
run: |
(./scripts/configure_paths.sh || echo "=== Script failed ===") > output
echo "- Check errors:"
grep "::error::Unsupported runner OS 'unsupportedOS'" output
grep "=== Script failed ===" output
- name: Unsupported architecture
shell: bash
env:
OS: 'Linux'
ARCH: 'X86'
run: |
(./scripts/configure_paths.sh || echo "=== Script failed ===") > output
echo "- Check errors:"
grep "::error::Architecture 'X86' is unsupported by build-wrapper" output
grep "=== Script failed ===" output
download-script-test:
name: download.sh
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Download test without validation
shell: bash
env:
INSTALL_PATH: 'install-directory-no-sha-validation'
DOWNLOAD_URL: 'https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.6.2.2472-linux.zip'
EXPECTED_SHA: 'incorrect-sha-not-validated'
TMP_ZIP_PATH: ${{ runner.temp }}/sonar-scanner.zip
run: |
./scripts/download.sh > output
test -f "$TMP_ZIP_PATH"
grep -v "::error::" output
- name: Download test with validation
shell: bash
env:
INSTALL_PATH: 'install-directory-sha-validation'
DOWNLOAD_URL: 'https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.6.2.2472-linux.zip'
EXPECTED_SHA: '9411331814c1d002bd65d37758b872918b7602e7cf3ca5b83a3e19a729b2be05'
TMP_ZIP_PATH: ${{ runner.temp }}/sonar-scanner.zip
run: |
./scripts/download.sh -v > output
test -f "$TMP_ZIP_PATH"
grep -v "::error::" output
- name: Incorrect install dir
shell: bash
env:
INSTALL_PATH: ''
run: |
(./scripts/download.sh || echo "=== Script failed ===") > output
grep "::error::Failed to create" output
grep "=== Script failed ===" output
- name: Incorrect download url
shell: bash
env:
INSTALL_PATH: 'install-directory-incorrect-url'
DOWNLOAD_URL: 'incorrect-url'
run: |
(./scripts/download.sh || echo "=== Script failed ===") > output
grep "::error::Failed to download 'incorrect-url'" output
grep "=== Script failed ===" output
- name: Incorrect SHA256
shell: bash
env:
INSTALL_PATH: 'install-directory-incorrect-sha'
DOWNLOAD_URL: 'https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.6.2.2472-linux.zip'
EXPECTED_SHA: 'incorrect-sha256'
TMP_ZIP_PATH: ${{ runner.temp }}/sonar-scanner.zip
run: |
(./scripts/download.sh -v || echo "=== Script failed ===") > output
grep "::error::Checking sha256 failed" output
grep "=== Script failed ===" output
- name: Mismatching SHA256
shell: bash
env:
INSTALL_PATH: 'install-directory-mismtaching-sha'
DOWNLOAD_URL: 'https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.6.2.2472-linux.zip'
EXPECTED_SHA: '3e121d85a4adb1f30b917d5f3eb897966b59e02c3d6d313a78dcd964193dc963'
TMP_ZIP_PATH: ${{ runner.temp }}/sonar-scanner.zip
run: |
(./scripts/download.sh -v || echo "=== Script failed ===") > output
grep "::error::Checking sha256 failed" output
grep "=== Script failed ===" output
fetch-latest-version-test:
name: fetch_latest_version.sh
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Test script
shell: bash
run: |
./scripts/fetch_latest_version.sh > output
echo "- Check sonar-scanner version:"
grep "sonar-scanner-version=" output
SONAR_SCANNER_VERSION=$(cat output | cut -d= -f 2)
test ! -z "${SONAR_SCANNER_VERSION}"
echo "- Check windows sonar-scanner URLs:"
grep "sonar-scanner-url-windows-x64=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-windows-x64.zip" output
grep -e "^sonar-scanner-sha-windows-x64=[0-9A-Fa-f]\+$" output
echo "- Check linux sonar-scanner URLs:"
grep "sonar-scanner-url-linux-x64=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux-x64.zip" output
grep -e "^sonar-scanner-sha-linux-x64=[0-9A-Fa-f]\+$" output
grep "sonar-scanner-url-linux-aarch64=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux-aarch64.zip" output
grep -e "^sonar-scanner-sha-linux-aarch64=[0-9A-Fa-f]\+$" output
echo "- Check macosx sonar-scanner URLs:"
grep "sonar-scanner-url-linux-x64=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux-x64.zip" output
grep -e "^sonar-scanner-sha-linux-x64=[0-9A-Fa-f]\+$" output
grep "sonar-scanner-url-linux-aarch64=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux-aarch64.zip" output
grep -e "^sonar-scanner-sha-linux-aarch64=[0-9A-Fa-f]\+$" output

258
.github/workflows/qa.yml vendored Normal file
View file

@ -0,0 +1,258 @@
name: QA
on:
push:
branches:
- master
pull_request:
types: [opened, synchronize, reopened]
jobs:
noInputsTest:
name: >
No inputs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Run action with args
uses: ./
env:
SONAR_HOST_URL: http://not_actually_used
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
- name: Assert
run: |
./test/assertFileContains ./output.properties "sonar.projectBaseDir=."
argsInputTest:
name: >
'args' input
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Run action with args
uses: ./
with:
args: -Dsonar.someArg=aValue
env:
SONAR_HOST_URL: http://not_actually_used
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
- name: Assert
run: |
./test/assertFileContains ./output.properties "sonar.someArg=aValue"
projectBaseDirInputTest:
name: >
'projectBaseDir' input
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- run: mkdir -p ./baseDir
- name: Run action with projectBaseDir
uses: ./
with:
projectBaseDir: ./baseDir
env:
SONAR_HOST_URL: http://not_actually_used
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
- name: Assert
run: |
./test/assertFileContains ./output.properties "sonar.projectBaseDir=.*/baseDir"
dontFailGradleTest:
name: >
Don't fail on Gradle project
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Run action on Gradle project
id: runTest
uses: ./
continue-on-error: true
env:
SONAR_HOST_URL: http://not_actually_used
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
with:
projectBaseDir: ./test/gradle-project
- name: Assert
run: |
./test/assertFileExists ./output.properties
dontFailGradleKotlinTest:
name: >
Don't fail on Kotlin Gradle project
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Run action on Kotlin Gradle project
id: runTest
uses: ./
continue-on-error: true
env:
SONAR_HOST_URL: http://not_actually_used
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
with:
projectBaseDir: ./test/gradle-project
- name: Assert
run: |
./test/assertFileExists ./output.properties
dontFailMavenTest:
name: >
Don't fail on Maven project
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Run action on Maven project
id: runTest
uses: ./
continue-on-error: true
env:
SONAR_HOST_URL: http://not_actually_used
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
with:
projectBaseDir: ./test/maven-project
- name: Assert
run: |
./test/assertFileExists ./output.properties
runAnalysisTest:
runs-on: ubuntu-latest
services:
sonarqube:
image: sonarqube:lts-community
ports:
- 9000:9000
volumes:
- sonarqube_data:/opt/sonarqube/data
- sonarqube_logs:/opt/sonarqube/logs
- sonarqube_extensions:/opt/sonarqube/extensions
options: >-
--health-cmd "grep -Fq \"SonarQube is operational\" /opt/sonarqube/logs/sonar.log"
--health-interval 10s
--health-timeout 5s
--health-retries 10
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Run action on sample project
id: runTest
uses: ./
env:
SONAR_HOST_URL: http://sonarqube:9000
with:
args: -Dsonar.login=admin -Dsonar.password=admin
projectBaseDir: ./test/example-project
- name: Assert
run: |
./test/assertFileExists ./test/example-project/.scannerwork/report-task.txt
runnerDebugUsedTest:
name: >
'RUNNER_DEBUG' is used
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Run action with debug mode
uses: ./
env:
RUNNER_DEBUG: 1
SONAR_HOST_URL: http://not_actually_used
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
- name: Assert
run: |
./test/assertFileContains ./output.properties "sonar.verbose=true"
runAnalysisWithCacheTest:
runs-on: ubuntu-latest
services:
sonarqube:
image: sonarqube:lts-community
ports:
- 9000:9000
volumes:
- sonarqube_data:/opt/sonarqube/data
- sonarqube_logs:/opt/sonarqube/logs
- sonarqube_extensions:/opt/sonarqube/extensions
options: >-
--health-cmd "grep -Fq \"SonarQube is operational\" /opt/sonarqube/logs/sonar.log"
--health-interval 10s
--health-timeout 5s
--health-retries 10
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: SonarQube Cache
uses: actions/cache@v4
with:
path: ${{ github.workspace }}/.sonar/cache
key: ${{ runner.os }}-sonar
- name: Run action on sample project
id: runTest
uses: ./
env:
SONAR_HOST_URL: http://sonarqube:9000
SONAR_USER_HOME: ${{ github.workspace }}/.sonar
with:
args: -Dsonar.login=admin -Dsonar.password=admin
projectBaseDir: ./test/example-project
- name: Assert
run: |
./test/assertFileExists ./test/example-project/.scannerwork/report-task.txt
useSslCertificate:
name: >
'SONAR_ROOT_CERT' is converted to truststore
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Run action with SSL certificate
uses: ./
env:
SONAR_ROOT_CERT: |
-----BEGIN CERTIFICATE-----
MIIFtjCCA56gAwIBAgIULroxFuPWyNOiQtAVPS/XFFMXp6owDQYJKoZIhvcNAQEL
BQAwXDELMAkGA1UEBhMCQ0gxDzANBgNVBAgMBkdlbmV2YTEPMA0GA1UEBwwGR2Vu
ZXZhMRcwFQYDVQQKDA5Tb25hclNvdXJjZSBTQTESMBAGA1UEAwwJbG9jYWxob3N0
MB4XDTI0MDQxNjA4NDUyMVoXDTM0MDQxNDA4NDUyMVowXDELMAkGA1UEBhMCQ0gx
DzANBgNVBAgMBkdlbmV2YTEPMA0GA1UEBwwGR2VuZXZhMRcwFQYDVQQKDA5Tb25h
clNvdXJjZSBTQTESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEF
AAOCAg8AMIICCgKCAgEArRRQF25E5NCgXdoEBU2SWyAoyOWMGVT1Ioltnr3sJP6L
MjjfozK5YgaRn504291lwlG+k6tvzTSR9HB8q3ITa8AdnwMiL7jzbveYKWIlLQ7k
dHKXWbiaIjTaZCyfnWUlDFIuR7BHwOXVwyLrBQfhoyDVaaoyowQEsUro3okIR/kB
sqM+KH8bcdl06DMMppZ8Qy1DYvPodhnNRyOSSpfbIoodE1fju+5U0OKzvGIc9WpG
5pKIysaW3whOa/ieb02SXrgoiHnYPpmmGzm4u/Wn8jGwhYQJSQT10yjMacGHwmBE
q7FUr854cVd+eend056P6pwUukdNeVHCFjYRkmWCNzIxV+sS9PPtDs77/bLFIItr
nBMHVsId38tPoru/z1S1p2dzCX3Nq09aJFF/vH2u9Sg5aerHJ7xnRroR1jIrAZtc
jBkJHEiTlG+WaavP4j6oym+lvHvgHHL3Qwhh8emg0JiLYExVV7ma70aRDh8yoQtS
zAUDMVfhVPKd92MS+7DC2pv2KviUNKqbHDFadl01JN3t+17/gstUNSk1jpoUfUhK
BeUQxVEdVUy2p0HeD/TYpRvF2FEsWneq3+ZbnRp17I/uEQOck0LP2tkzAd4tmRgH
+95yyB8MgbAfvyKWkB4+3BhtdfoYDe1asqR6z43mejDHHqgBXn+u3UKjPypKfPEC
AwEAAaNwMG4wHwYDVR0jBBgwFoAUINXfg3fn6/RUenW3EobpMoP8wDQwCQYDVR0T
BAIwADALBgNVHQ8EBAMCBPAwFAYDVR0RBA0wC4IJbG9jYWxob3N0MB0GA1UdDgQW
BBRX4bsny+8GQcFpM10jtAfFxzNxzzANBgkqhkiG9w0BAQsFAAOCAgEAa+Myw6li
Fme95cPpINTite/9LXk+TlHHnXiV5Z+Um3NTLSllX3zPuRFiOE71OKFrWQPqH2N/
85l6h19G9xQsaqkkVFyQENkNzykZpJL/jU4+wgRtwcEDkaRGGURZacz3vfLTc1HX
tPDNv/JsZ5HE2d7cF5YhN4UahtxS2lvarrSujaOBpFZTT6PbEYX9EnwCdapORHOh
wKMc3OGGOiGWvRlVaWu/Huq2HvXXcK0pmaYWWKX3u21evthSYOu9U4Rk0z1y7m3/
CIYaIrvSbkzq2KKXMn7lr26bv2cthAQrPAjb2ILPUoyzKa3wEK3lkhanM6PN9CMH
y5KRTpqwV45Qr6BAVY1bP67pEkay2T31chIVKds6dkx9b2/bWpW9PWuymsbWX2vO
Q1MiaPkXKSTgCRwQUR0SNbPHw3X+VhrKKJB+beX8Bh2fcKw3jGGM8oHiA1hpdnbg
Y5fW7EupF5gabf2jNB1XJ4gowlpB3nTooKFgbcgsvi68MRdBno2TWUhsZ3zCVyaH
KFdDV0f78Fg7oL79K3kBL/iqr+jsb8sFHKIS4Dyyz2rDJrE0q0xAPes+Bu75R3/5
M/s2H7KuLqLdDYsCsMeMqOVuIcAyPp2MFWInYPyi0zY4fwKwm8f/Kv8Lzb+moxqI
Fct6d1S08JAosVnZcP2P7Yz+TbmDRtsqCgk=
-----END CERTIFICATE-----
SONAR_HOST_URL: http://not_actually_used
SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
- name: Assert
run: |
./test/assertFileContains ./output.properties "sonar.scanner.truststorePassword=changeit"

View file

@ -1,25 +0,0 @@
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,16 +7,16 @@ on:
jobs:
generate:
runs-on: github-ubuntu-latest-s
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v4
- name: Parse semver
uses: madhead/semver-utils@36d1e0ed361bd7b4b77665de8093092eaeabe6ba # v4.3.0
uses: madhead/semver-utils@v4
id: version
with:
version: ${{ github.ref_name }}

View file

@ -1,83 +0,0 @@
name: sonar-scanner version check
on:
workflow_dispatch:
schedule:
- cron: '15 10 * * *'
jobs:
check-version:
name: Check for sonar-scanner version update
runs-on: github-ubuntu-latest-s
outputs:
should_update: ${{ steps.version-check.outputs.should_update }}
new-version: ${{ steps.latest-version.outputs.sonar-scanner-version }}
steps:
- run: sudo apt install -y jq
- uses: actions/checkout@v5
with:
ref: master
fetch-depth: 0
- name: "Fetch currently used sonar-scanner version"
id: tagged-version
shell: bash
run: cat sonar-scanner-version >> $GITHUB_OUTPUT
- name: "Fetch latest sonar-scanner version"
id: latest-version
shell: bash
run: |
./scripts/fetch_latest_version.sh > sonar-scanner-version
cat sonar-scanner-version >> $GITHUB_OUTPUT
- name: "Determine if update is needed"
id: version-check
shell: bash
run: |
if [[ "${{ steps.tagged-version.outputs.sonar-scanner-version }}" != "${{ steps.latest-version.outputs.sonar-scanner-version }}" ]]; then
echo "should_update=true" >> $GITHUB_OUTPUT
else
echo "should_update=false" >> $GITHUB_OUTPUT
fi
update-version:
name: Prepare pull request for sonar-scanner version update
needs: check-version
runs-on: github-ubuntu-latest-s
permissions:
contents: write
pull-requests: write
if: needs.check-version.outputs.should_update == 'true'
steps:
- uses: actions/checkout@v5
with:
ref: master
persist-credentials: true
fetch-depth: 0
- run: sudo snap install yq
- name: "Update default version"
shell: bash
env:
NEW_VERSION: ${{ needs.check-version.outputs.new-version }}
run: |
yq -i '.inputs.scannerVersion.default = strenv(NEW_VERSION)' action.yml
./scripts/fetch_latest_version.sh > sonar-scanner-version
- name: "Create Pull Request for version update"
shell: bash
env:
UPDATE_BRANCH: update-to-sonar-scanner-${{ needs.check-version.outputs.new-version }}
TITLE: "Update SonarScanner CLI to ${{ needs.check-version.outputs.new-version }}"
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git config --global user.name "SonarTech"
git config --global user.email "sonartech@sonarsource.com"
git checkout -b ${UPDATE_BRANCH}
git add sonar-scanner-version
git add action.yml
git commit -m "${TITLE}"
git push --force-with-lease origin ${UPDATE_BRANCH}
gh pr list
if [[ $(gh pr list -H "${UPDATE_BRANCH}" | grep "${UPDATE_BRANCH}" | wc -l) -eq 0 ]]; then
gh pr create -B master -H ${UPDATE_BRANCH} --title "${TITLE}" --body "Automatic update of the sonar-scanner version value. Be sure to trigger the QA workflow by closing and reopening this PR (see https://github.com/orgs/community/discussions/65321)."
fi

3
.gitignore vendored
View file

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

21
Dockerfile Normal file
View file

@ -0,0 +1,21 @@
FROM sonarsource/sonar-scanner-cli:11.1
LABEL version="3.1.0" \
repository="https://github.com/sonarsource/sonarqube-scan-action" \
homepage="https://github.com/sonarsource/sonarqube-scan-action" \
maintainer="SonarSource" \
com.github.actions.name="SonarQube Scan" \
com.github.actions.description="Scan your code with SonarQube to detect Bugs, Vulnerabilities and Code Smells in up to 27 programming languages!" \
com.github.actions.icon="check" \
com.github.actions.color="green"
# GitHub actions should be run under ROOT
# https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#docker-container-filesystem
USER 0
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
COPY cleanup.sh /cleanup.sh
RUN chmod +x /cleanup.sh
ENTRYPOINT ["/entrypoint.sh"]

466
README.md
View file

@ -1,42 +1,33 @@
# 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)
# Scan your code with SonarQube [![QA](https://github.com/SonarSource/sonarqube-scan-action/actions/workflows/qa.yml/badge.svg)](https://github.com/SonarSource/sonarqube-scan-action/actions/workflows/qa.yml)
<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>
This SonarSource project, available as a GitHub Action, scans your projects with SonarQube, and helps developers produce
[Clean Code](https://www.sonarsource.com/solutions/clean-code/?utm_medium=referral&utm_source=github&utm_campaign=clean-code&utm_content=sonarqube-scan-action).
<img src="./images/SonarQube-72px.png">
## 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.**
[SonarQube](https://www.sonarsource.com/products/sonarqube/) is a widely used static analysis solution for continuous code quality and security inspection.
It helps developers identify and fix issues in their code that could lead to bugs, vulnerabilities, or decreased development velocity.
SonarQube supports the most popular programming languages, including Java, JavaScript, TypeScript, C#, Python, C, C++, and [many more](https://www.sonarsource.com/knowledge/languages/).
## Requirements
### 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/).
To run an analysis on your code, you first need to set up your project on SonarQube. Your SonarQube instance must be accessible from GitHub, and you will need an access token to run the analysis (more information below under **Environment variables**).
## Quick Start
Read more information on how to analyze your code [here](https://docs.sonarqube.org/latest/analysis/github-integration/).
### 1. Prerequisites:
## Usage
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.
Project metadata, including the location of the sources to be analyzed, must be declared in the file `sonar-project.properties` in the base directory:
For more information, see [Key Requirements](#key-requirements).
```properties
sonar.projectKey=<replace with the key generated when setting up the project on SonarQube>
# relative paths to source directories. More details and properties are described
# at https://docs.sonarqube.org/latest/project-administration/narrowing-the-focus/
sonar.sources=.
```
### 2. Required variables:
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.
`SONAR_TOKEN` : The authentication token required to access the SonarQube instance. This is a mandatory secret for all use cases.
`SONAR_HOST_URL` : The URL of the SonarQube Server. This is required for self-hosted SonarQube Server but not needed for SonarQube Cloud.
For more information, see [Configuration](#configuration).
### 3. Quick Start Workflow Example (for SonarQube Cloud)
Create or update your CI pipeline to run the scan action:
The workflow YAML file will usually look something like this:
```yaml
on:
@ -60,407 +51,92 @@ 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 or sha1> # Ex: v4.1.0 or sha1, See the latest version at https://github.com/marketplace/actions/official-sonarqube-scan
uses: sonarsource/sonarqube-scan-action@<action version> # Ex: v2.1.0, 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 }}
SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }}
```
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
uses: sonarsource/sonarqube-scan-action@<action version> # Ex: v2.1.0, 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 }}
LC_ALL: "ru_RU.UTF-8"
```
#### 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:
If your SonarQube server uses a self-signed certificate, you can pass a root certificate (in PEM format) to the Java certificate store:
```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>
env:
SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }}
- name: Run Build Wrapper
run: |
# Here goes your compilation wrapped with Build Wrapper
# For more information, see https://docs.sonarsource.com/sonarqube-server/latest/analyzing-source-code/languages/c-family/prerequisites/#using-buildwrapper
# 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>
uses: sonarsource/sonarqube-scan-action@<action version> # Ex: v2.1.0, 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 }}
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"
```
If you are using SonarQube Server 10.5 or earlier, use `sonar.cfamily.build-wrapper-output` instead of `sonar.cfamily.compile-commands` in the `args` property of the last step, as Build Wrapper does not generate a `compile_commands.json` file before SonarQube Server 10.6.
It should look like this:
You can change the analysis base directory by using the optional input `projectBaseDir` like this:
```yaml
with:
args: >
--define "sonar.cfamily.build-wrapper-output=${{ env.BUILD_WRAPPER_OUT_DIR }}"
- name: SonarQube Scan
uses: sonarsource/sonarqube-scan-action@<action version> # Ex: v2.1.0, See the latest version at https://github.com/marketplace/actions/official-sonarqube-scan
with:
projectBaseDir: app/src
```
See also [example configurations of C++ projects for SonarQube Server](https://github.com/search?q=org%3Asonarsource-cfamily-examples+gh-actions-sq&type=repositories).
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:
## Advanced Settings
```yaml
- name: SonarQube Scan
uses: sonarsource/sonarqube-scan-action@<action version> # Ex: v2.1.0, See the latest version at https://github.com/marketplace/actions/official-sonarqube-scan
with:
projectBaseDir: app/src
args: >
-Dsonar.python.coverage.reportPaths=coverage.xml
-Dsonar.tests=tests/
-Dsonar.verbose=true
```
### Self-hosted runner or container
More information about possible analysis parameters can be found in [the documentation](https://redirect.sonarsource.com/doc/analysis-parameters.html).
When running the action in a self-hosted runner or container, please ensure that the following programs are installed:
### Environment variables
* **curl** or **wget**
* **unzip**
- `SONAR_TOKEN` **Required** this is the token used to authenticate access to SonarQube. You can read more about security tokens [here](https://docs.sonarqube.org/latest/user-guide/user-token/). 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` **Required** this tells the scanner where SonarQube 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).
- `SONAR_ROOT_CERT` Holds an additional root certificate (in PEM format) that is used to validate the SonarQube server certificate. 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).
### Additional information
## Alternatives for Java, .NET, and C/C++ projects
The `sonarqube-scan-action/install-build-wrapper` action installs `coreutils` if run on macOS.
This GitHub Action will not work for all technologies. If you are in one of the following situations, you should use the following alternatives:
## Support & Community
* Your code is built with Maven. Read the documentation about our [Scanner for Maven](https://redirect.sonarsource.com/doc/install-configure-scanner-maven.html).
* Your code is built with Gradle. Read the documentation about our [Scanner for Gradle](https://redirect.sonarsource.com/doc/gradle.html).
* You want to analyze a .NET solution. Read the documentation about our [Scanner for .NET](https://redirect.sonarsource.com/doc/install-configure-scanner-msbuild.html).
* You want to analyze C or C++ code. Starting from SonarQube 10.6, this GitHub Action will scan C and C++ out of the box. If you want to have better control over the scan configuration/setup, you can switch to the [SonarQube C and C++](https://github.com/SonarSource/sonarqube-github-c-cpp) GitHub Action.
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).
## Error cleaning up workspace
### License
In some cases, the checkout action may fail to clean up the workspace. This is a known problem for GitHub actions implemented as a docker container (such as `sonarqube-scan-action`) when self-hosted runners are used.
Example of the error message: `File was unable to be removed Error: EACCES: permission denied, unlink '/actions-runner/_work//project/.scannerwork/.sonar_lock'`
To work around the problem, `sonarqube-scan-action` attempts to fix the permission of the temporary files that it creates. If that doesn't work, you can manually clean up the workspace by running the following action:
```
- name: Clean the workspace
uses: docker://alpine
with:
args: /bin/sh -c "find \"${GITHUB_WORKSPACE}\" -mindepth 1 ! -name . -prune -exec rm -rf {} +"
```
You can find more info [here](https://github.com/actions/runner/issues/434).
## Have questions or feedback?
To provide feedback (requesting a feature or reporting a bug) please post on the [SonarSource Community Forum](https://community.sonarsource.com/tags/c/help/sq/github-actions).
## License
The Dockerfile and associated scripts and documentation in this project are released under the LGPLv3 License.
Container images built with this project include third-party materials.

View file

@ -1,29 +1,19 @@
name: Official SonarQube Scan
# Warning: changing name would change URL in the marketplace
description: >
Scan your code with SonarQube Server and Cloud to detect issues in 30+ languages. (Formerly SonarQube and SonarCloud)
Scan your code with SonarQube to detect Bugs, Vulnerabilities and Code Smells in up to 27 programming languages!
branding:
icon: check
color: green
runs:
using: docker
image: Dockerfile
entrypoint: "/entrypoint.sh"
post-entrypoint: "/cleanup.sh"
inputs:
args:
description: Additional arguments to the Sonar Scanner CLI
description: Additional arguments to the sonar-scanner
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: 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: node20
main: dist/index.js
default: .

13
cleanup.sh Executable file
View file

@ -0,0 +1,13 @@
#!/bin/bash
set -e
# Reset all files permissions to the default Runner user and group to allow the follow up steps (mainly cache) to access all files.
# Assume that the first (non-hidden) file in the project directory is one from the project, and not one written by the scanner
_tmp_file=$(ls "${INPUT_PROJECTBASEDIR%/}/" | head -1)
echo "Reading permissions from $_tmp_file"
PERM=$(stat -c "%u:%g" "${INPUT_PROJECTBASEDIR%/}/$_tmp_file")
echo "Applying permissions $PERM to all files in the project base directory"
chown -R "$PERM" "${INPUT_PROJECTBASEDIR%/}/"

View file

@ -1,18 +1,17 @@
# SonarQube Scan Action
## Contributing
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:
@ -25,34 +24,3 @@ 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
```

View file

@ -1,134 +0,0 @@
name: 'SonarQube Scan for C and C++'
description: 'Scan your C and C++ code with SonarQube to detect bugs, vulnerabilities and code smells.'
branding:
icon: check
color: green
inputs:
installation-path:
description: 'Directory where the sonar-scanner and build wrapper will be installed. Created if does not exists.'
required: false
default: '.sonar'
cache-binaries:
description: 'Controls if installed binaries are cached using GitHub cache.'
required: false
default: 'true'
outputs:
sonar-scanner-binary:
description: "Absolute path to sonar-scanner binary."
value: ${{ steps.setup-outputs.outputs.sonar-scanner-binary }}
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: Verify and create installation path
shell: bash
env:
INSTALL_PATH: ${{ inputs.installation-path }}
run: ${GITHUB_ACTION_PATH}/../scripts/create_install_path.sh
- name: Set version of sonar-scanner
id: sonar-scanner-version
shell: bash
run: cat ${GITHUB_ACTION_PATH}/../sonar-scanner-version >> $GITHUB_OUTPUT
- name: Configure paths
id: configure_paths
shell: bash
env:
OS: ${{ runner.os }}
ARCH: ${{ runner.arch }}
INSTALL_PATH: ${{ inputs.installation-path }}
SONAR_SCANNER_VERSION: ${{ steps.sonar-scanner-version.outputs.sonar-scanner-version }}
SONAR_SCANNER_URL_WINDOWS_X64: ${{ steps.sonar-scanner-version.outputs.sonar-scanner-url-windows-x64 }}
SONAR_SCANNER_SHA_WINDOWS_X64: ${{ steps.sonar-scanner-version.outputs.sonar-scanner-sha-windows-x64 }}
SONAR_SCANNER_URL_LINUX_X64: ${{ steps.sonar-scanner-version.outputs.sonar-scanner-url-linux-x64 }}
SONAR_SCANNER_SHA_LINUX_X64: ${{ steps.sonar-scanner-version.outputs.sonar-scanner-sha-linux-x64 }}
SONAR_SCANNER_URL_LINUX_AARCH64: ${{ steps.sonar-scanner-version.outputs.sonar-scanner-url-linux-aarch64 }}
SONAR_SCANNER_SHA_LINUX_AARCH64: ${{ steps.sonar-scanner-version.outputs.sonar-scanner-sha-linux-aarch64 }}
SONAR_SCANNER_URL_MACOSX_X64: ${{ steps.sonar-scanner-version.outputs.sonar-scanner-url-macosx-x64 }}
SONAR_SCANNER_SHA_MACOSX_X64: ${{ steps.sonar-scanner-version.outputs.sonar-scanner-sha-macosx-x64 }}
SONAR_SCANNER_URL_MACOSX_AARCH64: ${{ steps.sonar-scanner-version.outputs.sonar-scanner-url-macosx-aarch64 }}
SONAR_SCANNER_SHA_MACOSX_AARCH64: ${{ steps.sonar-scanner-version.outputs.sonar-scanner-sha-macosx-aarch64 }}
run: ${GITHUB_ACTION_PATH}/../scripts/configure_paths.sh >> $GITHUB_OUTPUT
- name: Cache sonar-scanner installation
id: cache-sonar-tools
if: inputs.cache-binaries == 'true'
uses: actions/cache@v4
env:
# The default value is 60mins. Reaching timeout is treated the same as a cache miss.
SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1
with:
key: sonar-scanner-${{ runner.os }}-${{ runner.arch }}-${{ steps.sonar-scanner-version.outputs.sonar-scanner-version }}
path: ${{ steps.configure_paths.outputs.sonar-scanner-dir }}
- name: Download and install sonar-scanner
if: steps.cache-sonar-tools.outputs.cache-hit != 'true'
shell: bash
env:
DOWNLOAD_URL: ${{ steps.configure_paths.outputs.sonar-scanner-url }}
EXPECTED_SHA: ${{ steps.configure_paths.outputs.sonar-scanner-sha }}
INSTALL_PATH: ${{ inputs.installation-path }}
TMP_ZIP_PATH: ${{ runner.temp }}/sonar-scanner.zip
run: ${GITHUB_ACTION_PATH}/../scripts/download.sh -v
- name: Add the custom root certificate to java certificate store
shell: bash
run: ${GITHUB_ACTION_PATH}/../scripts/cert.sh
- name: Download and install build-wrapper
shell: bash
env:
DOWNLOAD_URL: ${{ steps.configure_paths.outputs.build-wrapper-url }}
INSTALL_PATH: ${{ inputs.installation-path }}
TMP_ZIP_PATH: ${{ runner.temp }}/build-wrapper.zip
run: ${GITHUB_ACTION_PATH}/../scripts/download.sh
- name: Setup action outputs
id: setup-outputs
shell: bash
env:
SONAR_SCANNER_DIR: ${{ steps.configure_paths.outputs.sonar-scanner-dir }}
SONAR_SCANNER_BIN: ${{ steps.configure_paths.outputs.sonar-scanner-bin }}
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
echo "::group::Action outputs"
echo "SONAR_HOST_URL=${SONAR_HOST_URL}" >> $GITHUB_ENV
echo "'SONAR_HOST_URL' environment variable set to '${SONAR_HOST_URL}'"
SONAR_SCANNER_BIN_DIR=$(realpath "${SONAR_SCANNER_DIR}/bin")
echo "${SONAR_SCANNER_BIN_DIR}" >> $GITHUB_PATH
echo "'${SONAR_SCANNER_BIN_DIR}' added to the path"
SONAR_SCANNER_BIN=$(realpath "${SONAR_SCANNER_BIN}")
echo "sonar-scanner-binary=${SONAR_SCANNER_BIN}" >> $GITHUB_OUTPUT
echo "'sonar-scanner-binary' output set to '${SONAR_SCANNER_BIN}'"
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::"

27277
dist/exec-BTlTa8sL.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2979
dist/index.js vendored

File diff suppressed because it is too large Load diff

1
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

View file

@ -1,207 +0,0 @@
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

File diff suppressed because one or more lines are too long

47
entrypoint.sh Executable file
View file

@ -0,0 +1,47 @@
#!/bin/bash
set -eo pipefail
echo "::warning title=Docker removed in the next major version::Users on the master branch of this GitHub action will be upgraded automatically on December 9th to its next major version, which replaces Docker with a composite action, executing in the runner environment. Self-hosted runners analyzing JS/TS code against SonarQube 10.2 and below will need to have Node JS installed."
declare -a args=()
if [[ -z "${SONAR_TOKEN}" ]]; then
echo "============================ WARNING ============================"
echo "Running this GitHub Action without SONAR_TOKEN is not recommended"
echo "============================ WARNING ============================"
fi
if [[ -n "${SONAR_ROOT_CERT}" ]]; then
echo "Adding custom root certificate to the scanner truststore"
rm -f /tmp/tmpcert.pem
echo "${SONAR_ROOT_CERT}" > /tmp/tmpcert.pem
# we can't use the default "sonar" password as keytool requires a password with at least 6 characters
args+=("-Dsonar.scanner.truststorePassword=changeit")
mkdir -p $SONAR_USER_HOME/ssl
keytool -storetype PKCS12 -keystore $SONAR_USER_HOME/ssl/truststore.p12 -storepass changeit -noprompt -trustcacerts -importcert -alias sonarqube -file /tmp/tmpcert.pem
# for older SQ versions < 10.6
export SONAR_SCANNER_OPTS="${SONAR_SCANNER_OPTS:-} -Djavax.net.ssl.trustStore=$SONAR_USER_HOME/ssl/truststore.p12 -Djavax.net.ssl.trustStorePassword=changeit"
fi
if [[ -f "${INPUT_PROJECTBASEDIR%/}/pom.xml" ]]; then
echo "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."
fi
if [[ -f "${INPUT_PROJECTBASEDIR%/}/build.gradle" || -f "${INPUT_PROJECTBASEDIR%/}/build.gradle.kts" ]]; then
echo "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."
fi
if [[ "$RUNNER_DEBUG" == '1' ]]; then
args+=("--debug")
fi
unset JAVA_HOME
args+=("-Dsonar.projectBaseDir=${INPUT_PROJECTBASEDIR}")
sonar-scanner "${args[@]}" ${INPUT_ARGS}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

BIN
images/SonarQube-72px.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

View file

@ -1,13 +0,0 @@
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.
branding:
icon: check
color: green
outputs:
build-wrapper-binary:
description: "Absolute path to Build Wrapper binary."
runs:
using: node20
main: ../dist/install-build-wrapper.js

945
package-lock.json generated
View file

@ -1,945 +0,0 @@
{
"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"
}
}
}

View file

@ -1,24 +0,0 @@
{
"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"
}
}

View file

@ -1,18 +0,0 @@
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,8 +0,0 @@
#!/usr/bin/env bash
if [[ -n "${SONAR_ROOT_CERT}" ]]; then
echo "Adding custom root certificate to java certificate store"
rm -f /tmp/tmpcert.pem
echo "${SONAR_ROOT_CERT}" > /tmp/tmpcert.pem
keytool -keystore /etc/ssl/certs/java/cacerts -storepass changeit -noprompt -trustcacerts -importcert -alias sonarqube -file /tmp/tmpcert.pem
fi

View file

@ -1,71 +0,0 @@
#!/usr/bin/env bash
if [[ ${ARCH} != "X64" && ! (${ARCH} == "ARM64" && (${OS} == "macOS" || ${OS} == "Linux")) ]]; then
echo "::error::Architecture '${ARCH}' is unsupported by build-wrapper"
exit 1
fi
case ${OS} in
Windows)
SONAR_SCANNER_SUFFIX="windows-x64"
BUILD_WRAPPER_SUFFIX="win-x86"
SONAR_SCANNER_NAME="sonar-scanner.bat"
BUILD_WRAPPER_NAME="build-wrapper-win-x86-64.exe"
SONAR_SCANNER_URL="${SONAR_SCANNER_URL_WINDOWS_X64}"
SONAR_SCANNER_SHA="${SONAR_SCANNER_SHA_WINDOWS_X64}"
;;
Linux)
case ${ARCH} in
X64)
SONAR_SCANNER_SUFFIX="linux-x64"
BUILD_WRAPPER_SUFFIX="linux-x86"
BUILD_WRAPPER_NAME="build-wrapper-linux-x86-64"
SONAR_SCANNER_URL="${SONAR_SCANNER_URL_LINUX_X64}"
SONAR_SCANNER_SHA="${SONAR_SCANNER_SHA_LINUX_X64}"
;;
ARM64)
SONAR_SCANNER_SUFFIX="linux-aarch64"
BUILD_WRAPPER_SUFFIX="linux-aarch64"
BUILD_WRAPPER_NAME="build-wrapper-linux-aarch64"
SONAR_SCANNER_URL="${SONAR_SCANNER_URL_LINUX_AARCH64}"
SONAR_SCANNER_SHA="${SONAR_SCANNER_SHA_LINUX_AARCH64}"
;;
esac
SONAR_SCANNER_NAME="sonar-scanner"
;;
macOS)
case ${ARCH} in
X64)
SONAR_SCANNER_SUFFIX="macosx-x64"
SONAR_SCANNER_URL="${SONAR_SCANNER_URL_MACOSX_X64}"
SONAR_SCANNER_SHA="${SONAR_SCANNER_SHA_MACOSX_X64}"
;;
ARM64)
SONAR_SCANNER_SUFFIX="macosx-aarch64"
SONAR_SCANNER_URL="${SONAR_SCANNER_URL_MACOSX_AARCH64}"
SONAR_SCANNER_SHA="${SONAR_SCANNER_SHA_MACOSX_AARCH64}"
;;
esac
BUILD_WRAPPER_SUFFIX="macosx-x86"
SONAR_SCANNER_NAME="sonar-scanner"
BUILD_WRAPPER_NAME="build-wrapper-macosx-x86"
;;
*)
echo "::error::Unsupported runner OS '${OS}'"
exit 1
;;
esac
echo "sonar-scanner-url=${SONAR_SCANNER_URL}"
echo "sonar-scanner-sha=${SONAR_SCANNER_SHA}"
SONAR_SCANNER_DIR="${INSTALL_PATH}/sonar-scanner-${SONAR_SCANNER_VERSION}-${SONAR_SCANNER_SUFFIX}"
echo "sonar-scanner-dir=${SONAR_SCANNER_DIR}"
echo "sonar-scanner-bin=${SONAR_SCANNER_DIR}/bin/${SONAR_SCANNER_NAME}"
BUILD_WRAPPER_DIR="${INSTALL_PATH}/build-wrapper-${BUILD_WRAPPER_SUFFIX}"
echo "build-wrapper-url=${SONAR_HOST_URL%/}/static/cpp/build-wrapper-${BUILD_WRAPPER_SUFFIX}.zip"
echo "build-wrapper-dir=${BUILD_WRAPPER_DIR}"
echo "build-wrapper-bin=${BUILD_WRAPPER_DIR}/${BUILD_WRAPPER_NAME}"

View file

@ -1,26 +0,0 @@
#!/usr/bin/env bash
source "$(dirname -- "$0")/utils.sh"
echo "Installation path is '${INSTALL_PATH}'"
test ! -z "${INSTALL_PATH}"
check_status "Empty installation path specified"
if [[ ! -e "${INSTALL_PATH}" ]]; then
mkdir -p "${INSTALL_PATH}"
check_status "Failed to create non-existing installation path '${INSTALL_PATH}'"
fi
ABSOLUTE_INSTALL_PATH=$(realpath "${INSTALL_PATH}")
echo "Absolute installation path is '${ABSOLUTE_INSTALL_PATH}'"
test -d "${INSTALL_PATH}"
check_status "Installation path '${INSTALL_PATH}' is not a directory (absolute path is '${ABSOLUTE_INSTALL_PATH}')"
test -r "${INSTALL_PATH}"
check_status "Installation path '${INSTALL_PATH}' is not readable (absolute path is '${ABSOLUTE_INSTALL_PATH}')"
test -w "${INSTALL_PATH}"
check_status "Installation path '${INSTALL_PATH}' is not writeable (absolute path is '${ABSOLUTE_INSTALL_PATH}')"

View file

@ -1,58 +0,0 @@
#!/usr/bin/env bash
source "$(dirname -- "$0")/utils.sh"
VERIFY_CORRECTNESS=false
help() {
cat <<EOF
Usage: ./download [-v]
-h Display help
-v Verify correctness of a download with SHA256 checksum; Optional
EOF
}
parse_arguments() {
while getopts "hv" arg; do
case $arg in
v)
VERIFY_CORRECTNESS=true
echo "Verify correctness is set to true"
;;
?)
help
exit 0
;;
esac
done
}
verify_download_correctness() {
echo "${EXPECTED_SHA} ${TMP_ZIP_PATH}" | sha256sum -c -
check_status "Checking sha256 failed"
}
download() {
echo "Downloading '${DOWNLOAD_URL}'"
mkdir -p "${INSTALL_PATH}"
check_status "Failed to create ${INSTALL_PATH}"
curl -sSLo "${TMP_ZIP_PATH}" "${DOWNLOAD_URL}"
check_status "Failed to download '${DOWNLOAD_URL}'"
}
decompress() {
echo "Decompressing"
unzip -o -d "${INSTALL_PATH}" "${TMP_ZIP_PATH}"
check_status "Failed to unzip the archive into '${INSTALL_PATH}'"
}
####################################################################################
echo "::group::Download ${DOWNLOAD_URL}"
parse_arguments $@
download
if [ "$VERIFY_CORRECTNESS" = true ]; then
verify_download_correctness
fi
decompress
echo "::endgroup::"

View file

@ -1,25 +0,0 @@
#!/usr/bin/env bash
source "$(dirname -- "$0")/utils.sh"
SONAR_SCANNER_VERSION=$(curl -sSL -H "Accept: application/vnd.github+json" \
https://api.github.com/repos/SonarSource/sonar-scanner-cli/releases/latest | jq -r '.tag_name')
check_status "Failed to fetch latest sonar-scanner version from GitHub API"
echo "sonar-scanner-version=${SONAR_SCANNER_VERSION}"
for OS in windows linux macosx; do
if [[ "$OS" == "windows" ]]; then
ARCHS=("x64")
else
ARCHS=("x64" "aarch64")
fi
for ARCH in "${ARCHS[@]}"; do
SONAR_SCANNER_URL="https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-${OS}-${ARCH}.zip"
SONAR_SCANNER_SHA=$(curl -sSL "${SONAR_SCANNER_URL}.sha256")
check_status "Failed to download ${OS} ${ARCH} sonar-scanner checksum from '${SONAR_SCANNER_URL}'"
echo "sonar-scanner-url-${OS}-${ARCH}=${SONAR_SCANNER_URL}"
echo "sonar-scanner-sha-${OS}-${ARCH}=${SONAR_SCANNER_SHA}"
done
done

View file

@ -1,25 +0,0 @@
#!/usr/bin/env bash
check_status() {
exit_status=$?
if [ $exit_status -ne 0 ]; then
echo "::error::$1"
exit $exit_status
fi
}
realpath() {
case ${RUNNER_OS} in
Windows)
cygpath --absolute --windows "$1"
;;
Linux)
readlink -f "$1"
;;
macOS)
# installed by coreutils package
greadlink -f "$1"
;;
esac
}

View file

@ -1,11 +0,0 @@
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

@ -1,80 +0,0 @@
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

@ -1,85 +0,0 @@
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

@ -1,98 +0,0 @@
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

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

View file

@ -1,177 +0,0 @@
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

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

View file

@ -1,75 +0,0 @@
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

@ -1,59 +0,0 @@
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

@ -1,152 +0,0 @@
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"],
});
}

View file

@ -1,44 +0,0 @@
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."
);
}
}

View file

@ -1,35 +0,0 @@
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}`;

View file

@ -1,14 +1,10 @@
#!/usr/bin/env bash
set -eou pipefail
#!/bin/bash
error() { echo -e "\\e[31m✗ $*\\e[0m"; }
scriptDir=$(dirname -- "$(readlink -f -- "${BASH_SOURCE[0]}")")
assertFileExists $1
$scriptDir/assertFileExists "$1"
if ! grep -q "$2" "$1"; then
if ! grep -q $2 $1; then
error "'$2' not found in '$1'"
exit 1
fi

View file

@ -1,10 +0,0 @@
#!/usr/bin/env bash
set -eou pipefail
error() { echo -e "\\e[31m✗ $*\\e[0m"; }
if [ -f "$1" ]; then
error "File '$1' found"
exit 1
fi

View file

@ -1,10 +1,8 @@
#!/usr/bin/env bash
set -eou pipefail
#!/bin/bash
error() { echo -e "\\e[31m✗ $*\\e[0m"; }
if [ ! -f "$1" ]; then
if [ ! -f $1 ]; then
error "File '$1' not found"
exit 1
fi