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 vm alias for easy connectivity.

Prerequisites

Before running a scan, ensure you have the following:

  • SSH Access: Configured access to the remote VM using the vm alias.
  • Sonar Scanner: The sonar-scanner tool installed locally.
  • Auth Token: A SONAR_TOKEN generated 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 bin directory to your PATH.

3. Generate Auth Tokens

The project uses two different tokens for least-privilege security:

  1. 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>.
  2. 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>.

[!IMPORTANT] Both tokens should be added as GitHub Actions Secrets (SONAR_TOKEN and SONAR_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+C in 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_TOKEN to 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 to http://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 20 on the VM.
  • Token Permissions (403 Forbidden): If sonar-report fails 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_HERE and 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=524288 on the VM.