CI Integration with GitHub Actions, GitLab CI, Jenkins, and Other Runners: Core Concepts and Mental Model
Model CI analysis as a sequence of independently observable checkout, build/test/report, scanner, Compute Engine, Quality Gate, CI-status, and evidence-retention states.
Learning objectives
- Separate CI runner state, checkout state, build/test evidence, scanner state, asynchronous Compute Engine state, Quality Gate state, and CI job/check state.
- Explain why a green build step, a successful scanner process, a successful Compute Engine task, and a passing Quality Gate are different facts.
- Identify the current Community Build CI path for GitHub Actions, GitLab CI/CD, Jenkins, and generic runners without implying commercial branch/PR capabilities.
-
Inspect runner image/toolchain, checkout depth, coverage/test
reports, scanner/runtime/cache, secret source, effective analysis
parameters,
ceTaskId, gate result, and retained artifacts before changing a pipeline. -
Explain when
sonar.qualitygate.wait=trueis appropriate and why it changes CI waiting/failure semantics rather than analysis semantics. - Define which caches are performance aids and which files are first-failure evidence that must never be silently reused as analysis truth.
1. The practical problem: “CI passed” is too ambiguous
Chapter 18 separated provider credentials from SonarQube analysis credentials. Chapter 19 moves inside the runner. A pipeline can say success even though no coverage report existed, the scanner uploaded a report whose Compute Engine task later failed, the Quality Gate failed after the job ended, or provider decoration never happened. The reverse can also occur: tests and analysis can be correct while a runner, artifact upload, or provider API step fails.
The engineering goal is therefore not “put SonarQube in CI.” It is to create a reproducible stage in which every important state has an owner and durable evidence:
clean runner/workspace → exact checkout SHA + full-enough history
→ build/test/coverage artifacts → scanner + effective parameters +
secret token → report upload → report-task.txt / ceTaskId →
Compute Engine terminal state → Quality Gate → CI job/check
decision → retained evidence
2. Mental model: CI is an evidence conveyor, not one command
flowchart TD R[Runner image / toolchain] --> C[Checkout SHA + SCM history] C --> B[Build + tests] B --> A[Coverage / test / build reports] A --> S[Sonar scanner] T[SONAR_TOKEN secret] --> S S --> U[Upload analysis report] U --> Q[Compute Engine task / ceTaskId] Q --> G[Quality Gate] G --> J[CI job / check decision] B --> E[Retained CI artifacts] S --> E Q --> E G --> E
Every arrow answers a different question. The test runner owns
whether tests executed. The coverage producer owns whether
coverage.xml exists and corresponds to the checked-out
revision. The scanner owns file indexing and report upload.
SonarQube Compute Engine owns background processing. The project
gate owns the pass/fail policy. The CI system owns whether that
result fails the job. An artifact store owns how long the evidence
remains accessible.
3. State map: capture before mutation
Runner/toolchain
Runner OS/image, CPU/architecture, Python/Java/build tool versions, scanner/action/plugin version, network path to SonarQube, and available utilities.
Checkout
Repository, commit SHA, branch/event, shallow/full status, merge-base/target data where licensed, submodules, generated files, and workspace root.
Build/test reports
Build exit status, test count/result, coverage producer/version, report path, report timestamp, and whether the report maps to the current source tree.
Scanner
Scanner family/version, JRE/runtime,
SONAR_USER_HOME, cache path, project key/base
directory, effective parameters, indexed files, upload status.
Server/policy
ceTaskId, Compute Engine state, analysis ID,
profile/gate/new-code context, Quality Gate status, and server
edition/version.
CI/governance
Secret source, job exit status, gate enforcement mechanism, artifact retention, concurrency controls, action/plugin provenance, and cleanup/revocation history.
4. Current version baseline and CI integration assumptions
fetch-depth: 0. Jenkins
documentation requires SonarQube Scanner plugin 2.11+ and a
SonarQube→Jenkins webhook for waitForQualityGate.
Community Build can run analysis inside all of these CI systems, but it does not acquire commercial multi-branch or pull/merge-request analysis merely because the runner exposes branch metadata. In Community Build, keep the executable provider examples on the main branch. Chapter 17 already provided the faithful branch/PR simulation boundary.
5. Checkout depth is analysis input, not a Git cosmetic
SCM blame, changed-code calculation, and branch/PR comparison depend on Git history. Current SonarSource GitHub and GitLab guidance explicitly disables shallow checkout for analysis. A shallow workspace can produce warnings such as missing blame or missing refs, and it can invalidate later reasoning about New Code.
git rev-parse HEAD
git rev-parse --is-shallow-repository
git status --short
git log -1 --decorate --oneline
# GitHub Actions: current SonarSource examples use a full checkout.
# with:
# fetch-depth: 0
# GitLab CI/CD: current SonarSource examples set:
# GIT_DEPTH: "0"
The correct fix is to fetch enough history, not to disable SCM analysis simply to silence the warning.
6. Build/test/report order: produce evidence before the scanner consumes it
SonarQube does not run your unit tests. A CI pipeline must first
execute the build/test tool and create coverage or other external
reports. The scanner then imports those files. If
coverage.xml is created after the scan, a later
artifact upload cannot retroactively change the analysis that was
already submitted.
| Stage | Owned state | Evidence | Wrong shortcut |
|---|---|---|---|
| Build | Compiled/generated program state where required | Build log, binaries, compiler version | Assume scanner compiles the project. |
| Test | Executed test cases | Test exit status, JUnit/xUnit output | Run scanner first and tests later. |
| Coverage producer | Covered executable lines/branches | Coverage XML at a known path | Expect SonarQube to generate coverage. |
| Scanner | Indexed files + imported reports + analysis report | Scanner log, report-task.txt |
Ignore warnings about missing report paths. |
7. Cache the expensive downloads—not the analysis truth
The scanner cache under SONAR_USER_HOME is a
performance cache for analyzer/scanner downloads. Reusing it is
normally safe when the key and permissions are controlled. The
project-local .scannerwork directory is different: it
contains ephemeral state for one analysis, including
report-task.txt. Do not restore an old
.scannerwork directory from CI cache and mistake its
task ID for the current run.
-
Cache:
.sonar/cacheor the configured user-home cache. -
Do not cache as reusable truth:
.scannerwork,coverage.xml, build outputs that were not rebuilt for the current SHA, oldreport-task.txt. - Archive evidence after the run; do not feed archived evidence into the next run unless the workflow explicitly validates provenance.
8. Secret path: CI secret store → environment → scanner
The scanner token belongs in the provider secret store or Jenkins
credential store. It must not appear literally in YAML, committed
properties, shell history, or retained logs. Current GitHub and
GitLab guidance uses the conventional
SONAR_TOKEN secret/variable and
SONAR_HOST_URL variable.
# Safe inspection prints presence, never the value.
if [ -n "${SONAR_TOKEN:-}" ]; then
echo "SONAR_TOKEN=set"
else
echo "SONAR_TOKEN=missing" >&2
exit 2
fi
printf 'SONAR_HOST_URL=%s\n' "$SONAR_HOST_URL"
# Avoid: set -x around commands that could expand credentials.
# Avoid: echo "$SONAR_TOKEN"
# Avoid: sonar-scanner -Dsonar.token="$SONAR_TOKEN" when environment auth works.
9. Scanner success, Compute Engine success, and Quality Gate are separate
By default, a scanner can finish after report upload while SonarQube
continues processing asynchronously. Preserve
.scannerwork/report-task.txt; its
ceTaskId is the durable bridge from runner evidence to
the server background task.
cat .scannerwork/report-task.txt
CE_TASK_ID="$(sed -n 's/^ceTaskId=//p' .scannerwork/report-task.txt)"
printf 'ceTaskId=%s\n' "$CE_TASK_ID"
# A later evidence collector can query /api/ce/task?id=$CE_TASK_ID,
# then query the Quality Gate only after the CE task is terminal.
If the CI system must fail immediately on gate failure, current
SonarQube supports explicit waiting. For generic CI, set
sonar.qualitygate.wait=true;
sonar.qualitygate.timeout defaults to 300 seconds. This
makes the scanner job poll and fail when the gate fails. It does not
mean the scanner itself computed the gate.
10. Provider patterns: same evidence contract, different orchestration
| CI | Current Sonar path | Gate enforcement | Evidence detail |
|---|---|---|---|
| GitHub Actions | Official SonarQube Scan Action or maintained build-system scanner | Quality Gate Check Action or scanner wait |
Use SONAR_TOKEN secret,
SONAR_HOST_URL variable, full checkout;
Community Build main only.
|
| GitLab CI/CD | Direct/build-system scanner in a Docker-executor job |
sonar.qualitygate.wait=true when required
|
GIT_DEPTH: "0",
SONAR_USER_HOME cache, protected/masked CI
variables.
|
| Jenkins |
SonarQube Scanner plugin + withSonarQubeEnv
|
waitForQualityGate via SonarQube webhook |
Webhook to /sonarqube-webhook/ is mandatory for
pipeline pause; webhook secret is recommended.
|
| Other runners | Direct supported scanner |
sonar.qualitygate.wait=true or explicit CE/gate
API logic
|
Preserve logs/task IDs and use CI-native secret/artifact stores. |
11. Read-only CI preflight
printf 'revision='; git rev-parse HEAD
printf 'shallow='; git rev-parse --is-shallow-repository
printf 'workspace=%s\n' "$PWD"
python --version
sonar-scanner --version
[ -f coverage.xml ] && ls -l coverage.xml || echo 'coverage.xml: absent before scan'
[ -n "${SONAR_TOKEN:-}" ] && echo 'SONAR_TOKEN=set' || echo 'SONAR_TOKEN=missing'
curl -fsS "$SONAR_HOST_URL/api/system/status"
This inspection changes no project policy. It proves whether the runner is even capable of producing a trustworthy analysis before scanner execution.
Knowledge check
The scanner exits 0, but the Quality Gate later fails. Did the scanner lie?
No. Without explicit gate waiting, scanner success primarily proves scanner/report-upload success. Compute Engine and the gate are asynchronous server states.
Why is .scannerwork a bad cross-run cache?
It contains per-analysis ephemeral state such as
report-task.txt. Restoring it can mix task identity
across revisions/runs.
What is wrong with generating coverage.xml after
the scanner stage?
The submitted analysis already imported—or failed to import—the coverage input. A later file cannot retroactively modify that report.
Why use fetch-depth: 0 or
GIT_DEPTH: "0"?
To give analysis sufficient SCM history for blame and code-comparison semantics rather than relying on a shallow checkout.
Where should SONAR_TOKEN come from in CI?
From the CI/Jenkins secret store as an environment credential with the narrowest practical SonarQube scope—not from committed YAML or properties.
Official references and version notes
-
SonarQube Community Build — CI integration overview
— current gate-wait mechanisms, Jenkins/GitHub/Bitbucket options,
sonar.qualitygate.wait, and 300-second default timeout. -
Adding analysis to GitHub Actions
— current Scan Action v8 guidance,
SONAR_TOKEN/SONAR_HOST_URL, Community Build main-only workflow, and full-history checkout recommendation. - SonarQube Scan Action v8.2.1 — current action release used/pinned in the teaching workflow.
-
SonarQube Quality Gate Check Action v1.2.0
— current gate-action release; default scanner metadata file is
.scannerwork/report-task.txt. -
Adding analysis to GitLab CI/CD
— Docker executor,
GIT_DEPTH: "0",SONAR_USER_HOMEcache, CI variables, and gate wait guidance. - Jenkins integration key features — SonarQube Scanner plugin behavior and Quality Gate integration.
-
Jenkins pipeline pause
—
withSonarQubeEnv, mandatory/sonarqube-webhook/, andwaitForQualityGate. - Verifying code checkout — full SCM history and shallow-clone failure guidance.
- SonarScanner CLI 8.1.0.6389 — scanner baseline.
- SonarQube release announcements — Community Build 26.9.0.129388, Server 2026 Release 4.1, and 2026 Release 1.5 LTA.
Rechecked 2026-09-08. Mandatory labs target SonarQube Community
Build 26.9.0.129388 and SonarScanner CLI
8.1.0.6389. The GitHub example pins SonarQube
Scan Action v8.2.1 to commit
22918119ff8e1ca75a623e15c8296b6ea4fbe28f, Quality
Gate Check Action v1.2.0 to
cf038b0e0cdecfa9e56c198bbb7d21d751d62c3b, checkout
v6.1.0 to d23441a48e516b6c34aea4fa41551a30e30af803,
setup-python v6.2.0 to
a309ff8b426b58ec0e2a45f0f869d46889d02405, and
upload-artifact v4.6.2 to
ea165f8d65b6e75b540449e92b4886f43607fa02. Current
SonarSource Community Build guidance restricts GitHub analysis to
the main branch and recommends full Git history; GitLab examples
use GIT_DEPTH: "0". Jenkins requires the SonarQube
Scanner plugin (2.11+ per current docs) and a SonarQube webhook
for waitForQualityGate. Recheck action/plugin
versions, runner requirements, scanner/JRE behavior, provider
deprecations, and edition boundaries before copying the examples
into another release.
.scannerwork/report-task.txt and its
ceTaskId; runner status, scanner exit, Compute Engine,
Quality Gate, and provider/CI status remain separate states.
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.