├── test ├── maven-project │ └── pom.xml ├── gradle-project │ └── build.gradle ├── gradle-project-kotlin │ └── build.gradle.kts ├── example-project │ ├── sonar-project.properties │ └── src │ │ └── main.js ├── assertFileDoesntExist ├── assertFileExists └── assertFileContains ├── .gitignore ├── .github ├── CODEOWNERS ├── qa-nginx-redirecting │ ├── generate-ssl.sh │ ├── compose.yml │ └── nginx.conf ├── workflows │ ├── unit-tests.yml │ ├── update-tags.yml │ ├── PullRequestClosed.yml │ ├── RequestReview.yml │ ├── SubmitReview.yml │ ├── PullRequestCreated.yml │ ├── qa-install-build-wrapper.yml │ ├── qa-deprecated-c-cpp.yml │ ├── version_update.yml │ ├── qa-scripts.yml │ └── qa-main.yml ├── dependabot.yml ├── qa-sq-behind-ngix │ ├── compose.yml │ └── nginx.conf └── PULL_REQUEST_TEMPLATE.md ├── images ├── SQ_Logo_Server_Cloud_Dark_Backgrounds.png └── SQ_Logo_Server_Cloud_Light_Backgrounds.png ├── src ├── main │ ├── __tests__ │ │ ├── mocks.js │ │ ├── utils.test.js │ │ └── sanity-checks.test.js │ ├── utils.js │ ├── sanity-checks.js │ ├── install-sonar-scanner.js │ ├── index.js │ └── run-sonar-scanner.js └── install-build-wrapper │ ├── install-build-wrapper.js │ ├── __tests__ │ └── utils.test.js │ └── utils.js ├── scripts ├── cert.sh ├── utils.sh ├── create_install_path.sh ├── fetch_latest_version.sh ├── download.sh └── configure_paths.sh ├── install-build-wrapper └── action.yml ├── rollup.config.js ├── package.json ├── action.yml ├── SECURITY.md ├── sonar-scanner-version ├── contributing.md ├── dist ├── install-build-wrapper.js └── install-build-wrapper.js.map ├── deprecated-c-cpp └── action.yml ├── LICENSE.txt └── README.md /test/maven-project/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/gradle-project/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/gradle-project-kotlin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store 3 | 4 | # Node 5 | node_modules/ 6 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | .github/* @sonarsource/orchestration-processing-squad 2 | -------------------------------------------------------------------------------- /test/example-project/sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=foo 2 | sonar.sources=src/ 3 | -------------------------------------------------------------------------------- /test/example-project/src/main.js: -------------------------------------------------------------------------------- 1 | function main() { 2 | console.log("Hello World"); 3 | } 4 | 5 | main(); 6 | -------------------------------------------------------------------------------- /images/SQ_Logo_Server_Cloud_Dark_Backgrounds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSource/sonarqube-scan-action/HEAD/images/SQ_Logo_Server_Cloud_Dark_Backgrounds.png -------------------------------------------------------------------------------- /images/SQ_Logo_Server_Cloud_Light_Backgrounds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSource/sonarqube-scan-action/HEAD/images/SQ_Logo_Server_Cloud_Light_Backgrounds.png -------------------------------------------------------------------------------- /test/assertFileDoesntExist: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eou pipefail 4 | 5 | error() { echo -e "\\e[31m✗ $*\\e[0m"; } 6 | 7 | if [ -f "$1" ]; then 8 | error "File '$1' found" 9 | exit 1 10 | fi -------------------------------------------------------------------------------- /test/assertFileExists: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eou pipefail 4 | 5 | error() { echo -e "\\e[31m✗ $*\\e[0m"; } 6 | 7 | if [ ! -f "$1" ]; then 8 | error "File '$1' not found" 9 | exit 1 10 | fi -------------------------------------------------------------------------------- /src/main/__tests__/mocks.js: -------------------------------------------------------------------------------- 1 | export function mockCore(overrides = {}) { 2 | return { 3 | setFailed: (msg) => console.error(msg), 4 | warning: (msg) => console.log(msg), 5 | ...overrides, 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /test/assertFileContains: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eou pipefail 4 | 5 | error() { echo -e "\\e[31m✗ $*\\e[0m"; } 6 | 7 | scriptDir=$(dirname -- "$(readlink -f -- "${BASH_SOURCE[0]}")") 8 | 9 | $scriptDir/assertFileExists "$1" 10 | 11 | if ! grep -q "$2" "$1"; then 12 | error "'$2' not found in '$1'" 13 | exit 1 14 | fi -------------------------------------------------------------------------------- /scripts/cert.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [[ -n "${SONAR_ROOT_CERT}" ]]; then 4 | echo "Adding custom root certificate to java certificate store" 5 | rm -f /tmp/tmpcert.pem 6 | echo "${SONAR_ROOT_CERT}" > /tmp/tmpcert.pem 7 | keytool -keystore /etc/ssl/certs/java/cacerts -storepass changeit -noprompt -trustcacerts -importcert -alias sonarqube -file /tmp/tmpcert.pem 8 | fi 9 | -------------------------------------------------------------------------------- /.github/qa-nginx-redirecting/generate-ssl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Generate self-signed SSL certificate for localhost with 1-day expiry 4 | openssl req -x509 -nodes -days 1 -newkey rsa:2048 \ 5 | -keyout nginx.key \ 6 | -out nginx.crt \ 7 | -subj "/C=US/ST=CA/L=Local/O=Test/CN=localhost" \ 8 | -addext "subjectAltName=DNS:localhost,IP:127.0.0.1" 9 | 10 | echo "SSL certificates generated with 1-day expiry: nginx.crt and nginx.key" -------------------------------------------------------------------------------- /install-build-wrapper/action.yml: -------------------------------------------------------------------------------- 1 | name: "Install Build Wrapper for C and C++" 2 | description: > 3 | Download and install the Build Wrapper for C, C++, and Objective-C 4 | projects analyzed with manual config. 5 | branding: 6 | icon: check 7 | color: green 8 | outputs: 9 | build-wrapper-binary: 10 | description: "Absolute path to Build Wrapper binary." 11 | runs: 12 | using: node20 13 | main: ../dist/install-build-wrapper.js 14 | -------------------------------------------------------------------------------- /scripts/utils.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | check_status() { 4 | exit_status=$? 5 | if [ $exit_status -ne 0 ]; then 6 | echo "::error::$1" 7 | exit $exit_status 8 | fi 9 | } 10 | 11 | realpath() { 12 | case ${RUNNER_OS} in 13 | Windows) 14 | cygpath --absolute --windows "$1" 15 | ;; 16 | Linux) 17 | readlink -f "$1" 18 | ;; 19 | macOS) 20 | # installed by coreutils package 21 | greadlink -f "$1" 22 | ;; 23 | esac 24 | } 25 | 26 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import commonjs from "@rollup/plugin-commonjs"; 2 | import { nodeResolve } from "@rollup/plugin-node-resolve"; 3 | 4 | const config = { 5 | input: [ 6 | "src/main/index.js", 7 | "src/install-build-wrapper/install-build-wrapper.js", 8 | ], 9 | output: { 10 | esModule: true, 11 | dir: "dist", 12 | format: "es", 13 | sourcemap: true, 14 | }, 15 | plugins: [commonjs(), nodeResolve({ preferBuiltins: true })], 16 | }; 17 | 18 | export default config; 19 | -------------------------------------------------------------------------------- /.github/workflows/unit-tests.yml: -------------------------------------------------------------------------------- 1 | name: Unit tests 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v5 14 | 15 | - name: Setup Node.js 16 | uses: actions/setup-node@v6 17 | with: 18 | node-version: "20" 19 | cache: "npm" 20 | 21 | - name: Install dependencies 22 | run: npm ci 23 | 24 | - name: Run tests 25 | run: npm test 26 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | 8 | updates: 9 | - package-ecosystem: "github-actions" 10 | directory: "/" 11 | schedule: 12 | interval: "daily" 13 | timezone: "CET" 14 | open-pull-requests-limit: 100 15 | commit-message: 16 | prefix: "NO-JIRA " 17 | -------------------------------------------------------------------------------- /.github/qa-nginx-redirecting/compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | https-proxy: 3 | image: nginx 4 | ports: 5 | - 8080:8080 6 | volumes: 7 | - $GITHUB_WORKSPACE/.github/qa-nginx-redirecting/nginx.conf:/etc/nginx/nginx.conf:ro 8 | - $GITHUB_WORKSPACE/.github/qa-nginx-redirecting/nginx.crt:/etc/nginx/nginx.crt:ro 9 | - $GITHUB_WORKSPACE/.github/qa-nginx-redirecting/nginx.key:/etc/nginx/nginx.key:ro 10 | healthcheck: 11 | test: ["CMD", "curl", "--fail", "--insecure", "https://localhost:8080/health"] 12 | interval: 10s 13 | timeout: 5s 14 | retries: 20 15 | start_period: 2m 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sonarqube-scan-action", 3 | "version": "6.0.0", 4 | "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/).", 5 | "type": "module", 6 | "main": "src/main/index.js", 7 | "scripts": { 8 | "build": "rollup --config rollup.config.js", 9 | "test": "node --test" 10 | }, 11 | "license": "LGPL-3.0-only", 12 | "dependencies": { 13 | "@actions/core": "1.11.1", 14 | "@actions/github": "6.0.1", 15 | "@actions/tool-cache": "2.0.2", 16 | "string-argv": "0.3.2" 17 | }, 18 | "devDependencies": { 19 | "@rollup/plugin-commonjs": "28.0.6", 20 | "@rollup/plugin-node-resolve": "16.0.1", 21 | "mock-fs": "5.5.0", 22 | "rollup": "4.50.1" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.github/qa-sq-behind-ngix/compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | sonarqube: 3 | image: sonarqube:lts-community 4 | ports: 5 | - 9000:9000 6 | healthcheck: 7 | test: 'grep -Fq "SonarQube is operational" /opt/sonarqube/logs/sonar.log' 8 | interval: 10s 9 | timeout: 5s 10 | retries: 20 11 | start_period: 2m 12 | 13 | https-proxy: 14 | image: nginx 15 | ports: 16 | - 4443:4443 17 | volumes: 18 | - $GITHUB_WORKSPACE/.github/qa-sq-behind-ngix/nginx.conf:/etc/nginx/nginx.conf:ro 19 | - $GITHUB_WORKSPACE/.github/qa-sq-behind-ngix/server.crt:/etc/nginx/server.crt:ro 20 | - $GITHUB_WORKSPACE/.github/qa-sq-behind-ngix/server.key:/etc/nginx/server.key:ro 21 | healthcheck: 22 | test: ["CMD", "curl", "--fail", "localhost:8080/health"] 23 | interval: 10s 24 | timeout: 5s 25 | retries: 20 26 | start_period: 2m -------------------------------------------------------------------------------- /.github/workflows/update-tags.yml: -------------------------------------------------------------------------------- 1 | name: Update Tags 2 | 3 | on: 4 | push: 5 | tags: 6 | - v*.*.* 7 | 8 | jobs: 9 | generate: 10 | runs-on: github-ubuntu-latest-s 11 | permissions: 12 | contents: write 13 | 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v5 17 | 18 | - name: Parse semver 19 | uses: madhead/semver-utils@36d1e0ed361bd7b4b77665de8093092eaeabe6ba # v4.3.0 20 | id: version 21 | with: 22 | version: ${{ github.ref_name }} 23 | 24 | - name: Update tags 25 | run: | 26 | TAGS='v${{ steps.version.outputs.major }} v${{ steps.version.outputs.major }}.${{ steps.version.outputs.minor }}' 27 | 28 | for t in $TAGS; do 29 | git tag -f "$t" 30 | git push origin ":$t" 2>/dev/null || true 31 | git push origin "$t" 32 | done 33 | -------------------------------------------------------------------------------- /src/main/utils.js: -------------------------------------------------------------------------------- 1 | const platformFlavor = { 2 | linux: { 3 | x64: "linux-x64", 4 | arm64: "linux-aarch64", 5 | }, 6 | win32: { 7 | x64: "windows-x64", 8 | }, 9 | darwin: { 10 | x64: "macosx-x64", 11 | arm64: "macosx-aarch64", 12 | }, 13 | }; 14 | 15 | export function getPlatformFlavor(platform, arch) { 16 | const flavor = platformFlavor[platform]?.[arch]; 17 | 18 | if (!flavor) { 19 | throw new Error(`Platform ${platform} ${arch} not supported`); 20 | } 21 | 22 | return flavor; 23 | } 24 | 25 | export function getScannerDownloadURL({ 26 | scannerBinariesUrl, 27 | scannerVersion, 28 | flavor, 29 | }) { 30 | const trimURL = scannerBinariesUrl.replace(/\/$/, ""); 31 | return `${trimURL}/sonar-scanner-cli-${scannerVersion}-${flavor}.zip`; 32 | } 33 | 34 | export const scannerDirName = (version, flavor) => 35 | `sonar-scanner-${version}-${flavor}`; 36 | -------------------------------------------------------------------------------- /.github/qa-nginx-redirecting/nginx.conf: -------------------------------------------------------------------------------- 1 | user nginx; 2 | worker_processes auto; 3 | 4 | error_log /var/log/nginx/error.log notice; 5 | 6 | events { 7 | worker_connections 1024; 8 | } 9 | 10 | http { 11 | include /etc/nginx/mime.types; 12 | default_type application/octet-stream; 13 | 14 | sendfile on; 15 | 16 | keepalive_timeout 65; 17 | 18 | include /etc/nginx/conf.d/*.conf; 19 | 20 | server { 21 | listen 8080 ssl; 22 | ssl_certificate /etc/nginx/nginx.crt; 23 | ssl_certificate_key /etc/nginx/nginx.key; 24 | 25 | location /health { 26 | add_header 'Content-Type' 'text/plain'; 27 | return 200 "healthy\n"; 28 | } 29 | 30 | location ~ /clientRedirectToSonarBinaries/(.*) { 31 | return 301 "https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/$1"; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /scripts/create_install_path.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source "$(dirname -- "$0")/utils.sh" 4 | 5 | echo "Installation path is '${INSTALL_PATH}'" 6 | 7 | test ! -z "${INSTALL_PATH}" 8 | check_status "Empty installation path specified" 9 | 10 | if [[ ! -e "${INSTALL_PATH}" ]]; then 11 | mkdir -p "${INSTALL_PATH}" 12 | check_status "Failed to create non-existing installation path '${INSTALL_PATH}'" 13 | fi 14 | 15 | ABSOLUTE_INSTALL_PATH=$(realpath "${INSTALL_PATH}") 16 | echo "Absolute installation path is '${ABSOLUTE_INSTALL_PATH}'" 17 | 18 | test -d "${INSTALL_PATH}" 19 | check_status "Installation path '${INSTALL_PATH}' is not a directory (absolute path is '${ABSOLUTE_INSTALL_PATH}')" 20 | 21 | test -r "${INSTALL_PATH}" 22 | check_status "Installation path '${INSTALL_PATH}' is not readable (absolute path is '${ABSOLUTE_INSTALL_PATH}')" 23 | 24 | test -w "${INSTALL_PATH}" 25 | check_status "Installation path '${INSTALL_PATH}' is not writeable (absolute path is '${ABSOLUTE_INSTALL_PATH}')" 26 | 27 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: Official SonarQube Scan 2 | # Warning: changing name would change URL in the marketplace 3 | description: > 4 | Scan your code with SonarQube Server and Cloud to detect issues in 30+ languages. (Formerly SonarQube and SonarCloud) 5 | 6 | branding: 7 | icon: check 8 | color: green 9 | inputs: 10 | args: 11 | description: Additional arguments to the Sonar Scanner CLI 12 | required: false 13 | default: "" 14 | projectBaseDir: 15 | description: Set the sonar.projectBaseDir analysis property 16 | required: false 17 | default: "." 18 | scannerVersion: 19 | description: Version of the Sonar Scanner CLI to use 20 | required: false 21 | # to be kept in sync with sonar-scanner-version 22 | default: 8.0.1.6346 23 | scannerBinariesUrl: 24 | description: URL to download the Sonar Scanner CLI binaries from 25 | required: false 26 | default: https://binaries.sonarsource.com/Distribution/sonar-scanner-cli 27 | runs: 28 | using: node20 29 | main: dist/index.js 30 | -------------------------------------------------------------------------------- /.github/workflows/PullRequestClosed.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request Closed 2 | 3 | on: 4 | pull_request: 5 | types: [closed] 6 | 7 | jobs: 8 | PullRequestClosed_job: 9 | name: Pull Request Closed 10 | runs-on: github-ubuntu-latest-s 11 | permissions: 12 | id-token: write 13 | pull-requests: read 14 | # For external PR, ticket should be moved manually 15 | if: | 16 | github.event.pull_request.head.repo.full_name == github.repository 17 | steps: 18 | - id: secrets 19 | uses: SonarSource/vault-action-wrapper@v3 20 | with: 21 | secrets: | 22 | development/kv/data/jira user | JIRA_USER; 23 | development/kv/data/jira token | JIRA_TOKEN; 24 | - uses: sonarsource/gh-action-lt-backlog/PullRequestClosed@v2 25 | with: 26 | github-token: ${{secrets.GITHUB_TOKEN}} 27 | jira-user: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_USER }} 28 | jira-token: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_TOKEN }} 29 | -------------------------------------------------------------------------------- /.github/qa-sq-behind-ngix/nginx.conf: -------------------------------------------------------------------------------- 1 | user nginx; 2 | worker_processes auto; 3 | 4 | error_log /var/log/nginx/error.log notice; 5 | 6 | events { 7 | worker_connections 1024; 8 | } 9 | 10 | http { 11 | include /etc/nginx/mime.types; 12 | default_type application/octet-stream; 13 | 14 | sendfile on; 15 | 16 | keepalive_timeout 65; 17 | 18 | include /etc/nginx/conf.d/*.conf; 19 | 20 | server { 21 | listen 8080; 22 | 23 | location /health { 24 | add_header 'Content-Type' 'text/plain'; 25 | return 200 "healthy\n"; 26 | } 27 | } 28 | 29 | server { 30 | listen 4443 ssl; 31 | 32 | ssl_protocols TLSv1.1 TLSv1.2; 33 | ssl_certificate /etc/nginx/server.crt; 34 | ssl_certificate_key /etc/nginx/server.key; 35 | 36 | location / { 37 | proxy_pass http://sonarqube:9000; 38 | proxy_set_header Host $host; 39 | proxy_set_header X-Forwarded-For $remote_addr; 40 | proxy_set_header X-Forwarded-Proto https; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /scripts/fetch_latest_version.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source "$(dirname -- "$0")/utils.sh" 4 | 5 | SONAR_SCANNER_VERSION=$(curl -sSL -H "Accept: application/vnd.github+json" \ 6 | https://api.github.com/repos/SonarSource/sonar-scanner-cli/releases/latest | jq -r '.tag_name') 7 | check_status "Failed to fetch latest sonar-scanner version from GitHub API" 8 | 9 | echo "sonar-scanner-version=${SONAR_SCANNER_VERSION}" 10 | 11 | for OS in windows linux macosx; do 12 | if [[ "$OS" == "windows" ]]; then 13 | ARCHS=("x64") 14 | else 15 | ARCHS=("x64" "aarch64") 16 | fi 17 | for ARCH in "${ARCHS[@]}"; do 18 | SONAR_SCANNER_URL="https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-${OS}-${ARCH}.zip" 19 | SONAR_SCANNER_SHA=$(curl -sSL "${SONAR_SCANNER_URL}.sha256") 20 | check_status "Failed to download ${OS} ${ARCH} sonar-scanner checksum from '${SONAR_SCANNER_URL}'" 21 | 22 | echo "sonar-scanner-url-${OS}-${ARCH}=${SONAR_SCANNER_URL}" 23 | echo "sonar-scanner-sha-${OS}-${ARCH}=${SONAR_SCANNER_SHA}" 24 | done 25 | done 26 | -------------------------------------------------------------------------------- /.github/workflows/RequestReview.yml: -------------------------------------------------------------------------------- 1 | name: Request review 2 | 3 | on: 4 | pull_request: 5 | types: ["review_requested"] 6 | 7 | jobs: 8 | RequestReview_job: 9 | name: Request review 10 | runs-on: github-ubuntu-latest-s 11 | permissions: 12 | id-token: write 13 | # For external PR, ticket should be moved manually 14 | if: | 15 | github.event.pull_request.head.repo.full_name == github.repository 16 | steps: 17 | - id: secrets 18 | uses: SonarSource/vault-action-wrapper@v3 19 | with: 20 | secrets: | 21 | development/github/token/{REPO_OWNER_NAME_DASH}-jira token | GITHUB_TOKEN; 22 | development/kv/data/jira user | JIRA_USER; 23 | development/kv/data/jira token | JIRA_TOKEN; 24 | - uses: sonarsource/gh-action-lt-backlog/RequestReview@v2 25 | with: 26 | github-token: ${{ fromJSON(steps.secrets.outputs.vault).GITHUB_TOKEN }} 27 | jira-user: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_USER }} 28 | jira-token: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_TOKEN }} 29 | -------------------------------------------------------------------------------- /.github/workflows/SubmitReview.yml: -------------------------------------------------------------------------------- 1 | name: Submit Review 2 | 3 | on: 4 | pull_request_review: 5 | types: [submitted] 6 | 7 | jobs: 8 | SubmitReview_job: 9 | name: Submit Review 10 | runs-on: github-ubuntu-latest-s 11 | permissions: 12 | id-token: write 13 | pull-requests: read 14 | # For external PR, ticket should be moved manually 15 | if: | 16 | github.event.pull_request.head.repo.full_name == github.repository 17 | && (github.event.review.state == 'changes_requested' 18 | || github.event.review.state == 'approved') 19 | steps: 20 | - id: secrets 21 | uses: SonarSource/vault-action-wrapper@v3 22 | with: 23 | secrets: | 24 | development/kv/data/jira user | JIRA_USER; 25 | development/kv/data/jira token | JIRA_TOKEN; 26 | - uses: sonarsource/gh-action-lt-backlog/SubmitReview@v2 27 | with: 28 | github-token: ${{secrets.GITHUB_TOKEN}} 29 | jira-user: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_USER }} 30 | jira-token: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_TOKEN }} 31 | -------------------------------------------------------------------------------- /.github/workflows/PullRequestCreated.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request Created 2 | 3 | on: 4 | pull_request: 5 | types: ["opened"] 6 | 7 | jobs: 8 | PullRequestCreated_job: 9 | name: Pull Request Created 10 | runs-on: github-ubuntu-latest-s 11 | permissions: 12 | id-token: write 13 | # For external PR, ticket should be created manually 14 | if: | 15 | github.event.pull_request.head.repo.full_name == github.repository 16 | steps: 17 | - id: secrets 18 | uses: SonarSource/vault-action-wrapper@v3 19 | with: 20 | secrets: | 21 | development/github/token/{REPO_OWNER_NAME_DASH}-jira token | GITHUB_TOKEN; 22 | development/kv/data/jira user | JIRA_USER; 23 | development/kv/data/jira token | JIRA_TOKEN; 24 | - uses: sonarsource/gh-action-lt-backlog/PullRequestCreated@v2 25 | with: 26 | github-token: ${{ fromJSON(steps.secrets.outputs.vault).GITHUB_TOKEN }} 27 | jira-user: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_USER }} 28 | jira-token: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_TOKEN }} 29 | jira-project: SQSCANGHA 30 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Reporting Security Issues 2 | 3 | A mature software vulnerability treatment process is a cornerstone of a robust information security management system. Contributions from the community play an important role in the evolution and security of our products, and in safeguarding the security and privacy of our users. 4 | 5 | If you believe you have discovered a security vulnerability in Sonar's products, we encourage you to report it immediately. 6 | 7 | To responsibly report a security issue, please email us at [security@sonarsource.com](mailto:security@sonarsource.com). Sonar’s security team will acknowledge your report, guide you through the next steps, or request additional information if necessary. Customers with a support contract can also report the vulnerability directly through the support channel. 8 | 9 | For security vulnerabilities found in third-party libraries, please also contact the library's owner or maintainer directly. 10 | 11 | ## Responsible Disclosure Policy 12 | 13 | For more information about disclosing a security vulnerability to Sonar, please refer to our community post: [Responsible Vulnerability Disclosure](https://community.sonarsource.com/t/responsible-vulnerability-disclosure/9317). -------------------------------------------------------------------------------- /sonar-scanner-version: -------------------------------------------------------------------------------- 1 | sonar-scanner-version=8.0.1.6346 2 | sonar-scanner-url-windows-x64=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-8.0.1.6346-windows-x64.zip 3 | sonar-scanner-sha-windows-x64=52b35b24be4ce5ec2e2933b32683db45db139581c46945546d9739b0c8866231 4 | sonar-scanner-url-linux-x64=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-8.0.1.6346-linux-x64.zip 5 | sonar-scanner-sha-linux-x64=4bd40bf8411ed104853e94a3746ec92bc92845fde2b27dbf5c33fb5cfa8ecbe9 6 | sonar-scanner-url-linux-aarch64=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-8.0.1.6346-linux-aarch64.zip 7 | sonar-scanner-sha-linux-aarch64=ae2b062ed6d640ab9014ab576042385d54c910857de952f5cb2592d2a2d7c8d8 8 | sonar-scanner-url-macosx-x64=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-8.0.1.6346-macosx-x64.zip 9 | sonar-scanner-sha-macosx-x64=aa9065347ba834ff6f3d461183eb40a67a321e6996206875fd257e8e7d5745b2 10 | sonar-scanner-url-macosx-aarch64=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-8.0.1.6346-macosx-aarch64.zip 11 | sonar-scanner-sha-macosx-aarch64=2d65d49c327ec8ca5ec7c6dc2af17749f5b43c596fd906501bba5a0b09edc5e2 12 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | 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). 9 | 10 | 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: 11 | 12 | - [ ] Please explain your motives to contribute this change: what problem you are trying to fix, what improvement you are trying to make 13 | - [ ] Make sure any code you changed is covered by tests 14 | - [ ] If there is a [JIRA](http://jira.sonarsource.com/browse/SONAR) ticket available, please make your commits and pull request start with the ticket ID (SONAR-XXXX) 15 | 16 | We will try to give you feedback on your contribution as quickly as possible. 17 | 18 | Thank You! 19 | The SonarSource Team 20 | -------------------------------------------------------------------------------- /scripts/download.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source "$(dirname -- "$0")/utils.sh" 4 | 5 | VERIFY_CORRECTNESS=false 6 | 7 | help() { 8 | cat < ⚠️ Since the action uses the code in the repository, it is necessary to commit the bundled code! ⚠️ 52 | 53 | 54 | To run the js unit tests, run the `test` command: 55 | 56 | ```sh 57 | npm run test 58 | ``` 59 | -------------------------------------------------------------------------------- /src/install-build-wrapper/install-build-wrapper.js: -------------------------------------------------------------------------------- 1 | import * as core from "@actions/core"; 2 | import * as exec from "@actions/exec"; 3 | import * as fs from "fs"; 4 | import * as path from "path"; 5 | import { getBuildWrapperInfo, getRealPath } from "./utils"; 6 | 7 | async function installMacOSPackages() { 8 | if (process.platform === "darwin") { 9 | core.info("Installing required packages for macOS"); 10 | await exec.exec("brew", ["install", "coreutils"]); 11 | } 12 | } 13 | 14 | /** 15 | * These RUNNER_XX env variables come from GitHub by default. 16 | * See https://docs.github.com/en/actions/reference/workflows-and-actions/variables#default-environment-variables 17 | * 18 | * If SONAR_HOST_URL is omitted, we assume sonarcloud.io 19 | */ 20 | function getEnvVariables() { 21 | const sonarHostUrl = process.env.SONAR_HOST_URL 22 | ? process.env.SONAR_HOST_URL.replace(/\/$/, "") 23 | : "https://sonarcloud.io"; 24 | 25 | return { 26 | runnerOS: process.env.RUNNER_OS, 27 | runnerArch: process.env.RUNNER_ARCH, 28 | runnerTemp: process.env.RUNNER_TEMP, 29 | sonarHostUrl, 30 | }; 31 | } 32 | 33 | async function downloadAndInstallBuildWrapper(downloadUrl, runnerEnv) { 34 | const { runnerArch, runnerOS, runnerTemp } = runnerEnv; 35 | const tmpZipPath = path.join( 36 | runnerTemp, 37 | `build-wrapper-${runnerOS}-${runnerArch}.zip` 38 | ); 39 | 40 | core.startGroup(`Download ${downloadUrl}`); 41 | 42 | core.info(`Downloading '${downloadUrl}'`); 43 | 44 | if (!fs.existsSync(runnerTemp)) { 45 | fs.mkdirSync(runnerTemp, { recursive: true }); 46 | } 47 | 48 | await exec.exec("curl", ["-sSLo", tmpZipPath, downloadUrl]); 49 | 50 | core.info("Decompressing"); 51 | await exec.exec("unzip", ["-o", "-d", runnerTemp, tmpZipPath]); 52 | 53 | core.endGroup(); 54 | } 55 | 56 | async function run() { 57 | try { 58 | await installMacOSPackages(); 59 | 60 | const envVariables = getEnvVariables(); 61 | 62 | const { buildWrapperBin, buildWrapperDir, buildWrapperUrl } = 63 | getBuildWrapperInfo(envVariables); 64 | 65 | await downloadAndInstallBuildWrapper(buildWrapperUrl, envVariables); 66 | 67 | const buildWrapperBinDir = await getRealPath( 68 | buildWrapperDir, 69 | envVariables.runnerOS 70 | ); 71 | core.addPath(buildWrapperBinDir); 72 | core.info(`'${buildWrapperBinDir}' added to the path`); 73 | 74 | const buildWrapperBinPath = await getRealPath( 75 | buildWrapperBin, 76 | envVariables.runnerOS 77 | ); 78 | core.setOutput("build-wrapper-binary", buildWrapperBinPath); 79 | core.info(`'build-wrapper-binary' output set to '${buildWrapperBinPath}'`); 80 | } catch (error) { 81 | core.setFailed(error.message); 82 | } 83 | } 84 | 85 | run(); 86 | -------------------------------------------------------------------------------- /src/install-build-wrapper/__tests__/utils.test.js: -------------------------------------------------------------------------------- 1 | import assert from "node:assert/strict"; 2 | import { describe, it } from "node:test"; 3 | import { getBuildWrapperInfo } from "../utils.js"; 4 | 5 | describe("getBuildWrapperInfo", () => { 6 | const supportedPlatforms = [ 7 | { 8 | platform: "Linux", 9 | arch: "X64", 10 | expectedSuffix: "linux-x86", 11 | expectedName: "build-wrapper-linux-x86-64", 12 | }, 13 | { 14 | platform: "Linux", 15 | arch: "ARM64", 16 | expectedSuffix: "linux-aarch64", 17 | expectedName: "build-wrapper-linux-aarch64", 18 | }, 19 | { 20 | platform: "Windows", 21 | arch: "X64", 22 | expectedSuffix: "win-x86", 23 | expectedName: "build-wrapper-win-x86-64.exe", 24 | }, 25 | { 26 | platform: "macOS", 27 | arch: "X64", 28 | expectedSuffix: "macosx-x86", 29 | expectedName: "build-wrapper-macosx-x86", 30 | }, 31 | { 32 | platform: "macOS", 33 | arch: "ARM64", 34 | expectedSuffix: "macosx-x86", 35 | expectedName: "build-wrapper-macosx-x86", 36 | }, 37 | ]; 38 | 39 | const unsupportedPlatforms = [ 40 | { platform: "linux", arch: "arm" }, 41 | { platform: "openbsd", arch: "X64" }, 42 | { platform: undefined, arch: "X64" }, 43 | { platform: "Linux", arch: undefined }, 44 | { platform: null, arch: "X64" }, 45 | { platform: "Linux", arch: null }, 46 | ]; 47 | 48 | supportedPlatforms.forEach( 49 | ({ platform, arch, expectedSuffix, expectedName }) => { 50 | it(`returns ${expectedName} for ${platform} ${arch}`, () => { 51 | const result = getBuildWrapperInfo({ 52 | runnerOS: platform, 53 | runnerArch: arch, 54 | runnerTemp: "/tmp", 55 | sonarHostUrl: "https://sonarcloud.io" 56 | }); 57 | assert.equal(result.buildWrapperUrl, `https://sonarcloud.io/static/cpp/build-wrapper-${expectedSuffix}.zip`); 58 | assert.equal(result.buildWrapperDir, `/tmp/build-wrapper-${expectedSuffix}`); 59 | assert.equal(result.buildWrapperBin, `/tmp/build-wrapper-${expectedSuffix}/${expectedName}`); 60 | }); 61 | } 62 | ); 63 | 64 | unsupportedPlatforms.forEach(({ platform, arch }) => { 65 | it(`throws for unsupported platform ${platform} ${arch}`, () => { 66 | assert.throws( 67 | () => getBuildWrapperInfo({ 68 | runnerOS: platform, 69 | runnerArch: arch, 70 | runnerTemp: "/tmp", 71 | sonarHostUrl: "https://sonarcloud.io" 72 | }), 73 | (error) => { 74 | return error.message.includes('unsupported') || error.message.includes('Unsupported'); 75 | }, 76 | `should have thrown for ${platform} ${arch}` 77 | ); 78 | }); 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /src/install-build-wrapper/utils.js: -------------------------------------------------------------------------------- 1 | import * as exec from "@actions/exec"; 2 | import * as path from "path"; 3 | 4 | /** 5 | * Compute all names and paths related to the build wrapper 6 | * based on the runner environment 7 | */ 8 | export function getBuildWrapperInfo({ 9 | runnerOS, 10 | runnerArch, 11 | runnerTemp, 12 | sonarHostUrl, 13 | }) { 14 | const { buildWrapperSuffix, buildWrapperName } = getSuffixAndName( 15 | runnerOS, 16 | runnerArch 17 | ); 18 | 19 | const buildWrapperDir = `${runnerTemp}/build-wrapper-${buildWrapperSuffix}`; 20 | const buildWrapperUrl = `${sonarHostUrl}/static/cpp/build-wrapper-${buildWrapperSuffix}.zip`; 21 | const buildWrapperBin = `${buildWrapperDir}/${buildWrapperName}`; 22 | 23 | return { 24 | buildWrapperUrl, 25 | buildWrapperDir, 26 | buildWrapperBin, 27 | }; 28 | } 29 | 30 | function getSuffixAndName(runnerOS, runnerArch) { 31 | if ( 32 | runnerArch !== "X64" && 33 | !(runnerArch === "ARM64" && (runnerOS === "macOS" || runnerOS === "Linux")) 34 | ) { 35 | throw new Error( 36 | `Architecture '${runnerArch}' is unsupported by build-wrapper` 37 | ); 38 | } 39 | 40 | switch (runnerOS) { 41 | case "Windows": 42 | return { 43 | buildWrapperSuffix: "win-x86", 44 | buildWrapperName: "build-wrapper-win-x86-64.exe", 45 | }; 46 | 47 | case "Linux": 48 | switch (runnerArch) { 49 | case "X64": 50 | return { 51 | buildWrapperSuffix: "linux-x86", 52 | buildWrapperName: "build-wrapper-linux-x86-64", 53 | }; 54 | 55 | case "ARM64": 56 | return { 57 | buildWrapperSuffix: "linux-aarch64", 58 | buildWrapperName: "build-wrapper-linux-aarch64", 59 | }; 60 | } 61 | break; // handled before the switch 62 | 63 | case "macOS": 64 | return { 65 | buildWrapperSuffix: "macosx-x86", 66 | buildWrapperName: "build-wrapper-macosx-x86", 67 | }; 68 | 69 | default: 70 | throw new Error(`Unsupported runner OS '${runnerOS}'`); 71 | } 72 | } 73 | 74 | export async function getRealPath(filePath, runnerOS) { 75 | switch (runnerOS) { 76 | case "Windows": { 77 | const windowsResult = await exec.getExecOutput("cygpath", [ 78 | "--absolute", 79 | "--windows", 80 | filePath, 81 | ]); 82 | return windowsResult.stdout.trim(); 83 | } 84 | case "Linux": { 85 | const linuxResult = await exec.getExecOutput("readlink", [ 86 | "-f", 87 | filePath, 88 | ]); 89 | return linuxResult.stdout.trim(); 90 | } 91 | case "macOS": { 92 | const macResult = await exec.getExecOutput("greadlink", ["-f", filePath]); 93 | return macResult.stdout.trim(); 94 | } 95 | default: 96 | return path.resolve(filePath); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/__tests__/utils.test.js: -------------------------------------------------------------------------------- 1 | import assert from "node:assert/strict"; 2 | import { describe, it } from "node:test"; 3 | import { 4 | getPlatformFlavor, 5 | getScannerDownloadURL, 6 | scannerDirName, 7 | } from "../utils.js"; 8 | 9 | describe("getPlatformFlavor", () => { 10 | const supportedPlatforms = [ 11 | { platform: "linux", arch: "x64", expected: "linux-x64" }, 12 | { platform: "linux", arch: "arm64", expected: "linux-aarch64" }, 13 | { platform: "win32", arch: "x64", expected: "windows-x64" }, 14 | { platform: "darwin", arch: "x64", expected: "macosx-x64" }, 15 | { platform: "darwin", arch: "arm64", expected: "macosx-aarch64" }, 16 | ]; 17 | 18 | const unsupportedPlatforms = [ 19 | { platform: "linux", arch: "arm" }, 20 | { platform: "openbsd", arch: "x64" }, 21 | { platform: undefined, arch: "x64" }, 22 | { platform: "linux", arch: undefined }, 23 | { platform: null, arch: "x64" }, 24 | { platform: "linux", arch: null }, 25 | ]; 26 | 27 | supportedPlatforms.forEach(({ platform, arch, expected }) => { 28 | it(`returns ${expected} for ${platform} ${arch}`, () => { 29 | assert.equal(getPlatformFlavor(platform, arch), expected); 30 | }); 31 | }); 32 | 33 | unsupportedPlatforms.forEach(({ platform, arch }) => { 34 | it(`throws for unsupported platform ${platform} ${arch}`, () => { 35 | assert.throws( 36 | () => getPlatformFlavor(platform, arch), 37 | { 38 | message: `Platform ${platform} ${arch} not supported`, 39 | }, 40 | `should have thrown for ${platform} ${arch}` 41 | ); 42 | }); 43 | }); 44 | }); 45 | 46 | describe("getScannerDownloadURL", () => { 47 | it("generates correct URL without trailing slash", () => { 48 | const result = getScannerDownloadURL({ 49 | scannerBinariesUrl: 50 | "https://binaries.sonarsource.com/Distribution/sonar-scanner-cli", 51 | scannerVersion: "7.2.0.5079", 52 | flavor: "linux-x64", 53 | }); 54 | assert.equal( 55 | result, 56 | "https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-7.2.0.5079-linux-x64.zip" 57 | ); 58 | }); 59 | 60 | it("generates correct URL with trailing slash", () => { 61 | const result = getScannerDownloadURL({ 62 | scannerBinariesUrl: 63 | "https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/", 64 | scannerVersion: "7.2.0.5079", 65 | flavor: "linux-x64", 66 | }); 67 | assert.equal( 68 | result, 69 | "https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-7.2.0.5079-linux-x64.zip" 70 | ); 71 | }); 72 | }); 73 | 74 | describe("scannerDirName", () => { 75 | it("handles special characters", () => { 76 | assert.equal( 77 | scannerDirName("7.2.0-SNAPSHOT", "linux_x64"), 78 | "sonar-scanner-7.2.0-SNAPSHOT-linux_x64" 79 | ); 80 | }); 81 | }); 82 | -------------------------------------------------------------------------------- /.github/workflows/qa-deprecated-c-cpp.yml: -------------------------------------------------------------------------------- 1 | name: QA Deprecated C and C++ action 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | types: [opened, synchronize, reopened] 9 | 10 | jobs: 11 | output-test: 12 | name: Action outputs 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest, macos-14] 17 | cache: [true, false] 18 | include: 19 | - arch: X64 20 | - os: macos-latest 21 | arch: ARM64 22 | - os: macos-14 23 | arch: ARM64 24 | runs-on: ${{ matrix.os }} 25 | steps: 26 | # Specifying a specific architecture of the runner is not possible for Github hosted runners 27 | # We can only check if the runner architecture matches the expected one 28 | - name: check_runner_arch 29 | shell: bash 30 | run: | 31 | echo "Runner architecture: ${{ runner.arch }}" 32 | if [[ "${{ runner.arch }}" != "${{ matrix.arch }}" ]]; then 33 | echo "##[error]Runner architecture does not match the expected one" 34 | exit 1 35 | fi 36 | 37 | - uses: actions/checkout@v5 38 | with: 39 | fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis 40 | 41 | - name: Run SonarQube C/C++ action 42 | id: run-action 43 | uses: ./deprecated-c-cpp 44 | env: 45 | SONAR_HOST_URL: 'https://next.sonarqube.com/sonarqube/' 46 | with: 47 | cache-binaries: ${{ matrix.cache }} 48 | 49 | - name: SONAR_HOST_URL is set 50 | shell: bash 51 | run: | 52 | [[ $SONAR_HOST_URL == "https://next.sonarqube.com/sonarqube/" ]] 53 | 54 | - name: sonar-scanner is installed and in PATH 55 | run: | 56 | sonar-scanner --help | grep "usage: sonar-scanner " 57 | 58 | - name: sonar-scanner-binary output is correct 59 | shell: bash 60 | env: 61 | BINARY: ${{ steps.run-action.outputs.sonar-scanner-binary }} 62 | run: | 63 | "$BINARY" --help | grep "usage: sonar-scanner " 64 | 65 | # build-wrapper does not have --help or equivalent option. 66 | # Pass to few arguments and ignore error code 67 | - name: build-wrapper is installed and in PATH on Windows 68 | if: runner.os == 'Windows' 69 | shell: bash 70 | run: | 71 | (build-wrapper-win-x86-64.exe || true) | grep "build-wrapper, version " 72 | 73 | - name: build-wrapper is installed and in PATH on Linux 74 | if: runner.os == 'Linux' 75 | shell: bash 76 | run: | 77 | (build-wrapper-linux-x86-64 || true) | grep "build-wrapper, version " 78 | 79 | - name: build-wrapper is installed and in PATH on macOS 80 | if: runner.os == 'macOs' 81 | shell: bash 82 | run: | 83 | (build-wrapper-macosx-x86 || true) | grep "build-wrapper, version " 84 | 85 | - name: build-wrapper-binary output is correct 86 | shell: bash 87 | env: 88 | BINARY: ${{ steps.run-action.outputs.build-wrapper-binary }} 89 | run: | 90 | ("$BINARY" || true) | grep "build-wrapper, version " 91 | -------------------------------------------------------------------------------- /.github/workflows/version_update.yml: -------------------------------------------------------------------------------- 1 | name: sonar-scanner version check 2 | on: 3 | workflow_dispatch: 4 | schedule: 5 | - cron: '15 10 * * *' 6 | 7 | jobs: 8 | check-version: 9 | name: Check for sonar-scanner version update 10 | runs-on: github-ubuntu-latest-s 11 | outputs: 12 | should_update: ${{ steps.version-check.outputs.should_update }} 13 | new-version: ${{ steps.latest-version.outputs.sonar-scanner-version }} 14 | steps: 15 | - run: sudo apt install -y jq 16 | - uses: actions/checkout@v5 17 | with: 18 | ref: master 19 | fetch-depth: 0 20 | 21 | - name: "Fetch currently used sonar-scanner version" 22 | id: tagged-version 23 | shell: bash 24 | run: cat sonar-scanner-version >> $GITHUB_OUTPUT 25 | 26 | - name: "Fetch latest sonar-scanner version" 27 | id: latest-version 28 | shell: bash 29 | run: | 30 | ./scripts/fetch_latest_version.sh > sonar-scanner-version 31 | cat sonar-scanner-version >> $GITHUB_OUTPUT 32 | 33 | - name: "Determine if update is needed" 34 | id: version-check 35 | shell: bash 36 | run: | 37 | if [[ "${{ steps.tagged-version.outputs.sonar-scanner-version }}" != "${{ steps.latest-version.outputs.sonar-scanner-version }}" ]]; then 38 | echo "should_update=true" >> $GITHUB_OUTPUT 39 | else 40 | echo "should_update=false" >> $GITHUB_OUTPUT 41 | fi 42 | 43 | update-version: 44 | name: Prepare pull request for sonar-scanner version update 45 | needs: check-version 46 | runs-on: github-ubuntu-latest-s 47 | permissions: 48 | contents: write 49 | pull-requests: write 50 | if: needs.check-version.outputs.should_update == 'true' 51 | steps: 52 | - uses: actions/checkout@v5 53 | with: 54 | ref: master 55 | persist-credentials: true 56 | fetch-depth: 0 57 | - run: sudo snap install yq 58 | - name: "Update default version" 59 | shell: bash 60 | env: 61 | NEW_VERSION: ${{ needs.check-version.outputs.new-version }} 62 | run: | 63 | yq -i '.inputs.scannerVersion.default = strenv(NEW_VERSION)' action.yml 64 | ./scripts/fetch_latest_version.sh > sonar-scanner-version 65 | - name: "Create Pull Request for version update" 66 | shell: bash 67 | env: 68 | UPDATE_BRANCH: update-to-sonar-scanner-${{ needs.check-version.outputs.new-version }} 69 | TITLE: "Update SonarScanner CLI to ${{ needs.check-version.outputs.new-version }}" 70 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 71 | run: | 72 | git config --global user.name "SonarTech" 73 | git config --global user.email "sonartech@sonarsource.com" 74 | git checkout -b ${UPDATE_BRANCH} 75 | git add sonar-scanner-version 76 | git add action.yml 77 | git commit -m "${TITLE}" 78 | git push --force-with-lease origin ${UPDATE_BRANCH} 79 | gh pr list 80 | 81 | if [[ $(gh pr list -H "${UPDATE_BRANCH}" | grep "${UPDATE_BRANCH}" | wc -l) -eq 0 ]]; then 82 | 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)." 83 | fi 84 | -------------------------------------------------------------------------------- /src/main/run-sonar-scanner.js: -------------------------------------------------------------------------------- 1 | import * as core from "@actions/core"; 2 | import * as exec from "@actions/exec"; 3 | import * as fs from "fs"; 4 | import * as os from "os"; 5 | import * as path from "path"; 6 | import { parseArgsStringToArgv } from "string-argv"; 7 | 8 | const KEYTOOL_MAIN_CLASS = "sun.security.tools.keytool.Main"; 9 | const TRUSTSTORE_PASSWORD = "changeit"; // default password of the Java truststore! 10 | 11 | export async function runSonarScanner( 12 | inputArgs, 13 | projectBaseDir, 14 | scannerDir, 15 | runnerEnv = {} 16 | ) { 17 | const { runnerDebug, runnerOs, runnerTemp, sonarRootCert, sonarcloudUrl } = 18 | runnerEnv; 19 | 20 | const scannerBin = 21 | runnerOs === "Windows" ? "sonar-scanner.bat" : "sonar-scanner"; 22 | 23 | const scannerArgs = []; 24 | 25 | /** 26 | * Not sanitization is needed when populating scannerArgs. 27 | * @actions/exec will take care of sanitizing the args it receives. 28 | */ 29 | 30 | if (sonarcloudUrl) { 31 | scannerArgs.push(`-Dsonar.scanner.sonarcloudUrl=${sonarcloudUrl}`); 32 | } 33 | 34 | if (runnerDebug === "1") { 35 | scannerArgs.push("--debug"); 36 | } 37 | 38 | if (projectBaseDir) { 39 | scannerArgs.push(`-Dsonar.projectBaseDir=${projectBaseDir}`); 40 | } 41 | 42 | // The SSL folder may exist on an uncleaned self-hosted runner 43 | const sslFolder = path.join(os.homedir(), ".sonar", "ssl"); 44 | const truststoreFile = path.join(sslFolder, "truststore.p12"); 45 | 46 | const keytoolParams = { 47 | scannerDir, 48 | truststoreFile, 49 | }; 50 | 51 | if (fs.existsSync(truststoreFile)) { 52 | let aliasSonarIsPresent = true; 53 | 54 | try { 55 | await checkSonarAliasInTruststore(keytoolParams); 56 | } catch (_) { 57 | aliasSonarIsPresent = false; 58 | core.info( 59 | `Existing Scanner truststore ${truststoreFile} does not contain 'sonar' alias` 60 | ); 61 | } 62 | 63 | if (aliasSonarIsPresent) { 64 | core.info( 65 | `Removing 'sonar' alias from already existing Scanner truststore: ${truststoreFile}` 66 | ); 67 | await deleteSonarAliasFromTruststore(keytoolParams); 68 | } 69 | } 70 | 71 | if (sonarRootCert) { 72 | core.info("Adding SSL certificate to the Scanner truststore"); 73 | const tempCertPath = path.join(runnerTemp, "tmpcert.pem"); 74 | 75 | try { 76 | fs.unlinkSync(tempCertPath); 77 | } catch (_) { 78 | // File doesn't exist, ignore 79 | } 80 | 81 | fs.writeFileSync(tempCertPath, sonarRootCert); 82 | fs.mkdirSync(sslFolder, { recursive: true }); 83 | 84 | await importCertificateToTruststore(keytoolParams, tempCertPath); 85 | 86 | scannerArgs.push( 87 | `-Dsonar.scanner.truststorePassword=${TRUSTSTORE_PASSWORD}` 88 | ); 89 | } 90 | 91 | if (inputArgs) { 92 | /** 93 | * No sanitization, but it is parsing a string into an array of arguments in a safe way (= no command execution), 94 | * and with good enough support of quotes to support arguments containing spaces. 95 | */ 96 | const args = parseArgsStringToArgv(inputArgs); 97 | scannerArgs.push(...args); 98 | } 99 | 100 | /** 101 | * Arguments are sanitized by `exec` 102 | */ 103 | await exec.exec(scannerBin, scannerArgs); 104 | } 105 | 106 | /** 107 | * Use keytool for now, as SonarQube 10.6 and below doesn't support openssl generated keystores 108 | * keytool requires a password > 6 characters, so we won't use the default password 'sonar' 109 | */ 110 | function executeKeytoolCommand({ 111 | scannerDir, 112 | truststoreFile, 113 | extraArgs, 114 | options = {}, 115 | }) { 116 | const baseArgs = [ 117 | KEYTOOL_MAIN_CLASS, 118 | "-storetype", 119 | "PKCS12", 120 | "-keystore", 121 | truststoreFile, 122 | "-storepass", 123 | TRUSTSTORE_PASSWORD, 124 | "-noprompt", 125 | "-trustcacerts", 126 | ...extraArgs, 127 | ]; 128 | 129 | return exec.exec(`${scannerDir}/jre/bin/java`, baseArgs, options); 130 | } 131 | 132 | function importCertificateToTruststore(keytoolParams, certPath) { 133 | return executeKeytoolCommand({ 134 | ...keytoolParams, 135 | extraArgs: ["-importcert", "-alias", "sonar", "-file", certPath], 136 | }); 137 | } 138 | 139 | function checkSonarAliasInTruststore(keytoolParams) { 140 | return executeKeytoolCommand({ 141 | ...keytoolParams, 142 | extraArgs: ["-list", "-v", "-alias", "sonar"], 143 | options: { silent: true }, 144 | }); 145 | } 146 | 147 | function deleteSonarAliasFromTruststore(keytoolParams) { 148 | return executeKeytoolCommand({ 149 | ...keytoolParams, 150 | extraArgs: ["-delete", "-alias", "sonar"], 151 | }); 152 | } 153 | -------------------------------------------------------------------------------- /src/main/__tests__/sanity-checks.test.js: -------------------------------------------------------------------------------- 1 | import mockfs from "mock-fs"; 2 | import assert from "node:assert/strict"; 3 | import { describe, it, mock } from "node:test"; 4 | import { 5 | checkGradleProject, 6 | checkMavenProject, 7 | checkSonarToken, 8 | validateScannerVersion, 9 | } from "../sanity-checks.js"; 10 | import { mockCore } from "./mocks.js"; 11 | 12 | describe("validateScannerVersion", () => { 13 | const expected = 14 | "Invalid scannerVersion format. Expected format: x.y.z.w (e.g., 7.1.0.4889)"; 15 | 16 | const validVersions = [undefined, "", "7.1.0.4889", "1.2.3.4"]; 17 | 18 | const invalidVersions = [ 19 | "wrong", 20 | "4.2.", 21 | "7.1.0", 22 | "7.1.0.abc", 23 | "7.1.0.4889.5", 24 | "7.1", 25 | "7", 26 | "7.1.0.", 27 | ".7.1.0.4889", 28 | "7..1.0.4889", 29 | "7.1..0.4889", 30 | "7.1.0..4889", 31 | "a.b.c.d", 32 | "7.1.0.4889-SNAPSHOT", 33 | "v7.1.0.4889", 34 | "7.1.0.4889.0.0", 35 | "-7.1.0.4889", 36 | "7.-1.0.4889", 37 | "7.1.-0.4889", 38 | "7.1.0.-4889", 39 | "7.1.0.4889 ", 40 | " 7.1.0.4889", 41 | "7.1.0.4889\n", 42 | "7,1,0,4889", 43 | ]; 44 | 45 | validVersions.forEach((version) => { 46 | it(`accepts ${version}`, () => { 47 | assert.equal(validateScannerVersion(version), undefined); 48 | }); 49 | }); 50 | 51 | invalidVersions.forEach((version) => 52 | it(`throws for ${version}`, () => { 53 | assert.throws( 54 | () => validateScannerVersion(version), 55 | { 56 | message: expected, 57 | }, 58 | `should have thrown for ${version}` 59 | ); 60 | }) 61 | ); 62 | }); 63 | 64 | describe("checkSonarToken", () => { 65 | it("calls core.warning when SONAR_TOKEN is not set", () => { 66 | const warning = mock.fn(); 67 | 68 | checkSonarToken(mockCore({ warning })); 69 | 70 | assert.equal(warning.mock.calls.length, 1); 71 | assert.equal( 72 | warning.mock.calls[0].arguments[0], 73 | "Running this GitHub Action without SONAR_TOKEN is not recommended" 74 | ); 75 | }); 76 | 77 | it("does not call core.warning when SONAR_TOKEN is set", () => { 78 | const warning = mock.fn(); 79 | 80 | checkSonarToken(mockCore({ warning }), "test-token"); 81 | 82 | assert.equal(warning.mock.calls.length, 0); 83 | }); 84 | }); 85 | 86 | describe("checkMavenProject", () => { 87 | it("calls core.warning when pom.xml exists", async () => { 88 | mockfs({ "/test/project/": { "pom.xml": "" } }); 89 | const warning = mock.fn(); 90 | 91 | checkMavenProject({ warning }, "/test/project"); 92 | 93 | assert.equal(warning.mock.calls.length, 1); 94 | assert.equal( 95 | warning.mock.calls[0].arguments[0], 96 | "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." 97 | ); 98 | 99 | mockfs.restore(); 100 | }); 101 | 102 | it("does not call core.warning when pom.xml does not exist", async () => { 103 | mockfs({ "/test/project/": {} }); 104 | const warning = mock.fn(); 105 | 106 | checkMavenProject(mockCore({ warning }), "/test/project"); 107 | 108 | assert.equal(warning.mock.calls.length, 0); 109 | 110 | mockfs.restore(); 111 | }); 112 | 113 | it("handles project base dir with trailing slash", async () => { 114 | mockfs({ "/test/project/": { "pom.xml": "" } }); 115 | const warning = mock.fn(); 116 | 117 | checkMavenProject(mockCore({ warning }), "/test/project/"); 118 | assert.equal(warning.mock.calls.length, 1); 119 | 120 | mockfs.restore(); 121 | }); 122 | }); 123 | 124 | describe("checkGradleProject", () => { 125 | it("calls core.warning when build.gradle exists", async () => { 126 | mockfs({ "/test/project/": { "build.gradle": "" } }); 127 | 128 | const warning = mock.fn(); 129 | 130 | checkGradleProject(mockCore({ warning }), "/test/project"); 131 | 132 | assert.equal(warning.mock.calls.length, 1); 133 | assert.equal( 134 | warning.mock.calls[0].arguments[0], 135 | "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." 136 | ); 137 | 138 | mockfs.restore(); 139 | }); 140 | 141 | it("calls core.warning when build.gradle.kts exists", async () => { 142 | mockfs({ "/test/project/": { "build.gradle.kts": "" } }); 143 | 144 | const warning = mock.fn(); 145 | 146 | checkGradleProject(mockCore({ warning }), "/test/project"); 147 | 148 | assert.equal(warning.mock.calls.length, 1); 149 | assert.equal( 150 | warning.mock.calls[0].arguments[0], 151 | "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." 152 | ); 153 | 154 | mockfs.restore(); 155 | }); 156 | 157 | it("does not call core.warning when neither gradle file exists", async () => { 158 | mockfs({ "/test/project/": {} }); 159 | 160 | const warning = mock.fn(); 161 | 162 | checkGradleProject(mockCore({ warning }), "/test/project"); 163 | 164 | assert.equal(warning.mock.calls.length, 0); 165 | 166 | mockfs.restore(); 167 | }); 168 | 169 | it("handles project base dir with trailing slash", async () => { 170 | mockfs({ "/test/project/": { "build.gradle": "" } }); 171 | const warning = mock.fn(); 172 | 173 | checkGradleProject(mockCore({ warning }), "/test/project/"); 174 | 175 | assert.equal(warning.mock.calls.length, 1); 176 | }); 177 | }); 178 | -------------------------------------------------------------------------------- /dist/install-build-wrapper.js: -------------------------------------------------------------------------------- 1 | import { f as execExports, e as coreExports } from './exec-BTlTa8sL.js'; 2 | import * as fs from 'fs'; 3 | import * as path from 'path'; 4 | import 'os'; 5 | import 'crypto'; 6 | import 'http'; 7 | import 'https'; 8 | import 'net'; 9 | import 'tls'; 10 | import 'events'; 11 | import 'assert'; 12 | import 'util'; 13 | import 'stream'; 14 | import 'buffer'; 15 | import 'querystring'; 16 | import 'stream/web'; 17 | import 'node:stream'; 18 | import 'node:util'; 19 | import 'node:events'; 20 | import 'worker_threads'; 21 | import 'perf_hooks'; 22 | import 'util/types'; 23 | import 'async_hooks'; 24 | import 'console'; 25 | import 'url'; 26 | import 'zlib'; 27 | import 'string_decoder'; 28 | import 'diagnostics_channel'; 29 | import 'child_process'; 30 | import 'timers'; 31 | 32 | /** 33 | * Compute all names and paths related to the build wrapper 34 | * based on the runner environment 35 | */ 36 | function getBuildWrapperInfo({ 37 | runnerOS, 38 | runnerArch, 39 | runnerTemp, 40 | sonarHostUrl, 41 | }) { 42 | const { buildWrapperSuffix, buildWrapperName } = getSuffixAndName( 43 | runnerOS, 44 | runnerArch 45 | ); 46 | 47 | const buildWrapperDir = `${runnerTemp}/build-wrapper-${buildWrapperSuffix}`; 48 | const buildWrapperUrl = `${sonarHostUrl}/static/cpp/build-wrapper-${buildWrapperSuffix}.zip`; 49 | const buildWrapperBin = `${buildWrapperDir}/${buildWrapperName}`; 50 | 51 | return { 52 | buildWrapperUrl, 53 | buildWrapperDir, 54 | buildWrapperBin, 55 | }; 56 | } 57 | 58 | function getSuffixAndName(runnerOS, runnerArch) { 59 | if ( 60 | runnerArch !== "X64" && 61 | !(runnerArch === "ARM64" && (runnerOS === "macOS" || runnerOS === "Linux")) 62 | ) { 63 | throw new Error( 64 | `Architecture '${runnerArch}' is unsupported by build-wrapper` 65 | ); 66 | } 67 | 68 | switch (runnerOS) { 69 | case "Windows": 70 | return { 71 | buildWrapperSuffix: "win-x86", 72 | buildWrapperName: "build-wrapper-win-x86-64.exe", 73 | }; 74 | 75 | case "Linux": 76 | switch (runnerArch) { 77 | case "X64": 78 | return { 79 | buildWrapperSuffix: "linux-x86", 80 | buildWrapperName: "build-wrapper-linux-x86-64", 81 | }; 82 | 83 | case "ARM64": 84 | return { 85 | buildWrapperSuffix: "linux-aarch64", 86 | buildWrapperName: "build-wrapper-linux-aarch64", 87 | }; 88 | } 89 | break; // handled before the switch 90 | 91 | case "macOS": 92 | return { 93 | buildWrapperSuffix: "macosx-x86", 94 | buildWrapperName: "build-wrapper-macosx-x86", 95 | }; 96 | 97 | default: 98 | throw new Error(`Unsupported runner OS '${runnerOS}'`); 99 | } 100 | } 101 | 102 | async function getRealPath(filePath, runnerOS) { 103 | switch (runnerOS) { 104 | case "Windows": { 105 | const windowsResult = await execExports.getExecOutput("cygpath", [ 106 | "--absolute", 107 | "--windows", 108 | filePath, 109 | ]); 110 | return windowsResult.stdout.trim(); 111 | } 112 | case "Linux": { 113 | const linuxResult = await execExports.getExecOutput("readlink", [ 114 | "-f", 115 | filePath, 116 | ]); 117 | return linuxResult.stdout.trim(); 118 | } 119 | case "macOS": { 120 | const macResult = await execExports.getExecOutput("greadlink", ["-f", filePath]); 121 | return macResult.stdout.trim(); 122 | } 123 | default: 124 | return path.resolve(filePath); 125 | } 126 | } 127 | 128 | async function installMacOSPackages() { 129 | if (process.platform === "darwin") { 130 | coreExports.info("Installing required packages for macOS"); 131 | await execExports.exec("brew", ["install", "coreutils"]); 132 | } 133 | } 134 | 135 | /** 136 | * These RUNNER_XX env variables come from GitHub by default. 137 | * See https://docs.github.com/en/actions/reference/workflows-and-actions/variables#default-environment-variables 138 | * 139 | * If SONAR_HOST_URL is omitted, we assume sonarcloud.io 140 | */ 141 | function getEnvVariables() { 142 | const sonarHostUrl = process.env.SONAR_HOST_URL 143 | ? process.env.SONAR_HOST_URL.replace(/\/$/, "") 144 | : "https://sonarcloud.io"; 145 | 146 | return { 147 | runnerOS: process.env.RUNNER_OS, 148 | runnerArch: process.env.RUNNER_ARCH, 149 | runnerTemp: process.env.RUNNER_TEMP, 150 | sonarHostUrl, 151 | }; 152 | } 153 | 154 | async function downloadAndInstallBuildWrapper(downloadUrl, runnerEnv) { 155 | const { runnerArch, runnerOS, runnerTemp } = runnerEnv; 156 | const tmpZipPath = path.join( 157 | runnerTemp, 158 | `build-wrapper-${runnerOS}-${runnerArch}.zip` 159 | ); 160 | 161 | coreExports.startGroup(`Download ${downloadUrl}`); 162 | 163 | coreExports.info(`Downloading '${downloadUrl}'`); 164 | 165 | if (!fs.existsSync(runnerTemp)) { 166 | fs.mkdirSync(runnerTemp, { recursive: true }); 167 | } 168 | 169 | await execExports.exec("curl", ["-sSLo", tmpZipPath, downloadUrl]); 170 | 171 | coreExports.info("Decompressing"); 172 | await execExports.exec("unzip", ["-o", "-d", runnerTemp, tmpZipPath]); 173 | 174 | coreExports.endGroup(); 175 | } 176 | 177 | async function run() { 178 | try { 179 | await installMacOSPackages(); 180 | 181 | const envVariables = getEnvVariables(); 182 | 183 | const { buildWrapperBin, buildWrapperDir, buildWrapperUrl } = 184 | getBuildWrapperInfo(envVariables); 185 | 186 | await downloadAndInstallBuildWrapper(buildWrapperUrl, envVariables); 187 | 188 | const buildWrapperBinDir = await getRealPath( 189 | buildWrapperDir, 190 | envVariables.runnerOS 191 | ); 192 | coreExports.addPath(buildWrapperBinDir); 193 | coreExports.info(`'${buildWrapperBinDir}' added to the path`); 194 | 195 | const buildWrapperBinPath = await getRealPath( 196 | buildWrapperBin, 197 | envVariables.runnerOS 198 | ); 199 | coreExports.setOutput("build-wrapper-binary", buildWrapperBinPath); 200 | coreExports.info(`'build-wrapper-binary' output set to '${buildWrapperBinPath}'`); 201 | } catch (error) { 202 | coreExports.setFailed(error.message); 203 | } 204 | } 205 | 206 | run(); 207 | //# sourceMappingURL=install-build-wrapper.js.map 208 | -------------------------------------------------------------------------------- /deprecated-c-cpp/action.yml: -------------------------------------------------------------------------------- 1 | name: 'SonarQube Scan for C and C++' 2 | description: 'Scan your C and C++ code with SonarQube to detect bugs, vulnerabilities and code smells.' 3 | branding: 4 | icon: check 5 | color: green 6 | inputs: 7 | installation-path: 8 | description: 'Directory where the sonar-scanner and build wrapper will be installed. Created if does not exists.' 9 | required: false 10 | default: '.sonar' 11 | cache-binaries: 12 | description: 'Controls if installed binaries are cached using GitHub cache.' 13 | required: false 14 | default: 'true' 15 | 16 | outputs: 17 | sonar-scanner-binary: 18 | description: "Absolute path to sonar-scanner binary." 19 | value: ${{ steps.setup-outputs.outputs.sonar-scanner-binary }} 20 | build-wrapper-binary: 21 | description: "Absolute path to build-wrapper binary." 22 | value: ${{ steps.setup-outputs.outputs.build-wrapper-binary }} 23 | 24 | runs: 25 | using: "composite" 26 | steps: 27 | # install packaged required for greadlink and sha256sum command on macOS 28 | - name: Install required packages for macOS 29 | if: runner.os == 'macOS' 30 | shell: bash 31 | run: brew install coreutils 32 | 33 | - name: Set SONAR_HOST_URL to 'https://sonarcloud.io' 34 | if: env.SONAR_HOST_URL == '' 35 | shell: bash 36 | run: | 37 | echo "Setting SONAR_HOST_URL to 'https://sonarcloud.io'" 38 | echo "SONAR_HOST_URL=https://sonarcloud.io" >> $GITHUB_ENV 39 | 40 | - name: Verify and create installation path 41 | shell: bash 42 | env: 43 | INSTALL_PATH: ${{ inputs.installation-path }} 44 | run: ${GITHUB_ACTION_PATH}/../scripts/create_install_path.sh 45 | 46 | - name: Set version of sonar-scanner 47 | id: sonar-scanner-version 48 | shell: bash 49 | run: cat ${GITHUB_ACTION_PATH}/../sonar-scanner-version >> $GITHUB_OUTPUT 50 | 51 | - name: Configure paths 52 | id: configure_paths 53 | shell: bash 54 | env: 55 | OS: ${{ runner.os }} 56 | ARCH: ${{ runner.arch }} 57 | INSTALL_PATH: ${{ inputs.installation-path }} 58 | SONAR_SCANNER_VERSION: ${{ steps.sonar-scanner-version.outputs.sonar-scanner-version }} 59 | SONAR_SCANNER_URL_WINDOWS_X64: ${{ steps.sonar-scanner-version.outputs.sonar-scanner-url-windows-x64 }} 60 | SONAR_SCANNER_SHA_WINDOWS_X64: ${{ steps.sonar-scanner-version.outputs.sonar-scanner-sha-windows-x64 }} 61 | SONAR_SCANNER_URL_LINUX_X64: ${{ steps.sonar-scanner-version.outputs.sonar-scanner-url-linux-x64 }} 62 | SONAR_SCANNER_SHA_LINUX_X64: ${{ steps.sonar-scanner-version.outputs.sonar-scanner-sha-linux-x64 }} 63 | SONAR_SCANNER_URL_LINUX_AARCH64: ${{ steps.sonar-scanner-version.outputs.sonar-scanner-url-linux-aarch64 }} 64 | SONAR_SCANNER_SHA_LINUX_AARCH64: ${{ steps.sonar-scanner-version.outputs.sonar-scanner-sha-linux-aarch64 }} 65 | SONAR_SCANNER_URL_MACOSX_X64: ${{ steps.sonar-scanner-version.outputs.sonar-scanner-url-macosx-x64 }} 66 | SONAR_SCANNER_SHA_MACOSX_X64: ${{ steps.sonar-scanner-version.outputs.sonar-scanner-sha-macosx-x64 }} 67 | SONAR_SCANNER_URL_MACOSX_AARCH64: ${{ steps.sonar-scanner-version.outputs.sonar-scanner-url-macosx-aarch64 }} 68 | SONAR_SCANNER_SHA_MACOSX_AARCH64: ${{ steps.sonar-scanner-version.outputs.sonar-scanner-sha-macosx-aarch64 }} 69 | run: ${GITHUB_ACTION_PATH}/../scripts/configure_paths.sh >> $GITHUB_OUTPUT 70 | 71 | - name: Cache sonar-scanner installation 72 | id: cache-sonar-tools 73 | if: inputs.cache-binaries == 'true' 74 | uses: actions/cache@v4 75 | env: 76 | # The default value is 60mins. Reaching timeout is treated the same as a cache miss. 77 | SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1 78 | with: 79 | key: sonar-scanner-${{ runner.os }}-${{ runner.arch }}-${{ steps.sonar-scanner-version.outputs.sonar-scanner-version }} 80 | path: ${{ steps.configure_paths.outputs.sonar-scanner-dir }} 81 | 82 | - name: Download and install sonar-scanner 83 | if: steps.cache-sonar-tools.outputs.cache-hit != 'true' 84 | shell: bash 85 | env: 86 | DOWNLOAD_URL: ${{ steps.configure_paths.outputs.sonar-scanner-url }} 87 | EXPECTED_SHA: ${{ steps.configure_paths.outputs.sonar-scanner-sha }} 88 | INSTALL_PATH: ${{ inputs.installation-path }} 89 | TMP_ZIP_PATH: ${{ runner.temp }}/sonar-scanner.zip 90 | run: ${GITHUB_ACTION_PATH}/../scripts/download.sh -v 91 | 92 | - name: Add the custom root certificate to java certificate store 93 | shell: bash 94 | run: ${GITHUB_ACTION_PATH}/../scripts/cert.sh 95 | 96 | - name: Download and install build-wrapper 97 | shell: bash 98 | env: 99 | DOWNLOAD_URL: ${{ steps.configure_paths.outputs.build-wrapper-url }} 100 | INSTALL_PATH: ${{ inputs.installation-path }} 101 | TMP_ZIP_PATH: ${{ runner.temp }}/build-wrapper.zip 102 | run: ${GITHUB_ACTION_PATH}/../scripts/download.sh 103 | 104 | - name: Setup action outputs 105 | id: setup-outputs 106 | shell: bash 107 | env: 108 | SONAR_SCANNER_DIR: ${{ steps.configure_paths.outputs.sonar-scanner-dir }} 109 | SONAR_SCANNER_BIN: ${{ steps.configure_paths.outputs.sonar-scanner-bin }} 110 | BUILD_WRAPPER_DIR: ${{ steps.configure_paths.outputs.build-wrapper-dir }} 111 | BUILD_WRAPPER_BIN: ${{ steps.configure_paths.outputs.build-wrapper-bin }} 112 | run: | 113 | source ${GITHUB_ACTION_PATH}/../scripts/utils.sh 114 | 115 | echo "::group::Action outputs" 116 | echo "SONAR_HOST_URL=${SONAR_HOST_URL}" >> $GITHUB_ENV 117 | echo "'SONAR_HOST_URL' environment variable set to '${SONAR_HOST_URL}'" 118 | 119 | SONAR_SCANNER_BIN_DIR=$(realpath "${SONAR_SCANNER_DIR}/bin") 120 | echo "${SONAR_SCANNER_BIN_DIR}" >> $GITHUB_PATH 121 | echo "'${SONAR_SCANNER_BIN_DIR}' added to the path" 122 | 123 | SONAR_SCANNER_BIN=$(realpath "${SONAR_SCANNER_BIN}") 124 | echo "sonar-scanner-binary=${SONAR_SCANNER_BIN}" >> $GITHUB_OUTPUT 125 | echo "'sonar-scanner-binary' output set to '${SONAR_SCANNER_BIN}'" 126 | 127 | BUILD_WRAPPER_BIN_DIR=$(realpath "${BUILD_WRAPPER_DIR}") 128 | echo "${BUILD_WRAPPER_BIN_DIR}" >> $GITHUB_PATH 129 | echo "'${BUILD_WRAPPER_BIN_DIR}' added to the path" 130 | 131 | BUILD_WRAPPER_BIN=$(realpath "${BUILD_WRAPPER_BIN}") 132 | echo "build-wrapper-binary=${BUILD_WRAPPER_BIN}" >> $GITHUB_OUTPUT 133 | echo "'build-wrapper-binary' output set to '${BUILD_WRAPPER_BIN}'" 134 | echo "::endgroup::" 135 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /dist/install-build-wrapper.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"install-build-wrapper.js","sources":["../src/install-build-wrapper/utils.js","../src/install-build-wrapper/install-build-wrapper.js"],"sourcesContent":["import * as exec from \"@actions/exec\";\nimport * as path from \"path\";\n\n/**\n * Compute all names and paths related to the build wrapper\n * based on the runner environment\n */\nexport function getBuildWrapperInfo({\n runnerOS,\n runnerArch,\n runnerTemp,\n sonarHostUrl,\n}) {\n const { buildWrapperSuffix, buildWrapperName } = getSuffixAndName(\n runnerOS,\n runnerArch\n );\n\n const buildWrapperDir = `${runnerTemp}/build-wrapper-${buildWrapperSuffix}`;\n const buildWrapperUrl = `${sonarHostUrl}/static/cpp/build-wrapper-${buildWrapperSuffix}.zip`;\n const buildWrapperBin = `${buildWrapperDir}/${buildWrapperName}`;\n\n return {\n buildWrapperUrl,\n buildWrapperDir,\n buildWrapperBin,\n };\n}\n\nfunction getSuffixAndName(runnerOS, runnerArch) {\n if (\n runnerArch !== \"X64\" &&\n !(runnerArch === \"ARM64\" && (runnerOS === \"macOS\" || runnerOS === \"Linux\"))\n ) {\n throw new Error(\n `Architecture '${runnerArch}' is unsupported by build-wrapper`\n );\n }\n\n switch (runnerOS) {\n case \"Windows\":\n return {\n buildWrapperSuffix: \"win-x86\",\n buildWrapperName: \"build-wrapper-win-x86-64.exe\",\n };\n\n case \"Linux\":\n switch (runnerArch) {\n case \"X64\":\n return {\n buildWrapperSuffix: \"linux-x86\",\n buildWrapperName: \"build-wrapper-linux-x86-64\",\n };\n\n case \"ARM64\":\n return {\n buildWrapperSuffix: \"linux-aarch64\",\n buildWrapperName: \"build-wrapper-linux-aarch64\",\n };\n }\n break; // handled before the switch\n\n case \"macOS\":\n return {\n buildWrapperSuffix: \"macosx-x86\",\n buildWrapperName: \"build-wrapper-macosx-x86\",\n };\n\n default:\n throw new Error(`Unsupported runner OS '${runnerOS}'`);\n }\n}\n\nexport async function getRealPath(filePath, runnerOS) {\n switch (runnerOS) {\n case \"Windows\": {\n const windowsResult = await exec.getExecOutput(\"cygpath\", [\n \"--absolute\",\n \"--windows\",\n filePath,\n ]);\n return windowsResult.stdout.trim();\n }\n case \"Linux\": {\n const linuxResult = await exec.getExecOutput(\"readlink\", [\n \"-f\",\n filePath,\n ]);\n return linuxResult.stdout.trim();\n }\n case \"macOS\": {\n const macResult = await exec.getExecOutput(\"greadlink\", [\"-f\", filePath]);\n return macResult.stdout.trim();\n }\n default:\n return path.resolve(filePath);\n }\n}\n","import * as core from \"@actions/core\";\nimport * as exec from \"@actions/exec\";\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport { getBuildWrapperInfo, getRealPath } from \"./utils\";\n\nasync function installMacOSPackages() {\n if (process.platform === \"darwin\") {\n core.info(\"Installing required packages for macOS\");\n await exec.exec(\"brew\", [\"install\", \"coreutils\"]);\n }\n}\n\n/**\n * These RUNNER_XX env variables come from GitHub by default.\n * See https://docs.github.com/en/actions/reference/workflows-and-actions/variables#default-environment-variables\n *\n * If SONAR_HOST_URL is omitted, we assume sonarcloud.io\n */\nfunction getEnvVariables() {\n const sonarHostUrl = process.env.SONAR_HOST_URL\n ? process.env.SONAR_HOST_URL.replace(/\\/$/, \"\")\n : \"https://sonarcloud.io\";\n\n return {\n runnerOS: process.env.RUNNER_OS,\n runnerArch: process.env.RUNNER_ARCH,\n runnerTemp: process.env.RUNNER_TEMP,\n sonarHostUrl,\n };\n}\n\nasync function downloadAndInstallBuildWrapper(downloadUrl, runnerEnv) {\n const { runnerArch, runnerOS, runnerTemp } = runnerEnv;\n const tmpZipPath = path.join(\n runnerTemp,\n `build-wrapper-${runnerOS}-${runnerArch}.zip`\n );\n\n core.startGroup(`Download ${downloadUrl}`);\n\n core.info(`Downloading '${downloadUrl}'`);\n\n if (!fs.existsSync(runnerTemp)) {\n fs.mkdirSync(runnerTemp, { recursive: true });\n }\n\n await exec.exec(\"curl\", [\"-sSLo\", tmpZipPath, downloadUrl]);\n\n core.info(\"Decompressing\");\n await exec.exec(\"unzip\", [\"-o\", \"-d\", runnerTemp, tmpZipPath]);\n\n core.endGroup();\n}\n\nasync function run() {\n try {\n await installMacOSPackages();\n\n const envVariables = getEnvVariables();\n\n const { buildWrapperBin, buildWrapperDir, buildWrapperUrl } =\n getBuildWrapperInfo(envVariables);\n\n await downloadAndInstallBuildWrapper(buildWrapperUrl, envVariables);\n\n const buildWrapperBinDir = await getRealPath(\n buildWrapperDir,\n envVariables.runnerOS\n );\n core.addPath(buildWrapperBinDir);\n core.info(`'${buildWrapperBinDir}' added to the path`);\n\n const buildWrapperBinPath = await getRealPath(\n buildWrapperBin,\n envVariables.runnerOS\n );\n core.setOutput(\"build-wrapper-binary\", buildWrapperBinPath);\n core.info(`'build-wrapper-binary' output set to '${buildWrapperBinPath}'`);\n } catch (error) {\n core.setFailed(error.message);\n }\n}\n\nrun();\n"],"names":["exec.getExecOutput","core.info","exec.exec","core.startGroup","core.endGroup","core.addPath","core.setOutput","core.setFailed"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGA;AACA;AACA;AACA;AACO,SAAS,mBAAmB,CAAC;AACpC,EAAE,QAAQ;AACV,EAAE,UAAU;AACZ,EAAE,UAAU;AACZ,EAAE,YAAY;AACd,CAAC,EAAE;AACH,EAAE,MAAM,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,GAAG,gBAAgB;AACnE,IAAI,QAAQ;AACZ,IAAI;AACJ,GAAG;;AAEH,EAAE,MAAM,eAAe,GAAG,CAAC,EAAE,UAAU,CAAC,eAAe,EAAE,kBAAkB,CAAC,CAAC;AAC7E,EAAE,MAAM,eAAe,GAAG,CAAC,EAAE,YAAY,CAAC,0BAA0B,EAAE,kBAAkB,CAAC,IAAI,CAAC;AAC9F,EAAE,MAAM,eAAe,GAAG,CAAC,EAAE,eAAe,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC;;AAElE,EAAE,OAAO;AACT,IAAI,eAAe;AACnB,IAAI,eAAe;AACnB,IAAI,eAAe;AACnB,GAAG;AACH;;AAEA,SAAS,gBAAgB,CAAC,QAAQ,EAAE,UAAU,EAAE;AAChD,EAAE;AACF,IAAI,UAAU,KAAK,KAAK;AACxB,IAAI,EAAE,UAAU,KAAK,OAAO,KAAK,QAAQ,KAAK,OAAO,IAAI,QAAQ,KAAK,OAAO,CAAC;AAC9E,IAAI;AACJ,IAAI,MAAM,IAAI,KAAK;AACnB,MAAM,CAAC,cAAc,EAAE,UAAU,CAAC,iCAAiC;AACnE,KAAK;AACL,EAAE;;AAEF,EAAE,QAAQ,QAAQ;AAClB,IAAI,KAAK,SAAS;AAClB,MAAM,OAAO;AACb,QAAQ,kBAAkB,EAAE,SAAS;AACrC,QAAQ,gBAAgB,EAAE,8BAA8B;AACxD,OAAO;;AAEP,IAAI,KAAK,OAAO;AAChB,MAAM,QAAQ,UAAU;AACxB,QAAQ,KAAK,KAAK;AAClB,UAAU,OAAO;AACjB,YAAY,kBAAkB,EAAE,WAAW;AAC3C,YAAY,gBAAgB,EAAE,4BAA4B;AAC1D,WAAW;;AAEX,QAAQ,KAAK,OAAO;AACpB,UAAU,OAAO;AACjB,YAAY,kBAAkB,EAAE,eAAe;AAC/C,YAAY,gBAAgB,EAAE,6BAA6B;AAC3D,WAAW;AACX;AACA,MAAM,MAAM;;AAEZ,IAAI,KAAK,OAAO;AAChB,MAAM,OAAO;AACb,QAAQ,kBAAkB,EAAE,YAAY;AACxC,QAAQ,gBAAgB,EAAE,0BAA0B;AACpD,OAAO;;AAEP,IAAI;AACJ,MAAM,MAAM,IAAI,KAAK,CAAC,CAAC,uBAAuB,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;AAC5D;AACA;;AAEO,eAAe,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE;AACtD,EAAE,QAAQ,QAAQ;AAClB,IAAI,KAAK,SAAS,EAAE;AACpB,MAAM,MAAM,aAAa,GAAG,MAAMA,yBAAkB,CAAC,SAAS,EAAE;AAChE,QAAQ,YAAY;AACpB,QAAQ,WAAW;AACnB,QAAQ,QAAQ;AAChB,OAAO,CAAC;AACR,MAAM,OAAO,aAAa,CAAC,MAAM,CAAC,IAAI,EAAE;AACxC,IAAI;AACJ,IAAI,KAAK,OAAO,EAAE;AAClB,MAAM,MAAM,WAAW,GAAG,MAAMA,yBAAkB,CAAC,UAAU,EAAE;AAC/D,QAAQ,IAAI;AACZ,QAAQ,QAAQ;AAChB,OAAO,CAAC;AACR,MAAM,OAAO,WAAW,CAAC,MAAM,CAAC,IAAI,EAAE;AACtC,IAAI;AACJ,IAAI,KAAK,OAAO,EAAE;AAClB,MAAM,MAAM,SAAS,GAAG,MAAMA,yBAAkB,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC/E,MAAM,OAAO,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE;AACpC,IAAI;AACJ,IAAI;AACJ,MAAM,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;AACnC;AACA;;AC3FA,eAAe,oBAAoB,GAAG;AACtC,EAAE,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE;AACrC,IAAIC,gBAAS,CAAC,wCAAwC,CAAC;AACvD,IAAI,MAAMC,gBAAS,CAAC,MAAM,EAAE,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;AACrD,EAAE;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,eAAe,GAAG;AAC3B,EAAE,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC;AACnC,MAAM,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE;AAClD,MAAM,uBAAuB;;AAE7B,EAAE,OAAO;AACT,IAAI,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS;AACnC,IAAI,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW;AACvC,IAAI,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW;AACvC,IAAI,YAAY;AAChB,GAAG;AACH;;AAEA,eAAe,8BAA8B,CAAC,WAAW,EAAE,SAAS,EAAE;AACtE,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,SAAS;AACxD,EAAE,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI;AAC9B,IAAI,UAAU;AACd,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC,EAAE,UAAU,CAAC,IAAI;AAChD,GAAG;;AAEH,EAAEC,sBAAe,CAAC,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC;;AAE5C,EAAEF,gBAAS,CAAC,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;;AAE3C,EAAE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE;AAClC,IAAI,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;AACjD,EAAE;;AAEF,EAAE,MAAMC,gBAAS,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;;AAE7D,EAAED,gBAAS,CAAC,eAAe,CAAC;AAC5B,EAAE,MAAMC,gBAAS,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;;AAEhE,EAAEE,oBAAa,EAAE;AACjB;;AAEA,eAAe,GAAG,GAAG;AACrB,EAAE,IAAI;AACN,IAAI,MAAM,oBAAoB,EAAE;;AAEhC,IAAI,MAAM,YAAY,GAAG,eAAe,EAAE;;AAE1C,IAAI,MAAM,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAAE;AAC/D,MAAM,mBAAmB,CAAC,YAAY,CAAC;;AAEvC,IAAI,MAAM,8BAA8B,CAAC,eAAe,EAAE,YAAY,CAAC;;AAEvE,IAAI,MAAM,kBAAkB,GAAG,MAAM,WAAW;AAChD,MAAM,eAAe;AACrB,MAAM,YAAY,CAAC;AACnB,KAAK;AACL,IAAIC,mBAAY,CAAC,kBAAkB,CAAC;AACpC,IAAIJ,gBAAS,CAAC,CAAC,CAAC,EAAE,kBAAkB,CAAC,mBAAmB,CAAC,CAAC;;AAE1D,IAAI,MAAM,mBAAmB,GAAG,MAAM,WAAW;AACjD,MAAM,eAAe;AACrB,MAAM,YAAY,CAAC;AACnB,KAAK;AACL,IAAIK,qBAAc,CAAC,sBAAsB,EAAE,mBAAmB,CAAC;AAC/D,IAAIL,gBAAS,CAAC,CAAC,sCAAsC,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAC;AAC9E,EAAE,CAAC,CAAC,OAAO,KAAK,EAAE;AAClB,IAAIM,qBAAc,CAAC,KAAK,CAAC,OAAO,CAAC;AACjC,EAAE;AACF;;AAEA,GAAG,EAAE"} -------------------------------------------------------------------------------- /.github/workflows/qa-scripts.yml: -------------------------------------------------------------------------------- 1 | name: QA Scripts 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | types: [opened, synchronize, reopened] 9 | 10 | jobs: 11 | create-install-dir-test: 12 | name: create_install_path.sh 13 | runs-on: github-ubuntu-latest-s 14 | steps: 15 | - uses: actions/checkout@v5 16 | with: 17 | fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis 18 | 19 | - name: Existing 20 | shell: bash 21 | env: 22 | INSTALL_PATH: '.sonar' 23 | run: | 24 | echo "- Create dir" 25 | mkdir -p "${INSTALL_PATH}" 26 | 27 | echo "- Test script behavior" 28 | ./scripts/create_install_path.sh > output 29 | grep -v "::error::" output 30 | 31 | - name: Non-existing nested in current dir 32 | shell: bash 33 | env: 34 | INSTALL_PATH: '.sonar' 35 | run: | 36 | ./scripts/create_install_path.sh > output 37 | grep -v "::error::" output 38 | test -d "${INSTALL_PATH}" 39 | 40 | - name: Nonexisting nested in home 41 | shell: bash 42 | env: 43 | INSTALL_PATH: '~/third_party/.sonar' 44 | run: | 45 | ./scripts/create_install_path.sh > output 46 | grep -v "::error::" output 47 | test -d "${INSTALL_PATH}" 48 | 49 | - name: Empty install dir specified 50 | shell: bash 51 | env: 52 | INSTALL_PATH: '' 53 | run: | 54 | (./scripts/create_install_path.sh || echo "=== Script failed ===") > output 55 | grep "::error::Empty installation path specified" output 56 | grep "=== Script failed ===" output 57 | 58 | - name: No permission to create directory 59 | shell: bash 60 | env: 61 | INSTALL_PATH: '/non_creatable' 62 | run: | 63 | (./scripts/create_install_path.sh || echo "=== Script failed ===") > output 64 | grep "::error::Failed to create non-existing installation path '/non_creatable'" output 65 | grep "=== Script failed ===" output 66 | 67 | - name: Existing but not directory 68 | shell: bash 69 | env: 70 | INSTALL_PATH: 'not_directory' 71 | run: | 72 | echo "- Create normal file" 73 | echo "content" > "${INSTALL_PATH}" 74 | 75 | echo "- Test script behavior" 76 | (./scripts/create_install_path.sh || echo "=== Script failed ===") > output 77 | grep "::error::Installation path 'not_directory' is not a directory" output 78 | grep "=== Script failed ===" output 79 | 80 | 81 | - name: Existing but not readable 82 | shell: bash 83 | env: 84 | INSTALL_PATH: 'not_readable' 85 | run: | 86 | echo "- Create dir and make it not readable" 87 | mkdir -p "${INSTALL_PATH}" 88 | chmod -r "${INSTALL_PATH}" 89 | 90 | echo "- Test script behavior" 91 | (./scripts/create_install_path.sh || echo "=== Script failed ===") > output 92 | grep "::error::Installation path 'not_readable' is not readable" output 93 | grep "=== Script failed ===" output 94 | 95 | - name: Existing but not writeable 96 | shell: bash 97 | env: 98 | INSTALL_PATH: 'not_writeable' 99 | run: | 100 | echo "- Create dir and make it not writeable" 101 | mkdir -p "${INSTALL_PATH}" 102 | chmod -w "${INSTALL_PATH}" 103 | 104 | echo "- Test script behavior" 105 | (./scripts/create_install_path.sh || echo "=== Script failed ===") > output 106 | grep "::error::Installation path 'not_writeable' is not writeable" output 107 | grep "=== Script failed ===" output 108 | setup-script-test: 109 | name: configure_paths.sh 110 | runs-on: github-ubuntu-latest-s 111 | env: 112 | INSTALL_PATH: 'install-directory' 113 | SONAR_HOST_URL: 'http://sonar-host.com' 114 | SONAR_SCANNER_VERSION: 'vX.Y.Z.MMMM' 115 | SONAR_SCANNER_URL_WINDOWS_X64: 'https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-vX.Y.Z.MMMM-windows-x64.zip' 116 | SONAR_SCANNER_SHA_WINDOWS_X64: 'DOWNLOAD-SHA-WINDOWS-X64' 117 | SONAR_SCANNER_URL_LINUX_X64: 'https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-vX.Y.Z.MMMM-linux-x64.zip' 118 | SONAR_SCANNER_SHA_LINUX_X64: 'DOWNLOAD-SHA-LINUX-X64' 119 | SONAR_SCANNER_URL_LINUX_AARCH64: 'https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-vX.Y.Z.MMMM-linux-aarch64.zip' 120 | SONAR_SCANNER_SHA_LINUX_AARCH64: 'DOWNLOAD-SHA-LINUX-AARCH64' 121 | SONAR_SCANNER_URL_MACOSX_X64: 'https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-vX.Y.Z.MMMM-macosx-x64.zip' 122 | SONAR_SCANNER_SHA_MACOSX_X64: 'DOWNLOAD-SHA-MACOSX-X64' 123 | SONAR_SCANNER_URL_MACOSX_AARCH64: 'https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-vX.Y.Z.MMMM-macosx-aarch64.zip' 124 | SONAR_SCANNER_SHA_MACOSX_AARCH64: 'DOWNLOAD-SHA-MACOSX-AARCH64' 125 | steps: 126 | - uses: actions/checkout@v5 127 | with: 128 | fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis 129 | 130 | - name: Windows 131 | shell: bash 132 | env: 133 | OS: 'Windows' 134 | ARCH: 'X64' 135 | run: | 136 | ./scripts/configure_paths.sh > output 137 | grep -v "::error::" output 138 | 139 | echo "- Check sonar-scanner:" 140 | grep "sonar-scanner-url=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-vX.Y.Z.MMMM-windows-x64.zip" output 141 | grep "sonar-scanner-sha=DOWNLOAD-SHA-WINDOWS-X64" output 142 | grep "sonar-scanner-dir=install-directory/sonar-scanner-vX.Y.Z.MMMM-windows-x64" output 143 | grep "sonar-scanner-bin=install-directory/sonar-scanner-vX.Y.Z.MMMM-windows-x64/bin/sonar-scanner.bat" output 144 | 145 | echo "- Check build-wrapper:" 146 | grep "build-wrapper-url=http://sonar-host.com/static/cpp/build-wrapper-win-x86.zip" output 147 | grep "build-wrapper-dir=install-directory/build-wrapper-win-x86" output 148 | grep "build-wrapper-bin=install-directory/build-wrapper-win-x86/build-wrapper-win-x86-64.exe" output 149 | 150 | - name: Linux X64 151 | shell: bash 152 | env: 153 | OS: 'Linux' 154 | ARCH: 'X64' 155 | run: | 156 | ./scripts/configure_paths.sh > output 157 | grep -v "::error::" output 158 | 159 | echo "- Check sonar-scanner:" 160 | grep "sonar-scanner-url=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-vX.Y.Z.MMMM-linux-x64.zip" output 161 | grep "sonar-scanner-sha=DOWNLOAD-SHA-LINUX-X64" output 162 | grep "sonar-scanner-dir=install-directory/sonar-scanner-vX.Y.Z.MMMM-linux-x64" output 163 | grep "sonar-scanner-bin=install-directory/sonar-scanner-vX.Y.Z.MMMM-linux-x64/bin/sonar-scanner" output 164 | 165 | echo "- Check build-wrapper:" 166 | grep "build-wrapper-url=http://sonar-host.com/static/cpp/build-wrapper-linux-x86.zip" output 167 | grep "build-wrapper-dir=install-directory/build-wrapper-linux-x86" output 168 | grep "build-wrapper-bin=install-directory/build-wrapper-linux-x86/build-wrapper-linux-x86-64" output 169 | 170 | - name: Linux ARM64 171 | shell: bash 172 | env: 173 | OS: 'Linux' 174 | ARCH: 'ARM64' 175 | run: | 176 | ./scripts/configure_paths.sh > output 177 | grep -v "::error::" output 178 | echo "- Check sonar-scanner:" 179 | grep "sonar-scanner-url=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-vX.Y.Z.MMMM-linux-aarch64.zip" output 180 | grep "sonar-scanner-sha=DOWNLOAD-SHA-LINUX-AARCH64" output 181 | grep "sonar-scanner-dir=install-directory/sonar-scanner-vX.Y.Z.MMMM-linux-aarch64" output 182 | grep "sonar-scanner-bin=install-directory/sonar-scanner-vX.Y.Z.MMMM-linux-aarch64/bin/sonar-scanner" output 183 | echo "- Check build-wrapper:" 184 | grep "build-wrapper-url=http://sonar-host.com/static/cpp/build-wrapper-linux-aarch64.zip" output 185 | grep "build-wrapper-dir=install-directory/build-wrapper-linux-aarch64" output 186 | grep "build-wrapper-bin=install-directory/build-wrapper-linux-aarch64/build-wrapper-linux-aarch64" output 187 | 188 | - name: macOSX_X64 189 | shell: bash 190 | env: 191 | OS: 'macOS' 192 | ARCH: 'X64' 193 | run: | 194 | ./scripts/configure_paths.sh > output 195 | grep -v "::error::" output 196 | 197 | echo "- Check sonar-scanner:" 198 | grep "sonar-scanner-url=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-vX.Y.Z.MMMM-macosx-x64.zip" output 199 | grep "sonar-scanner-sha=DOWNLOAD-SHA-MACOSX-X64" output 200 | grep "sonar-scanner-dir=install-directory/sonar-scanner-vX.Y.Z.MMMM-macosx-x64" output 201 | grep "sonar-scanner-bin=install-directory/sonar-scanner-vX.Y.Z.MMMM-macosx-x64/bin/sonar-scanner" output 202 | 203 | echo "- Check build-wrapper:" 204 | grep "build-wrapper-url=http://sonar-host.com/static/cpp/build-wrapper-macosx-x86.zip" output 205 | grep "build-wrapper-dir=install-directory/build-wrapper-macosx-x86" output 206 | grep "build-wrapper-bin=install-directory/build-wrapper-macosx-x86/build-wrapper-macosx-x86" output 207 | 208 | - name: macOSX_ARM64 209 | shell: bash 210 | env: 211 | OS: 'macOS' 212 | ARCH: 'ARM64' 213 | run: | 214 | ./scripts/configure_paths.sh > output 215 | grep -v "::error::" output 216 | 217 | echo "- Check sonar-scanner:" 218 | grep "sonar-scanner-url=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-vX.Y.Z.MMMM-macosx-aarch64.zip" output 219 | grep "sonar-scanner-sha=DOWNLOAD-SHA-MACOSX-AARCH64" output 220 | grep "sonar-scanner-dir=install-directory/sonar-scanner-vX.Y.Z.MMMM-macosx-aarch64" output 221 | grep "sonar-scanner-bin=install-directory/sonar-scanner-vX.Y.Z.MMMM-macosx-aarch64/bin/sonar-scanner" output 222 | 223 | echo "- Check build-wrapper:" 224 | grep "build-wrapper-url=http://sonar-host.com/static/cpp/build-wrapper-macosx-x86.zip" output 225 | grep "build-wrapper-dir=install-directory/build-wrapper-macosx-x86" output 226 | grep "build-wrapper-bin=install-directory/build-wrapper-macosx-x86/build-wrapper-macosx-x86" output 227 | 228 | - name: Unsupported OS 229 | shell: bash 230 | env: 231 | OS: 'unsupportedOS' 232 | ARCH: 'X64' 233 | run: | 234 | (./scripts/configure_paths.sh || echo "=== Script failed ===") > output 235 | 236 | echo "- Check errors:" 237 | grep "::error::Unsupported runner OS 'unsupportedOS'" output 238 | grep "=== Script failed ===" output 239 | 240 | - name: Unsupported architecture 241 | shell: bash 242 | env: 243 | OS: 'Linux' 244 | ARCH: 'X86' 245 | run: | 246 | (./scripts/configure_paths.sh || echo "=== Script failed ===") > output 247 | 248 | echo "- Check errors:" 249 | grep "::error::Architecture 'X86' is unsupported by build-wrapper" output 250 | grep "=== Script failed ===" output 251 | download-script-test: 252 | name: download.sh 253 | runs-on: github-ubuntu-latest-s 254 | steps: 255 | - uses: actions/checkout@v5 256 | with: 257 | fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis 258 | 259 | - name: Download test without validation 260 | shell: bash 261 | env: 262 | INSTALL_PATH: 'install-directory-no-sha-validation' 263 | DOWNLOAD_URL: 'https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.6.2.2472-linux.zip' 264 | EXPECTED_SHA: 'incorrect-sha-not-validated' 265 | TMP_ZIP_PATH: ${{ runner.temp }}/sonar-scanner.zip 266 | run: | 267 | ./scripts/download.sh > output 268 | test -f "$TMP_ZIP_PATH" 269 | grep -v "::error::" output 270 | - name: Download test with validation 271 | shell: bash 272 | env: 273 | INSTALL_PATH: 'install-directory-sha-validation' 274 | DOWNLOAD_URL: 'https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.6.2.2472-linux.zip' 275 | EXPECTED_SHA: '9411331814c1d002bd65d37758b872918b7602e7cf3ca5b83a3e19a729b2be05' 276 | TMP_ZIP_PATH: ${{ runner.temp }}/sonar-scanner.zip 277 | run: | 278 | ./scripts/download.sh -v > output 279 | test -f "$TMP_ZIP_PATH" 280 | grep -v "::error::" output 281 | - name: Incorrect install dir 282 | shell: bash 283 | env: 284 | INSTALL_PATH: '' 285 | run: | 286 | (./scripts/download.sh || echo "=== Script failed ===") > output 287 | grep "::error::Failed to create" output 288 | grep "=== Script failed ===" output 289 | - name: Incorrect download url 290 | shell: bash 291 | env: 292 | INSTALL_PATH: 'install-directory-incorrect-url' 293 | DOWNLOAD_URL: 'incorrect-url' 294 | run: | 295 | (./scripts/download.sh || echo "=== Script failed ===") > output 296 | grep "::error::Failed to download 'incorrect-url'" output 297 | grep "=== Script failed ===" output 298 | - name: Incorrect SHA256 299 | shell: bash 300 | env: 301 | INSTALL_PATH: 'install-directory-incorrect-sha' 302 | DOWNLOAD_URL: 'https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.6.2.2472-linux.zip' 303 | EXPECTED_SHA: 'incorrect-sha256' 304 | TMP_ZIP_PATH: ${{ runner.temp }}/sonar-scanner.zip 305 | run: | 306 | (./scripts/download.sh -v || echo "=== Script failed ===") > output 307 | grep "::error::Checking sha256 failed" output 308 | grep "=== Script failed ===" output 309 | - name: Mismatching SHA256 310 | shell: bash 311 | env: 312 | INSTALL_PATH: 'install-directory-mismtaching-sha' 313 | DOWNLOAD_URL: 'https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.6.2.2472-linux.zip' 314 | EXPECTED_SHA: '3e121d85a4adb1f30b917d5f3eb897966b59e02c3d6d313a78dcd964193dc963' 315 | TMP_ZIP_PATH: ${{ runner.temp }}/sonar-scanner.zip 316 | run: | 317 | (./scripts/download.sh -v || echo "=== Script failed ===") > output 318 | grep "::error::Checking sha256 failed" output 319 | grep "=== Script failed ===" output 320 | fetch-latest-version-test: 321 | name: fetch_latest_version.sh 322 | runs-on: github-ubuntu-latest-s 323 | steps: 324 | - uses: actions/checkout@v5 325 | with: 326 | fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis 327 | - name: Test script 328 | shell: bash 329 | run: | 330 | ./scripts/fetch_latest_version.sh > output 331 | 332 | echo "- Check sonar-scanner version:" 333 | grep "sonar-scanner-version=" output 334 | SONAR_SCANNER_VERSION=$(cat output | cut -d= -f 2) 335 | test ! -z "${SONAR_SCANNER_VERSION}" 336 | 337 | echo "- Check windows sonar-scanner URLs:" 338 | grep "sonar-scanner-url-windows-x64=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-windows-x64.zip" output 339 | grep -e "^sonar-scanner-sha-windows-x64=[0-9A-Fa-f]\+$" output 340 | 341 | echo "- Check linux sonar-scanner URLs:" 342 | grep "sonar-scanner-url-linux-x64=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux-x64.zip" output 343 | grep -e "^sonar-scanner-sha-linux-x64=[0-9A-Fa-f]\+$" output 344 | grep "sonar-scanner-url-linux-aarch64=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux-aarch64.zip" output 345 | grep -e "^sonar-scanner-sha-linux-aarch64=[0-9A-Fa-f]\+$" output 346 | 347 | echo "- Check macosx sonar-scanner URLs:" 348 | grep "sonar-scanner-url-linux-x64=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux-x64.zip" output 349 | grep -e "^sonar-scanner-sha-linux-x64=[0-9A-Fa-f]\+$" output 350 | grep "sonar-scanner-url-linux-aarch64=https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux-aarch64.zip" output 351 | grep -e "^sonar-scanner-sha-linux-aarch64=[0-9A-Fa-f]\+$" output 352 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 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) 2 | 3 | 4 | 5 | SonarQube Logo 6 | 7 | 8 | 9 | ## A GitHub Action for SonarQube 10 | 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.** 11 | 12 | 13 | ### What is SonarQube? 14 | [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. 15 | 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/). 16 | 17 | ## Quick Start 18 | 19 | ### 1. Prerequisites: 20 | 21 | 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. 22 | 23 | For more information, see [Key Requirements](#key-requirements). 24 | 25 | 26 | ### 2. Required variables: 27 | 28 | 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. 29 | 30 | • `SONAR_TOKEN` : The authentication token required to access the SonarQube instance. This is a mandatory secret for all use cases. 31 | 32 | • `SONAR_HOST_URL` : The URL of the SonarQube Server. This is required for self-hosted SonarQube Server but not needed for SonarQube Cloud. 33 | 34 | For more information, see [Configuration](#configuration). 35 | 36 | ### 3. Quick Start Workflow Example (for SonarQube Cloud) 37 | 38 | Create or update your CI pipeline to run the scan action: 39 | 40 | 41 | ```yaml 42 | on: 43 | # Trigger analysis when pushing to your main branches, and when creating a pull request. 44 | push: 45 | branches: 46 | - main 47 | - master 48 | - develop 49 | - 'releases/**' 50 | pull_request: 51 | types: [opened, synchronize, reopened] 52 | 53 | name: Main Workflow 54 | jobs: 55 | sonarqube: 56 | runs-on: ubuntu-latest 57 | steps: 58 | - uses: actions/checkout@v4 59 | with: 60 | # Disabling shallow clones is recommended for improving the relevancy of reporting 61 | fetch-depth: 0 62 | - name: SonarQube Scan 63 | uses: SonarSource/sonarqube-scan-action@ # Ex: v4.1.0 or sha1, See the latest version at https://github.com/marketplace/actions/official-sonarqube-scan 64 | env: 65 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 66 | ``` 67 | 68 | Create a configuration file in the root directory of the project and name it `sonar-project.properties`: 69 | 70 | 71 | ```properties 72 | sonar.organization= 73 | sonar.projectKey= 74 | 75 | # relative paths to source directories. More details and properties are described 76 | # at https://docs.sonarsource.com/sonarqube-cloud/advanced-setup/analysis-scope/ 77 | sonar.sources=src 78 | ``` 79 | 80 | For other workflows, see [Workflow Examples](#workflow-examples). 81 | 82 | 83 | 84 | ## Important: Special Cases and alternatives 85 | 86 | This GitHub Action will not work for all technologies. If you are in one of the following situations, you should use the following alternatives: 87 | 88 | * **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/). 89 | * **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/). 90 | * **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/). 91 | 92 | **Do not use this GitHub action if:** 93 | 94 | * 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. 95 | 96 | **If you want to use Software Composition Analysis (SCA)** 97 | 98 | 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). 99 | 100 | ## Key requirements 101 | 102 | To use this GitHub Action you need to meet the following prerequisites for your choosen SonarQube platform. 103 | 104 | ### For SonarQube Cloud 105 | 106 | * 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! 107 | * [Set up a repository to be analyzed](https://sonarcloud.io/projects/create) in just one click. 108 | 109 | ### For SonarQube Server 110 | 111 | * 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**). 112 | * To run an analysis on your code, you first need to set up your project on SonarQube Server. 113 | 114 | Read more information on how to analyze your code [here](https://docs.sonarsource.com/sonarqube-server/latest/devops-platform-integration/github-integration/introduction/). 115 | 116 | 117 | 118 | 119 | 120 | ## Configuration 121 | 122 | ### Action parameters 123 | 124 | #### `projectBaseDir` 125 | 126 | You can change the analysis base directory by using the optional input `projectBaseDir` like this: 127 | 128 | ```yaml 129 | - uses: SonarSource/sonarqube-scan-action@ 130 | with: 131 | projectBaseDir: app/src 132 | ``` 133 | 134 | #### `scannerVersion` 135 | 136 | In case you need to specify the version of the Sonar Scanner, you can use the `scannerVersion` option: 137 | 138 | ```yaml 139 | - uses: SonarSource/sonarqube-scan-action@ 140 | with: 141 | scannerVersion: 6.2.0.4584 142 | ``` 143 | 144 | #### `args` 145 | 146 | 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: 147 | 148 | ```yaml 149 | - uses: SonarSource/sonarqube-scan-action@ 150 | with: 151 | projectBaseDir: app/src 152 | args: > 153 | -Dsonar.organization=my-organization # For SonarQube Cloud only 154 | "-Dsonar.projectName=My Project" 155 | -Dsonar.projectKey=my-projectkey 156 | -Dsonar.python.coverage.reportPaths=coverage.xml 157 | -Dsonar.sources=lib/ 158 | -Dsonar.tests=tests/ 159 | -Dsonar.test.exclusions=tests/** 160 | -Dsonar.verbose=true 161 | ``` 162 | 163 | > [!NOTE] 164 | > In version 6, the way the `args` option is handled has been changed to prevent command injection. 165 | > As a result, we no longer support the full bash syntax. 166 | > This means there is now a much more restricted use of quoting and escaping compared to older versions of the action. 167 | > Example: 168 | > ```yaml 169 | > with: 170 | > args: > 171 | > -testing test 172 | > -valid=true 173 | > --quotes "test quotes" "nested \'quotes\'" 174 | > -Dsonar.property="some value" 175 | > "-Dsonar.property=some value" 176 | > ``` 177 | > will be parsed as the following array of strings: 178 | > ``` 179 | > [ 180 | > '-testing', 181 | > 'test', 182 | > '-valid=true', 183 | > '--quotes', 184 | > 'test quotes', # Surrounding quotes are removed 185 | > 'nested \'quotes\'', 186 | > '-Dsonar.property="some value"', # Internal quotes are NOT removed, contrary to the bash syntax 187 | > '-Dsonar.property=some value', # This is the proper way to pass scanner arguments with spaces 188 | > ] 189 | > ``` 190 | 191 | #### `scannerBinariesUrl` 192 | 193 | You can also specify the URL where to retrieve the SonarScanner CLI from. 194 | The specified URL overrides the default address: `https://binaries.sonarsource.com/Distribution/sonar-scanner-cli`. 195 | This can be useful when the runner executing the action is self-hosted and has regulated or no access to the Internet: 196 | 197 | ```yaml 198 | - uses: SonarSource/sonarqube-scan-action@ 199 | with: 200 | scannerBinariesUrl: https://my.custom.binaries.url.com/Distribution/sonar-scanner-cli/ 201 | ``` 202 | 203 | More information about possible analysis parameters can be found: 204 | * in the [Analysis parameters page](https://docs.sonarsource.com/sonarqube-server/latest/analyzing-source-code/analysis-parameters/) of the SonarQube Server documentation 205 | * in the [Analysis parameters page](https://docs.sonarsource.com/sonarqube-cloud/advanced-setup/analysis-parameters/) of the SonarQube Cloud documentation 206 | 207 | 208 | ### Environment variables 209 | 210 | - `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). 211 | - `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. 212 | - `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). 213 | 214 | Here is an example of how you can pass a certificate (in PEM format) to the Scanner truststore: 215 | 216 | ```yaml 217 | - uses: SonarSource/sonarqube-scan-action@ 218 | env: 219 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 220 | SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }} 221 | SONAR_ROOT_CERT: ${{ secrets.SONAR_ROOT_CERT }} 222 | ``` 223 | 224 | 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: 225 | 226 | ```yaml 227 | - uses: SonarSource/sonarqube-scan-action@ 228 | env: 229 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 230 | SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }} # or https://sonarcloud.io 231 | LC_ALL: "ru_RU.UTF-8" 232 | ``` 233 | 234 | ## Workflow Examples 235 | 236 | 237 | ### For SonarQube Cloud 238 | 239 | Project metadata, including the location of the sources to be analyzed, must be declared in the file sonar-project.properties in the base directory: 240 | 241 | ```properties 242 | sonar.organization= 243 | sonar.projectKey= 244 | 245 | # relative paths to source directories. More details and properties are described 246 | # at https://docs.sonarsource.com/sonarqube-cloud/advanced-setup/analysis-scope/ 247 | sonar.sources=src 248 | ``` 249 | 250 | 251 | #### Standard Projects 252 | 253 | For projects that: 254 | - do not contain C, C++, or Objective-C, and 255 | - 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) 256 | 257 | the workflow, usually declared under `.github/workflows/build.yml`, looks like the following: 258 | 259 | ```yaml 260 | on: 261 | # Trigger analysis when pushing to your main branches, and when creating a pull request. 262 | push: 263 | branches: 264 | - main 265 | - master 266 | - develop 267 | - 'releases/**' 268 | pull_request: 269 | types: [opened, synchronize, reopened] 270 | 271 | name: Main Workflow 272 | jobs: 273 | sonarqube: 274 | runs-on: ubuntu-latest 275 | steps: 276 | - uses: actions/checkout@v4 277 | with: 278 | # Disabling shallow clones is recommended for improving the relevancy of reporting 279 | fetch-depth: 0 280 | - name: SonarQube Scan 281 | uses: SonarSource/sonarqube-scan-action@ # Ex: v4.1.0 or sha1, See the latest version at https://github.com/marketplace/actions/official-sonarqube-scan 282 | env: 283 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 284 | ``` 285 | 286 | #### C/C++/Objective-C with Build Wrapper 287 | 288 | 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: 289 | 290 | ```yaml 291 | # Trigger analysis when pushing to your main branches, and when creating a pull request. 292 | push: 293 | branches: 294 | - main 295 | - master 296 | - develop 297 | - 'releases/**' 298 | pull_request: 299 | types: [opened, synchronize, reopened] 300 | 301 | name: Main Workflow 302 | jobs: 303 | sonarqube: 304 | runs-on: ubuntu-latest 305 | env: 306 | BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed 307 | steps: 308 | - uses: actions/checkout@v4 309 | with: 310 | # Disabling shallow clone is recommended for improving relevancy of reporting 311 | fetch-depth: 0 312 | - name: Install Build Wrapper 313 | uses: SonarSource/sonarqube-scan-action/install-build-wrapper@ 314 | - name: Run Build Wrapper 315 | run: | 316 | # Here goes your compilation wrapped with Build Wrapper 317 | # For more information, see https://docs.sonarsource.com/sonarqube-cloud/advanced-setup/languages/c-family/prerequisites/#using-build-wrapper 318 | # build-preparation steps 319 | # build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} build-command 320 | - name: SonarQube Scan 321 | uses: SonarSource/sonarqube-scan-action@ 322 | env: 323 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 324 | SONAR_ROOT_CERT: ${{ secrets.SONAR_ROOT_CERT }} 325 | with: 326 | # Consult https://docs.sonarsource.com/sonarqube-server/latest/analyzing-source-code/scanners/sonarscanner/ for more information and options 327 | args: > 328 | --define "sonar.cfamily.compile-commands=${{ env.BUILD_WRAPPER_OUT_DIR }}/compile_commands.json" 329 | ``` 330 | 331 | See also [example configurations of C++ projects for SonarQube Cloud](https://github.com/search?q=org%3Asonarsource-cfamily-examples+gh-actions-sc&type=repositories). 332 | 333 | ### For SonarQube Server 334 | 335 | Project metadata, including the location of the sources to be analyzed, can be declared in the file `sonar-project.properties` in the base directory: 336 | 337 | ```properties 338 | sonar.projectKey= 339 | 340 | # relative paths to source directories. More details and properties are described 341 | # at https://docs.sonarsource.com/sonarqube-server/latest/project-administration/analysis-scope/ 342 | sonar.sources=src 343 | ``` 344 | 345 | #### Standard Projects 346 | 347 | For projects that: 348 | - do not contain C, C++, or Objective-C, and 349 | - 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) 350 | the workflow, usually declared under `.github/workflows/build.yml`, looks like the following: 351 | 352 | ```yaml 353 | on: 354 | # Trigger analysis when pushing to your main branches, and when creating a pull request. 355 | push: 356 | branches: 357 | - main 358 | - master 359 | - develop 360 | - 'releases/**' 361 | pull_request: 362 | types: [opened, synchronize, reopened] 363 | 364 | name: Main Workflow 365 | jobs: 366 | sonarqube: 367 | runs-on: ubuntu-latest 368 | steps: 369 | - uses: actions/checkout@v4 370 | with: 371 | # Disabling shallow clones is recommended for improving the relevancy of reporting 372 | fetch-depth: 0 373 | - name: SonarQube Scan 374 | uses: SonarSource/sonarqube-scan-action@ # Ex: v4.1.0, or sha1, See the latest version at https://github.com/marketplace/actions/official-sonarqube-scan 375 | env: 376 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 377 | SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }} 378 | ``` 379 | 380 | #### C/C++/Objective-C with Build Wrapper 381 | 382 | 383 | 384 | This subsection would contain the more complex YAML configuration for projects that require the 385 | build wrapper to generate a compilation database. The example would detail the three-step 386 | process: checking out the code, installing the build wrapper, and then running the SonarQube 387 | scan with the appropriate parameters. 388 | 389 | 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: 390 | 391 | ```yaml 392 | # Trigger analysis when pushing to your main branches, and when creating a pull request. 393 | push: 394 | branches: 395 | - main 396 | - master 397 | - develop 398 | - 'releases/**' 399 | pull_request: 400 | types: [opened, synchronize, reopened] 401 | 402 | name: Main Workflow 403 | jobs: 404 | sonarqube: 405 | runs-on: ubuntu-latest 406 | env: 407 | BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed 408 | steps: 409 | - uses: actions/checkout@v4 410 | with: 411 | # Disabling shallow clone is recommended for improving relevancy of reporting 412 | fetch-depth: 0 413 | - name: Install Build Wrapper 414 | uses: SonarSource/sonarqube-scan-action/install-build-wrapper@ 415 | env: 416 | SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }} 417 | - name: Run Build Wrapper 418 | run: | 419 | # Here goes your compilation wrapped with Build Wrapper 420 | # For more information, see https://docs.sonarsource.com/sonarqube-server/latest/analyzing-source-code/languages/c-family/prerequisites/#using-buildwrapper 421 | # build-preparation steps 422 | # build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} build-command 423 | - name: SonarQube Scan 424 | uses: SonarSource/sonarqube-scan-action@ 425 | env: 426 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 427 | SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }} 428 | SONAR_ROOT_CERT: ${{ secrets.SONAR_ROOT_CERT }} 429 | with: 430 | # Consult https://docs.sonarsource.com/sonarqube-server/latest/analyzing-source-code/scanners/sonarscanner/ for more information and options 431 | args: > 432 | --define sonar.cfamily.compile-commands="${{ env.BUILD_WRAPPER_OUT_DIR }}/compile_commands.json" 433 | ``` 434 | 435 | 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. 436 | 437 | It should look like this: 438 | 439 | ```yaml 440 | with: 441 | args: > 442 | --define "sonar.cfamily.build-wrapper-output=${{ env.BUILD_WRAPPER_OUT_DIR }}" 443 | ``` 444 | 445 | See also [example configurations of C++ projects for SonarQube Server](https://github.com/search?q=org%3Asonarsource-cfamily-examples+gh-actions-sq&type=repositories). 446 | 447 | ## Advanced Settings 448 | 449 | ### Self-hosted runner or container 450 | 451 | When running the action in a self-hosted runner or container, please ensure that the following programs are installed: 452 | 453 | * **curl** or **wget** 454 | * **unzip** 455 | 456 | ### Additional information 457 | 458 | The `sonarqube-scan-action/install-build-wrapper` action installs `coreutils` if run on macOS. 459 | 460 | ## Support & Community 461 | 462 | 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). 463 | 464 | ### License 465 | 466 | Container images built with this project include third-party materials. 467 | -------------------------------------------------------------------------------- /.github/workflows/qa-main.yml: -------------------------------------------------------------------------------- 1 | name: QA Main action 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | types: [opened, synchronize, reopened] 9 | 10 | jobs: 11 | noInputsTest: 12 | name: > 13 | No inputs 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | os: [github-ubuntu-latest-s, macos-latest] 18 | runs-on: ${{ matrix.os }} 19 | steps: 20 | - uses: actions/checkout@v5 21 | with: 22 | token: ${{ secrets.GITHUB_TOKEN }} 23 | - name: Run action without args 24 | uses: ./ 25 | env: 26 | SONAR_HOST_URL: http://not_actually_used 27 | SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}' 28 | - name: Assert 29 | run: | 30 | ./test/assertFileContains ./output.properties "sonar.projectBaseDir=." 31 | argsInputTest: 32 | name: > 33 | 'args' input 34 | strategy: 35 | fail-fast: false 36 | matrix: 37 | os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest] 38 | runs-on: ${{ matrix.os }} 39 | steps: 40 | - uses: actions/checkout@v5 41 | with: 42 | token: ${{ secrets.GITHUB_TOKEN }} 43 | - name: Run action with args 44 | uses: ./ 45 | with: 46 | args: -Dsonar.someArg=aValue -Dsonar.anotherArgWithSpaces="Another Value" -Dsonar.argWithSingleQuotes='Another Value' 47 | env: 48 | SONAR_HOST_URL: http://not_actually_used 49 | SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}' 50 | - name: Assert 51 | run: | 52 | ./test/assertFileContains ./output.properties "sonar.someArg=aValue" 53 | ./test/assertFileContains ./output.properties 'sonar.anotherArgWithSpaces="Another Value"' 54 | ./test/assertFileContains ./output.properties "sonar.argWithSingleQuotes='Another Value'" 55 | argsInputInjectionTest: 56 | name: > 57 | 'args' input with command injection will fail 58 | strategy: 59 | fail-fast: false 60 | matrix: 61 | os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest] 62 | args: 63 | [ 64 | -Dsonar.someArg=aValue && echo "Injection", 65 | -Dsonar.someArg="value\"; whoami; echo \"", 66 | ] 67 | runs-on: ${{ matrix.os }} 68 | steps: 69 | - uses: actions/checkout@v5 70 | with: 71 | token: ${{ secrets.GITHUB_TOKEN }} 72 | - name: Run action with args 73 | id: runTest 74 | uses: ./ 75 | continue-on-error: true 76 | with: 77 | args: ${{ matrix.args }} 78 | env: 79 | SONAR_HOST_URL: http://not_actually_used 80 | SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}' 81 | - name: Fail if action succeeded 82 | if: steps.runTest.outcome == 'success' 83 | run: exit 1 84 | - name: Assert the scanner was not called 85 | run: | 86 | ./test/assertFileDoesntExist ./output.properties 87 | backtickCommandInjectionTest: 88 | name: > 89 | 'args' input with backticks injection does not execute command 90 | strategy: 91 | fail-fast: false 92 | matrix: 93 | os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest] 94 | runs-on: ${{ matrix.os }} 95 | steps: 96 | - uses: actions/checkout@v5 97 | with: 98 | token: ${{ secrets.GITHUB_TOKEN }} 99 | - name: Run action with args 100 | uses: ./ 101 | continue-on-error: true 102 | with: 103 | args: > 104 | -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" 105 | env: 106 | SONAR_HOST_URL: http://not_actually_used 107 | SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}' 108 | - name: Assert command in arg is not executed 109 | run: | 110 | ./test/assertFileContains ./output.properties 'sonar.arg1="refs/heads/branch\\: \[workflows\] Bump `actions/\*`"' 111 | ./test/assertFileContains ./output.properties 'sonar.arg2="test `echo Command Injection`"' 112 | ./test/assertFileContains ./output.properties 'sonar.arg3="`id`"' 113 | ./test/assertFileContains ./output.properties "sonar.arg4=\"test'; \`echo injection\`; echo '\"" 114 | ./test/assertFileContains ./output.properties 'sonar.arg5=" `whoami` "' 115 | ./test/assertFileContains ./output.properties 'sonar.arg6="test\\\\`echo injection\\\\`test"' 116 | dollarSymbolCommandInjectionTest: 117 | name: > 118 | 'args' input with dollar command injection does not execute command 119 | strategy: 120 | matrix: 121 | os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest] 122 | runs-on: ${{ matrix.os }} 123 | steps: 124 | - uses: actions/checkout@v5 125 | with: 126 | token: ${{ secrets.GITHUB_TOKEN }} 127 | - name: Run action with args 128 | uses: ./ 129 | continue-on-error: true 130 | with: 131 | 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 "' 132 | env: 133 | SONAR_HOST_URL: http://not_actually_used 134 | SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}' 135 | - name: Assert command in arg is not executed 136 | run: | 137 | ./test/assertFileContains ./output.properties 'sonar.arg1="$(whoami)"' 138 | ./test/assertFileContains ./output.properties 'sonar.arg2="$GITHUB_TOKEN"' 139 | ./test/assertFileContains ./output.properties 'sonar.arg3="$(echo outer $(echo inner))"' 140 | ./test/assertFileContains ./output.properties 'sonar.arg4="value\\\\$(whoami)end"' 141 | ./test/assertFileContains ./output.properties 'sonar.arg5="$(printf '\''A%.0s'\'' {1..10000})"' 142 | ./test/assertFileContains ./output.properties 'sonar.arg6='\''value"; $(whoami); echo "'\''' 143 | otherCommandInjectionVariantsTest: 144 | name: > 145 | 'args' input with other command injection variants does not execute command 146 | strategy: 147 | matrix: 148 | os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest] 149 | runs-on: ${{ matrix.os }} 150 | steps: 151 | - uses: actions/checkout@v5 152 | with: 153 | token: ${{ secrets.GITHUB_TOKEN }} 154 | - name: Run action with args 155 | uses: ./ 156 | continue-on-error: true 157 | with: 158 | 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" 159 | env: 160 | SONAR_HOST_URL: http://not_actually_used 161 | SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}' 162 | - name: Assert command in arg is not executed 163 | run: | 164 | ./test/assertFileContains ./output.properties 'sonar.arg1="test | base64"' 165 | ./test/assertFileContains ./output.properties 'sonar.arg2="value; whoami"' 166 | ./test/assertFileContains ./output.properties 'sonar.arg3="value && echo test"' 167 | ./test/assertFileContains ./output.properties 'sonar.arg4="value > /tmp/output.txt"' 168 | ./test/assertFileContains ./output.properties 'sonar.arg5="< /etc/passwd"' 169 | ./test/assertFileContains ./output.properties 'sonar.arg6=""' 170 | ./test/assertFileContains ./output.properties 'sonar.arg7="../../../\*"' 171 | ./test/assertFileContains ./output.properties 'sonar.arg8="\*.key"' 172 | ./test/assertFileContains ./output.properties 'sonar.arg9="test\\\\u0027\\\\u0060whoami\\\\u0060"' 173 | projectBaseDirInputTest: 174 | name: > 175 | 'projectBaseDir' input 176 | strategy: 177 | matrix: 178 | os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest] 179 | runs-on: ${{ matrix.os }} 180 | steps: 181 | - uses: actions/checkout@v5 182 | with: 183 | token: ${{ secrets.GITHUB_TOKEN }} 184 | - run: mkdir -p ./baseDir 185 | - name: Run action with projectBaseDir 186 | uses: ./ 187 | with: 188 | args: -Dsonar.scanner.internal.dumpToFile=./output.properties 189 | projectBaseDir: ./baseDir 190 | env: 191 | SONAR_HOST_URL: http://not_actually_used 192 | SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}' 193 | - name: Assert 194 | run: | 195 | ./test/assertFileContains ./output.properties "sonar.projectBaseDir=.*/baseDir" 196 | scannerVersionTest: 197 | name: > 198 | 'scannerVersion' input 199 | runs-on: github-ubuntu-latest-s # assumes default RUNNER_ARCH for linux is X64 200 | steps: 201 | - uses: actions/checkout@v5 202 | with: 203 | token: ${{ secrets.GITHUB_TOKEN }} 204 | - name: Run action with scannerVersion 205 | uses: ./ 206 | with: 207 | scannerVersion: 6.1.0.4477 208 | args: -Dsonar.scanner.internal.dumpToFile=./output.properties 209 | env: 210 | NO_CACHE: true # force install-sonar-scanner-cli.sh execution 211 | SONAR_HOST_URL: http://not_actually_used 212 | SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}' 213 | - name: Assert 214 | run: | 215 | # Verify the tool was installed by checking it's in PATH 216 | if ! command -v sonar-scanner &> /dev/null; then 217 | echo "Error: sonar-scanner not found in PATH" 218 | exit 1 219 | fi 220 | scannerBinariesUrlTest: 221 | name: > 222 | 'scannerBinariesUrl' input with invalid URL 223 | runs-on: github-ubuntu-latest-s # assumes default RUNNER_ARCH for linux is X64 224 | steps: 225 | - uses: actions/checkout@v5 226 | with: 227 | token: ${{ secrets.GITHUB_TOKEN }} 228 | - name: Run action with scannerBinariesUrl 229 | id: runTest 230 | uses: ./ 231 | continue-on-error: true 232 | with: 233 | scannerVersion: 6.2.1.4610 234 | scannerBinariesUrl: https://invalid_uri/Distribution/sonar-scanner-cli 235 | env: 236 | NO_CACHE: true # force install-sonar-scanner-cli.sh execution 237 | SONAR_HOST_URL: http://not_actually_used 238 | SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}' 239 | - name: Fail if action succeeded 240 | if: steps.runTest.outcome == 'success' 241 | run: exit 1 242 | - name: Assert Sonar Scanner CLI was not downloaded 243 | run: | 244 | ./test/assertFileDoesntExist "$RUNNER_TEMP/sonarscanner/sonar-scanner-cli-6.2.1.4610-linux-x64.zip" 245 | - name: Assert Sonar Scanner CLI was not executed 246 | run: | 247 | ./test/assertFileDoesntExist ./output.properties 248 | scannerBinariesUrlIsEscapedWithWget: 249 | name: > 250 | 'scannerBinariesUrl' is escaped with wget so special chars are not injected in the download command 251 | runs-on: github-ubuntu-latest-s 252 | steps: 253 | - uses: actions/checkout@v5 254 | with: 255 | token: ${{ secrets.GITHUB_TOKEN }} 256 | - name: Run action with scannerBinariesUrl 257 | id: runTest 258 | uses: ./ 259 | continue-on-error: true 260 | with: 261 | scannerBinariesUrl: "http://some_uri;touch file.txt;" 262 | env: 263 | NO_CACHE: true 264 | SONAR_HOST_URL: http://not_actually_used 265 | SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output1.properties"}' 266 | - name: Assert file.txt does not exist 267 | run: | 268 | ./test/assertFileDoesntExist "$RUNNER_TEMP/sonarscanner/file.txt" 269 | scannerBinariesUrlIsEscapedWithCurl: 270 | name: > 271 | 'scannerBinariesUrl' is escaped with curl so special chars are not injected in the download command 272 | runs-on: github-ubuntu-latest-s 273 | steps: 274 | - uses: actions/checkout@v5 275 | with: 276 | token: ${{ secrets.GITHUB_TOKEN }} 277 | - name: Remove wget 278 | run: sudo apt-get remove -y wget 279 | - name: Assert wget is not available 280 | run: | 281 | if command -v wget 2>&1 >/dev/null 282 | then 283 | exit 1 284 | fi 285 | - name: Run action with scannerBinariesUrl 286 | id: runTest 287 | uses: ./ 288 | continue-on-error: true 289 | with: 290 | scannerBinariesUrl: "http://some_uri http://another_uri'; touch file.txt;" 291 | env: 292 | NO_CACHE: true 293 | SONAR_HOST_URL: http://not_actually_used 294 | SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output1.properties"}' 295 | - name: Assert file.txt does not exist 296 | run: | 297 | ./test/assertFileDoesntExist "$RUNNER_TEMP/sonarscanner/file.txt" 298 | dontFailGradleTest: 299 | name: > 300 | Don't fail on Gradle project 301 | runs-on: github-ubuntu-latest-s 302 | steps: 303 | - uses: actions/checkout@v5 304 | with: 305 | token: ${{ secrets.GITHUB_TOKEN }} 306 | - name: Run action on Gradle project 307 | id: runTest 308 | uses: ./ 309 | continue-on-error: true 310 | env: 311 | SONAR_HOST_URL: http://not_actually_used 312 | SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}' 313 | with: 314 | projectBaseDir: ./test/gradle-project 315 | args: -Dsonar.scanner.internal.dumpToFile=./output.properties 316 | - name: Assert 317 | run: | 318 | ./test/assertFileExists ./output.properties 319 | dontFailGradleKotlinTest: 320 | name: > 321 | Don't fail on Kotlin Gradle project 322 | runs-on: github-ubuntu-latest-s 323 | steps: 324 | - uses: actions/checkout@v5 325 | with: 326 | token: ${{ secrets.GITHUB_TOKEN }} 327 | - name: Run action on Kotlin Gradle project 328 | id: runTest 329 | uses: ./ 330 | continue-on-error: true 331 | env: 332 | SONAR_HOST_URL: http://not_actually_used 333 | SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}' 334 | with: 335 | projectBaseDir: ./test/gradle-project 336 | args: -Dsonar.scanner.internal.dumpToFile=./output.properties 337 | - name: Assert 338 | run: | 339 | ./test/assertFileExists ./output.properties 340 | dontFailMavenTest: 341 | name: > 342 | Don't fail on Maven project 343 | runs-on: github-ubuntu-latest-s 344 | steps: 345 | - uses: actions/checkout@v5 346 | with: 347 | token: ${{ secrets.GITHUB_TOKEN }} 348 | - name: Run action on Maven project 349 | id: runTest 350 | uses: ./ 351 | continue-on-error: true 352 | env: 353 | SONAR_HOST_URL: http://not_actually_used 354 | SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}' 355 | with: 356 | projectBaseDir: ./test/maven-project 357 | args: -Dsonar.scanner.internal.dumpToFile=./output.properties 358 | - name: Assert 359 | run: | 360 | ./test/assertFileExists ./output.properties 361 | runAnalysisTest: 362 | runs-on: github-ubuntu-latest-s 363 | services: 364 | sonarqube: 365 | image: sonarqube:lts-community 366 | ports: 367 | - 9000:9000 368 | volumes: 369 | - sonarqube_data:/opt/sonarqube/data 370 | - sonarqube_logs:/opt/sonarqube/logs 371 | - sonarqube_extensions:/opt/sonarqube/extensions 372 | options: >- 373 | --health-cmd "grep -Fq \"SonarQube is operational\" /opt/sonarqube/logs/sonar.log" 374 | --health-interval 10s 375 | --health-timeout 5s 376 | --health-retries 10 377 | steps: 378 | - uses: actions/checkout@v5 379 | with: 380 | token: ${{ secrets.GITHUB_TOKEN }} 381 | - name: Run action on sample project 382 | id: runTest 383 | uses: ./ 384 | env: 385 | SONAR_HOST_URL: http://localhost:9000 386 | with: 387 | args: -Dsonar.login=admin -Dsonar.password=admin 388 | projectBaseDir: ./test/example-project 389 | - name: Assert 390 | run: | 391 | ./test/assertFileExists ./test/example-project/.scannerwork/report-task.txt 392 | runnerDebugUsedTest: 393 | name: > 394 | 'RUNNER_DEBUG' is used 395 | strategy: 396 | fail-fast: false 397 | matrix: 398 | os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest] 399 | runs-on: ${{ matrix.os }} 400 | steps: 401 | - uses: actions/checkout@v5 402 | with: 403 | token: ${{ secrets.GITHUB_TOKEN }} 404 | - name: Run action with debug mode 405 | uses: ./ 406 | with: 407 | args: -Dsonar.scanner.internal.dumpToFile=./output.properties 408 | env: 409 | RUNNER_DEBUG: 1 410 | SONAR_HOST_URL: http://not_actually_used 411 | SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}' 412 | - name: Assert 413 | run: | 414 | ./test/assertFileContains ./output.properties "sonar.verbose=true" 415 | runAnalysisWithCacheTest: 416 | runs-on: github-ubuntu-latest-s 417 | services: 418 | sonarqube: 419 | image: sonarqube:lts-community 420 | ports: 421 | - 9000:9000 422 | volumes: 423 | - sonarqube_data:/opt/sonarqube/data 424 | - sonarqube_logs:/opt/sonarqube/logs 425 | - sonarqube_extensions:/opt/sonarqube/extensions 426 | options: >- 427 | --health-cmd "grep -Fq \"SonarQube is operational\" /opt/sonarqube/logs/sonar.log" 428 | --health-interval 10s 429 | --health-timeout 5s 430 | --health-retries 10 431 | steps: 432 | - uses: actions/checkout@v5 433 | with: 434 | token: ${{ secrets.GITHUB_TOKEN }} 435 | - name: SonarQube Cache 436 | uses: actions/cache@v4 437 | with: 438 | path: ${{ github.workspace }}/.sonar/cache 439 | key: ${{ runner.os }}-${{ runner.arch }}-sonar 440 | - name: Run action on sample project 441 | id: runTest 442 | uses: ./ 443 | env: 444 | SONAR_HOST_URL: http://localhost:9000 445 | SONAR_USER_HOME: ${{ github.workspace }}/.sonar 446 | with: 447 | args: -Dsonar.login=admin -Dsonar.password=admin 448 | projectBaseDir: ./test/example-project 449 | - name: Assert 450 | run: | 451 | ./test/assertFileExists ./test/example-project/.scannerwork/report-task.txt 452 | overrideSonarcloudUrlTest: 453 | name: > 454 | 'SONARCLOUD_URL' is used 455 | strategy: 456 | fail-fast: false 457 | matrix: 458 | os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest] 459 | runs-on: ${{ matrix.os }} 460 | steps: 461 | - uses: actions/checkout@v5 462 | with: 463 | token: ${{ secrets.GITHUB_TOKEN }} 464 | - name: Run action with SONARCLOUD_URL 465 | uses: ./ 466 | with: 467 | args: -Dsonar.scanner.apiBaseUrl=api.mirror.sonarcloud.io -Dsonar.scanner.internal.dumpToFile=./output.properties 468 | env: 469 | SONARCLOUD_URL: mirror.sonarcloud.io 470 | SONAR_TOKEN: FAKE_TOKEN 471 | - name: Assert 472 | run: | 473 | ./test/assertFileContains ./output.properties "sonar.host.url=mirror.sonarcloud.io" 474 | ./test/assertFileContains ./output.properties "sonar.scanner.sonarcloudUrl=mirror.sonarcloud.io" 475 | curlPerformsRedirect: 476 | name: > 477 | curl performs redirect when scannerBinariesUrl returns 3xx 478 | runs-on: github-ubuntu-latest-s 479 | steps: 480 | - uses: actions/checkout@v5 481 | with: 482 | token: ${{ secrets.GITHUB_TOKEN }} 483 | - name: Remove wget 484 | run: sudo apt-get remove -y wget 485 | - name: Assert wget is not available 486 | run: | 487 | if command -v wget 2>&1 >/dev/null 488 | then 489 | exit 1 490 | fi 491 | - name: Generate SSL certificates for nginx 492 | run: ./generate-ssl.sh 493 | working-directory: .github/qa-nginx-redirecting 494 | - name: Start nginx via Docker Compose 495 | run: docker compose up -d --wait 496 | working-directory: .github/qa-nginx-redirecting 497 | - name: Run action with scannerBinariesUrl 498 | id: runTest 499 | uses: ./ 500 | with: 501 | scannerVersion: 6.2.1.4610 502 | scannerBinariesUrl: https://localhost:8080/clientRedirectToSonarBinaries 503 | env: 504 | NO_CACHE: true 505 | NODE_TLS_REJECT_UNAUTHORIZED: 0 506 | SONAR_HOST_URL: http://not_actually_used 507 | SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output1.properties"}' 508 | - name: Assert Sonar Scanner CLI was downloaded 509 | run: | 510 | # Verify the tool was installed by checking it's in PATH 511 | if ! command -v sonar-scanner &> /dev/null; then 512 | echo "Error: sonar-scanner not found in PATH" 513 | exit 1 514 | fi 515 | useSslCertificate: 516 | name: > 517 | 'SONAR_ROOT_CERT' is converted to truststore 518 | strategy: 519 | fail-fast: false 520 | matrix: 521 | os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest] 522 | runs-on: ${{ matrix.os }} 523 | steps: 524 | - uses: actions/checkout@v5 525 | with: 526 | token: ${{ secrets.GITHUB_TOKEN }} 527 | - name: Run action with SSL certificate 528 | uses: ./ 529 | with: 530 | args: -Dsonar.scanner.internal.dumpToFile=./output.properties 531 | env: 532 | SONAR_ROOT_CERT: | 533 | -----BEGIN CERTIFICATE----- 534 | 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 564 | Fct6d1S08JAosVnZcP2P7Yz+TbmDRtsqCgk= 565 | -----END CERTIFICATE----- 566 | SONAR_HOST_URL: http://not_actually_used 567 | - name: Assert 568 | run: | 569 | ./test/assertFileExists ~/.sonar/ssl/truststore.p12 570 | analysisWithSslCertificate: 571 | name: > 572 | Analysis takes into account 'SONAR_ROOT_CERT' 573 | runs-on: github-ubuntu-latest-s 574 | steps: 575 | - uses: actions/checkout@v5 576 | with: 577 | token: ${{ secrets.GITHUB_TOKEN }} 578 | - name: Generate server certificate 579 | run: | 580 | openssl req \ 581 | -newkey rsa:4096 \ 582 | -x509 \ 583 | -sha256 \ 584 | -addext "subjectAltName = DNS:localhost" \ 585 | -days 3650 \ 586 | -nodes \ 587 | -out server.crt \ 588 | -subj "/C=CH/ST=Geneva/L=Geneva/O=Server/OU=Dept" \ 589 | -keyout server.key 590 | working-directory: .github/qa-sq-behind-ngix 591 | - name: Start nginx and SonarQube via Docker Compose 592 | run: docker compose up -d --wait 593 | working-directory: .github/qa-sq-behind-ngix 594 | - name: Read correct server certificate 595 | run: | 596 | # read server.crt from .github/qa-sq-behind-ngix/ and store into the SONAR_ROOT_CERT_VALID 597 | # environment variable, to be able to read it in the next step 598 | { 599 | echo 'SONAR_ROOT_CERT_VALID<<==========' 600 | cat .github/qa-sq-behind-ngix/server.crt 601 | echo ========== 602 | } >> $GITHUB_ENV 603 | - name: Run action with the correct SSL certificate 604 | uses: ./ 605 | env: 606 | SONAR_ROOT_CERT: ${{ env.SONAR_ROOT_CERT_VALID }} 607 | SONAR_HOST_URL: https://localhost:4443 608 | with: 609 | args: -Dsonar.login=admin -Dsonar.password=admin 610 | projectBaseDir: ./test/example-project 611 | - name: Clear imported SSL certificates 612 | run: | 613 | rm -f ~/.sonar/ssl/truststore.p12 614 | - name: Run action with an invalid SSL certificate 615 | id: invalid_ssl_certificate 616 | continue-on-error: true 617 | uses: ./ 618 | env: 619 | SONAR_ROOT_CERT: | 620 | -----BEGIN CERTIFICATE----- 621 | INVALID 622 | -----END CERTIFICATE----- 623 | SONAR_HOST_URL: https://localhost:4443 624 | with: 625 | args: -Dsonar.login=admin -Dsonar.password=admin 626 | projectBaseDir: ./test/example-project 627 | - name: Assert failure of previous step 628 | if: steps.invalid_ssl_certificate.outcome == 'success' 629 | run: exit 1 630 | - name: Clear imported SSL certificates 631 | run: | 632 | rm -f ~/.sonar/ssl/truststore.p12 633 | - name: Run action with the wrong SSL certificate 634 | id: wrong_ssl_certificate 635 | continue-on-error: true 636 | uses: ./ 637 | env: 638 | SONAR_ROOT_CERT: | 639 | -----BEGIN CERTIFICATE----- 640 | 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 670 | -----END CERTIFICATE----- 671 | SONAR_HOST_URL: https://localhost:4443 672 | with: 673 | args: -Dsonar.login=admin -Dsonar.password=admin 674 | projectBaseDir: ./test/example-project 675 | - name: Assert failure of previous step 676 | if: steps.wrong_ssl_certificate.outcome == 'success' 677 | run: exit 1 678 | updateTruststoreWhenPresent: # can happen in uncleaned self-hosted runners 679 | name: > 680 | truststore.p12 is updated when present 681 | runs-on: github-ubuntu-latest-s 682 | steps: 683 | - uses: actions/checkout@v5 684 | with: 685 | token: ${{ secrets.GITHUB_TOKEN }} 686 | - name: Create SONAR_SSL_FOLDER with a file in it (not-truststore.p12) 687 | run: | 688 | SONAR_SSL_FOLDER=~/.sonar/ssl 689 | mkdir -p "$SONAR_SSL_FOLDER" 690 | touch "$SONAR_SSL_FOLDER/not-truststore.p12" 691 | # emit SONAR_SSL_FOLDER to be able to read it in the next steps 692 | echo "SONAR_SSL_FOLDER=$SONAR_SSL_FOLDER" >> $GITHUB_ENV 693 | - name: Assert truststore.p12 does not file exists 694 | run: | 695 | [ ! -f "$SONAR_SSL_FOLDER/truststore.p12" ] || exit 1 696 | - name: Run action with SONAR_ROOT_CERT 697 | uses: ./ 698 | env: 699 | # NO_CACHE not needed, as SONAR_SSL_FOLDER is setup when the Sonar Scanner is run, not installed 700 | SONAR_HOST_URL: http://not_actually_used 701 | SONAR_ROOT_CERT: | 702 | -----BEGIN CERTIFICATE----- 703 | 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 733 | -----END CERTIFICATE----- 734 | with: 735 | args: -Dsonar.scanner.internal.dumpToFile=./output.properties 736 | - name: Assert not-truststore.p12 file still exists 737 | run: | 738 | [ -f "$SONAR_SSL_FOLDER/not-truststore.p12" ] || exit 1 739 | - name: Assert truststore.p12 file now exists and take note of modification time 740 | run: | 741 | [ -f "$SONAR_SSL_FOLDER/truststore.p12" ] || exit 1 742 | # emit the modification time of the truststore.p12 file to be able to read it in the next steps 743 | TRUSTSTORE_P12_MOD_TIME_T1=$(stat -c %Y "$SONAR_SSL_FOLDER/truststore.p12") 744 | echo "TRUSTSTORE_P12_MOD_TIME_T1=$TRUSTSTORE_P12_MOD_TIME_T1" >> $GITHUB_ENV 745 | - name: Run action a second time with a different SONAR_ROOT_CERT 746 | uses: ./ 747 | env: 748 | # NO_CACHE not needed, as SONAR_SSL_FOLDER is setup when the Sonar Scanner is run, not installed 749 | SONAR_HOST_URL: http://not_actually_used 750 | SONAR_ROOT_CERT: | 751 | -----BEGIN CERTIFICATE----- 752 | MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMC 753 | Tk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYD 754 | VQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG 755 | 9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4 756 | MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xi 757 | ZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2Zl 758 | aWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5v 759 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LO 760 | NoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHIS 761 | KOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d 762 | 1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8 763 | BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7n 764 | bK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2Qar 765 | Q4/67OZfHd7R+POBXhophSMv1ZOo 766 | -----END CERTIFICATE----- 767 | with: 768 | args: -Dsonar.scanner.internal.dumpToFile=./output.properties 769 | - name: Assert truststore.p12 still exists, but it has been updated, and take note of modification time 770 | run: | 771 | [ -f "$SONAR_SSL_FOLDER/truststore.p12" ] || exit 1 772 | TRUSTSTORE_P12_MOD_TIME_T2=$(stat -c %Y "$SONAR_SSL_FOLDER/truststore.p12") 773 | [ "$TRUSTSTORE_P12_MOD_TIME_T1" != "$TRUSTSTORE_P12_MOD_TIME_T2" ] || exit 1 774 | # emit the modification time of the truststore.p12 file to be able to read it in the next steps 775 | echo "TRUSTSTORE_P12_MOD_TIME_T2=$TRUSTSTORE_P12_MOD_TIME_T2" >> $GITHUB_ENV 776 | - name: Remove sonar alias from truststore.p12 777 | run: keytool -delete -alias sonar -keystore "$SONAR_SSL_FOLDER/truststore.p12" -storepass changeit 778 | - name: Run action a third time 779 | uses: ./ 780 | env: 781 | # NO_CACHE not needed, as SONAR_SSL_FOLDER is setup when the Sonar Scanner is run, not installed 782 | SONAR_HOST_URL: http://not_actually_used 783 | SONAR_ROOT_CERT: | 784 | -----BEGIN CERTIFICATE----- 785 | MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMC 786 | Tk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYD 787 | VQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG 788 | 9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4 789 | MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xi 790 | ZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2Zl 791 | aWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5v 792 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LO 793 | NoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHIS 794 | KOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d 795 | 1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8 796 | BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7n 797 | bK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2Qar 798 | Q4/67OZfHd7R+POBXhophSMv1ZOo 799 | -----END CERTIFICATE----- 800 | with: 801 | args: -Dsonar.scanner.internal.dumpToFile=./output.properties 802 | - name: Assert truststore.p12 still exists, and it has been updated again 803 | run: | 804 | [ -f "$SONAR_SSL_FOLDER/truststore.p12" ] || exit 1 805 | TRUSTSTORE_P12_MOD_TIME_T3=$(stat -c %Y "$SONAR_SSL_FOLDER/truststore.p12") 806 | [ "$TRUSTSTORE_P12_MOD_TIME_T2" != "$TRUSTSTORE_P12_MOD_TIME_T3" ] || exit 1 807 | scannerVersionValidationTest: 808 | name: > 809 | 'scannerVersion' input validation 810 | runs-on: github-ubuntu-latest-s 811 | steps: 812 | - uses: actions/checkout@v5 813 | with: 814 | token: ${{ secrets.GITHUB_TOKEN }} 815 | - name: Run action with invalid scannerVersion 816 | id: invalid_version 817 | uses: ./ 818 | continue-on-error: true 819 | with: 820 | scannerVersion: "7.1.0-SNAPSHOT" 821 | args: -Dsonar.scanner.internal.dumpToFile=./output.properties 822 | env: 823 | NO_CACHE: true 824 | SONAR_HOST_URL: http://not_actually_used 825 | - name: Assert failure of previous step 826 | if: steps.invalid_version.outcome == 'success' 827 | run: | 828 | echo "Action with invalid scannerVersion should have failed but succeeded" 829 | exit 1 830 | --------------------------------------------------------------------------------