Skip to content

ADR-0020: SonarQube Self-Hosted Quality Gate for Cornerstone CI/CD

Status: accepted Deciders: DeAcero Platform Team Date: 2026-03-26


Context and Problem Statement

Cornerstone generates projects with ADR-gate, linting (ruff), type checking (mypy), and architectural layer validation (import-linter). However, there is no centralized static analysis quality gate that tracks code smells, coverage trends, duplications, security hotspots, and vulnerability history across all generated projects over time.

As Cornerstone scales toward the 11 MCP agents target (F3), a shared quality gate running on 192.99.38.188 (vpc.kronosb.com) — which is a temporary measure until dedicated hardware availability is secured — enables: - Visibility into code health per project over time - Security hotspot detection aligned with ADR-0007 (OWASP CI/CD posture) - A single dashboard for the Platform Team to monitor code quality across all Cornerstone-generated projects


Decision Drivers

  • Must be self-hosted — no third-party SaaS (consistent with ADR-0001 telemetry philosophy)
  • Must integrate into existing GitHub Actions CI workflows without breaking the ADR-gate or test jobs
  • Must run on the existing VPS (192.99.38.188) alongside the Observability Service (Docker Compose already active)
  • Zero-impact on generated projects that do not configure SONAR_TOKEN — the scan step is skipped if the secret is absent
  • Must not require changes to generated project source code — scanner is configured via sonar-project.properties

Considered Options

  1. SonarQube Community Edition — self-hosted on 192.99.38.188
  2. SonarCloud — SaaS by SonarSource
  3. CodeClimate — SaaS alternative
  4. Semgrep OSS — CLI-only, no persistent dashboard

Decision Outcome

Chosen option: Option 1 — SonarQube Community Edition, self-hosted via Docker Compose on 192.99.38.188.

Rationale: - SaaS options (2, 3) raise data residency concerns for DeAcero source code (consistent with ADR-0001) - Semgrep OSS (4) has no persistent dashboard or trend history — cannot track quality evolution over time - SonarQube Community Edition is free, battle-tested, and integrates natively with GitHub Actions via sonarsource/sonarqube-scan-action - The existing VPS already runs Docker Compose with nginx-proxy — SonarQube fits naturally into that stack


Architecture

vpc.kronosb.com (192.99.38.188)
├── services/observability/    → port 8000 (FastAPI + PostgreSQL:5432)
└── services/sonarqube/        → port 9000 (SonarQube + PostgreSQL:5433)

GitHub Actions CI (generated projects)
└── sonar job
    ├── runs after: test job
    ├── uses: sonarsource/sonarqube-scan-action
    ├── SONAR_HOST_URL: http://192.99.38.188:9000
    └── SONAR_TOKEN: org secret (skipped if absent)

Deployment

  • Stack: services/sonarqube/docker-compose.yml — SonarQube LTS + dedicated PostgreSQL
  • Persistence: named Docker volumes (sonarqube_data, sonarqube_logs, sonarqube_extensions, sonarqube_db)
  • System requirements: VPS must have vm.max_map_count=524288 (set via sysctl, persisted in /etc/sysctl.d/)
  • URL: http://192.99.38.188:9000 (or http://vpc.kronosb.com:9000 once DNS resolves)

CI Integration

The sonar scan is added as a separate job that runs after test:

sonar:
  needs: test
  if: ${{ secrets.SONAR_TOKEN != '' }}
  steps:
    - uses: sonarsource/sonarqube-scan-action@v4
    - uses: sonarsource/sonarqube-quality-gate-action@v1

Projects without SONAR_TOKEN skip the job silently — zero-impact principle preserved.


Consequences

Positive

  • Centralized code quality dashboard for all Cornerstone-generated projects
  • Security hotspot tracking aligned with ADR-0007
  • Coverage trends visible over time
  • Free tier covers all current and planned Cornerstone projects

Negative

  • VPS must have sufficient RAM for SonarQube (minimum 2 GB available; recommended 4 GB)
  • vm.max_map_count requires a one-time sysctl change on the host
  • Community Edition does not support branch analysis (only main branch + PRs via PR decoration)

Neutral

  • SonarQube token must be provisioned manually per project (or as a GitHub org secret for shared use)

Implementation

  • Server stack: services/sonarqube/docker-compose.yml
  • Environment template: services/sonarqube/.env.example
  • Scanner config (root): sonar-project.properties
  • Scanner config (builder template): builder/{{cookiecutter.project_slug}}/sonar-project.properties
  • CI step (builder template): builder/{{cookiecutter.project_slug}}/.github/workflows/ci.yml
  • CI step (template): {{cookiecutter.project_slug}}/.github/workflows/ci.yml