Chapter 07Lesson 03~125 minutes

Scanner Ecosystem: CLI, Maven, Gradle, .NET, and Build Integration: Configuration, Design Patterns, and Trade-Offs

Choose scanner architecture deliberately by balancing native build context, CI portability, runtime provisioning, version governance, least privilege, and rollback.

Design trade-offsVersion pinningCIJRE provisioningBuild integration

Learning objectives

  • Compare standalone CLI with Maven, Gradle, and .NET build-integrated scanners.
  • Choose local versus CI execution without confusing orchestration with analysis semantics.
  • Choose JRE auto-provisioning versus managed system Java based on evidence and network policy.
  • Compare centrally managed and repository-pinned scanner versions.
  • Justify a scanner design with edition/version prerequisites and observable rollback evidence.

1. Scanner ecosystem decision matrix

Choice Strength Risk Evidence to require
Standalone CLI Simple for build-neutral repositories Manual build context/scoping burden base dir, indexed files, config, CLI/runtime version
Maven scanner Native reactor and Java build context plugin/version drift through implicit prefix POM, Maven/plugin version, target classes, task
Gradle scanner Native project/source-set integration task ordering and Gradle/plugin compatibility wrapper/plugin version, task graph, class dirs
.NET scanner Correct MSBuild/dotnet lifecycle capture begin/build/end can be split/misordered SDK/scanner version, begin/build/end logs, assemblies
Local execution Fast developer feedback environment drift and secret handling toolchain manifest, local token scope
CI execution repeatable shared policy/evidence runner/cache/network coupling pinned toolchain, job artifacts, task ID

2. Pin scanner versions where reproducibility matters

Implicit plugin resolution is convenient but can move beneath a stable pipeline. For Maven, production CI can invoke the full coordinate with an explicit scanner plugin version. Gradle can pin org.sonarqube in the plugins block or version catalog. .NET global tools can be pinned by version during installation or managed by a tool manifest. Scanner CLI can be installed from a versioned distribution with checksum verification.

Central management is still useful: organizations may publish a tested scanner baseline and automated upgrade policy. The design failure is not “central” or “local”; it is unreviewed drift with no provenance or rollback.

3. Auto-provisioned JRE versus system Java

Auto-provisioning reduces the need to synchronize scanner Java across every runner. It also introduces a download/cache/network/trust dependency that must be observable. Highly controlled or disconnected environments may disable provisioning and provide an approved Java runtime instead. That choice increases responsibility: current scanner Java requirements must be tracked and upgraded intentionally.

Do not conflate: Java used to compile the project, Java used to launch Maven/Gradle, and Java used by the scanner engine can be different runtime evidence.

4. Local versus CI execution

Moving analysis into CI does not change scanner semantics. CI adds checkout depth, workspace paths, caches, secret stores, network policy, artifact retention, and job status. A scanner that works locally can fail in CI because the checkout lacks blame history, the build output was not produced in that job, a cache is unwritable, or the server certificate/token is unavailable. Diagnose those as orchestration boundaries, not analyzer defects.

5. Test and coverage reports are imported evidence, not generated magically

Build-integrated scanners know where many build artifacts live, but coverage still comes from the appropriate coverage tool. For Java that may be JaCoCo XML; for .NET the format depends on the chosen test/coverage tooling. Record the command that produced the report, the report path, and its timestamp before analysis. A scanner warning that a report is missing is not fixed by inventing a path to a stale file.

6. .NET design pattern: preserve the lifecycle envelope

dotnet tool install --global dotnet-sonarscanner --version 11.2.1.137242
export SONAR_TOKEN='<process-secret>'
dotnet sonarscanner begin /k:"academy-dotnet" /d:sonar.host.url="http://127.0.0.1:9000" /d:sonar.token="$SONAR_TOKEN"
dotnet build Demo.sln --no-incremental
dotnet sonarscanner end /d:sonar.token="$SONAR_TOKEN"

The command illustrates lifecycle placement; the raw token must come from a protected process/secret store. Do not commit the example marker as a real credential. The build between begin/end is part of analysis capture.

7. Gradle design pattern: build context plus explicit task relationship

plugins {
    java
    id("org.sonarqube") version "7.3.1.8318"
}

Current documentation requires Java bytecode for Java analysis. Make the build/analysis ordering explicit in your pipeline rather than assuming the sonar task compiled everything you need. Record the wrapper/Gradle version, plugin version, Java runtime, and produced class directories.

8. Worked architecture choice

Scenario Recommended path Why Rollback
Small Python repo, no native build Pinned CLI Explicit filesystem scope is sufficient revert scanner/config version
Multi-module Maven Java service Pinned Maven scanner reactor/bytecode/build properties already exist revert plugin version + build change
Gradle Kotlin/Java monorepo Pinned Gradle scanner source sets/tasks/build outputs are native Gradle state revert plugin/config commit
.NET solution Pinned .NET scanner begin/build/end is analysis contract restore prior tool version + pipeline envelope

9. Edition and product boundaries

Scanner choice is generally independent of Community Build versus commercial Server editions: the same client ecosystem can target different SonarQube Server offerings subject to current compatibility and feature rules. Branch/PR decoration, advanced security, portfolio/application, and Data Center capabilities are separate edition/server concerns. SonarQube Cloud is a distinct hosted product; do not teach its defaults as if they were a self-managed Server deployment.

Knowledge check

When is central scanner management a problem?

What new dependency does JRE auto-provisioning introduce?

Does CI make Scanner CLI equivalent to Maven scanner?

Who generates a coverage report?

What makes .NET scanner placement non-negotiable?

Next lesson

Diagnose scanner-layer failures without erasing evidence

Lesson 4 breaks lifecycle, bytecode, working-directory, and version assumptions and uses the smallest causal repair.

Official references and version notes

Version and compatibility note

Rechecked on 2026-09-07. The chapter targets local/private Community Build 26.9.0.129388. Current release baselines used for reproducible examples are Scanner CLI 8.1.0.6389, SonarScanner for Maven 5.7.0.6970, SonarScanner for Gradle 7.3.1.8318, and SonarScanner for .NET 11.2.1.137242. Mandatory execution uses CLI plus Maven; Gradle and .NET are optional executable extensions when their build prerequisites are installed. JRE auto-provisioning is kept enabled unless a lesson explicitly demonstrates the system-Java boundary. Re-check current scanner/server compatibility before future runs because scanner release trains move independently.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.