Chapter 25Lesson 05240–300 min

Checkpoint Lab — CI/CD Integration with GitHub Actions, GitLab CI, and Jenkins

Prove a complete CI/CD operating contract locally and with one concrete GitHub Actions job: force a Robot failure, retain evidence while the gate stays non-zero, repair the expectation, and verify a clean run with a version manifest and provider mappings for GitLab and Jenkins.

Checkpoint labGitHub ActionsFailure injectionArtifact retentionVersion manifestOperator evidence

Learning objectives

  • Build and predict the state transitions of a local CI simulation before running it.
  • Demonstrate that an intentional Robot failure produces a non-zero process status and retains native/xUnit evidence.
  • Run the repaired contract to a clean gate without deleting or overwriting the original failed evidence.
  • Create a current GitHub Actions job and map its semantics concisely to GitLab CI/CD and Jenkins.
  • Deliver an evidence packet and a short operator runbook that another engineer can reproduce.
Checkpoint safety

Use only the synthetic suite in this lesson. Keep the fake token fake. Do not point the exercise at a browser cloud, database, SSH server, API, deployment environment, production repository secret, or real customer system.

1. Checkpoint architecture

Failure-preserving CI checkpoint
flowchart TD
A[Local or CI trigger] --> B[requirements-ci.txt]
B --> C[tools/ci_run.py]
C --> D[Robot 7.4.2 suite]
D --> E[exit code]
D --> F[output.xml / log / report / xUnit]
C --> G[runtime manifest + exit-code.txt]
E --> H[Gate]
F --> I[Immutable run evidence]
G --> I
J[Fake provider secret] -. presence only .-> C
K[GitHub Actions wrapper] -. same command .-> C
L[GitLab / Jenkins mapping] -. same contract .-> C

The checkpoint has one intentional source of failure: EXPECTED_GATE. Everything else should be deterministic. Failed-run evidence and repaired-run evidence must be stored in different directories or copied before the second execution so the repair cannot overwrite the diagnosis.

2. Preflight and exact assumptions

Component Checkpoint baseline Reason
Robot Framework 7.4.2 Stable course baseline
Python 3.13 example runtime Same interpreter generation in local/GitHub/GitLab examples; re-check your environment
Pabot not required; 5.2.2 optional Parallelism is outside the mandatory checkpoint
GitHub Actions checkout/setup-python/upload-artifact major v7 Current action generation at 2026-08-31
GitLab current artifacts + JUnit report syntax Provider mapping only
Jenkins Declarative Pipeline + core archiveArtifacts + JUnit step Provider mapping only
External SUT none Synthetic local gate only
python --version
python -m pip --version
python -m robot --version
python -c "from pathlib import Path; print('repo=', Path.cwd())"

3. Build the checkpoint files

Use the same requirements-ci.txt, suites/ci_contract.robot, and tools/ci_run.py from Lesson 2. Add one tiny operator note:

# RUNBOOK.md
1. Run from repository root with the pinned virtual environment.
2. Never use a real credential; RF_FAKE_TOKEN is lab-only.
3. FAIL exercise: RF_EXPECTED_GATE=FAIL -> expect non-zero.
4. Copy artifacts/robot to artifacts/evidence-fail before repair.
5. PASS exercise: unset RF_EXPECTED_GATE -> expect zero.
6. Copy artifacts/robot to artifacts/evidence-pass.
7. Compare exit-code.txt, output.xml, xunit.xml, and runtime-manifest.txt.
8. Do not delete the failed packet until review is complete.

4. Predict before execution

Write down at least these two predictions before running anything:

Prediction Expected mutation Independent verification
Forced failure Robot test Release Gate Matches Expected State becomes FAIL; process exits non-zero shell status + exit-code.txt + output.xml/xunit.xml
Evidence retention HTML/XML/manifest files exist even though the gate is closed filesystem listing and open log.html
Fake secret handling manifest says the variable is present but contains no value grep/search evidence packet for the fake token value
Repair same source/runtime with default expected PASS exits zero pass packet + version manifest comparison

5. Execute the controlled failure and preserve it

# Bash/zsh
export RF_FAKE_TOKEN='fake-training-token'
export RF_EXPECTED_GATE='FAIL'
python tools/ci_run.py
rc=$?
echo "failed-run-exit=$rc"

mkdir -p artifacts/evidence-fail
cp -R artifacts/robot/. artifacts/evidence-fail/

# Windows PowerShell:
# $env:RF_FAKE_TOKEN='fake-training-token'
# $env:RF_EXPECTED_GATE='FAIL'
# python .	ools\ci_run.py
# $rc=$LASTEXITCODE
# New-Item -ItemType Directory -Force .rtifacts\evidence-fail | Out-Null
# Copy-Item .rtifacts
obot\* .rtifacts\evidence-fail\ -Recurse -Force

Expected: exit code 1 and a complete failed evidence packet. If the process is zero, stop and repair status propagation before continuing. If the process is non-zero but no Robot evidence exists, distinguish “Robot ran and failed” from “Robot never started/imported.”

6. Verify the failed packet independently

python -c "from pathlib import Path; p=Path('artifacts/evidence-fail'); print(sorted(x.name for x in p.iterdir()))"
python -c "from pathlib import Path; print(Path('artifacts/evidence-fail/exit-code.txt').read_text().strip())"

# Safe secret-leak check using the known fake training value.
python -c "from pathlib import Path; token='fake-training-token'; hits=[str(p) for p in Path('artifacts/evidence-fail').glob('*') if p.is_file() and token in p.read_text(errors='ignore')]; print('secret_hits=', hits)"

The expected secret_hits list is empty. Open log.html and confirm the failure reason is the synthetic mismatch, not a setup/import/environment error.

7. Repair without erasing first-failure evidence

# Bash/zsh
unset RF_EXPECTED_GATE
python tools/ci_run.py
rc=$?
echo "repaired-run-exit=$rc"
mkdir -p artifacts/evidence-pass
cp -R artifacts/robot/. artifacts/evidence-pass/

# PowerShell:
# Remove-Item Env:RF_EXPECTED_GATE -ErrorAction SilentlyContinue
# python .	ools\ci_run.py
# $rc=$LASTEXITCODE
# New-Item -ItemType Directory -Force .rtifacts\evidence-pass | Out-Null
# Copy-Item .rtifacts
obot\* .rtifacts\evidence-pass\ -Recurse -Force

Expected repaired status: 0. Compare the two manifests to ensure the interpreter/platform stayed the same. The important change should be the controlled expectation and resulting Robot status—not an unexplained dependency or target change.

8. Concrete provider job: GitHub Actions

name: robot-checkpoint

on:
  workflow_dispatch:
  pull_request:

permissions:
  contents: read

jobs:
  robot:
    runs-on: ubuntu-latest
    env:
      RF_FAKE_TOKEN: ${{ secrets.RF_FAKE_TOKEN }}
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-python@v7
        with:
          python-version: '3.13'

      - name: Install
        run: python -m pip install --requirement requirements-ci.txt

      - name: Robot gate
        run: python tools/ci_run.py

      - name: Retain evidence on every outcome
        if: always()
        uses: actions/upload-artifact@v7
        with:
          name: robot-${{ github.run_id }}-${{ github.job }}
          path: artifacts/robot/
          if-no-files-found: warn

For a failure-path proof in a disposable branch, temporarily set a non-secret environment input such as RF_EXPECTED_GATE=FAIL at the job level, run once, preserve the artifact, then remove that injection and rerun. Do not use a real credential or modify production branch protection merely to demonstrate failure.

9. Concise mappings for GitLab CI/CD and Jenkins

Invariant GitLab CI/CD Jenkins
Same command script: python tools/ci_run.py sh 'python tools/ci_run.py' (or Windows equivalent)
Failure remains non-zero default script semantics shell step fails stage on non-zero
Artifacts after failure artifacts: when: always post { always { archiveArtifacts ... } }
xUnit surface artifacts:reports:junit junit Pipeline step
Fake secret lab CI/CD variable lab credential binding or literal simulation only
Run identity pipeline/job variables in artifact naming build number/job identity in archive context

10. Required evidence packet

  • The exact provider-neutral command and dependency manifest.
  • Python/Robot version manifest and repository/run identity.
  • Failed run process exit code and preserved output.xml, log.html, report.html, xunit.xml.
  • Proof that the fake secret value is absent from evidence.
  • Repaired run with exit code 0 and comparable version manifest.
  • One concrete GitHub Actions job plus concise GitLab/Jenkins mapping.
  • The operator RUNBOOK.md describing failure, preservation, repair, and cleanup.

11. Cleanup and rollback

After review, delete only the disposable lab directory or its generated artifacts/ subtree. If you created a provider secret containing the fake token, remove that lab secret. Do not delete repository history, real CI credentials, or unrelated run artifacts. If the exercise was performed on a branch, revert only the intentional failure injection and keep the useful provider-neutral runner if it is being adopted.

12. Verification checklist

  • ☐ Exactly the same python tools/ci_run.py command works locally and in the concrete CI job.
  • ☐ Forced Robot failure closes the gate with a non-zero status.
  • ☐ Failed-run evidence remains available and is not overwritten by the repaired run.
  • ☐ output.xml, log/report, xUnit, runtime manifest, and exit code are present where expected.
  • ☐ No real credential was used; fake value is absent from retained logs/artifacts.
  • ☐ Repaired run exits zero without changing unrelated versions or target state.
  • ☐ GitLab/Jenkins mappings preserve the same status/evidence contract.

Knowledge check

The forced-failure run exits 0 but xUnit clearly shows a failed Robot test. What must be repaired before this checkpoint can pass?

Why copy the failed packet before executing the repaired run?

GitHub uploads artifacts with always(). Does that make the job successful?

Which file is the richest Robot-native source for later post-processing: xUnit or output.xml?

A team proposes both a 10-job matrix and pabot --processes 8 immediately. What should the runbook require first?

Chapter 25 completion and bridge to Chapter 26

This chapter adds a production CI/CD operating contract to the Robot Framework model: reproducible command, pinned runtime, explicit target/input boundaries, authentic process status, provider-independent evidence, secret minimization, and failure-path artifact retention. Chapter 26 builds on that contract by moving the runtime into containers and examining image provenance, filesystem mounts, users, networking, result volumes, and distributed services without assuming containers automatically create isolation.

Next lesson

Containers, Reproducible Runtimes, and Distributed Test Environments: Core Concepts and Mental Model

Continue with Containers, Reproducible Runtimes, and Distributed Test Environments: Core Concepts and Mental Model. It builds directly on the state, evidence, and operating assumptions established here, so carry those constraints forward rather than treating the next page as an isolated topic.

Current primary references

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
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.