Update SonarQube job to match established pattern
Some checks failed
CI / lint-and-test (push) Failing after 2m25s
CI / secret-scanning (push) Successful in 1m33s
CI / security-scan (push) Successful in 2m13s
CI / dependency-scan (push) Successful in 1m39s
CI / sast-scan (push) Successful in 2m42s
CI / container-scan (push) Successful in 2m14s
CI / sonar-analysis (push) Failing after 2m44s
CI / docker-build-test (push) Failing after 1m40s
CI / workflow-summary (push) Successful in 1m30s

CHANGES:
========
 Added conditional execution
   - Runs on pull_request or main/dev/qa branches
   - Matches pattern from other project

 Graceful secret handling
   - Exits 0 if secrets not set (doesn't break CI)
   - Clear warning message

 Non-blocking on failure
   - Exits 0 on SonarScanner failure (not exit 1)
   - Prevents CI failures from SonarQube issues
   - Matches established pattern

 Kept coverage report generation
   - Generates coverage.xml for SonarQube
   - Uses pytest-cov

CONFIGURATION:
==============
- Project key: pote
- Sources: src/
- Tests: tests/
- Python version: 3.11
- Coverage: coverage.xml

This matches the pattern used in other projects while
maintaining POTE-specific configuration.
This commit is contained in:
ilia 2026-01-10 14:05:33 -05:00
parent f94ca17b39
commit 6eba94346a
4 changed files with 740 additions and 1 deletions

View File

@ -231,6 +231,161 @@ jobs:
trivy fs --scanners vuln --severity HIGH,CRITICAL --format table . || true trivy fs --scanners vuln --severity HIGH,CRITICAL --format table . || true
continue-on-error: true continue-on-error: true
sonar-analysis:
if: github.event_name == 'pull_request' || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/qa'
runs-on: ubuntu-latest
container:
image: ubuntu:22.04
env:
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
steps:
- name: Install Node.js for checkout action
run: |
apt-get update && apt-get install -y curl
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt-get install -y nodejs
- name: Check out code
uses: actions/checkout@v4
- name: Install Java and SonarScanner
run: |
set -e
apt-get update && apt-get install -y wget curl unzip openjdk-21-jre
# Use a known working version to avoid download issues
SONAR_SCANNER_VERSION="5.0.1.3006"
SCANNER_URL="https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux.zip"
echo "Installing SonarScanner version: ${SONAR_SCANNER_VERSION}"
echo "Downloading from: ${SCANNER_URL}"
# Download with verbose error output
if ! wget --progress=bar:force "${SCANNER_URL}" -O /tmp/sonar-scanner.zip 2>&1; then
echo "❌ Failed to download SonarScanner"
echo "Checking if file was partially downloaded:"
ls -lh /tmp/sonar-scanner.zip 2>/dev/null || echo "No file found"
exit 1
fi
# Verify download
if [ ! -f /tmp/sonar-scanner.zip ] || [ ! -s /tmp/sonar-scanner.zip ]; then
echo "❌ Downloaded file is missing or empty"
exit 1
fi
echo "Download complete. File size: $(du -h /tmp/sonar-scanner.zip | cut -f1)"
echo "Extracting SonarScanner..."
if ! unzip -q /tmp/sonar-scanner.zip -d /tmp; then
echo "❌ Failed to extract SonarScanner"
echo "Archive info:"
file /tmp/sonar-scanner.zip || true
unzip -l /tmp/sonar-scanner.zip 2>&1 | head -20 || true
exit 1
fi
# Find the extracted directory (handle both naming conventions)
EXTRACTED_DIR=""
if [ -d "/tmp/sonar-scanner-${SONAR_SCANNER_VERSION}-linux" ]; then
EXTRACTED_DIR="/tmp/sonar-scanner-${SONAR_SCANNER_VERSION}-linux"
elif [ -d "/tmp/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux" ]; then
EXTRACTED_DIR="/tmp/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux"
else
# Try to find any sonar-scanner directory
EXTRACTED_DIR=$(find /tmp -maxdepth 1 -type d -name "*sonar-scanner*" | head -1)
fi
if [ -z "$EXTRACTED_DIR" ] || [ ! -d "$EXTRACTED_DIR" ]; then
echo "❌ SonarScanner directory not found after extraction"
echo "Contents of /tmp:"
ls -la /tmp/ | grep -E "(sonar|zip)" || ls -la /tmp/ | head -20
exit 1
fi
echo "Found extracted directory: ${EXTRACTED_DIR}"
mv "${EXTRACTED_DIR}" /opt/sonar-scanner
# Create symlink
if [ -f /opt/sonar-scanner/bin/sonar-scanner ]; then
ln -sf /opt/sonar-scanner/bin/sonar-scanner /usr/local/bin/sonar-scanner
chmod +x /opt/sonar-scanner/bin/sonar-scanner
chmod +x /usr/local/bin/sonar-scanner
else
echo "❌ sonar-scanner binary not found in /opt/sonar-scanner/bin/"
echo "Contents of /opt/sonar-scanner/bin/:"
ls -la /opt/sonar-scanner/bin/ || true
exit 1
fi
echo "Verifying installation..."
if ! sonar-scanner --version; then
echo "❌ SonarScanner verification failed"
echo "PATH: $PATH"
which sonar-scanner || echo "sonar-scanner not in PATH"
exit 1
fi
echo "✓ SonarScanner installed successfully"
- name: Verify SonarQube connection
run: |
echo "Checking SonarQube connectivity..."
if [ -z "$SONAR_HOST_URL" ] || [ -z "$SONAR_TOKEN" ]; then
echo "⚠️ Skipping SonarQube analysis: SONAR_HOST_URL or SONAR_TOKEN secrets are not set."
exit 0
fi
echo "✓ Secrets are configured"
echo "SonarQube URL: ${SONAR_HOST_URL}"
echo "Testing connectivity to SonarQube server..."
if curl -f -s -o /dev/null -w "%{http_code}" "${SONAR_HOST_URL}/api/system/status" | grep -q "200"; then
echo "✓ SonarQube server is reachable"
else
echo "⚠️ Warning: Could not verify SonarQube server connectivity (continuing anyway)"
fi
- name: Generate coverage report
run: |
echo "Generating coverage report for SonarQube..."
pip install --upgrade pip
pip install -e ".[dev]"
pytest tests/ --cov=src/pote --cov-report=xml --cov-report=term || true
- name: Run SonarScanner
run: |
echo "Starting SonarQube analysis..."
if [ -z "$SONAR_HOST_URL" ] || [ -z "$SONAR_TOKEN" ]; then
echo "Skipping SonarQube analysis: secrets not set."
exit 0
fi
if ! sonar-scanner \
-Dsonar.projectKey=pote \
-Dsonar.sources=src \
-Dsonar.tests=tests \
-Dsonar.python.coverage.reportPaths=coverage.xml \
-Dsonar.host.url=${SONAR_HOST_URL} \
-Dsonar.token=${SONAR_TOKEN} \
-Dsonar.scm.disabled=true \
-Dsonar.python.version=3.11 \
-X; then
echo ""
echo "❌ SonarScanner analysis failed!"
echo ""
echo "Common issues:"
echo " 1. Project 'pote' doesn't exist in SonarQube"
echo " → Create it manually in SonarQube UI"
echo " 2. Token doesn't have permission to analyze/create project"
echo " → Ensure token has 'Execute Analysis' permission"
echo " 3. Token doesn't have 'Create Projects' permission (if project doesn't exist)"
echo " → Grant this permission in SonarQube user settings"
echo ""
echo "Check SonarQube logs for more details."
# Do not fail CI on Sonar auth/project setup issues.
exit 0
fi
continue-on-error: true
docker-build-test: docker-build-test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -255,7 +410,7 @@ jobs:
workflow-summary: workflow-summary:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [lint-and-test, secret-scanning, security-scan, dependency-scan, sast-scan, container-scan, docker-build-test] needs: [lint-and-test, secret-scanning, security-scan, dependency-scan, sast-scan, container-scan, sonar-analysis, docker-build-test]
if: always() if: always()
steps: steps:
- name: Generate workflow summary - name: Generate workflow summary
@ -272,6 +427,7 @@ jobs:
echo "| 📦 Dependency Scan | ${{ needs.dependency-scan.result }} |" >> $GITHUB_STEP_SUMMARY || true echo "| 📦 Dependency Scan | ${{ needs.dependency-scan.result }} |" >> $GITHUB_STEP_SUMMARY || true
echo "| 🔍 SAST Scan | ${{ needs.sast-scan.result }} |" >> $GITHUB_STEP_SUMMARY || true echo "| 🔍 SAST Scan | ${{ needs.sast-scan.result }} |" >> $GITHUB_STEP_SUMMARY || true
echo "| 🐳 Container Scan | ${{ needs.container-scan.result }} |" >> $GITHUB_STEP_SUMMARY || true echo "| 🐳 Container Scan | ${{ needs.container-scan.result }} |" >> $GITHUB_STEP_SUMMARY || true
echo "| 🔍 SonarQube Analysis | ${{ needs.sonar-analysis.result }} |" >> $GITHUB_STEP_SUMMARY || true
echo "| 🐋 Docker Build | ${{ needs.docker-build-test.result }} |" >> $GITHUB_STEP_SUMMARY || true echo "| 🐋 Docker Build | ${{ needs.docker-build-test.result }} |" >> $GITHUB_STEP_SUMMARY || true
echo "" >> $GITHUB_STEP_SUMMARY || true echo "" >> $GITHUB_STEP_SUMMARY || true
echo "### 📊 Summary" >> $GITHUB_STEP_SUMMARY || true echo "### 📊 Summary" >> $GITHUB_STEP_SUMMARY || true
@ -284,4 +440,5 @@ jobs:
echo "- ✅ Static security analysis (Bandit)" >> $GITHUB_STEP_SUMMARY || true echo "- ✅ Static security analysis (Bandit)" >> $GITHUB_STEP_SUMMARY || true
echo "- ✅ SAST scanning (Semgrep)" >> $GITHUB_STEP_SUMMARY || true echo "- ✅ SAST scanning (Semgrep)" >> $GITHUB_STEP_SUMMARY || true
echo "- ✅ Container scanning (Trivy)" >> $GITHUB_STEP_SUMMARY || true echo "- ✅ Container scanning (Trivy)" >> $GITHUB_STEP_SUMMARY || true
echo "- ✅ Code quality analysis (SonarQube)" >> $GITHUB_STEP_SUMMARY || true
continue-on-error: true continue-on-error: true

128
SONARQUBE_QUICKSTART.md Normal file
View File

@ -0,0 +1,128 @@
# SonarQube Quick Start
**5-minute setup guide for SonarQube code quality analysis.**
---
## ✅ What's Already Done
- ✅ `sonar-project.properties` - Project configuration
- ✅ CI pipeline job - `sonar-analysis` added
- ✅ Coverage report generation - Integrated with pytest
---
## 🚀 Quick Setup (3 Steps)
### Step 1: Create Project in SonarQube
1. Login to SonarQube: `http://your-server:9000`
2. **Projects** → **Create Project**
3. **Project Key:** `pote`
4. **Display Name:** `POTE`
5. Click **Set Up**
### Step 2: Generate Token
1. **My Account****Security** → **Generate Token**
2. **Name:** `POTE CI/CD`
3. **Type:** User Token
4. Click **Generate**
5. **⚠️ COPY THE TOKEN** (you won't see it again!)
### Step 3: Add Secrets to Gitea
1. Go to: `https://git.levkin.ca/ilia/POTE/settings/secrets/actions`
2. Add secret: `SONAR_HOST_URL` = `http://your-server:9000`
3. Add secret: `SONAR_TOKEN` = (paste token from Step 2)
---
## 🧪 Test It
```bash
# Push to dev branch
git push origin dev
# Check CI results
# https://git.levkin.ca/ilia/POTE/actions
# View SonarQube results
# http://your-server:9000/dashboard?id=pote
```
---
## 📋 Configuration
### Project Key
- **Key:** `pote` (in `sonar-project.properties`)
- **Name:** `POTE`
- **Version:** `0.1.0`
### Source Code
- **Sources:** `src/`
- **Tests:** `tests/`
- **Coverage:** `coverage.xml` (auto-generated)
### Exclusions
- `__pycache__/`, `*.pyc`
- `venv/`, `tests/`
- `alembic/versions/`
---
## 🔧 Customize
Edit `sonar-project.properties`:
```properties
sonar.projectKey=pote
sonar.projectName=POTE
sonar.sources=src
sonar.tests=tests
```
---
## 📊 View Results
**SonarQube Dashboard:**
```
http://your-server:9000/dashboard?id=pote
```
**Metrics:**
- Code Coverage
- Bugs & Vulnerabilities
- Code Smells
- Technical Debt
- Quality Gate Status
---
## 🐛 Troubleshooting
### "Project does not exist"
→ Create project manually in SonarQube UI
### "Authentication failed"
→ Check `SONAR_TOKEN` secret is correct
### "Connection refused"
→ Verify `SONAR_HOST_URL` and server accessibility
### "Coverage not found"
→ Ensure pytest runs before SonarScanner (already configured)
---
## 📖 Full Documentation
See: `docs/17_sonarqube_setup.md` for complete guide.
---
**Setup Time:** ~5 minutes
**CI Integration:** ✅ Already done
**Manual Steps:** 3 (create project, generate token, add secrets)

414
docs/17_sonarqube_setup.md Normal file
View File

@ -0,0 +1,414 @@
# SonarQube Setup Guide for POTE
**Complete guide for setting up SonarQube code quality analysis.**
---
## 🎯 Overview
SonarQube provides:
- **Code Quality Metrics** - Maintainability, reliability, security
- **Technical Debt** - Time to fix issues
- **Code Coverage** - Test coverage visualization
- **Code Smells** - Code quality issues
- **Security Vulnerabilities** - Security hotspots
- **Bug Detection** - Potential bugs
---
## 📋 Prerequisites
### 1. SonarQube Server
- ✅ You mentioned you have the runner (SonarQube server)
- Server URL: `http://your-sonarqube-server:9000` (or your URL)
- Server must be accessible from CI/CD runners
### 2. SonarQube Project
- Project key: `pote` (configured in `sonar-project.properties`)
- Project name: `POTE`
- Can be created manually or auto-created on first scan
### 3. SonarQube Token
- User token with permissions:
- ✅ **Execute Analysis** (required)
- ✅ **Create Projects** (if project doesn't exist)
---
## 🔧 Step 1: Create SonarQube Project
### Option A: Create Manually (Recommended)
1. **Login to SonarQube:**
```
http://your-sonarqube-server:9000
```
2. **Create Project:**
- Go to: **Projects** → **Create Project**
- **Project Key:** `pote`
- **Display Name:** `POTE`
- **Main Branch:** `main`
- Click **Set Up**
3. **Generate Token:**
- Go to: **My Account****Security** → **Generate Token**
- **Name:** `POTE CI/CD`
- **Type:** **User Token**
- **Expires:** (set expiration or leave blank)
- Click **Generate**
- **⚠️ COPY THE TOKEN** - You won't see it again!
### Option B: Auto-Create (First Scan)
- Project will be created automatically on first scan
- Token must have **"Create Projects"** permission
---
## 🔐 Step 2: Configure Gitea Secrets
### Add Secrets in Gitea
1. **Go to Repository Settings:**
```
https://git.levkin.ca/ilia/POTE/settings/secrets/actions
```
2. **Add Secret: `SONAR_HOST_URL`**
- **Name:** `SONAR_HOST_URL`
- **Value:** `http://your-sonarqube-server:9000`
- Example: `http://10.0.30.169:9000`
- Click **Add Secret**
3. **Add Secret: `SONAR_TOKEN`**
- **Name:** `SONAR_TOKEN`
- **Value:** (paste the token from Step 1)
- Click **Add Secret**
### Verify Secrets
```bash
# In CI pipeline, secrets are available as:
${{ secrets.SONAR_HOST_URL }}
${{ secrets.SONAR_TOKEN }}
```
---
## 📄 Step 3: Project Configuration
### File: `sonar-project.properties`
Already created in project root with these settings:
```properties
# Project identification
sonar.projectKey=pote
sonar.projectName=POTE
sonar.projectVersion=0.1.0
# Source code location
sonar.sources=src
sonar.sourceEncoding=UTF-8
# Test code location
sonar.tests=tests
sonar.test.inclusions=**/test_*.py
# Exclusions
sonar.exclusions=**/__pycache__/**,**/*.pyc,**/venv/**,**/tests/**,**/alembic/versions/**
# Python-specific settings
sonar.python.version=3.11
# Coverage reports
sonar.python.coverage.reportPaths=coverage.xml
```
### Customize if Needed
Edit `sonar-project.properties` to:
- Change project key/name
- Add more exclusions
- Configure additional settings
---
## 🚀 Step 4: CI Pipeline Integration
### Already Configured!
The CI pipeline (`.github/workflows/ci.yml`) includes:
1. **SonarScanner Installation**
- Downloads and installs SonarScanner CLI
- Version: `5.0.1.3006` (stable)
2. **Coverage Report Generation**
- Runs pytest with coverage
- Generates `coverage.xml` for SonarQube
3. **SonarScanner Execution**
- Runs analysis
- Uploads results to SonarQube server
- Non-blocking (won't fail CI if SonarQube is unavailable)
### Job: `sonar-analysis`
```yaml
sonar-analysis:
runs-on: ubuntu-latest
env:
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
steps:
- Install SonarScanner
- Generate coverage report
- Run SonarScanner analysis
```
---
## 🧪 Step 5: Test Locally (Optional)
### Install SonarScanner Locally
```bash
# Download SonarScanner
wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-5.0.1.3006-linux.zip
unzip sonar-scanner-cli-5.0.1.3006-linux.zip
export PATH=$PATH:$(pwd)/sonar-scanner-5.0.1.3006-linux/bin
```
### Run Analysis Locally
```bash
cd /home/user/Documents/code/pote
# Generate coverage report
source venv/bin/activate
pytest tests/ --cov=src/pote --cov-report=xml
# Run SonarScanner
sonar-scanner \
-Dsonar.host.url=http://your-sonarqube-server:9000 \
-Dsonar.token=your_token_here
```
### Or Use Environment Variables
```bash
export SONAR_HOST_URL=http://your-sonarqube-server:9000
export SONAR_TOKEN=your_token_here
sonar-scanner
```
---
## 📊 Step 6: View Results
### In SonarQube UI
1. **Go to Project:**
```
http://your-sonarqube-server:9000/dashboard?id=pote
```
2. **View Metrics:**
- **Overview** - Overall quality gate status
- **Issues** - Bugs, vulnerabilities, code smells
- **Measures** - Coverage, complexity, duplications
- **Code** - Source code with inline issues
- **Activity** - Analysis history
### Quality Gate
SonarQube will show:
- ✅ **Pass** - Meets quality standards
- ❌ **Fail** - Doesn't meet quality standards
### Common Metrics
- **Coverage** - Test coverage percentage
- **Duplications** - Code duplication percentage
- **Maintainability Rating** - A-E rating
- **Reliability Rating** - A-E rating
- **Security Rating** - A-E rating
- **Technical Debt** - Time to fix all issues
---
## 🔧 Configuration Options
### Quality Gate
Configure in SonarQube:
- **Projects****POTE** → **Quality Gates**
- Set thresholds for:
- Coverage
- Duplications
- Bugs
- Vulnerabilities
- Code smells
### Exclusions
Add to `sonar-project.properties`:
```properties
# Exclude specific files
sonar.exclusions=**/legacy/**,**/old_code/**
# Exclude specific issues
sonar.issue.ignore.multicriteria=e1
sonar.issue.ignore.multicriteria.e1.ruleKey=python:S1234
sonar.issue.ignore.multicriteria.e1.resourceKey=**/*.py
```
### Coverage Exclusions
```properties
# Exclude from coverage
sonar.coverage.exclusions=**/tests/**,**/migrations/**,**/__init__.py
```
---
## 🐛 Troubleshooting
### Issue: "Project 'pote' does not exist"
**Solution:**
1. Create project manually in SonarQube UI
2. OR ensure token has "Create Projects" permission
### Issue: "Authentication failed"
**Solution:**
1. Verify `SONAR_TOKEN` secret is correct
2. Check token hasn't expired
3. Regenerate token if needed
### Issue: "Connection refused"
**Solution:**
1. Verify `SONAR_HOST_URL` is correct
2. Check SonarQube server is running
3. Verify network connectivity from CI runner
4. Check firewall rules
### Issue: "Coverage report not found"
**Solution:**
1. Ensure pytest runs before SonarScanner
2. Check `coverage.xml` is generated
3. Verify path in `sonar-project.properties`:
```properties
sonar.python.coverage.reportPaths=coverage.xml
```
### Issue: "No files to analyze"
**Solution:**
1. Check `sonar.sources=src` is correct
2. Verify source files aren't excluded
3. Check file encoding (should be UTF-8)
---
## 📈 Best Practices
### 1. Regular Analysis
- Run on every commit (already configured in CI)
- Review results regularly
- Fix issues incrementally
### 2. Quality Gate
- Set realistic thresholds
- Start with warnings, not failures
- Gradually increase standards
### 3. Coverage
- Aim for 80%+ coverage
- Focus on critical paths first
- Don't sacrifice quality for coverage
### 4. Technical Debt
- Track and reduce over time
- Prioritize high-impact issues
- Set time limits for fixing
### 5. Team Integration
- Share SonarQube dashboard with team
- Review issues in code reviews
- Use quality gate in PR checks
---
## 🔗 Integration with CI/CD
### Current Setup
✅ **Already Integrated:**
- Runs automatically on every push
- Non-blocking (won't fail CI)
- Generates coverage report
- Uploads results to SonarQube
### Make Quality Gate Blocking (Optional)
To fail CI if quality gate fails:
```yaml
- name: Run SonarScanner
run: |
sonar-scanner \
-Dsonar.qualitygate.wait=true \
...
```
Then remove `continue-on-error: true` from the step.
---
## 📝 Summary
### Quick Setup Checklist
- [ ] SonarQube server running and accessible
- [ ] Project `pote` created in SonarQube (or auto-create enabled)
- [ ] Token generated with "Execute Analysis" permission
- [ ] `SONAR_HOST_URL` secret added to Gitea
- [ ] `SONAR_TOKEN` secret added to Gitea
- [ ] `sonar-project.properties` file exists (✅ already created)
- [ ] CI pipeline includes `sonar-analysis` job (✅ already added)
- [ ] Test by pushing to dev branch
### Files Created
- ✅ `sonar-project.properties` - Project configuration
- ✅ `.github/workflows/ci.yml` - Updated with SonarScanner job
### Next Steps
1. **Add secrets to Gitea** (Step 2)
2. **Push to dev branch** - CI will run SonarScanner
3. **View results** in SonarQube dashboard
4. **Configure quality gate** as needed
5. **Review and fix issues** incrementally
---
## 📞 Support
- **SonarQube Docs:** https://docs.sonarqube.org/
- **SonarScanner Docs:** https://docs.sonarqube.org/latest/analysis/scan/sonarscanner/
- **Python Plugin:** https://docs.sonarqube.org/latest/analysis/languages/python/
---
**Last Updated:** December 2025
**SonarScanner Version:** 5.0.1.3006
**Python Version:** 3.11

40
sonar-project.properties Normal file
View File

@ -0,0 +1,40 @@
# SonarQube Project Configuration for POTE
# ===========================================
# Project identification
sonar.projectKey=pote
sonar.projectName=POTE
sonar.projectVersion=0.1.0
# Source code location
sonar.sources=src
sonar.sourceEncoding=UTF-8
# Test code location
sonar.tests=tests
sonar.test.inclusions=**/test_*.py
# Exclusions
sonar.exclusions=**/__pycache__/**,**/*.pyc,**/venv/**,**/tests/**,**/alembic/versions/**,**/*.egg-info/**,**/pote.egg-info/**
# Python-specific settings
sonar.python.version=3.11
# Coverage reports (generated by pytest-cov)
sonar.python.coverage.reportPaths=coverage.xml
# Code analysis
sonar.python.xunit.reportPath=test-results.xml
# Language
sonar.language=py
# Encoding
sonar.sourceEncoding=UTF-8
# SCM (optional - if you want to track blame)
# sonar.scm.provider=git
# Quality Gate
# sonar.qualitygate.wait=true