JS-1227 perf: Optimize S125 commented-out code detection #1979
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build | |
| on: | |
| push: | |
| branches: | |
| - master | |
| - branch-* | |
| - dogfood-* | |
| pull_request: | |
| merge_group: | |
| workflow_dispatch: | |
| schedule: | |
| - cron: '0 0 * * *' # Nightly for analyze and iris tasks | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| setup: | |
| runs-on: github-ubuntu-latest-s | |
| name: Setup - Prepare Node.js versions and test hashes | |
| permissions: &read_permissions | |
| id-token: write | |
| contents: read | |
| outputs: | |
| node-matrix: ${{ steps.generate-matrix.outputs.matrix }} | |
| js-files-hash: ${{ steps.compute-js-hash.outputs.hash }} | |
| maven-hash: ${{ steps.compute-maven-hash.outputs.hash }} | |
| npm-hash: ${{ steps.compute-npm-hash.outputs.hash }} | |
| cache-month: ${{ steps.cache-month.outputs.month }} | |
| is-default-branch: ${{ github.ref_name == github.event.repository.default_branch }} | |
| steps: | |
| - &checkout | |
| name: Checkout source code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Generate Node.js version matrix from package.json | |
| id: generate-matrix | |
| run: | | |
| # Extract node version range from package.json and parse versions with jq | |
| MATRIX=$(jq -c '{ | |
| "node-version": ( | |
| .engines.node | |
| | split(" || ") | |
| | map(gsub("^[~^>=<]+"; "")) | |
| ) | |
| }' package.json) | |
| echo "matrix=$MATRIX" >> $GITHUB_OUTPUT | |
| echo "Generated Node.js version matrix from package.json: $MATRIX" | |
| - name: Compute JS files hash for test caching | |
| id: compute-js-hash | |
| run: | | |
| HASH=$(find packages patches sonar-plugin/javascript-checks/src/main/resources/org/sonar/l10n/javascript/rules tools typings .nycrc package.json server.mjs -type f 2>/dev/null | sort | xargs sha256sum | sha256sum | cut -d' ' -f1) | |
| echo "hash=$HASH" >> $GITHUB_OUTPUT | |
| echo "Computed JS files hash: $HASH" | |
| - name: Compute Maven hash for cache key | |
| id: compute-maven-hash | |
| run: | | |
| HASH=$(find . -name 'pom.xml' -type f | sort | xargs sha256sum | sha256sum | cut -d' ' -f1) | |
| echo "hash=$HASH" >> $GITHUB_OUTPUT | |
| echo "Computed Maven hash: $HASH" | |
| - name: Compute NPM hash for cache key | |
| id: compute-npm-hash | |
| run: | | |
| HASH=$(sha256sum package-lock.json patches/* 2>/dev/null | sha256sum | cut -d' ' -f1) | |
| echo "hash=$HASH" >> $GITHUB_OUTPUT | |
| echo "Computed NPM hash: $HASH" | |
| - name: Compute cache month for periodic reset | |
| id: cache-month | |
| run: | | |
| MONTH=$(date +'%Y-%m') | |
| echo "month=$MONTH" >> $GITHUB_OUTPUT | |
| echo "Cache month: $MONTH" | |
| get_build_number: | |
| runs-on: github-ubuntu-latest-s | |
| name: Get build number | |
| needs: setup | |
| if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false | |
| permissions: *read_permissions | |
| steps: | |
| - uses: SonarSource/ci-github-actions/get-build-number@master | |
| populate_npm_cache: | |
| runs-on: github-ubuntu-latest-s | |
| name: Populate NPM cache for Linux | |
| needs: setup | |
| if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false | |
| permissions: *read_permissions | |
| steps: &populate_npm_cache_steps | |
| - name: Cache NPM dependencies | |
| id: cache | |
| uses: SonarSource/gh-action_cache@v1 | |
| with: | |
| path: node_modules | |
| key: npm-${{ runner.os }}-${{ needs.setup.outputs.npm-hash }} | |
| lookup-only: true | |
| - name: Checkout source code | |
| if: steps.cache.outputs.cache-hit != 'true' | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - if: steps.cache.outputs.cache-hit != 'true' | |
| uses: jdx/mise-action@v3.6.1 | |
| with: | |
| version: 2025.11.2 | |
| cache_save: ${{ needs.setup.outputs.is-default-branch == 'true' }} | |
| mise_toml: | | |
| [tools] | |
| java = "21.0" | |
| maven = "3.9" | |
| node = "24.11.0" | |
| - if: steps.cache.outputs.cache-hit != 'true' | |
| id: secrets | |
| name: Access vault secrets | |
| uses: SonarSource/vault-action-wrapper@v3 | |
| with: | |
| secrets: | | |
| development/artifactory/token/${{ github.repository_owner }}-${{ github.event.repository.name }}-private-reader access_token | ARTIFACTORY_ACCESS_TOKEN; | |
| - if: steps.cache.outputs.cache-hit != 'true' | |
| name: Configure npm registry | |
| run: | | |
| npm config set //repox.jfrog.io/artifactory/api/npm/:_authToken=${{ fromJSON(steps.secrets.outputs.vault).ARTIFACTORY_ACCESS_TOKEN }} | |
| npm config set registry https://repox.jfrog.io/artifactory/api/npm/npm/ | |
| - if: steps.cache.outputs.cache-hit != 'true' | |
| name: Install NPM dependencies | |
| run: npm ci | |
| populate_npm_cache_win: | |
| runs-on: github-windows-latest-s | |
| name: Populate NPM cache for Windows | |
| needs: setup | |
| if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false | |
| permissions: *read_permissions | |
| steps: *populate_npm_cache_steps | |
| build: | |
| runs-on: github-ubuntu-latest-s | |
| name: Build SonarJS on Linux | |
| needs: [setup, get_build_number, populate_npm_cache] | |
| if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false | |
| permissions: *read_permissions | |
| steps: | |
| - *checkout | |
| - &mise | |
| uses: jdx/mise-action@v3.6.1 | |
| with: | |
| version: 2025.11.2 | |
| cache_save: ${{ needs.setup.outputs.is-default-branch == 'true' }} | |
| mise_toml: | | |
| [tools] | |
| java = "21.0" | |
| maven = "3.9" | |
| node = "24.11.0" | |
| - &npm_cache | |
| name: Cache NPM dependencies | |
| uses: SonarSource/gh-action_cache@v1 | |
| with: | |
| path: node_modules | |
| key: npm-${{ runner.os }}-${{ needs.setup.outputs.npm-hash }} | |
| - &maven_cache | |
| uses: ./.github/actions/maven-cache | |
| with: | |
| cache-month: ${{ needs.setup.outputs.cache-month }} | |
| maven-hash: ${{ needs.setup.outputs.maven-hash }} | |
| - &config_maven | |
| name: Configure Maven | |
| id: config-maven | |
| uses: SonarSource/ci-github-actions/config-maven@master | |
| with: | |
| artifactory-reader-role: private-reader | |
| disable-caching: 'true' | |
| - id: deployer-secrets | |
| uses: SonarSource/vault-action-wrapper@v3 | |
| with: | |
| secrets: | | |
| development/artifactory/token/{REPO_OWNER_NAME_DASH}-qa-deployer username | ARTIFACTORY_DEPLOY_USERNAME; | |
| development/artifactory/token/{REPO_OWNER_NAME_DASH}-qa-deployer access_token | ARTIFACTORY_DEPLOY_ACCESS_TOKEN; | |
| development/kv/data/sign key | SIGN_KEY; | |
| development/kv/data/sign passphrase | PGP_PASSPHRASE; | |
| - &java_coverage_cache | |
| name: Cache Java coverage | |
| uses: SonarSource/gh-action_cache@v1 | |
| with: | |
| path: coverage/java | |
| key: java-coverage-${{ github.sha }} | |
| - name: Build, test and deploy Maven artifacts | |
| run: mvn deploy -Pdeploy-sonarsource,coverage,coverage-report,sign,release -T1C | |
| env: | |
| ARTIFACTORY_DEPLOY_USERNAME: ${{ fromJSON(steps.deployer-secrets.outputs.vault).ARTIFACTORY_DEPLOY_USERNAME }} | |
| ARTIFACTORY_DEPLOY_PASSWORD: ${{ fromJSON(steps.deployer-secrets.outputs.vault).ARTIFACTORY_DEPLOY_ACCESS_TOKEN }} | |
| ARTIFACTORY_DEPLOY_REPO: sonarsource-public-qa | |
| SIGN_KEY: ${{ fromJSON(steps.deployer-secrets.outputs.vault).SIGN_KEY }} | |
| PGP_PASSPHRASE: ${{ fromJSON(steps.deployer-secrets.outputs.vault).PGP_PASSPHRASE }} | |
| - name: Upload SonarJS artifacts | |
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | |
| with: | |
| name: sonarjs-m2 | |
| path: ~/.m2/repository/org/sonarsource/javascript | |
| retention-days: 1 | |
| - name: Upload Maven target artifacts | |
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | |
| with: | |
| name: maven-targets-${{ github.sha }} | |
| path: | | |
| **/target/ | |
| !**/target/site/ | |
| retention-days: 1 | |
| # Clean up SonarJS artifacts before post-job cache save (only on default branch where cache is saved) | |
| - name: Clean up SonarJS artifacts before cache save | |
| if: github.ref_name == github.event.repository.default_branch | |
| run: rm -rf ~/.m2/repository/org/sonarsource/javascript | |
| # Windows builds and tests | |
| build_win: | |
| runs-on: github-windows-latest-s | |
| name: Build SonarJS on Windows | |
| needs: [setup, populate_npm_cache_win] | |
| if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false | |
| permissions: *read_permissions | |
| steps: | |
| - *checkout | |
| - *mise | |
| - *npm_cache | |
| - *maven_cache | |
| - *config_maven | |
| - name: Build and test Maven (no deploy) | |
| run: mvn verify -T1C | |
| build_eslint_plugin: | |
| runs-on: github-ubuntu-latest-s | |
| needs: [setup, populate_npm_cache] | |
| name: Build ESLint Plugin | |
| if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false | |
| permissions: *read_permissions | |
| steps: | |
| - *checkout | |
| - *mise | |
| - id: secrets | |
| name: Access vault secrets | |
| uses: SonarSource/vault-action-wrapper@v3 | |
| with: | |
| secrets: | | |
| development/artifactory/token/${{ github.repository_owner }}-${{ github.event.repository.name }}-private-reader access_token | ARTIFACTORY_ACCESS_TOKEN; | |
| - name: Configure npm registry | |
| run: | | |
| npm config set //repox.jfrog.io/artifactory/api/npm/:_authToken=${{ fromJSON(steps.secrets.outputs.vault).ARTIFACTORY_ACCESS_TOKEN }} | |
| npm config set registry https://repox.jfrog.io/artifactory/api/npm/npm/ | |
| - *npm_cache | |
| - name: Build ESLint plugin | |
| run: npm run eslint-plugin:build | |
| - &eslint_tarball_cache | |
| name: Cache ESLint plugin tarball | |
| uses: SonarSource/gh-action_cache@v1 | |
| with: | |
| path: lib/*.tgz | |
| key: eslint-tarball-${{ github.sha }} | |
| test_eslint_plugin: | |
| runs-on: github-ubuntu-latest-s | |
| name: ESLint Plugin Test - ESLint ${{ matrix.eslint-version }} Node ${{ matrix.node-version }} | |
| needs: [setup, build_eslint_plugin] | |
| permissions: *read_permissions | |
| strategy: | |
| matrix: | |
| include: | |
| - eslint-version: 9 | |
| node-version: '18.18.0' | |
| node-label: 'min supported' | |
| - eslint-version: 8 | |
| node-version: '18.18.0' | |
| node-label: 'min supported' | |
| - eslint-version: 8 | |
| node-version: '16.20.2' | |
| node-label: 'node 16' | |
| steps: | |
| - *checkout | |
| - uses: jdx/mise-action@v3.6.1 | |
| with: | |
| version: 2025.11.2 | |
| cache_save: ${{ needs.setup.outputs.is-default-branch == 'true' }} | |
| mise_toml: | | |
| [tools] | |
| node = "${{ matrix.node-version }}" | |
| - *eslint_tarball_cache | |
| - name: Test ESLint Plugin | |
| run: | | |
| cd its/eslint${{ matrix.eslint-version }}-plugin-sonarjs | |
| npm install --ignore-scripts | |
| npx tsc --noEmit | |
| npm run test | |
| knip: | |
| runs-on: github-ubuntu-latest-s | |
| name: Knip | |
| needs: [setup, populate_npm_cache] | |
| permissions: *read_permissions | |
| steps: | |
| - *checkout | |
| - *mise | |
| - *npm_cache | |
| - name: Run knip | |
| run: | | |
| npm run bbf | |
| npx knip | |
| test_js: | |
| runs-on: github-ubuntu-latest-m | |
| name: Unit tests JavaScript/TypeScript | |
| needs: [setup, populate_npm_cache] | |
| permissions: *read_permissions | |
| steps: | |
| - name: Check JS coverage cache | |
| id: cache | |
| uses: SonarSource/gh-action_cache@v1 | |
| with: | |
| path: coverage/js | |
| key: js-coverage-${{ needs.setup.outputs.js-files-hash }} | |
| lookup-only: true | |
| - name: Checkout source code | |
| if: steps.cache.outputs.cache-hit != 'true' | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - if: steps.cache.outputs.cache-hit != 'true' | |
| uses: jdx/mise-action@v3.6.1 | |
| with: | |
| version: 2025.11.2 | |
| cache_save: ${{ needs.setup.outputs.is-default-branch == 'true' }} | |
| mise_toml: | | |
| [tools] | |
| java = "21.0" | |
| maven = "3.9" | |
| node = "24.11.0" | |
| - name: Cache NPM dependencies | |
| if: steps.cache.outputs.cache-hit != 'true' | |
| uses: SonarSource/gh-action_cache@v1 | |
| with: | |
| path: node_modules | |
| key: npm-${{ runner.os }}-${{ needs.setup.outputs.npm-hash }} | |
| - if: steps.cache.outputs.cache-hit != 'true' | |
| name: Run JS tests with coverage | |
| run: | | |
| npm run generate-meta | |
| npm run bridge:compile | |
| npm run bridge:test:cov | |
| test_js_win: | |
| runs-on: github-windows-latest-s | |
| name: Unit tests JavaScript on Windows | |
| needs: [setup, populate_npm_cache_win] | |
| permissions: *read_permissions | |
| steps: | |
| - name: Cache JS test results marker (Windows) | |
| id: cache | |
| uses: SonarSource/gh-action_cache@v1 | |
| with: | |
| path: .js-test-marker-win | |
| key: js-test-win-${{ needs.setup.outputs.js-files-hash }} | |
| lookup-only: true | |
| - name: Checkout source code | |
| if: steps.cache.outputs.cache-hit != 'true' | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - if: steps.cache.outputs.cache-hit != 'true' | |
| uses: jdx/mise-action@v3.6.1 | |
| with: | |
| version: 2025.11.2 | |
| cache_save: ${{ needs.setup.outputs.is-default-branch == 'true' }} | |
| mise_toml: | | |
| [tools] | |
| java = "21.0" | |
| maven = "3.9" | |
| node = "24.11.0" | |
| - name: Cache NPM dependencies | |
| if: steps.cache.outputs.cache-hit != 'true' | |
| uses: SonarSource/gh-action_cache@v1 | |
| with: | |
| path: node_modules | |
| key: npm-${{ runner.os }}-${{ needs.setup.outputs.npm-hash }} | |
| - if: steps.cache.outputs.cache-hit != 'true' | |
| name: Run JS tests on Windows | |
| shell: bash | |
| run: | | |
| npm run generate-meta | |
| npm run bridge:compile | |
| npm run bridge:test:js | |
| mkdir -p .js-test-marker-win && touch .js-test-marker-win/success | |
| analyze_primary: | |
| runs-on: github-ubuntu-latest-s | |
| name: Analyze in SonarQube NEXT | |
| needs: [setup, test_js, build] | |
| if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false | |
| permissions: *read_permissions | |
| steps: | |
| - &checkout_with_tags | |
| name: Checkout source code with tags | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: '0' | |
| fetch-tags: 'true' | |
| - *mise | |
| - *npm_cache | |
| - *maven_cache | |
| - &js_coverage_cache | |
| name: Cache JS coverage | |
| id: js-coverage-cache | |
| uses: SonarSource/gh-action_cache@v1 | |
| with: | |
| path: coverage/js | |
| key: js-coverage-${{ needs.setup.outputs.js-files-hash }} | |
| - *java_coverage_cache | |
| - &download_maven_targets | |
| name: Download Maven target artifacts | |
| uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 | |
| with: | |
| name: maven-targets-${{ github.sha }} | |
| - *config_maven | |
| - id: secrets | |
| uses: SonarSource/vault-action-wrapper@v3 | |
| with: | |
| secrets: | | |
| development/kv/data/next url | SONAR_URL; | |
| development/kv/data/next token | SONAR_TOKEN; | |
| - name: Run SonarQube analysis on Next | |
| env: | |
| SONAR_HOST_URL: ${{ fromJSON(steps.secrets.outputs.vault).SONAR_URL }} | |
| SONAR_TOKEN: ${{ fromJSON(steps.secrets.outputs.vault).SONAR_TOKEN }} | |
| run: | | |
| SONAR_ARGS="-Dsonar.host.url=$SONAR_HOST_URL" | |
| SONAR_ARGS="$SONAR_ARGS -Dsonar.token=$SONAR_TOKEN" | |
| SONAR_ARGS="$SONAR_ARGS -Dsonar.projectKey=org.sonarsource.javascript:javascript" | |
| SONAR_ARGS="$SONAR_ARGS -Dsonar.projectVersion=${{ steps.config-maven.outputs.project-version }}" | |
| SONAR_ARGS="$SONAR_ARGS -Dsonar.scm.revision=${{ github.sha }}" | |
| SONAR_ARGS="$SONAR_ARGS -Dcommercial" | |
| # Add branch/PR information | |
| if [ "${{ github.event_name }}" == "pull_request" ]; then | |
| SONAR_ARGS="$SONAR_ARGS -Dsonar.pullrequest.key=${{ github.event.pull_request.number }}" | |
| SONAR_ARGS="$SONAR_ARGS -Dsonar.pullrequest.branch=${{ github.head_ref }}" | |
| SONAR_ARGS="$SONAR_ARGS -Dsonar.pullrequest.base=${{ github.base_ref }}" | |
| else | |
| SONAR_ARGS="$SONAR_ARGS -Dsonar.branch.name=${{ github.ref_name }}" | |
| fi | |
| mvn org.sonarsource.scanner.maven:sonar-maven-plugin:5.1.0.4751:sonar $SONAR_ARGS | |
| analyze_shadows: | |
| runs-on: github-ubuntu-latest-s | |
| name: Analyze in ${{ matrix.platform }} | |
| needs: [setup, test_js, build] | |
| permissions: *read_permissions | |
| if: github.event_name == 'schedule' | |
| strategy: | |
| matrix: | |
| include: | |
| - platform: SonarCloud EU | |
| sonar-platform: sonarcloud | |
| - platform: SonarQube US | |
| sonar-platform: sonarqube-us | |
| steps: | |
| - *checkout_with_tags | |
| - *mise | |
| - *npm_cache | |
| - *maven_cache | |
| - *js_coverage_cache | |
| - *java_coverage_cache | |
| - *download_maven_targets | |
| - *config_maven | |
| - id: secrets | |
| uses: SonarSource/vault-action-wrapper@v3 | |
| with: | |
| secrets: | | |
| development/kv/data/${{ matrix.sonar-platform }} url | SONAR_URL; | |
| development/kv/data/${{ matrix.sonar-platform }} token | SONAR_TOKEN; | |
| - name: Run SonarQube analysis on ${{ matrix.platform }} | |
| env: | |
| SONAR_HOST_URL: ${{ fromJSON(steps.secrets.outputs.vault).SONAR_URL }} | |
| SONAR_TOKEN: ${{ fromJSON(steps.secrets.outputs.vault).SONAR_TOKEN }} | |
| run: | | |
| SONAR_ARGS="-Dsonar.host.url=$SONAR_HOST_URL" | |
| SONAR_ARGS="$SONAR_ARGS -Dsonar.token=$SONAR_TOKEN" | |
| SONAR_ARGS="$SONAR_ARGS -Dsonar.projectKey=SonarSource_SonarJS" | |
| SONAR_ARGS="$SONAR_ARGS -Dsonar.projectVersion=${{ steps.config-maven.outputs.project-version }}" | |
| SONAR_ARGS="$SONAR_ARGS -Dsonar.scm.revision=${{ github.sha }}" | |
| SONAR_ARGS="$SONAR_ARGS -Dsonar.organization=sonarsource" | |
| SONAR_ARGS="$SONAR_ARGS -Dcommercial" | |
| # Add branch/PR information | |
| if [ "${{ github.event_name }}" == "pull_request" ]; then | |
| SONAR_ARGS="$SONAR_ARGS -Dsonar.pullrequest.key=${{ github.event.pull_request.number }}" | |
| SONAR_ARGS="$SONAR_ARGS -Dsonar.pullrequest.branch=${{ github.head_ref }}" | |
| SONAR_ARGS="$SONAR_ARGS -Dsonar.pullrequest.base=${{ github.base_ref }}" | |
| else | |
| SONAR_ARGS="$SONAR_ARGS -Dsonar.branch.name=${{ github.ref_name }}" | |
| fi | |
| mvn org.sonarsource.scanner.maven:sonar-maven-plugin:5.1.0.4751:sonar $SONAR_ARGS | |
| plugin_qa_with_node: | |
| runs-on: github-ubuntu-latest-s | |
| name: QA with Node ${{ matrix.node-version }} on Ubuntu | |
| needs: [setup, build] | |
| if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false | |
| permissions: *read_permissions | |
| strategy: | |
| matrix: ${{ fromJson(needs.setup.outputs.node-matrix) }} | |
| steps: | |
| - *checkout | |
| - &mise_java_matrix_node | |
| uses: jdx/mise-action@v3.6.1 | |
| with: | |
| version: 2025.11.2 | |
| cache_save: ${{ needs.setup.outputs.is-default-branch == 'true' }} | |
| mise_toml: | | |
| [tools] | |
| java = "21.0" | |
| maven = "3.9" | |
| node = "${{ matrix.node-version }}" | |
| - *maven_cache | |
| - &download_sonarjs_m2 | |
| name: Download SonarJS artifacts | |
| uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 | |
| with: | |
| name: sonarjs-m2 | |
| path: ~/.m2/repository/org/sonarsource/javascript | |
| - *config_maven | |
| - &get_licenses_token | |
| id: secrets | |
| uses: SonarSource/vault-action-wrapper@v3 | |
| with: | |
| secrets: | | |
| development/github/token/licenses-ro token | licenses_token; | |
| - &orchestrator_cache | |
| uses: ./.github/actions/orchestrator-cache | |
| - name: Run Plugin QA | |
| run: | | |
| mvn package -f its/plugin/plugins/consumer-plugin/pom.xml | |
| mvn -f its/plugin/sonarlint-tests/pom.xml -DskipTests=false -Dsonar.runtimeVersion=LATEST_RELEASE -B -e -V verify surefire-report:report | |
| mvn -f its/plugin/tests/pom.xml -DskipTests=false -Dsonar.runtimeVersion=LATEST_RELEASE -B -e -V verify surefire-report:report | |
| env: | |
| SONARSOURCE_QA: true | |
| GITHUB_TOKEN: ${{ fromJSON(steps.secrets.outputs.vault).licenses_token }} | |
| plugin_qa_fast_with_node: | |
| runs-on: github-ubuntu-latest-s | |
| name: Fast QA with Node ${{ matrix.node-version }} on Ubuntu | |
| needs: [setup, build] | |
| if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false | |
| permissions: *read_permissions | |
| strategy: | |
| matrix: ${{ fromJson(needs.setup.outputs.node-matrix) }} | |
| steps: | |
| - *checkout | |
| - *mise_java_matrix_node | |
| - *maven_cache | |
| - *download_sonarjs_m2 | |
| - *config_maven | |
| - *get_licenses_token | |
| - uses: ./.github/actions/orchestrator-cache | |
| with: | |
| key-prefix: orchestrator-fast | |
| - name: Run Fast Plugin QA | |
| run: | | |
| mvn package -f its/plugin/plugins/pom.xml | |
| mvn -f its/plugin/fast-tests/pom.xml -DskipTests=false -Dsonar.runtimeVersion=LATEST_RELEASE -B -e -V verify surefire-report:report | |
| env: | |
| SONARSOURCE_QA: true | |
| GITHUB_TOKEN: ${{ fromJSON(steps.secrets.outputs.vault).licenses_token }} | |
| plugin_qa_without_node: | |
| runs-on: github-ubuntu-latest-s | |
| name: QA without Node on Ubuntu SQ:LATEST_RELEASE | |
| needs: [setup, build] | |
| if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false | |
| permissions: *read_permissions | |
| steps: | |
| - *checkout | |
| - &mise_java_only | |
| name: Setup Java and Maven | |
| uses: jdx/mise-action@v3.6.1 | |
| with: | |
| version: 2025.11.2 | |
| cache_save: ${{ needs.setup.outputs.is-default-branch == 'true' }} | |
| mise_toml: | | |
| [tools] | |
| java = "21.0" | |
| maven = "3.9" | |
| - *maven_cache | |
| - *download_sonarjs_m2 | |
| - *config_maven | |
| - *get_licenses_token | |
| - &remove_node_from_path | |
| name: Disable existing node | |
| shell: bash | |
| run: | | |
| node --version | |
| NODE_PATH=$(which node) | |
| sudo mv "$NODE_PATH" "${NODE_PATH}.disabled" | |
| # Verify node is no longer accessible | |
| if which node 2>/dev/null; then | |
| echo "ERROR: node is still accessible!" | |
| exit 1 | |
| else | |
| echo "SUCCESS: node is no longer accessible" | |
| fi | |
| - *orchestrator_cache | |
| - name: Run Plugin QA without Node | |
| run: | | |
| mvn package -f its/plugin/plugins/consumer-plugin/pom.xml | |
| mvn -f its/plugin/sonarlint-tests/pom.xml -DskipTests=false -Dsonar.runtimeVersion=LATEST_RELEASE -B -e -V verify surefire-report:report | |
| mvn -f its/plugin/tests/pom.xml -DskipTests=false -Dsonar.runtimeVersion=LATEST_RELEASE -B -e -V verify surefire-report:report | |
| env: | |
| SONARSOURCE_QA: true | |
| SONARJS_ARTIFACT: multi | |
| GITHUB_TOKEN: ${{ fromJSON(steps.secrets.outputs.vault).licenses_token }} | |
| # DEV tests run only on nightly schedule to avoid constant downloads | |
| plugin_qa_without_node_dev: | |
| runs-on: github-ubuntu-latest-s | |
| name: QA without Node on Ubuntu SQ:DEV | |
| needs: [setup, build] | |
| if: github.event_name == 'schedule' | |
| permissions: *read_permissions | |
| steps: | |
| - *checkout | |
| - *mise_java_only | |
| - *maven_cache | |
| - *download_sonarjs_m2 | |
| - *config_maven | |
| - *get_licenses_token | |
| - *remove_node_from_path | |
| # No orchestrator cache for DEV - version changes too frequently | |
| - name: Run Plugin QA without Node (DEV) | |
| run: | | |
| mvn package -f its/plugin/plugins/consumer-plugin/pom.xml | |
| mvn -f its/plugin/sonarlint-tests/pom.xml -DskipTests=false -Dsonar.runtimeVersion=DEV -B -e -V verify surefire-report:report | |
| mvn -f its/plugin/tests/pom.xml -DskipTests=false -Dsonar.runtimeVersion=DEV -B -e -V verify surefire-report:report | |
| env: | |
| SONARSOURCE_QA: true | |
| SONARJS_ARTIFACT: multi | |
| GITHUB_TOKEN: ${{ fromJSON(steps.secrets.outputs.vault).licenses_token }} | |
| plugin_qa_fast_without_node: | |
| runs-on: github-ubuntu-latest-s | |
| name: Fast QA without Node on ${{ matrix.os }} SQ:${{ matrix.sq-version }} | |
| needs: [setup, build] | |
| if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false | |
| permissions: *read_permissions | |
| strategy: | |
| matrix: | |
| include: | |
| - os: Ubuntu | |
| sq-version: LATEST_RELEASE | |
| artifact: multi | |
| # - os: Alpine | |
| # sq-version: LATEST_RELEASE | |
| # artifact: linux-x64-musl | |
| steps: | |
| - *checkout | |
| - *mise_java_only | |
| - *maven_cache | |
| - *download_sonarjs_m2 | |
| - *config_maven | |
| - *get_licenses_token | |
| - *remove_node_from_path | |
| - uses: ./.github/actions/orchestrator-cache | |
| with: | |
| key-prefix: orchestrator-fast | |
| - name: Run Fast Plugin QA without Node | |
| run: | | |
| mvn package -f its/plugin/plugins/pom.xml --projects !org.sonarsource.javascript:eslint-custom-rules-plugin,!org.sonarsource.javascript:eslint-custom-rules-plugin-legacy | |
| mvn -f its/plugin/fast-tests/pom.xml -DskipTests=false -Dsonar.runtimeVersion=${{ matrix.sq-version }} -Dtest=!EslintCustomRulesTest,!EslintCustomRulesLegacyTest -B -e -V verify surefire-report:report | |
| env: | |
| SONARSOURCE_QA: true | |
| SONARJS_ARTIFACT: ${{ matrix.artifact }} | |
| GITHUB_TOKEN: ${{ fromJSON(steps.secrets.outputs.vault).licenses_token }} | |
| # DEV tests run only on nightly schedule to avoid constant downloads | |
| plugin_qa_fast_without_node_dev: | |
| runs-on: github-ubuntu-latest-s | |
| name: Fast QA without Node on Ubuntu SQ:DEV | |
| needs: [setup, build] | |
| if: github.event_name == 'schedule' | |
| permissions: *read_permissions | |
| steps: | |
| - *checkout | |
| - *mise_java_only | |
| - *maven_cache | |
| - *download_sonarjs_m2 | |
| - *config_maven | |
| - *get_licenses_token | |
| - *remove_node_from_path | |
| # No orchestrator cache for DEV - version changes too frequently | |
| - name: Run Fast Plugin QA without Node (DEV) | |
| run: | | |
| mvn package -f its/plugin/plugins/pom.xml --projects !org.sonarsource.javascript:eslint-custom-rules-plugin,!org.sonarsource.javascript:eslint-custom-rules-plugin-legacy | |
| mvn -f its/plugin/fast-tests/pom.xml -DskipTests=false -Dsonar.runtimeVersion=DEV -Dtest=!EslintCustomRulesTest,!EslintCustomRulesLegacyTest -B -e -V verify surefire-report:report | |
| env: | |
| SONARSOURCE_QA: true | |
| SONARJS_ARTIFACT: multi | |
| GITHUB_TOKEN: ${{ fromJSON(steps.secrets.outputs.vault).licenses_token }} | |
| plugin_qa_win: | |
| runs-on: github-windows-latest-s | |
| name: QA on Windows (${{ matrix.group }}) | |
| needs: [setup, build] | |
| if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false | |
| permissions: *read_permissions | |
| strategy: | |
| matrix: | |
| include: | |
| - group: 1 | |
| exclude: false | |
| - group: 2 | |
| exclude: true | |
| steps: | |
| - *checkout | |
| - *mise | |
| - *maven_cache | |
| - *download_sonarjs_m2 | |
| - *config_maven | |
| - *get_licenses_token | |
| - *orchestrator_cache | |
| - name: Run Plugin QA on Windows | |
| shell: bash | |
| run: | | |
| # Tests for group 1 (group 2 runs everything else) | |
| GROUP1_TESTS="CoverageTest,CssMetricsTest,MetricsTest,MinifiedFilesTest" | |
| if [ "${{ matrix.exclude }}" == "true" ]; then | |
| # Convert to exclusion pattern: add ! prefix to each test | |
| TEST_PATTERN=$(echo "$GROUP1_TESTS" | sed 's/,/,!/g' | sed 's/^/!/') | |
| else | |
| TEST_PATTERN="$GROUP1_TESTS" | |
| fi | |
| mvn package -f its/plugin/plugins/consumer-plugin/pom.xml | |
| mvn -f its/plugin/tests/pom.xml -DskipTests=false -Dtest="$TEST_PATTERN" -Dsonar.runtimeVersion=LATEST_RELEASE -B -e -V verify surefire-report:report | |
| env: | |
| SONARSOURCE_QA: true | |
| GITHUB_TOKEN: ${{ fromJSON(steps.secrets.outputs.vault).licenses_token }} | |
| plugin_qa_sonarlint_win: | |
| runs-on: github-windows-latest-s | |
| name: QA SonarLint on Windows | |
| needs: [setup, build] | |
| if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false | |
| permissions: *read_permissions | |
| steps: | |
| - *checkout | |
| - *mise | |
| - *maven_cache | |
| - *download_sonarjs_m2 | |
| - *config_maven | |
| - name: Run Plugin QA on Windows | |
| shell: bash | |
| run: | | |
| mvn -f its/plugin/sonarlint-tests/pom.xml -DskipTests=false -Dsonar.runtimeVersion=LATEST_RELEASE -B -e -V verify surefire-report:report | |
| env: | |
| SONARSOURCE_QA: true | |
| plugin_qa_win_fast_with_node: | |
| runs-on: github-windows-latest-s | |
| name: Fast QA on Windows with Node (${{ matrix.group }}) | |
| needs: [setup, build] | |
| if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false | |
| permissions: *read_permissions | |
| strategy: | |
| matrix: | |
| include: | |
| - group: 1 | |
| exclude: false | |
| - group: 2 | |
| exclude: true | |
| steps: | |
| - *checkout | |
| - *mise | |
| - *maven_cache | |
| - *download_sonarjs_m2 | |
| - *config_maven | |
| - *get_licenses_token | |
| - uses: ./.github/actions/orchestrator-cache | |
| with: | |
| key-prefix: orchestrator-fast | |
| - name: Run Fast Plugin QA on Windows | |
| shell: bash | |
| run: | | |
| # Tests for group 1 (group 2 runs everything else) | |
| GROUP1_TESTS="ConsumerPluginTest,CssIssuesTest,CssNoCssFileProjectTest,CssNonStandardPathTest,CssStylelintReportTest,ECMAScriptModulesTest,EmbeddedNodeTest,EslintBasedRulesTest,EslintCustomRulesLegacyTest,EslintCustomRulesTest,EslintReportTest,ExternalTSConfigDependencyTest" | |
| if [ "${{ matrix.exclude }}" == "true" ]; then | |
| # Convert to exclusion pattern: add ! prefix to each test | |
| TEST_PATTERN=$(echo "$GROUP1_TESTS" | sed 's/,/,!/g' | sed 's/^/!/') | |
| else | |
| TEST_PATTERN="$GROUP1_TESTS" | |
| fi | |
| mvn package -f its/plugin/plugins/pom.xml | |
| mvn -f its/plugin/fast-tests/pom.xml -DskipTests=false -Dtest="$TEST_PATTERN" -Dsonar.runtimeVersion=LATEST_RELEASE -B -e -V verify surefire-report:report | |
| env: | |
| SONARSOURCE_QA: true | |
| GITHUB_TOKEN: ${{ fromJSON(steps.secrets.outputs.vault).licenses_token }} | |
| js_ts_ruling: | |
| runs-on: github-ubuntu-latest-m | |
| name: JS/TS Ruling | |
| needs: [setup, populate_npm_cache] | |
| if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false | |
| permissions: | |
| id-token: write | |
| contents: write | |
| pull-requests: write | |
| steps: | |
| - &checkout_with_submodules | |
| name: Checkout source code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| submodules: true | |
| - *mise | |
| - *npm_cache | |
| - name: Run JS/TS Ruling | |
| id: ruling | |
| run: | | |
| npm run generate-meta | |
| npm run ruling | |
| - name: Update ruling and notify | |
| if: always() && github.event_name == 'pull_request' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| HEAD_REF: ${{ github.head_ref }} | |
| RULING_FAILED: ${{ steps.ruling.outcome == 'failure' }} | |
| run: | | |
| git fetch origin master | |
| # If ruling failed, sync the actual results first to get the differences | |
| if [ "$RULING_FAILED" = "true" ]; then | |
| # Check if last commit was already an auto-update (prevent infinite loop) | |
| LAST_COMMIT_MSG=$(git log -1 --format=%B) | |
| if echo "$LAST_COMMIT_MSG" | grep -q "🤖 Generated with GitHub Actions"; then | |
| echo "Last commit was an auto-update, skipping to prevent infinite loop" | |
| exit 0 | |
| fi | |
| # Sync ruling results (this modifies its/ruling/...) | |
| npm run ruling-sync | |
| fi | |
| # Generate report comparing against master (after sync if ruling failed) | |
| node tools/ruling-report.js > ruling-report.md | |
| # Check if there are ruling differences | |
| if [ -s ruling-report.md ]; then | |
| HAS_DIFFERENCES=true | |
| else | |
| HAS_DIFFERENCES=false | |
| fi | |
| # Commit and push the synced ruling files if there are differences | |
| if [ "$RULING_FAILED" = "true" ] && [ "$HAS_DIFFERENCES" = "true" ]; then | |
| # Stash changes before checkout | |
| git stash push -m "ruling-sync-changes" -- its/ruling/src/test/expected/jsts/ | |
| # Checkout PR branch | |
| git fetch origin "$HEAD_REF" | |
| git checkout "$HEAD_REF" | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| # Pop the stashed changes | |
| git stash pop | |
| git add its/ruling/src/test/expected/jsts/ | |
| if git diff --staged --quiet; then | |
| echo "No ruling changes to commit" | |
| else | |
| git commit -m "Update ruling results | |
| 🤖 Generated with GitHub Actions" | |
| git push origin "$HEAD_REF" | |
| fi | |
| fi | |
| # Build comment with marker for identification | |
| MARKER="<!-- ruling-report -->" | |
| if [ "$HAS_DIFFERENCES" = "true" ]; then | |
| # Build comment with ruling changes | |
| { | |
| echo "$MARKER" | |
| cat ruling-report.md | |
| echo "" | |
| if [ "$RULING_FAILED" = "true" ]; then | |
| echo "---" | |
| echo "✅ **Ruling has been updated.** To re-run CI, either:" | |
| echo "- Close and reopen this PR, or" | |
| echo "- Run: \`git pull && git commit --allow-empty -m 'Trigger CI' && git push\`" | |
| fi | |
| } > comment.md | |
| else | |
| # Build comment for no changes | |
| { | |
| echo "$MARKER" | |
| echo "## Ruling Report" | |
| echo "" | |
| echo "✅ **No changes to ruling expected issues in this PR**" | |
| } > comment.md | |
| fi | |
| # Find existing ruling comment and update it, or create new one | |
| EXISTING_COMMENT_ID=$(gh api "repos/${{ github.repository }}/issues/$PR_NUMBER/comments" \ | |
| --jq ".[] | select(.body | startswith(\"$MARKER\")) | .id" | head -1) | |
| if [ -n "$EXISTING_COMMENT_ID" ]; then | |
| gh api "repos/${{ github.repository }}/issues/comments/$EXISTING_COMMENT_ID" \ | |
| -X PATCH -F body=@comment.md | |
| else | |
| gh pr comment "$PR_NUMBER" --body-file comment.md | |
| fi | |
| ruling: | |
| runs-on: github-ubuntu-latest-m | |
| name: Ruling Test | |
| needs: [setup, build] | |
| if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false | |
| permissions: *read_permissions | |
| steps: | |
| - *checkout_with_submodules | |
| - *mise | |
| - *maven_cache | |
| - *download_sonarjs_m2 | |
| - *config_maven | |
| - *get_licenses_token | |
| - uses: ./.github/actions/orchestrator-cache | |
| with: | |
| save: 'false' | |
| - name: Run Ruling Tests | |
| run: | | |
| cd its/ruling | |
| mvn test -Dtest=JsTsRulingTest -DskipTests=false -Dsonar.runtimeVersion=LATEST_RELEASE -Dmaven.test.redirectTestOutputToFile=false -Djunit.jupiter.execution.parallel.config.dynamic.factor=1 -B -e -V | |
| env: | |
| SONARSOURCE_QA: true | |
| GITHUB_TOKEN: ${{ fromJSON(steps.secrets.outputs.vault).licenses_token }} | |
| - name: Upload ruling differences | |
| if: failure() | |
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | |
| with: | |
| name: ruling-differences | |
| path: its/ruling/target/actual/jsts/ | |
| css_ruling: | |
| runs-on: github-ubuntu-latest-s | |
| name: CSS Ruling | |
| needs: [setup, build] | |
| if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false | |
| permissions: *read_permissions | |
| steps: | |
| - *checkout_with_submodules | |
| - *mise | |
| - *maven_cache | |
| - *download_sonarjs_m2 | |
| - *config_maven | |
| - *get_licenses_token | |
| - uses: ./.github/actions/orchestrator-cache | |
| with: | |
| save: 'false' | |
| - name: Run CSS Ruling | |
| run: | | |
| cd its/ruling | |
| mvn test -DskipTests=false -Dtest=CssRulingTest -Dsonar.runtimeVersion=LATEST_RELEASE -Dmaven.test.redirectTestOutputToFile=false -Djunit.jupiter.execution.parallel.config.dynamic.factor=1 -B -e -V | |
| env: | |
| SONARSOURCE_QA: true | |
| GITHUB_TOKEN: ${{ fromJSON(steps.secrets.outputs.vault).licenses_token }} | |
| - name: Upload ruling differences | |
| if: failure() | |
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | |
| with: | |
| name: ruling-differences-css | |
| path: its/ruling/target/actual/ | |
| # IRIS tasks (nightly only) | |
| run_iris: | |
| runs-on: github-ubuntu-latest-s | |
| name: IRIS SQ NEXT -> ${{ matrix.shadow-name }} | |
| needs: [analyze_primary, analyze_shadows] | |
| if: github.event_name == 'schedule' | |
| permissions: | |
| id-token: write | |
| contents: read | |
| strategy: | |
| matrix: | |
| include: | |
| - shadow-name: SonarCloud EU | |
| shadow-platform: SQC-EU | |
| - shadow-name: SonarQube US | |
| shadow-platform: SQC-US | |
| steps: | |
| - uses: SonarSource/unified-dogfooding-actions/run-iris@v1 | |
| with: | |
| primary_project_key: org.sonarsource.javascript:javascript | |
| primary_platform: Next | |
| shadow1_project_key: SonarSource_SonarJS | |
| shadow1_platform: ${{ matrix.shadow-platform }} | |
| promote: | |
| runs-on: github-ubuntu-latest-s | |
| needs: | |
| - build | |
| - build_win | |
| - test_js | |
| - test_js_win | |
| - analyze_primary | |
| - test_eslint_plugin | |
| - plugin_qa_with_node | |
| - plugin_qa_without_node | |
| - plugin_qa_fast_with_node | |
| - plugin_qa_fast_without_node | |
| - plugin_qa_win | |
| - plugin_qa_sonarlint_win | |
| - plugin_qa_win_fast_with_node | |
| - css_ruling | |
| - ruling | |
| - js_ts_ruling | |
| if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false) | |
| permissions: *read_permissions | |
| steps: | |
| - *checkout | |
| - *mise | |
| - uses: SonarSource/ci-github-actions/promote@v1 | |
| with: | |
| promote-pull-request: true | |
| releasability: | |
| runs-on: github-ubuntu-latest-s | |
| name: Releasability | |
| needs: | |
| - promote | |
| permissions: | |
| id-token: write | |
| statuses: write | |
| contents: read | |
| steps: | |
| - uses: SonarSource/gh-action_releasability/releasability-status@v3 | |
| if: >- | |
| github.ref_name == github.event.repository.default_branch || | |
| startsWith(github.ref_name, 'branch-') || | |
| startsWith(github.ref_name, 'dogfood-') | |
| with: | |
| optional_checks: "Jira" | |
| env: | |
| GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' |