How to Mock a CI Pipeline with Build, Test, and Deploy Stages in Python

Simulate a three-stage CI pipeline (build, test, deploy) in Python with random pass/fail logic, early exit on failure, and measured stage durations.

Easy Python 3.9+ Aug 9, 2026 Production deployment patterns 14 views 0 copies

Python code

43 lines
Python 3.9+
import time
import random
from dataclasses import dataclass


@dataclass
class StageResult:
    name: str
    status: str
    duration: float


def run_stage(name: str, success_chance: float = 0.9) -> StageResult:
    """Simulate a pipeline stage with random success/failure."""
    start = time.time()
    time.sleep(random.uniform(0.05, 0.2))  # Simulate work
    status = "passed" if random.random() < success_chance else "failed"
    return StageResult(name, status, round(time.time() - start, 2))


def run_pipeline(build_chance: float = 0.95, test_chance: float = 0.9,
                 deploy_chance: float = 0.85) -> list[StageResult]:
    """Execute the CI pipeline: build -> test -> deploy (mock)."""
    results = []
    build = run_stage("build", build_chance)
    results.append(build)
    if build.status == "failed":
        return results  # Stop pipeline on build failure

    tests = run_stage("test", test_chance)
    results.append(tests)
    if tests.status == "failed":
        return results  # Stop pipeline on test failure

    deploy = run_stage("deploy", deploy_chance)
    results.append(deploy)
    return results


if __name__ == "__main__":
    random.seed(42)  # Deterministic output for demo
    for result in run_pipeline():
        print(f"{result.name}: {result.status} ({result.duration}s)")

Output

stdout
build: passed (0.07s)
test: failed (0.12s)

How it works

The @dataclass decorator automatically creates an __init__ and string representation for StageResult, keeping the model clean. run_stage uses time.sleep to mimic work and random.random with a success-chance threshold to determine status. run_pipeline chains stages and short-circuits by returning early when a stage fails, mirroring real CI behavior. Time is recorded with time.time() and rounded to two decimals for readable output. The random.seed(42) call makes the demo reproducible.

Common mistakes

  • Forgetting that early return stops all subsequent stages — deploy never runs if tests fail.
  • Using `time.sleep` in production code — it's only for simulation here.
  • Not rounding durations, producing noisy floats instead of clean output.
  • Assuming deterministic results without calling `random.seed`.

Variations

  1. Replace `random.random` with a mocked external API call to simulate real CI runners.
  2. Use `asyncio` with `await asyncio.sleep` for concurrent mock stages.

Real-world use cases

  • Predicting pipeline behavior and failure rates before provisioning real CI infrastructure.
  • Unit-testing deployment automation logic that must handle stage failures gracefully.
  • Demoing CI/CD concepts in trainings or talks without needing a live build system.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Production deployment patterns

Related tutorials and quizzes for this topic.