Checkpoint Lab — CI Integration with GitHub Actions, GitLab CI, Jenkins, and Other Runners
Produce a provider-neutral CI launcher and one GitHub Actions configuration that preserves build, report, ceTaskId, Compute Engine, Quality Gate, CI, and cleanup evidence across one deliberate ordering failure.
Learning objectives
- Build and execute a provider-neutral CI analysis launcher with pinned/recorded tool versions.
- Translate the launcher contract into one GitHub Actions workflow using immutable action revisions and CI secrets.
- Predict source/report/task/gate/CI state changes before execution and verify them independently.
- Reproduce one deliberate pipeline-order failure at the same Git revision, preserve its evidence, then repair only the ordering defect.
-
Assemble a checkpoint evidence packet covering runner/toolchain,
checkout depth, build/test outputs, scanner/runtime/cache, secret
source, effective parameters,
ceTaskId, gate wait/result, CI exit, and retained artifacts. - Clean up/revoke the disposable credential without deleting unrelated SonarQube/CI state.
1. Checkpoint scenario
You own a synthetic Python repository called
sq-ch19-ci-lab. The production requirement is simple:
tests and coverage must run before SonarQube analysis; the exact
analysis task and gate must be auditable; CI must fail when the gate
is enforced; and first-failure artifacts must survive an ephemeral
runner.
You will execute three controlled paths:
-
Broken-order run: scan before
coverage.xmlexists, then generate coverage too late. -
Repaired local run: same source revision, correct
test/report→scan order, preserve
ceTaskIdand gate evidence. - GitHub Actions design/execution: pinned official actions, secret injection, full checkout, gate action, always-retained evidence. If your local SonarQube cannot be reached by a hosted runner, validate the YAML/design faithfully and execute the provider-neutral local launcher instead.
2. Exact assumptions and resource/credential preflight
| Component | Checkpoint assumption |
|---|---|
| SonarQube | Community Build 26.9.0.129388; current commercial references 2026 Release 4.1 and 2026.1.5 LTA are informational only |
| Scanner | SonarScanner CLI 8.1.0.6389 |
| GitHub scan action |
v8.2.1 / commit
22918119ff8e1ca75a623e15c8296b6ea4fbe28f
|
| GitHub gate action |
v1.2.0 / commit
cf038b0e0cdecfa9e56c198bbb7d21d751d62c3b
|
| Jenkins optional reference |
SonarQube Scanner plugin 2.11+; webhook-backed
waitForQualityGate
|
| Python | 3.13 in GitHub example; pytest 9.1.1; pytest-cov 7.1.0 |
| Database/plugin/identity | No database/plugin/IdP mutation is required |
| Credential | Disposable SonarQube project-analysis token; provider secret store for GitHub execution |
| Edition | Community Build/main-branch mandatory path; commercial branch/PR capability is not required |
printf 'revision='; git rev-parse HEAD
printf 'shallow='; git rev-parse --is-shallow-repository
python --version
python -m pip freeze | grep -E '^(pytest|pytest-cov)==' || true
sonar-scanner --version
curl -fsS "$SONAR_HOST_URL/api/system/status"
[ -n "${SONAR_TOKEN:-}" ] && echo 'SONAR_TOKEN=set' || echo 'SONAR_TOKEN=missing'
3. Write predictions before execution
Record at least four predictions; the first two satisfy the required minimum:
- Prediction A — report population: the broken-order scan will not import the coverage report because it does not exist at scanner time, even though the file is created later in the same CI job.
- Prediction B — repaired analysis: with the same Git SHA and same project/profile/gate, generating coverage first will make scanner evidence show the report import path and produce a different coverage measure.
-
Prediction C — asynchronous identity: every
submitted analysis will create its own
ceTaskId; scanner exit and Compute Engine/gate state must be checked independently. - Prediction D — CI policy: explicit gate enforcement can make the CI stage fail on an ERROR gate even if the scanner/report upload itself succeeded.
- Prediction E — security: neither local logs nor GitHub artifacts will contain the token value; evidence records only that the credential source was present.
4. Execute the deliberate broken-order run
rm -rf evidence-checkpoint .scannerwork coverage.xml
mkdir -p evidence-checkpoint/broken
git rev-parse HEAD | tee evidence-checkpoint/broken/revision.txt
set +e
sonar-scanner -X 2>&1 | tee evidence-checkpoint/broken/scanner.log
BROKEN_RC=${PIPESTATUS[0]}
set -e
printf 'scanner_exit=%s\n' "$BROKEN_RC" \
| tee evidence-checkpoint/broken/final-state.txt
if [ -f .scannerwork/report-task.txt ]; then
cp .scannerwork/report-task.txt evidence-checkpoint/broken/report-task.txt
sed -n 's/^ceTaskId=/ceTaskId=/p' .scannerwork/report-task.txt \
| tee evidence-checkpoint/broken/ce-task-id.txt
fi
# Deliberately too late for the submitted analysis:
python -m pytest -q --junitxml=evidence-checkpoint/broken/junit.xml \
--cov=src --cov-report=xml:coverage.xml \
2>&1 | tee evidence-checkpoint/broken/test-after-scan.log
cp coverage.xml evidence-checkpoint/broken/coverage-after-scan.xml
Do not delete the broken scanner log. Mark the warning/report
evidence that proves coverage.xml was unavailable at
scan time.
5. Repair only pipeline order and rerun the same revision
rm -rf .scannerwork coverage.xml
mkdir -p evidence-checkpoint/repaired
git rev-parse HEAD | tee evidence-checkpoint/repaired/revision.txt
python -m pytest -q --junitxml=evidence-checkpoint/repaired/junit.xml \
--cov=src --cov-report=term --cov-report=xml:coverage.xml \
2>&1 | tee evidence-checkpoint/repaired/test.log
test -s coverage.xml
cp coverage.xml evidence-checkpoint/repaired/coverage.xml
set +e
sonar-scanner -X -Dsonar.qualitygate.wait=true -Dsonar.qualitygate.timeout=300 \
2>&1 | tee evidence-checkpoint/repaired/scanner.log
REPAIRED_RC=${PIPESTATUS[0]}
set -e
printf 'scanner_wait_exit=%s\n' "$REPAIRED_RC" \
| tee evidence-checkpoint/repaired/final-state.txt
if [ -f .scannerwork/report-task.txt ]; then
cp .scannerwork/report-task.txt evidence-checkpoint/repaired/report-task.txt
sed -n 's/^ceTaskId=/ceTaskId=/p' .scannerwork/report-task.txt \
| tee evidence-checkpoint/repaired/ce-task-id.txt
fi
Verify that broken/revision.txt and
repaired/revision.txt are identical. If they are not,
the experiment no longer isolates pipeline order.
6. Required GitHub Actions configuration
Create .github/workflows/sonarqube.yml using the pinned
workflow from Lesson 2. The acceptance criteria are:
-
Community Build trigger is
pushtomain(plus optional manual dispatch), not PR analysis. - Checkout uses full history and a reviewed immutable action revision.
- Python/test dependencies are pinned/recorded.
- Coverage is created and validated before the scan action.
- SonarSource scan action and Quality Gate Action are pinned to the reviewed SHAs recorded in assumptions.
-
SONAR_TOKENcomes only fromsecrets.SONAR_TOKEN; host URL comes from a variable. -
.scannerwork/report-task.txtis preserved after scan. - Evidence upload uses
if: always().
localhost. For mandatory completion, execute the local
launcher and review the GitHub YAML. Execute GitHub Actions only
with an authorized self-hosted runner or safely reachable disposable
SonarQube endpoint.
7. Independent verification checklist
- ☐ Runner/toolchain versions and action/plugin assumptions are recorded.
- ☐ Checkout SHA and shallow/full status are recorded.
- ☐ Test exit, JUnit output, and coverage producer/report exist before the repaired scan.
- ☐ Broken-order scanner log proves the report was absent/unimported at scanner time.
- ☐ Broken and repaired runs use the same source revision and project key.
-
☐ Each submitted run has its own
report-task.txt/ceTaskId. - ☐ Compute Engine state is checked independently from scanner exit.
- ☐ Quality Gate state is checked independently from Compute Engine success.
- ☐ CI/gate-wait exit is recorded separately from gate state.
- ☐ No evidence file contains the token value.
- ☐ GitHub actions are pinned to reviewed immutable revisions.
- ☐ Artifact retention is configured for failure paths.
8. Required evidence packet
chapter19-evidence/
assumptions.md
toolchain.txt
requirements-ci.txt
launcher/
ci-run-analysis.sh
sha256.txt
broken/
revision.txt
scanner.log
report-task.txt # if upload occurred
ce-task-id.txt # if upload occurred
test-after-scan.log
coverage-after-scan.xml
final-state.txt
repaired/
revision.txt
test.log
junit.xml
coverage.xml
scanner.log
report-task.txt
ce-task-id.txt
ce-task.json # after independent query
gate.json # after CE completion
final-state.txt
github/
sonarqube.yml
action-pins.txt
run-url-or-simulation-note.txt
credential-lifecycle.md
assumptions-and-limitations.md
The packet must state the secret source and token name/expiry if known, but never the token value.
9. Required diagnosis question
The repaired scanner exits nonzero because
sonar.qualitygate.wait=true observed an ERROR gate, but
ce-task.json shows Compute Engine SUCCESS.
Which layer failed?
Answer: analysis processing succeeded. The project Quality Gate evaluated to ERROR, and explicit CI enforcement translated that policy result into a nonzero scanner/job exit. Do not restart SonarQube, delete the project, or lower the gate as troubleshooting.
10. Cleanup and rollback
- Revoke the disposable project-analysis token and verify it no longer authenticates.
- Delete the disposable SonarQube project only if it was created solely for this lab and after evidence export.
-
Remove local
.venv,.scannerwork, coverage/test outputs, and fixture directory after preserving the evidence packet. - If a GitHub repository secret was created only for the lab, remove it after the run; do not print it first.
- Do not delete shared Jenkins credentials, global SonarQube integrations, provider runners, databases, Docker volumes, or unrelated CI caches.
11. What Chapter 19 adds to the operating model
Chapter 19 turns CI from a black box into an evidence-producing
control: source revision, build/test reports, scanner runtime/cache,
credential source, report upload, ceTaskId, Compute
Engine, Quality Gate, CI decision, and artifacts are individually
observable. That makes failures diagnosable and prevents “green
pipeline” from becoming a vague substitute for source-quality
evidence.
Chapter 20 moves the feedback loop earlier into SonarQube for IDE Connected Mode and Developer Feedback Loops, where local IDE findings must remain consistent with server profiles and project context without replacing CI/server analysis.
Knowledge check
Why must broken and repaired runs use the same source revision?
So the observed coverage/import difference can be attributed to pipeline ordering rather than a code change.
What does ceTaskId prove?
It identifies the exact SonarQube Compute Engine background task created by that uploaded analysis report.
Compute Engine is SUCCESS but the scanner wait-mode exit is nonzero. What should you inspect?
The Quality Gate result and explicit wait/enforcement semantics before treating the server as unhealthy.
Why is a localhost SonarQube instance usually unreachable from a GitHub-hosted runner?
The runner is a different machine/network namespace. Use an authorized self-hosted runner or safely reachable disposable endpoint; do not expose your workstation casually.
What is the minimum credential cleanup proof?
Record revocation/removal of the disposable token/secret and verify the revoked SonarQube token no longer authenticates, without retaining the secret value.
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.
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.