Chapter 19Lesson 05~190 minutes

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.

CI/CDGitHub ActionsGitLab CIJenkinsQuality Gate

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:

  1. Broken-order run: scan before coverage.xml exists, then generate coverage too late.
  2. Repaired local run: same source revision, correct test/report→scan order, preserve ceTaskId and gate evidence.
  3. 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 push to main (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_TOKEN comes only from secrets.SONAR_TOKEN; host URL comes from a variable.
  • .scannerwork/report-task.txt is preserved after scan.
  • Evidence upload uses if: always().
Network fallback. A GitHub-hosted runner cannot reach a SonarQube instance bound only to your workstation's 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

Recommended packet layout
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?

What does ceTaskId prove?

Compute Engine is SUCCESS but the scanner wait-mode exit is nonzero. What should you inspect?

Why is a localhost SonarQube instance usually unreachable from a GitHub-hosted runner?

What is the minimum credential cleanup proof?

Next lesson — Next chapter

SonarQube for IDE Connected Mode and Developer Feedback Loops

Chapter 20 moves SonarQube feedback into the IDE while preserving server profile, project, and CI authority boundaries.

Official references and version notes

Version and compatibility note

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.

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