SonarCloud #1284
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
| # SonarCloud Analysis Workflow for django-ansible-base | |
| # | |
| # This workflow runs SonarCloud analysis triggered by CI workflow completion. | |
| # It is split into two separate jobs for clarity and maintainability: | |
| # | |
| # FLOW: CI completes → workflow_run triggers this workflow → appropriate job runs | |
| # | |
| # JOB 1: sonar-pr-analysis (for PRs) | |
| # - Triggered by: workflow_run (CI on pull_request) | |
| # - Steps: Download coverage → Get PR info → Run SonarCloud PR analysis | |
| # - Scans: All files in the PR | |
| # - Quality gate: Focuses on new/changed code in PR only | |
| # | |
| # JOB 2: sonar-branch-analysis (for long-lived branches) | |
| # - Triggered by: workflow_run (CI on push to devel/stable-*) | |
| # - Steps: Download coverage → Run SonarCloud branch analysis | |
| # - Scans: Full codebase | |
| # - Quality gate: Focuses on overall project health | |
| # | |
| # This ensures coverage data is always available from CI before analysis runs. | |
| # With much help from: | |
| # https://community.sonarsource.com/t/how-to-use-sonarcloud-with-a-forked-repository-on-github/7363/30 | |
| # https://community.sonarsource.com/t/how-to-use-sonarcloud-with-a-forked-repository-on-github/7363/32 | |
| name: SonarCloud | |
| on: | |
| workflow_run: # This is triggered by CI being completed. | |
| workflows: | |
| - CI | |
| types: | |
| - completed | |
| permissions: read-all | |
| jobs: | |
| sonar-pr-analysis: | |
| name: SonarCloud PR Analysis | |
| runs-on: ubuntu-latest | |
| if: | | |
| github.event.workflow_run.conclusion == 'success' && | |
| github.event.workflow_run.event == 'pull_request' && | |
| startsWith(github.repository, 'ansible') && endsWith(github.repository, 'django-ansible-base') | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| show-progress: false | |
| # Download all individual coverage artifacts from CI workflow | |
| - name: Download coverage artifacts | |
| uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 | |
| with: | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| workflow: CI | |
| run_id: ${{ github.event.workflow_run.id }} | |
| pattern: coverage-* | |
| # Extract PR metadata from workflow_run event | |
| - name: Set PR metadata and prepare for analysis | |
| env: | |
| COMMIT_SHA: ${{ github.event.workflow_run.head_sha }} | |
| REPO_NAME: ${{ github.event.repository.full_name }} | |
| HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }} | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| # Find all downloaded coverage XML files (artifacts are extracted into subdirectories) | |
| coverage_files=$(find ./coverage-* -name "coverage.xml" -type f 2>/dev/null | tr '\n' ',' | sed 's/,$//') | |
| echo "Found coverage files: $coverage_files" | |
| echo "COVERAGE_PATHS=$coverage_files" >> $GITHUB_ENV | |
| echo "🔍 SonarCloud Analysis Decision Summary" | |
| echo "========================================" | |
| echo "├── CI Event: ✅ Pull Request" | |
| echo "├── Repo: $REPO_NAME" | |
| # Extract PR number from first coverage.xml file found | |
| first_coverage=$(find ./coverage-* -name "coverage.xml" -type f 2>/dev/null | head -1) | |
| if [ -f "$first_coverage" ]; then | |
| PR_NUMBER=$(grep -m 1 '<!-- PR' "$first_coverage" | awk '{print $3}' || echo "") | |
| else | |
| PR_NUMBER="" | |
| fi | |
| echo "├── PR Number from coverage.xml: #${PR_NUMBER:-<not found>}" | |
| if [ -z "$PR_NUMBER" ]; then | |
| echo "##[error]❌ FATAL: PR number not found in coverage.xml" | |
| echo "##[error]This job requires a PR number to run PR analysis." | |
| echo "##[error]The CI workflow should have injected the PR number into coverage.xml." | |
| exit 1 | |
| fi | |
| # Get PR metadata from GitHub API | |
| PR_DATA=$(gh api "repos/$REPO_NAME/pulls/$PR_NUMBER") | |
| PR_BASE=$(echo "$PR_DATA" | jq -r '.base.ref') | |
| PR_HEAD=$(echo "$PR_DATA" | jq -r '.head.ref') | |
| # Print summary | |
| echo "├── Base Branch: $PR_BASE" | |
| echo "├── Head Branch: $PR_HEAD" | |
| echo "├── Coverage Data: ✅ Available" | |
| echo "└── Result: ✅ Running SonarCloud PR analysis" | |
| # Validate coverage paths | |
| if [ -z "$coverage_files" ]; then | |
| echo "##[error]❌ FATAL: No coverage files found" | |
| echo "##[error]This job requires coverage data to run analysis." | |
| echo "##[error]The CI workflow should have uploaded coverage artifacts." | |
| exit 1 | |
| fi | |
| # Export to GitHub env for later steps | |
| echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV | |
| echo "PR_BASE=$PR_BASE" >> $GITHUB_ENV | |
| echo "PR_HEAD=$PR_HEAD" >> $GITHUB_ENV | |
| echo "COMMIT_SHA=$COMMIT_SHA" >> $GITHUB_ENV | |
| echo "REPO_NAME=$REPO_NAME" >> $GITHUB_ENV | |
| - name: Add base branch | |
| if: env.PR_NUMBER != '' | |
| run: | | |
| gh pr checkout ${{ env.PR_NUMBER }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: SonarCloud Scan | |
| uses: SonarSource/sonarcloud-github-action@e44258b109568baa0df60ed515909fc6c72cba92 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| SONAR_TOKEN: ${{ secrets[format('{0}', vars.SONAR_TOKEN_SECRET_NAME)] }} | |
| with: | |
| args: > | |
| -Dsonar.scm.revision=${{ env.COMMIT_SHA }} | |
| -Dsonar.pullrequest.key=${{ env.PR_NUMBER }} | |
| -Dsonar.pullrequest.branch=${{ env.PR_HEAD }} | |
| -Dsonar.pullrequest.base=${{ env.PR_BASE }} | |
| -Dsonar.python.coverage.reportPaths=${{ env.COVERAGE_PATHS }} | |
| sonar-branch-analysis: | |
| name: SonarCloud Branch Analysis | |
| runs-on: ubuntu-latest | |
| if: | | |
| github.event_name == 'workflow_run' && | |
| github.event.workflow_run.conclusion == 'success' && | |
| github.event.workflow_run.event == 'push' && | |
| startsWith(github.repository, 'ansible') && endsWith(github.repository, 'django-ansible-base') | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| show-progress: false | |
| # Download all individual coverage artifacts from CI workflow (optional for branch pushes) | |
| - name: Download coverage artifacts | |
| continue-on-error: true | |
| uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 | |
| with: | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| workflow: CI | |
| run_id: ${{ github.event.workflow_run.id }} | |
| pattern: coverage-* | |
| - name: Print SonarCloud Analysis Summary | |
| env: | |
| BRANCH_NAME: ${{ github.event.workflow_run.head_branch }} | |
| REPO_NAME: ${{ github.event.repository.full_name }} | |
| run: | | |
| # Find all downloaded coverage XML files (artifacts are extracted into subdirectories) | |
| coverage_files=$(find ./coverage-* -name "coverage.xml" -type f 2>/dev/null | tr '\n' ',' | sed 's/,$//') | |
| echo "Found coverage files: $coverage_files" | |
| echo "COVERAGE_PATHS=$coverage_files" >> $GITHUB_ENV | |
| echo "🔍 SonarCloud Analysis Summary" | |
| echo "==============================" | |
| echo "├── CI Event: ✅ Push (via workflow_run)" | |
| echo "├── Repo: $REPO_NAME" | |
| echo "├── Branch: $BRANCH_NAME" | |
| echo "├── Coverage Files: ${coverage_files:-none}" | |
| if [ -n "$coverage_files" ]; then | |
| echo "├── Coverage Data: ✅ Available" | |
| else | |
| echo "├── Coverage Data: ⚠️ Not available (analysis will proceed without coverage)" | |
| fi | |
| echo "└── Result: ✅ Running SonarCloud branch analysis" | |
| - name: SonarCloud Scan | |
| uses: SonarSource/sonarcloud-github-action@e44258b109568baa0df60ed515909fc6c72cba92 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| SONAR_TOKEN: ${{ secrets[format('{0}', vars.SONAR_TOKEN_SECRET_NAME)] }} | |
| with: | |
| args: > | |
| -Dsonar.scm.revision=${{ github.event.workflow_run.head_sha }} | |
| -Dsonar.branch.name=${{ github.event.workflow_run.head_branch }} | |
| ${{ env.COVERAGE_PATHS && format('-Dsonar.python.coverage.reportPaths={0}', env.COVERAGE_PATHS) || '' }} |