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
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:
parent
f94ca17b39
commit
6eba94346a
159
.github/workflows/ci.yml
vendored
159
.github/workflows/ci.yml
vendored
@ -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
128
SONARQUBE_QUICKSTART.md
Normal 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
414
docs/17_sonarqube_setup.md
Normal 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
40
sonar-project.properties
Normal 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
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user