CI on forks - Sonar analysis #174
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: CI on forks - Sonar analysis | |
| on: | |
| workflow_run: | |
| workflows: [CI on forks - build and tests] | |
| types: | |
| - completed | |
| permissions: {} | |
| jobs: | |
| java: | |
| name: Run Sonar Analysis for forks (Java) | |
| runs-on: ubuntu-latest | |
| if: > | |
| github.event.workflow_run.event == 'pull_request' && | |
| github.event.workflow_run.conclusion == 'success' | |
| permissions: | |
| actions: write | |
| checks: write | |
| contents: read | |
| issues: read | |
| pull-requests: write | |
| statuses: write | |
| steps: | |
| - name: Download PR information | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| run_id: context.payload.workflow_run.id, | |
| }); | |
| let prInfoArtifact = allArtifacts.data.artifacts.filter((artifact) => { | |
| return artifact.name.startsWith("pr-info-") | |
| })[0]; | |
| if (!prInfoArtifact) { | |
| core.setFailed("❌ No PR info artifact found"); | |
| return; | |
| } | |
| let download = await github.rest.actions.downloadArtifact({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| artifact_id: prInfoArtifact.id, | |
| archive_format: 'zip', | |
| }); | |
| const fs = require('fs'); | |
| const path = require('path'); | |
| const temp = '${{ runner.temp }}/pr-info'; | |
| if (!fs.existsSync(temp)){ | |
| fs.mkdirSync(temp, { recursive: true }); | |
| } | |
| fs.writeFileSync(path.join(temp, 'pr-info.zip'), Buffer.from(download.data)); | |
| console.log("PR information downloaded"); | |
| - name: Extract PR Information | |
| run: | | |
| mkdir -p ${{ runner.temp }}/pr-info-extracted | |
| unzip -q ${{ runner.temp }}/pr-info/pr-info.zip -d ${{ runner.temp }}/pr-info-extracted | |
| REPO_NAME=$(cat ${{ runner.temp }}/pr-info-extracted/repo-name) | |
| HEAD_REF=$(cat ${{ runner.temp }}/pr-info-extracted/head-ref) | |
| HEAD_SHA=$(cat ${{ runner.temp }}/pr-info-extracted/head-sha) | |
| PR_NUMBER=$(cat ${{ runner.temp }}/pr-info-extracted/pr-number) | |
| BASE_REF=$(cat ${{ runner.temp }}/pr-info-extracted/base-ref) | |
| echo "REPO_NAME=$REPO_NAME" >> $GITHUB_ENV | |
| echo "HEAD_REF=$HEAD_REF" >> $GITHUB_ENV | |
| echo "HEAD_SHA=$HEAD_SHA" >> $GITHUB_ENV | |
| echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV | |
| echo "BASE_REF=$BASE_REF" >> $GITHUB_ENV | |
| echo "PR information extracted: $REPO_NAME $HEAD_REF $HEAD_SHA $PR_NUMBER $BASE_REF" | |
| - name: Checkout sources | |
| uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 | |
| with: | |
| ref: ${{ env.HEAD_REF }} | |
| repository: ${{ env.REPO_NAME }} | |
| fetch-depth: 0 | |
| - name: Set up JDK 21 | |
| uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0 | |
| with: | |
| distribution: 'temurin' | |
| java-version: '21' | |
| - name: Download artifact | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| run_id: context.payload.workflow_run.id, | |
| }); | |
| let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => { | |
| return artifact.name.startsWith("data-for-sonar-analysis-") | |
| })[0]; | |
| if (!matchArtifact) { | |
| core.setFailed("❌ No matching artifact found"); | |
| return; | |
| } | |
| const prNumber = matchArtifact.name.replace("data-for-sonar-analysis-", ""); | |
| console.log(`PR number: ${prNumber}`); | |
| core.exportVariable('PR_NUMBER', prNumber); | |
| let download = await github.rest.actions.downloadArtifact({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| artifact_id: matchArtifact.id, | |
| archive_format: 'zip', | |
| }); | |
| const fs = require('fs'); | |
| const path = require('path'); | |
| const temp = '${{ runner.temp }}/artifacts'; | |
| if (!fs.existsSync(temp)){ | |
| fs.mkdirSync(temp); | |
| } | |
| fs.writeFileSync(path.join(temp, 'sonar-data.zip'), Buffer.from(download.data)); | |
| - name: Extract Sonar Analysis Data | |
| run: | | |
| mkdir -p ${{ runner.temp }}/extracted | |
| unzip -q ${{ runner.temp }}/artifacts/sonar-data.zip -d ${{ runner.temp }}/extracted | |
| cp -r ${{ runner.temp }}/extracted/* . | |
| ls -la metrix-distribution/target/site/jacoco-aggregate/ || echo "Jacoco report directory not found" | |
| - name: Prepare Sonar Analysis | |
| run: | | |
| echo "Checking required directories..." | |
| if [ -f "metrix-distribution/target/site/jacoco-aggregate/jacoco.xml" ]; then | |
| echo "Jacoco report found" | |
| else | |
| echo "Warning: Jacoco report not found at expected location" | |
| fi | |
| echo "Finding sources and binaries..." | |
| SOURCES=$(find . -type d -path "*/main/java" | sort -u | paste -sd "," -) | |
| if [ -z "$SOURCES" ]; then | |
| echo "Warning: No source directories found!" | |
| else | |
| echo "SOURCES : $SOURCES" | |
| echo "SOURCES=$SOURCES" >> $GITHUB_ENV | |
| fi | |
| GENERATED=$(find . -type d -path "*/target/generated-sources" | sort -u | paste -sd "," -) | |
| if [ -z "$GENERATED" ]; then | |
| echo "Warning: No generated source directories found!" | |
| else | |
| echo "GENERATED : $GENERATED" | |
| echo "GENERATED=$GENERATED" >> $GITHUB_ENV | |
| fi | |
| TESTS=$(find . -type d -path "*/test/java" | sort -u | paste -sd "," -) | |
| if [ -z "$TESTS" ]; then | |
| echo "Warning: No test directories found!" | |
| else | |
| echo "TESTS : $TESTS" | |
| echo "TESTS=$TESTS" >> $GITHUB_ENV | |
| fi | |
| BINARIES=$(find . -type d -path "*/target/classes" | sort -u | paste -sd "," -) | |
| if [ -z "$BINARIES" ]; then | |
| echo "Warning: No binaries directories found!" | |
| else | |
| echo "BINARIES : $BINARIES" | |
| echo "BINARIES=$BINARIES" >> $GITHUB_ENV | |
| fi | |
| LIBRARIES="metrix-distribution/target/dependency" | |
| if [ -z "$LIBRARIES" ]; then | |
| echo "Warning: No libraries directory found!" | |
| else | |
| echo "LIBRARIES : $LIBRARIES" | |
| echo "LIBRARIES=$LIBRARIES" >> $GITHUB_ENV | |
| fi | |
| # This sonar action should NOT be replaced by a direct use of the mvn verify command since we don't want external | |
| # code to run in a workflow_run workflow (it may lead to security issues). | |
| - name: Run Sonar Analysis (Java) | |
| uses: SonarSource/sonarqube-scan-action@2500896589ef8f7247069a56136f8dc177c27ccf # v5.2.0 | |
| env: | |
| SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| args: > | |
| -Dsonar.projectKey=com.powsybl:powsybl-metrix | |
| -Dsonar.organization=powsybl-ci-github | |
| -Dsonar.sources="${{ env.SOURCES }}" | |
| -Dsonar.generatedSources="${{ env.GENERATED }}" | |
| -Dsonar.tests="${{ env.TESTS }}" | |
| -Dsonar.java.binaries="${{ env.BINARIES }}" | |
| -Dsonar.java.libraries="${{ env.LIBRARIES }}" | |
| -Dsonar.java.test.libraries="${{ env.LIBRARIES }}" | |
| -Dsonar.coverage.jacoco.xmlReportPaths="metrix-distribution/target/site/jacoco-aggregate/jacoco.xml" | |
| -Dsonar.pullrequest.key="${{ env.PR_NUMBER }}" | |
| -Dsonar.pullrequest.branch="${{ env.HEAD_REF }}" | |
| -Dsonar.pullrequest.base="${{ env.BASE_REF }}" | |
| -Dsonar.pullrequest.provider=github | |
| -Dsonar.pullrequest.github.repository=${{ github.repository }} | |
| -Dsonar.host.url=https://sonarcloud.io | |
| -Dsonar.scm.provider=git | |
| -Dsonar.qualitygate.wait=true | |
| -Dsonar.scm.revision=${{ env.HEAD_SHA }} | |
| - name: Delete artifacts used in analysis | |
| if: always() | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| let artifacts = await github.rest.actions.listWorkflowRunArtifacts({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| run_id: context.payload.workflow_run.id, | |
| }); | |
| for (const artifact of artifacts.data.artifacts) { | |
| if ( | |
| artifact.name.startsWith("data-for-sonar-analysis-") || | |
| artifact.name.startsWith("pr-info-") | |
| ) { | |
| console.log(`Deleting artifact: ${artifact.name} (ID: ${artifact.id})`); | |
| await github.rest.actions.deleteArtifact({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| artifact_id: artifact.id | |
| }); | |
| } | |
| } | |
| cpp: | |
| name: Run Sonar Analysis for forks (C++) | |
| runs-on: ubuntu-latest | |
| if: > | |
| github.event.workflow_run.event == 'pull_request' && | |
| github.event.workflow_run.conclusion == 'success' | |
| steps: | |
| - name: Download PR information | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| run_id: context.payload.workflow_run.id, | |
| }); | |
| let prInfoArtifact = allArtifacts.data.artifacts.filter((artifact) => { | |
| return artifact.name.startsWith("cpp-pr-info-") | |
| })[0]; | |
| if (!prInfoArtifact) { | |
| core.setFailed("❌ No PR info artifact found"); | |
| return; | |
| } | |
| let download = await github.rest.actions.downloadArtifact({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| artifact_id: prInfoArtifact.id, | |
| archive_format: 'zip', | |
| }); | |
| const fs = require('fs'); | |
| const path = require('path'); | |
| const temp = '${{ runner.temp }}/cpp-pr-info'; | |
| if (!fs.existsSync(temp)){ | |
| fs.mkdirSync(temp, { recursive: true }); | |
| } | |
| fs.writeFileSync(path.join(temp, 'cpp-pr-info.zip'), Buffer.from(download.data)); | |
| console.log("PR information downloaded"); | |
| - name: Extract PR Information | |
| run: | | |
| mkdir -p ${{ runner.temp }}/cpp-pr-info-extracted | |
| unzip -q ${{ runner.temp }}/cpp-pr-info/cpp-pr-info.zip -d ${{ runner.temp }}/cpp-pr-info-extracted | |
| REPO_NAME=$(cat ${{ runner.temp }}/cpp-pr-info-extracted/repo-name) | |
| HEAD_REF=$(cat ${{ runner.temp }}/cpp-pr-info-extracted/head-ref) | |
| HEAD_SHA=$(cat ${{ runner.temp }}/cpp-pr-info-extracted/head-sha) | |
| PR_NUMBER=$(cat ${{ runner.temp }}/cpp-pr-info-extracted/pr-number) | |
| BASE_REF=$(cat ${{ runner.temp }}/cpp-pr-info-extracted/base-ref) | |
| echo "REPO_NAME=$REPO_NAME" >> $GITHUB_ENV | |
| echo "HEAD_REF=$HEAD_REF" >> $GITHUB_ENV | |
| echo "HEAD_SHA=$HEAD_SHA" >> $GITHUB_ENV | |
| echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV | |
| echo "BASE_REF=$BASE_REF" >> $GITHUB_ENV | |
| echo "PR information extracted: $REPO_NAME $HEAD_REF $HEAD_SHA $PR_NUMBER $BASE_REF" | |
| - name: Checkout sources | |
| uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 | |
| with: | |
| ref: ${{ env.HEAD_REF }} | |
| repository: ${{ env.REPO_NAME }} | |
| fetch-depth: 0 | |
| - name: Set up JDK 21 | |
| uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0 | |
| with: | |
| distribution: 'temurin' | |
| java-version: '21' | |
| - name: Download C++ artifact | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| run_id: context.payload.workflow_run.id, | |
| }); | |
| let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => { | |
| return artifact.name.startsWith("cpp-data-for-sonar-analysis-") | |
| })[0]; | |
| if (!matchArtifact) { | |
| core.setFailed("❌ No matching artifact found"); | |
| return; | |
| } | |
| const prNumber = matchArtifact.name.replace("cpp-data-for-sonar-analysis-", ""); | |
| console.log(`PR number: ${prNumber}`); | |
| core.exportVariable('PR_NUMBER', prNumber); | |
| let download = await github.rest.actions.downloadArtifact({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| artifact_id: matchArtifact.id, | |
| archive_format: 'zip', | |
| }); | |
| const fs = require('fs'); | |
| const path = require('path'); | |
| const temp = '${{ runner.temp }}/artifacts'; | |
| if (!fs.existsSync(temp)){ | |
| fs.mkdirSync(temp); | |
| } | |
| fs.writeFileSync(path.join(temp, 'cpp-sonar-data.zip'), Buffer.from(download.data)); | |
| - name: Extract Sonar Analysis Data | |
| run: | | |
| mkdir -p ${{ runner.temp }}/extracted | |
| unzip -q ${{ runner.temp }}/artifacts/cpp-sonar-data.zip -d ${{ runner.temp }}/extracted | |
| mkdir -p ./metrix-simulator/build | |
| cp -r ${{ runner.temp }}/extracted/* ./metrix-simulator/build/ | |
| ls -la metrix-simulator/build/ || echo "build directory not found" | |
| - name: Install Sonar wrapper | |
| working-directory: ${{ runner.workspace }} | |
| run: | | |
| wget https://sonarcloud.io/static/cpp/build-wrapper-linux-x86.zip | |
| unzip build-wrapper-linux-x86.zip | |
| - name: Install Sonar scanner | |
| working-directory: ${{ runner.workspace }} | |
| run: | | |
| wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_SCANNER_VERSION}.zip | |
| unzip sonar-scanner-cli-${SONAR_SCANNER_VERSION}.zip | |
| ln -s sonar-scanner-${SONAR_SCANNER_VERSION} sonar | |
| rm sonar-scanner-cli-${SONAR_SCANNER_VERSION}.zip | |
| env: | |
| SONAR_SCANNER_VERSION: 3.3.0.1492 | |
| # This step uses the sonar-project.properties file to get some parameters for the Sonar analysis | |
| - name: Run Sonar Analysis (C++) | |
| working-directory: ${{ runner.workspace }}/powsybl-metrix/metrix-simulator | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
| run: > | |
| ${{ runner.workspace }}/sonar/bin/sonar-scanner | |
| -Dsonar.host.url=https://sonarcloud.io | |
| -Dsonar.pullrequest.key="${{ env.PR_NUMBER }}" | |
| -Dsonar.pullrequest.branch="${{ env.HEAD_REF }}" | |
| -Dsonar.pullrequest.base="${{ env.BASE_REF }}" | |
| -Dsonar.pullrequest.provider=github | |
| -Dsonar.pullrequest.github.repository=${{ github.repository }} | |
| -Dsonar.scm.provider=git | |
| -Dsonar.qualitygate.wait=true | |
| -Dsonar.scm.revision=${{ env.HEAD_SHA }} | |
| - name: Delete artifacts used in analysis | |
| if: always() | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| let artifacts = await github.rest.actions.listWorkflowRunArtifacts({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| run_id: context.payload.workflow_run.id, | |
| }); | |
| for (const artifact of artifacts.data.artifacts) { | |
| if ( | |
| artifact.name.startsWith("cpp-data-for-sonar-analysis-") || | |
| artifact.name.startsWith("cpp-pr-info-") | |
| ) { | |
| console.log(`Deleting artifact: ${artifact.name} (ID: ${artifact.id})`); | |
| await github.rest.actions.deleteArtifact({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| artifact_id: artifact.id | |
| }); | |
| } | |
| } |