SonarQube
SonarQube is a static code analysis tool that helps developers write cleaner and safer code by detecting bugs, vulnerabilities, and code smells. The project uses the SonarQube Community Build for local analysis of JavaScript/TypeScript sources.
Architecture
To minimize costs and simplify infrastructure, SonarQube and its PostgreSQL database run as Docker containers on a remote Virtual Machine (VM).
- Backend: Docker Compose (SonarQube + PostgreSQL) on the remote VM.
- Local Access: Local developers access the dashboard via an SSH tunnel (port 9001).
- SSH Alias: Uses the
vmalias for easy connectivity.
Prerequisites
Before running a scan, ensure you have the following:
- SSH Access: Configured access to the remote VM using the
vmalias. - Sonar Scanner: The
sonar-scannertool installed locally. - Auth Token: A
SONAR_TOKENgenerated from your account.
1. Configure SSH Alias
Add the following to your ~/.ssh/config:
Host vm
HostName <vm-hostname-or-ip>
User <username>
IdentityFile ~/.ssh/<your-key>
IdentitiesOnly yes
2. Install Sonar Scanner
- macOS (Homebrew):
bash brew install sonar-scanner[!TIP] macOS Note: Developers on macOS can simply use Homebrew to install the scanner.
- Windows (Chocolatey):
powershell choco install sonar-scanner - Arch / CachyOS:
bash yay -S sonar-scanner - Ubuntu / Debian:
Download the CLI from the official SonarQube documentation and manually add the
bindirectory to yourPATH.
3. Generate Auth Tokens
The project uses two different tokens for least-privilege security:
SONAR_TOKEN(Analysis): Used for pushing code analysis results to the server.- Type: Project Analysis Token (starts with
sqp_) or Global Analysis Token (sqa_). - Setup: Generate in SonarQube and
export SONAR_TOKEN=<token>.
- Type: Project Analysis Token (starts with
SONAR_REPORT_TOKEN(Reports): Used for reading data via the API to generate PDFs.- Type: User Token (must start with
squ_). - Setup: Generate in SonarQube and
export SONAR_REPORT_TOKEN=<token>.
- Type: User Token (must start with
[!IMPORTANT] Both tokens should be added as GitHub Actions Secrets (
SONAR_TOKENandSONAR_REPORT_TOKEN) for the CI/CD pipeline to function correctly.
Usage
1. Start Tunnel
Run the following command from the fieldnotes-ai-app directory. This will automatically force kill any existing process on port 9001 and establish a new SSH tunnel to the SonarQube server:
make sonar-tunnel
[!TIP] Automatic Kill: You no longer need to manually kill the process on port 9001 before starting a new tunnel; the script handles this for you.
[!IMPORTANT] This command runs in the foreground. You must keep the terminal window open while using SonarQube. To close the tunnel, press
Ctrl+Cin that terminal.
2. Run a Scan
Runs Jest with coverage to generate the LCOV report, then submits the analysis to SonarQube:
make sonar-scan
[!NOTE] Requires
SONAR_TOKENto be exported in your shell (export SONAR_TOKEN=...). This may take a few minutes.
3. Generate a PDF Report
Three usage modes are available:
| Command | Effect |
|---|---|
make report |
Runs sonar-scan first, then generates the PDF report |
make report override |
Skips the scan — generates report from data already on the server |
# Full pipeline (scan + report)
export SONAR_TOKEN=<your-analysis-token>
export SONAR_REPORT_TOKEN=<your-user-token-squ_...>
make report
# Report only (scan already done)
export SONAR_REPORT_TOKEN=<your-user-token-squ_...>
make report override
The report will be saved to fieldnotes-ai-app/reports/sonar-quality-report.pdf.
Configuration (sonar-project.properties)
The project contains a sonar-project.properties file that defines how analysis is performed. Key fields:
sonar.projectKey: Unique identifier for the project.sonar.sources: Directory to scan (e.g.,src).sonar.exclusions: Patterns to ignore (e.g.,node_modules,coverage).sonar.javascript.lcov.reportPaths: Location of your Jest LCOV report.sonar.host.url: Points to the local tunnel endpoint (defaults tohttp://localhost:9001).
sonar.projectKey=fieldnotes-ai-app
sonar.projectName=FieldNotes AI
sonar.projectVersion=1.0
sonar.sources=.
sonar.exclusions=**/node_modules/**,**/coverage/**,**/.expo/**,**/android/**,**/ios/**,**/assets/**,**/scripts/**,**/reports/**,**/*.config.js,**/*.config.ts,**/*.config.mjs
sonar.tests=.
sonar.test.inclusions=**/__tests__/**,**/*.test.ts,**/*.test.tsx
sonar.javascript.lcov.reportPaths=coverage/lcov.info
sonar.typescript.lcov.reportPaths=coverage/lcov.info
sonar.sourceEncoding=UTF-8
sonar.host.url=http://localhost:9001
If the tunnel process is still running and you need to stop it, you can use the built-in kill target:
make kill <port_number>
Example: make kill 9001
Quality Reports
The project uses ReflectSonar to generate PDF quality reports from SonarQube.
Local Generation
You can generate a PDF report locally after running a scan (and while the tunnel is open):
# Run scan + report in one step
make report
# Or if you've already scanned and just need the report
make report override
The report will be saved to fieldnotes-ai-app/reports/sonar-quality-report.pdf.
CI/CD Reports
Every SonarQube Analysis run in GitHub Actions automatically generates a quality report.
- To download: Go to the Actions tab, select the latest SonarQube Analysis run, and look for the sonar-quality-report artifact at the bottom.
Limitations
- PR Decoration: Not available in Community Build. Results are viewed on the dashboard.
- Main Branch Only: Multi-branch analysis is restricted in the Community Build version.
Troubleshooting
- Port already in use: Kill the existing tunnel process with
make kill <port>. - SonarQube not yet responding: Wait ~60 seconds for the service to start. Check with
sudo docker logs sonarqube --tail 20on the VM. - Token Permissions (403 Forbidden): If
sonar-reportfails with a 403, your token likely lacks the "Browse" permission.- Ensure you generated a User Token and not an "Analysis Token".
- Test the token manually with this command (replace
TOKEN_HEREand keep the colon):bash curl -u "TOKEN_HERE:" "http://localhost:9000/api/components/show?component=fieldnotes-ai-app" - If this returns an error, the token is insufficient for API access.
- Missing/Invalid SONAR_TOKEN: Ensure the token is exported in your current shell session (
export SONAR_TOKEN=...). - vm.max_map_count too low: If the service fails to start on the VM, run
sudo sysctl -w vm.max_map_count=524288on the VM.