Hamburger Cross Icon
SonarQube Collector - Lunar Collector

SonarQube Collector

Collector Experimental Code Analysis

Detects SonarQube/SonarCloud via Web API reads, auto-run of `sonar-scanner`, in-repo config, CI scanner runs, and the GitHub App PR check. Normalizes results into `.code_quality` for tool-agnostic policy evaluation.

Add sonarqube to your lunar-config.yml:
uses: github://earthly/lunar-lib/collectors/sonarqube@v1.0.5

What This Integration Collects

This integration includes 5 collectors that gather metadata from your systems.

Collector code

api

Queries the SonarQube/SonarCloud Web API for code-quality metrics, scoped per-commit. On default-branch commits the query uses branch=<default>; on PR commits it uses pullRequest=$LUNAR_COMPONENT_PR — the only difference between the two paths is the query param, so they share one sub-collector. Because SonarQube's Compute Engine queues analyses asynchronously — typically publishing 10–60s after sonar-scanner exits, longer for large projects — this sub-collector polls api/project_analyses/search for up to api_poll_timeout_seconds (default 180s) waiting for an analysis whose revision matches the current head_sha. If the matching analysis never appears, writes .code_quality.source.analysis_status = "pending" and no metrics, so policies can distinguish "SonarQube hasn't finished yet" from "SonarQube isn't configured". Discovers the project key from the sonarqube/project-key meta annotation (typically set by a company-specific cataloger via lunar catalog component --meta sonarqube/project-key <key>), or falls back to the explicit project_key input. Writes a tool-agnostic passing signal, coverage and duplication percentages, and severity-bucketed issue counts at the .code_quality.* top level. SonarQube-specific structure (quality gate detail, the reliability/security/maintainability rating split, SQALE debt, native metric names) lands under .code_quality.native.sonarqube for SonarQube-specific policies that need it. Runs on both default-branch and PR commits by default — users who want only one can narrow runs_on on their import in lunar-config.yml.

sonarqube sonarcloud code-quality coverage bugs vulnerabilities quality-gate
Book a demo
Collector code

auto

Runs sonar-scanner on the checked-out source against the configured SonarQube/SonarCloud server, then polls the Web API for results — an all-in-one alternative to api for users who don't trigger SonarQube from their own CI. On default-branch commits the scanner is invoked with -Dsonar.branch.name=<default>; on PR commits with -Dsonar.pullrequest.key=$LUNAR_COMPONENT_PR, -Dsonar.pullrequest.branch=$LUNAR_COMPONENT_HEAD_BRANCH, and -Dsonar.pullrequest.base=$LUNAR_COMPONENT_BASE_BRANCH. Scanner invocation metadata (version, exit code, duration) is captured under .code_quality.native.sonarqube.auto. Once the scanner exits, the same polling path used by api reads the published analysis and writes the tool-agnostic .code_quality.* fields with "integration": "auto". If the scanner fails, writes .code_quality.native.sonarqube.auto.status = "scanner-failed" with the exit code and no downstream metrics. Requires SONARQUBE_TOKEN with Execute Analysis permission (read-only Browse is not sufficient). Mutually exclusive with api and with a user-run sonar-scanner in CI on the same commit — enabling both means the scanner runs twice. A future collector-dependency feature will let auto fire only when the cicd sub-collector did not capture a scan for the same head_sha. Runs on both default-branch and PR commits by default — narrow runs_on on the import to restrict the scope.

sonarqube sonarcloud code-quality auto-scan sonar-scanner
Book a demo
Collector code

config

Detects SonarQube/SonarCloud configuration in the repository — the sonar-project.properties file, the sonar-maven-plugin in pom.xml, the org.sonarqube plugin in build.gradle/build.gradle.kts, or <SonarQubeEnabled> in a .csproj. Writes discovered config file paths under .code_quality.native.sonarqube.config. Presence of this data signals that SonarQube is wired up for the component even when the api sub-collector cannot reach the project or no analysis has been published yet.

sonarqube sonar-project.properties config detection sonar-maven-plugin
Book a demo
Collector ci-after-command

cicd

Detects sonar-scanner invocations in CI pipelines via a ci-after-command hook. Captures the command string, exit code, and scanner version. Writes to .code_quality.native.sonarqube.cicd.cmds[], mirroring the snyk/cli pattern. Maven (mvn sonar:sonar) and Gradle (gradle sonarqube) launchers are not covered in V1 — those need separate binary-match rules and will be added in follow-up PRs.

sonarqube sonar-scanner ci integration cicd
Book a demo
Collector code

github-app

Detects the SonarCloud (or SonarQube-for-GitHub) PR analysis check on pull requests by querying the GitHub commit status API. Writes check state, context, and target_url to .code_quality.native.sonarqube.github_app, plus a status field set to "complete" on success or "pending" on timeout (mirrors the sibling source.analysis_status and auto.status pattern). Mirrors the snyk/github-app pattern. Only runs on PRs. Requires GH_TOKEN to read commit statuses. Because SonarCloud publishes its status check only after analysis completes, this sub-collector polls /repos/<owner>/<repo>/commits/<sha>/status for up to github_app_poll_timeout_seconds (default 180s) for a SonarCloud status on the PR head SHA. If it never appears, writes .code_quality.native.sonarqube.github_app.status = "pending" and exits cleanly.

sonarqube sonarcloud github app pr check
Book a demo

How Collectors Fit into Lunar

Lunar watches your code and CI/CD systems to collect SDLC data from config files, test results, IaC, deployment configurations, security scans, and more.

Collectors are the automatic data-gathering layer. They extract structured metadata from your repositories and pipelines, feeding it into Lunar's centralized database where guardrails evaluate it to enforce your engineering standards.

Learn How Lunar Works
1
Collectors Gather Data This Integration
Triggered by code changes or CI pipelines, collectors extract metadata from config files, tool outputs, test results, and scans
2
{ } Centralized as JSON
All data merged into each component's unified metadata document
3
Guardrails Enforce Standards
Real-time feedback in PRs and AI workflows

Example Collected Data

This collector writes structured metadata to the Component JSON. Here's an example of the data it produces:

{ } component.json Component JSON
{
  "code_quality": {
    "source": {
      "tool": "sonarqube",
      "integration": "api",
      "project_key": "my-org_my-service",
      "api_url": "https://sonarcloud.io",
      "analysis_status": "complete"
    },
    "passing": true,
    "coverage_percentage": 78.3,
    "duplication_percentage": 3.1,
    "issues": {
      "total": 46,
      "critical": 0,
      "high": 1,
      "medium": 3,
      "low": 42
    },
    "native": {
      "sonarqube": {
        "quality_gate": {
          "status": "OK",
          "conditions_failed": 0
        },
        "ratings": {
          "reliability": "A",
          "security": "B",
          "maintainability": "A",
          "security_review": "A"
        },
        "metrics": {
          "bugs": 3,
          "vulnerabilities": 1,
          "code_smells": 42,
          "lines_of_code": 12500
        },
        "auto": {
          "status": "complete",
          "version": "7.0.0.4796",
          "exit_code": 0,
          "duration_seconds": 47
        },
        "config": {
          "files": ["sonar-project.properties"]
        },
        "cicd": {
          "cmds": [
            {"cmd": "sonar-scanner -Dsonar.projectKey=my-org_my-service", "version": "5.0.1", "exit_code": 0}
          ]
        },
        "github_app": {
          "status": "complete",
          "state": "success",
          "context": "SonarCloud Code Analysis",
          "target_url": "https://sonarcloud.io/dashboard?id=my-org_my-service&pullRequest=42"
        }
      }
    }
  }
}

Configuration

Configure this collector in your lunar-config.yml.

Inputs

Input Required Default Description
project_key Required SonarQube/SonarCloud project key (e.g. `my-org_my-service`). Used by the `api` and `auto` sub-collectors. Optional if the component has a `sonarqube/project-key` meta annotation set by a cataloger.
sonarqube_base_url Optional https://sonarcloud.io SonarQube/SonarCloud API base URL. Defaults to SonarCloud. Set to your SonarQube server URL (e.g. `https://sonar.example.com`) for self-hosted.
api_poll_timeout_seconds Optional 180 Total seconds the `api` and `auto` sub-collectors will wait for a SonarQube analysis matching the current `head_sha` to appear before giving up and emitting `analysis_status: "pending"`. SonarQube's Compute Engine queues analyses asynchronously; results typically publish 10–60s after `sonar-scanner` exits, longer for large projects.
api_poll_interval_seconds Optional 10 Seconds between polls while the `api` and `auto` sub-collectors wait for SonarQube analysis to complete.
auto_scanner_version Optional 7.0.0.4796 Version of the `sonar-scanner` CLI downloaded and executed by the `auto` sub-collector. Pinned for reproducibility. If the collector image already ships with `sonar-scanner` on PATH, this input is ignored.
auto_sources Optional . Source root passed to `sonar-scanner` as `-Dsonar.sources=<value>` by the `auto` sub-collector. Defaults to the repo root. Override for monorepos where only a sub-tree should be analysed.
auto_extra_args Required Extra command-line arguments appended to every `sonar-scanner` invocation (e.g. `-Dsonar.exclusions=**/*.min.js`). Applied by the `auto` sub-collector on both default-branch and PR paths.
github_app_poll_timeout_seconds Optional 180 Total seconds the `github-app` sub-collector will wait for the SonarCloud GitHub check run to appear on the PR head SHA.
github_app_poll_interval_seconds Optional 10 Seconds between polls while the `github-app` sub-collector waits for the SonarCloud check run.

Secrets

This collector requires the following secrets to be configured in Lunar:

Secret Description
SONARQUBE_TOKEN SonarQube/SonarCloud user token. For the read-only `api` sub-collector, `Browse` permission on the target project is sufficient. For `auto`, which runs `sonar-scanner` itself, the token must additionally have `Execute Analysis` permission. Sent as the HTTP Basic username with an empty password, per the SonarQube Web API convention.
GH_TOKEN GitHub token for the GitHub Checks API. Used by the `github-app` sub-collector to read SonarCloud / SonarQube check runs on PRs.

Documentation

View on GitHub

SonarQube Collector

Detect SonarQube/SonarCloud via read-only Web API queries, optional auto-run of sonar-scanner, in-repo config, CI scanner runs, and the GitHub App PR check.

Overview

Five sub-collectors cover SonarQube: api reads an existing analysis via the Web API, auto runs sonar-scanner itself and then reads the result back, config flags in-repo configuration, cicd captures CI scanner runs, and github-app reads the SonarCloud GitHub App PR check. api and auto are alternate paths to the same .code_quality.* data — pick one per context (see Auto-run vs API-read). Both run on default-branch and PR commits by default, branching internally on LUNAR_COMPONENT_PR — narrow runs_on on the import to restrict scope. Tool-agnostic fields land at .code_quality.*; SonarQube-specific structure lives under .code_quality.native.sonarqube. Both self-hosted SonarQube and SonarCloud are supported via sonarqube_base_url.

Collected Data

This collector writes to the following Component JSON paths. The top-level .code_quality.* fields are tool-agnostic and intended for a generic code-quality policy. SonarQube-specific structure (the rating split, quality gate detail, SQALE debt, native metric names, config/CI/GitHub-App payloads) lives under .code_quality.native.sonarqube for SonarQube-aware policies.

Path Type Written by Description
.code_quality.source object api / auto Tool, integration (api or auto), project key, API URL, and analysis_status (complete or pending)
.code_quality.passing bool api / auto Overall pass/fail signal — derived from SonarQube's quality gate status
.code_quality.coverage_percentage number api / auto Line coverage percentage (0–100), if measured
.code_quality.duplication_percentage number api / auto Duplicated lines percentage (0–100), if measured
.code_quality.issues object api / auto Severity buckets: total, critical, high, medium, low (same shape as .sca.vulnerabilities / .sast.findings)
.code_quality.native.sonarqube.quality_gate object api / auto Quality gate status (OK/WARN/ERROR) and failed condition count
.code_quality.native.sonarqube.ratings object api / auto SonarQube letter ratings (A–E) per dimension: reliability, security, maintainability, security review
.code_quality.native.sonarqube.metrics object api / auto SonarQube-native metric names: bugs, vulnerabilities, code smells, lines of code
.code_quality.native.sonarqube.auto object auto Scanner run metadata: version, exit_code, duration_seconds, and status (complete or scanner-failed)
.code_quality.native.sonarqube.config object config Paths to SonarQube config files discovered in the repo
.code_quality.native.sonarqube.cicd object cicd sonar-scanner invocations captured in CI: command, version, exit code
.code_quality.native.sonarqube.github_app object github-app SonarCloud GitHub App PR check: state, context, target_url (from the GitHub commit status API), and status (complete on success, pending on timeout)

Collectors

This integration provides the following sub-collectors. Use include in lunar-config.yml to select a subset.

Collector Hook Description
api code Queries the SonarQube/SonarCloud Web API per-commit. On default-branch commits the query is scoped with branch=<default>; on PR commits with pullRequest=<PR number>. Polls for analysis completion before returning metrics (see Analysis completion & polling). Narrow runs_on on the import to only run on default-branch or only on PRs.
auto code Downloads/invokes sonar-scanner on the checked-out source — with -Dsonar.branch.name=<default> on default-branch commits, or -Dsonar.pullrequest.* on PR commits — then polls the Web API and writes the same fields as api with "integration": "auto". Requires SONARQUBE_TOKEN with Execute Analysis. Exclude when users run sonar-scanner themselves (see Auto-run vs API-read).
config code Detects sonar-project.properties, sonar-maven-plugin, org.sonarqube Gradle plugin, or <SonarQubeEnabled> in .csproj
cicd ci-after-command on sonar-scanner Captures sonar-scanner invocations in CI (mirrors snyk/cli). Maven and Gradle launchers are follow-ups.
github-app code (PRs only) Reads the SonarCloud GitHub App's check run on each PR (mirrors snyk/github-app). Polls for the check run to appear.

Installation

Add to your lunar-config.yml:

collectors:
  - uses: github://earthly/lunar-lib/collectors/sonarqube@v1.0.0
    on: ["domain:your-domain"]
    # with:
    #   project_key: "my-org_my-service"        # Optional — falls back to catalog meta annotation
    #   sonarqube_base_url: "https://sonarcloud.io"  # Or your self-hosted SonarQube URL

Required secrets:

  • SONARQUBE_TOKEN — SonarQube/SonarCloud user token. For read-only api use, Browse permission on the target project is sufficient. For auto, which runs sonar-scanner itself, the token also needs Execute Analysis permission. Sent as the HTTP Basic username with an empty password, per the SonarQube Web API convention.
  • GH_TOKEN — GitHub token with read access to PR check runs (used by the github-app sub-collector).

Project key discovery

The collector resolves the SonarQube project key in this order:

  1. Catalog meta annotation — reads sonarqube/project-key from the component's lunar catalog meta. Set via lunar catalog component --meta sonarqube/project-key <key>, typically invoked by a company-specific cataloger that knows which components map to which SonarQube projects. This is the recommended approach for orgs where each component has its own project.
  2. Explicit project_key input — set in lunar-config.yml for static cases, or when importing the collector multiple times with different on: scopes (e.g. one import per domain, each with its own project).
  3. Neither found — the collector exits cleanly with no data written.

Auto-run vs API-read

Three triggers produce the same .code_quality.* data — pick the one that matches how SonarQube is wired up for your component:

Trigger Sub-collectors to include Sub-collectors to exclude
User runs sonar-scanner in CI api, cicd, github-app auto
Collector auto-runs the scan auto, github-app api, cicd
Read-only (scan happens elsewhere, not in this CI) api auto, cicd

The config sub-collector is orthogonal — it flags whether SonarQube is wired up at all — and is safe to include in every configuration.

Including both api and auto for the same context runs the scanner once and then reads the API twice (wasteful but not harmful); including auto alongside a user-run sonar-scanner in CI double-scans the project, which is more expensive and may hit rate limits on the SonarQube server. A future collector-dependency feature will let auto fire conditionally — only when the cicd sub-collector didn't capture a scan for the current head_sha.

Each of api and auto runs on both default-branch and PR commits by default. Scope narrowing is per sub-collector via the usual platform mechanisms (plugin-level runs_on on a vendored copy, or excluding the sub-collector entirely with exclude: when scoping by domain that only exercises one side).

SonarQube vs SonarCloud

Both SonarQube (self-hosted) and SonarCloud expose the same Web API. Select the instance by overriding sonarqube_base_url:

Instance sonarqube_base_url
SonarCloud (default) https://sonarcloud.io
SonarQube self-hosted e.g. https://sonar.example.com

Tokens are created the same way on both — in the user profile under Security → Generate Tokens.

Analysis completion & polling

SonarQube's Compute Engine queues analyses asynchronously — when a scan finishes uploading (via user-run sonar-scanner or our own auto invocation), the results aren't immediately visible on the Web API. Typical latency is 10–60 seconds; large projects can take several minutes. Both api and auto handle this race by polling api/project_analyses/search until the most recent analysis's revision matches the current head_sha, or until api_poll_timeout_seconds elapses. If the timeout hits, they write .code_quality.source.analysis_status = "pending" with no metrics — policies that care about "is SonarQube wired up at all?" can still read the config sub-collector's output, and policies that want to gate on fresh results can treat analysis_status == "pending" as a skip rather than a fail.

The same principle applies to github-app: the SonarCloud status check on a PR appears only after analysis completes, so the sub-collector polls the GitHub commit status API up to github_app_poll_timeout_seconds and writes .code_quality.native.sonarqube.github_app.status = "complete" on success or "pending" on timeout.

Inputs

Input Default Description
project_key (empty — falls back to catalog meta) SonarQube/SonarCloud project key (e.g. my-org_my-service). Optional if sonarqube/project-key meta annotation is set.
sonarqube_base_url https://sonarcloud.io API base URL. Override for self-hosted SonarQube.
api_poll_timeout_seconds 180 Total seconds api/auto wait for a SonarQube analysis matching head_sha.
api_poll_interval_seconds 10 Seconds between polls while waiting for SonarQube analysis.
auto_scanner_version 7.0.0.4796 Pinned version of sonar-scanner used by auto. Ignored if the collector image already ships with sonar-scanner on PATH.
auto_sources . Value passed to -Dsonar.sources= by auto. Override for monorepos.
auto_extra_args (empty) Extra command-line args appended to every auto sonar-scanner invocation (e.g. -Dsonar.exclusions=**/*.min.js).
github_app_poll_timeout_seconds 180 Total seconds github-app waits for the SonarCloud GitHub check run on the PR head SHA.
github_app_poll_interval_seconds 10 Seconds between polls while waiting for the SonarCloud check run.

Open Source

This collector is open source and available on GitHub. Contribute improvements, report issues, or fork it for your own use.

View Repository

Ready to Automate Your Standards?

See how Lunar can turn your AGENTS.md, engineering wiki, compliance docs, or postmortem action items into automated guardrails with our 200+ built-in guardrails.

Works with any process
check AI agent rules & prompt files
check Post-mortem action items
check Security & compliance policies
check Testing & quality requirements
Automate Now
Paste your AGENTS.md or manual process doc and get guardrails in minutes
Book a Demo