SonarQube Analysis #101
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: SonarQube Analysis | |
| on: | |
| pull_request: | |
| branches: [ "main" ] | |
| schedule: | |
| - cron: '0 3 * * 1' # Weekly on Monday at 3 AM UTC | |
| permissions: | |
| contents: read | |
| issues: write | |
| pull-requests: write | |
| jobs: | |
| sonarqube: | |
| runs-on: ubuntu-latest | |
| services: | |
| sonarqube: | |
| image: sonarqube:community | |
| ports: | |
| - 9000:9000 | |
| env: | |
| SONAR_ES_BOOTSTRAP_CHECKS_DISABLE: true | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Wait for SonarQube | |
| run: | | |
| timeout 300 bash -c 'until curl -s http://localhost:9000/api/system/status | grep -q "UP"; do sleep 5; done' | |
| - name: Generate SonarQube token | |
| run: | | |
| sleep 10 | |
| TOKEN=$(curl -s -u admin:admin -X POST "http://localhost:9000/api/user_tokens/generate?name=github-actions" | jq -r '.token') | |
| echo "SONAR_TOKEN=$TOKEN" >> $GITHUB_ENV | |
| - name: Setup SonarQube Scanner | |
| uses: warchant/setup-sonar-scanner@v8 | |
| - name: Run SonarQube Analysis | |
| run: | | |
| sonar-scanner \ | |
| -Dsonar.projectKey=rashdf \ | |
| -Dsonar.sources=src \ | |
| -Dsonar.tests=tests \ | |
| -Dsonar.host.url=http://localhost:9000 \ | |
| -Dsonar.token=$SONAR_TOKEN \ | |
| -Dsonar.exclusions="tests/data/**,**/*.yml,**/*.yaml" | |
| - name: Extract SonarQube Results | |
| run: | | |
| # Wait for analysis to be processed | |
| echo "Waiting for SonarQube analysis processing..." | |
| sleep 15 | |
| echo "## SonarQube Analysis Results" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Get project measures | |
| echo "Fetching project measures..." | |
| MEASURES=$(curl -s -u admin:admin "http://localhost:9000/api/measures/component?component=rashdf&metricKeys=bugs,vulnerabilities,code_smells,coverage,duplicated_lines_density,ncloc,sqale_rating,reliability_rating,security_rating") | |
| echo "Measures response: $MEASURES" | |
| if echo "$MEASURES" | jq -e '.component.measures' > /dev/null 2>&1; then | |
| echo "### Key Metrics:" >> $GITHUB_STEP_SUMMARY | |
| echo "$MEASURES" | jq -r '.component.measures[] | "- **\(.metric)**: \(.value // "N/A")"' >> $GITHUB_STEP_SUMMARY | |
| # Extract specific metrics for later use | |
| BUGS=$(echo "$MEASURES" | jq -r '.component.measures[] | select(.metric == "bugs") | .value // "0"') | |
| VULNERABILITIES=$(echo "$MEASURES" | jq -r '.component.measures[] | select(.metric == "vulnerabilities") | .value // "0"') | |
| CODE_SMELLS=$(echo "$MEASURES" | jq -r '.component.measures[] | select(.metric == "code_smells") | .value // "0"') | |
| echo "Extracted metrics - Bugs: $BUGS, Vulnerabilities: $VULNERABILITIES, Code Smells: $CODE_SMELLS" | |
| else | |
| echo "### Key Metrics:" >> $GITHUB_STEP_SUMMARY | |
| echo "- Unable to retrieve metrics data" >> $GITHUB_STEP_SUMMARY | |
| BUGS=0 | |
| VULNERABILITIES=0 | |
| CODE_SMELLS=0 | |
| fi | |
| # Get issues summary | |
| echo "Fetching issues summary..." | |
| ISSUES=$(curl -s -u admin:admin "http://localhost:9000/api/issues/search?componentKeys=rashdf&facets=severities,types") | |
| echo "Issues response (first 500 chars): ${ISSUES:0:500}" | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if echo "$ISSUES" | jq -e '.facets' > /dev/null 2>&1; then | |
| echo "### Issues Summary:" >> $GITHUB_STEP_SUMMARY | |
| # Check if there are any issues | |
| TOTAL_ISSUES=$(echo "$ISSUES" | jq -r '.total // 0') | |
| echo "Total issues found: $TOTAL_ISSUES" | |
| if [ "$TOTAL_ISSUES" -eq 0 ]; then | |
| echo "- No issues found! ✅" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "**Total Issues:** $TOTAL_ISSUES" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Show breakdown by severity | |
| if echo "$ISSUES" | jq -e '.facets[] | select(.property == "severities")' > /dev/null 2>&1; then | |
| echo "**By Severity:**" >> $GITHUB_STEP_SUMMARY | |
| echo "$ISSUES" | jq -r '.facets[] | select(.property == "severities") | .values[] | "- \(.val): \(.count)"' >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| # Show breakdown by type | |
| if echo "$ISSUES" | jq -e '.facets[] | select(.property == "types")' > /dev/null 2>&1; then | |
| echo "**By Type:**" >> $GITHUB_STEP_SUMMARY | |
| echo "$ISSUES" | jq -r '.facets[] | select(.property == "types") | .values[] | "- \(.val): \(.count)"' >> $GITHUB_STEP_SUMMARY | |
| fi | |
| fi | |
| else | |
| echo "### Issues Summary:" >> $GITHUB_STEP_SUMMARY | |
| echo "- Unable to retrieve issues data" >> $GITHUB_STEP_SUMMARY | |
| TOTAL_ISSUES=0 | |
| fi | |
| # Store results for comment step | |
| echo "TOTAL_ISSUES=$TOTAL_ISSUES" >> $GITHUB_ENV | |
| echo "BUGS=$BUGS" >> $GITHUB_ENV | |
| echo "VULNERABILITIES=$VULNERABILITIES" >> $GITHUB_ENV | |
| echo "CODE_SMELLS=$CODE_SMELLS" >> $GITHUB_ENV | |
| - name: Generate Detailed Report | |
| run: | | |
| mkdir -p sonarqube-reports | |
| echo "Generating detailed SonarQube report..." | |
| # Create main report file | |
| cat > sonarqube-reports/sonarqube-analysis-report.md << 'EOF' | |
| # SonarQube Analysis Report | |
| **Analysis Date:** $(date -u '+%Y-%m-%d %H:%M:%S UTC') | |
| **Project:** rashdf | |
| **Branch:** ${{ github.ref_name }} | |
| **Commit:** ${{ github.sha }} | |
| ## Summary | |
| EOF | |
| # Add metrics to report | |
| if [ -n "$MEASURES" ] && echo "$MEASURES" | jq -e '.component.measures' > /dev/null 2>&1; then | |
| echo "### Key Metrics" >> sonarqube-reports/sonarqube-analysis-report.md | |
| echo "" >> sonarqube-reports/sonarqube-analysis-report.md | |
| echo "$MEASURES" | jq -r '.component.measures[] | "- **\(.metric)**: \(.value // "N/A")"' >> sonarqube-reports/sonarqube-analysis-report.md | |
| echo "" >> sonarqube-reports/sonarqube-analysis-report.md | |
| fi | |
| # Add issue summary | |
| if [ "$TOTAL_ISSUES" -gt 0 ]; then | |
| echo "### Issues Overview" >> sonarqube-reports/sonarqube-analysis-report.md | |
| echo "" >> sonarqube-reports/sonarqube-analysis-report.md | |
| echo "**Total Issues:** $TOTAL_ISSUES" >> sonarqube-reports/sonarqube-analysis-report.md | |
| echo "" >> sonarqube-reports/sonarqube-analysis-report.md | |
| if echo "$ISSUES" | jq -e '.facets' > /dev/null 2>&1; then | |
| echo "**By Severity:**" >> sonarqube-reports/sonarqube-analysis-report.md | |
| echo "$ISSUES" | jq -r '.facets[] | select(.property == "severities") | .values[] | "- \(.val): \(.count)"' >> sonarqube-reports/sonarqube-analysis-report.md | |
| echo "" >> sonarqube-reports/sonarqube-analysis-report.md | |
| echo "**By Type:**" >> sonarqube-reports/sonarqube-analysis-report.md | |
| echo "$ISSUES" | jq -r '.facets[] | select(.property == "types") | .values[] | "- \(.val): \(.count)"' >> sonarqube-reports/sonarqube-analysis-report.md | |
| echo "" >> sonarqube-reports/sonarqube-analysis-report.md | |
| fi | |
| # Get detailed issues | |
| echo "Fetching detailed issue information..." | |
| DETAILED_ISSUES=$(curl -s -u admin:admin "http://localhost:9000/api/issues/search?componentKeys=rashdf&ps=500") | |
| echo "$DETAILED_ISSUES" > sonarqube-reports/detailed-issues.json | |
| # Parse and format detailed issues | |
| echo "## Detailed Issues" >> sonarqube-reports/sonarqube-analysis-report.md | |
| echo "" >> sonarqube-reports/sonarqube-analysis-report.md | |
| if echo "$DETAILED_ISSUES" | jq -e '.issues' > /dev/null 2>&1; then | |
| # Group by severity | |
| for severity in BLOCKER CRITICAL MAJOR MINOR INFO; do | |
| COUNT=$(echo "$DETAILED_ISSUES" | jq -r "[.issues[] | select(.severity == \"$severity\")] | length") | |
| if [ "$COUNT" -gt 0 ]; then | |
| echo "### $severity Issues ($COUNT)" >> sonarqube-reports/sonarqube-analysis-report.md | |
| echo "" >> sonarqube-reports/sonarqube-analysis-report.md | |
| echo "$DETAILED_ISSUES" | jq -r ".issues[] | select(.severity == \"$severity\") | \"#### \(.rule) - \(.type) | |
| **File:** \(.component | split(\":\")[1] // \"N/A\") | |
| **Line:** \(.line // \"N/A\") | |
| **Message:** \(.message) | |
| **Effort:** \(.effort // \"N/A\") | |
| --- | |
| \"" >> sonarqube-reports/sonarqube-analysis-report.md | |
| fi | |
| done | |
| fi | |
| else | |
| echo "### ✅ No Issues Found!" >> sonarqube-reports/sonarqube-analysis-report.md | |
| echo "" >> sonarqube-reports/sonarqube-analysis-report.md | |
| echo "Great job! Your code passed SonarQube analysis without any issues." >> sonarqube-reports/sonarqube-analysis-report.md | |
| fi | |
| # Create CSV report for easy analysis | |
| if [ "$TOTAL_ISSUES" -gt 0 ] && echo "$DETAILED_ISSUES" | jq -e '.issues' > /dev/null 2>&1; then | |
| echo "Creating CSV report..." | |
| echo "Severity,Type,Rule,File,Line,Message,Effort" > sonarqube-reports/issues.csv | |
| echo "$DETAILED_ISSUES" | jq -r '.issues[] | [.severity, .type, .rule, (.component | split(":")[1] // "N/A"), (.line // "N/A"), .message, (.effort // "N/A")] | @csv' >> sonarqube-reports/issues.csv | |
| fi | |
| # Save raw JSON data | |
| echo "$MEASURES" > sonarqube-reports/measures.json | |
| echo "$ISSUES" > sonarqube-reports/issues-summary.json | |
| echo "Reports generated successfully!" | |
| ls -la sonarqube-reports/ | |
| - name: Upload SonarQube Reports | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: sonarqube-analysis-report-${{ github.run_id }} | |
| path: sonarqube-reports/ | |
| retention-days: 30 | |
| - name: Comment on PR | |
| if: github.event_name == 'pull_request' && (env.TOTAL_ISSUES != '0' || env.BUGS != '0' || env.VULNERABILITIES != '0' || env.CODE_SMELLS != '0') | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const totalIssues = '${{ env.TOTAL_ISSUES }}'; | |
| const bugs = '${{ env.BUGS }}'; | |
| const vulnerabilities = '${{ env.VULNERABILITIES }}'; | |
| const codeSmells = '${{ env.CODE_SMELLS }}'; | |
| const comment = `## 🔍 SonarQube Analysis Results | |
| **Summary:** | |
| - 🐛 **Bugs:** ${bugs} | |
| - 🔒 **Vulnerabilities:** ${vulnerabilities} | |
| - 🧹 **Code Smells:** ${codeSmells} | |
| - 📊 **Total Issues:** ${totalIssues} | |
| ### 📋 Detailed Reports Available | |
| 📥 **[Download Full Report](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})** - Click "Artifacts" section | |
| The detailed report includes: | |
| - Complete breakdown by severity and type | |
| - Specific file locations and line numbers | |
| - Rule descriptions and fix suggestions | |
| - CSV format for spreadsheet analysis | |
| - Raw JSON data for custom processing | |
| > 💡 Review the [workflow summary](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for metrics and download the artifact for complete details. | |
| `; | |
| github.rest.issues.createComment({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: comment | |
| }); |