Hamburger Cross Icon
Passing
+
SonarQube Collector

Passing + SonarQube Collector

Guardrail Collector Experimental Testing And Quality

Enforce Passing using data collected by SonarQube Collector. Automatically check testing and quality standards on every PR.

Guardrail: Verifies the scanner's overall pass/fail signal is green. Reads the tool-agnostic `.code_quality.passing` field, derived from the scanner's own quality gate outcome.
Data Source: 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.

How SonarQube Collector Powers This Guardrail

The SonarQube Collector gathers metadata from your code analysis systems. This data flows into Lunar's Component JSON, where the Passing guardrail evaluates it against your standards.

When enabled, this check runs automatically on every PR and in AI coding workflows, providing real-time enforcement with actionable feedback.

1
SonarQube Collector Gathers Data Collector
Extracts metadata from code, configs, and tool outputs
2
{ } Component JSON
Data centralized in structured format for evaluation
3
Passing Checks Guardrail
Pass/fail result with actionable feedback in PRs

Quick Start Configuration

Add both the collector and policy to your lunar-config.yml to enable this guardrail.

📄 lunar-config.yml
# Step 1: Enable the SonarQube Collector
collectors:
  - uses: github://earthly/lunar-lib/collectors/sonarqube@v1.0.5
    # with: ...

# Step 2: Enable the Code Quality Guardrails
policies:
  - uses: github://earthly/lunar-lib/policies/code-quality@v1.0.5
    include: [passing]
    # with: ...

What SonarQube Collector Collects

This collector gathers the following data that the Passing guardrail evaluates.

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.

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.

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.

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.

Example Data Flow

Here's an example of the data that SonarQube Collector writes to the Component JSON, which Passing then evaluates.

{ } component.json From SonarQube Collector
{
  "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 Options

SonarQube Collector 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.

Code Quality Guardrails Inputs

Input Required Default Description
min_severity Optional high Minimum severity to fail on (critical, high, medium, low)
max_total_threshold Required Maximum total issues allowed (must be configured)
min_coverage_percentage Optional 80 Minimum line-coverage percentage (0-100)
max_duplication_percentage Optional 5 Maximum duplicated-lines percentage (0-100)

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