Module 1: From Your Machine To The Pipeline

8. Mini-project: map your manual test steps to a pipeline

Description

This is the module's capstone. So far you've understood the problem ("it works on my machine"), the concept (Continuous Integration), its economics (the feedback loop), its anatomy (the pipeline stages), its purpose (the green main branch), and its boundary with CD. Now you're going to pull all of that together into a concrete and personal exercise: take your own manual flow for testing Reservo —what you do with your hands when you want to know if the suite is green— and map each step to the pipeline stage that would automate it. By the end you'll have, written in your own hand, the blueprint of your first pipeline: what a human does today that a machine will do tomorrow, and why.

You won't write the YAML file yet —that's module 2, and quite deliberately—. You're going to produce something more valuable for arriving at that module: the understanding of what that YAML is going to do and why. A pipeline written without this understanding is a copy-and-paste you wouldn't know how to debug; a pipeline written after this exercise is the automation of a flow you already comprehend step by step. You'll lean on Reservo's real suite, running it for real to see the green CI is going to guard, and on the "it works on my machine" case to identify the environment risk a pipeline erases.

Connection to the module: this lesson introduces no new concepts; it exercises the previous seven on a case that's yours. The map you produce is literally the list of steps you'll write in module 2, only in prose instead of YAML. On closing, you're left with the problem and the concept so clear that module 2's first workflow is going to feel like transcribing something you already know, not like learning something new. That's the goal: entering module 2 with the ground prepared.

The assignment

You're going to deliver an automation file of your Reservo testing flow, with five pieces. Read them in full before starting; then, further down, you have a reference solution to compare yours against (but try yours first: the value is in thinking it through, not in reading it).

  1. Your manual flow, step by step. Write, in order, what you do with your hands when you want to know if Reservo's suite is green on a new machine —from having nothing to deciding if the code is ready—. Be concrete: commands, not vagueness.

  2. The "forgettable" mark. For each step, mark whether it's one a human can forget or do differently each time (running the suite, remembering to install something). Those are the ones that gain most from automation: the machine doesn't forget.

  3. The mapping to the four stages. Assign each step of your flow to one of lesson 5's pipeline stages: checkout, install, test, report. Some step may fall between two; decide and justify.

  4. The "on my machine" risk. Identify the single point of your flow where an environment leak (lesson 2) could make your local green not hold on another machine. Say which of the leaks it is (variable, version, dependency, path, clock, order) and how CI's clean environment erases it.

  5. The justification: what breaks today without CI. Close with a paragraph that answers: if your team keeps this manual flow, without CI, what fails, and when? Use the module's concepts —forgetting, the environment leak, the long feedback loop, the branch that breaks—.

Prepare the ground: run the suite for real

Before writing the map, run Reservo's suite yourself, to have in front of you the green the pipeline is going to guard. With the reservo/ folder and the test_*.py, and pytest installed:

What to expect (the green CI will protect). With Python 3.14.0 and pytest 9.1.1:

============================= test session starts ==============================
platform darwin -- Python 3.14.0, pytest-9.1.1, pluggy-1.6.0
rootdir: /private/tmp/reservo-ci
collected 12 items

test_pricing.py ....                                                     [ 33%]
test_refunds.py .....                                                    [ 75%]
test_scheduling.py ...                                                   [100%]

============================== 12 passed in 0.04s ==============================

That 12 passed is your starting point: the "green" state module 2's pipeline is going to repeat on every push. Your whole file is the plan for that green to stop living only on your machine.

Reference solution

Try your version before opening this. Here's a complete file, for Reservo, to contrast yours against. Your flow may differ in details —maybe you use a different dependency manager—; what matters is that the five pieces are there and that the reasoning holds up.

See the complete reference solution

Piece 1 — The manual flow, step by step

This is what I do with my hands to test Reservo on a new machine:

1. git clone <reservo-repo>             # bring the code
2. cd reservo-ci                        # enter the project
3. python3 -m venv .venv                # create an isolated environment
4. source .venv/bin/activate            # activate it
5. pip install -r requirements.txt      # install pytest and the rest
6. python3 -m pytest                    # run the suite
7. (read the summary: "12 passed" or "N failed")
8. (decide: if green, the code is ready to merge; if red, fix)

Piece 2 — The "forgettable" mark

StepForgettable / variable between runs?Why
1. cloneNo (once per machine)Without code there's nothing; it's not forgotten because without it you don't start
3–5. venv + installYesIt's easy to forget to recreate the venv, or to install with a different version, or to skip the requirements.txt "because I already had it installed"
6. run the suiteYes, the most forgettableIt's the step people skip in a hurry —"it's a small change, surely nothing breaks"— and it's exactly the one that matters
7–8. read and decideYesA tired human can misread the summary, or merge "trusting" without having run step 6

The steps marked "Yes" are the ones automation gains most from: a machine does them always, the same, without tiring (pillar 1 of lesson 3, the forgetting crack).

Piece 3 — The mapping to the four stages

My manual stepPipeline stage
1. git clone / bring the change's commitCheckout
2. cd to the project(part of checkout: positioning yourself in the code)
3–5. create venv, activate, pip install -r requirements.txtInstall (build the clean and reproducible environment)
6. python3 -m pytestTest (the same command CI will run)
7–8. read the summary and decideReport (in CI, automatic: born from the exit code)

A detail I decided: steps 3–4 (create and activate the venv) I put in install, not in a separate stage, because their purpose is preparing the environment where to install. In CI, the runner already starts with an isolated and clean environment, so the install stage is even simpler: there's no need to create a venv, the whole machine is ephemeral.

Piece 4 — The "on my machine" risk

My flow's fragile point is at step 5 (install) and, above all, in what doesn't appear in my flow: I never declared which Python version I use. I run python3 -m pytest with whichever I have installed —today 3.14.0, as the output header shows—. If a teammate has 3.11, or the runner uses 3.12, my local green doesn't guarantee theirs. The leak is the Python version (and, secondarily, the dependencies' version if the requirements.txt isn't pinned).

A second, subtler risk: if at some point the code came to depend on an environment variable —like lesson 2's fragile price_with_env—, my flow would inherit it from my shell without me noticing. I check it by running that case in a clean environment:

$ env -u RESERVO_PRO_DISCOUNT python3 -m pytest test_env_pricing.py
============================== 1 failed in 0.03s ==============================
E       AssertionError: assert 7500 == 6000

There's the leak in action: without the variable, the same test that passes in my shell fails. How CI erases it: the runner starts clean, without my shell variables and with the Python version declared explicitly. It runs "case B" as routine, so any invisible assumption of my environment comes to light as a red in the pipeline —today, in a PR— instead of hiding until someone else's machine or until production.

Piece 5 — The justification: what breaks today without CI

If the team keeps this manual flow, without CI, three things fail, sooner or later:

  • Forgetting. Step 6 —running the suite— depends on someone remembering. On a hurried Friday, someone merges "a small change" without running it. The bug enters main.
  • The environment leak. Even if they do run the suite, they run it on their machine, with their Python version and their variables. Their green doesn't hold for the runner or for the teammate. The pro discount bug can pass in their shell and break elsewhere —"it works on my machine"—.
  • The long loop and the broken branch. When those two cracks come together, no one catches the bug until someone else clones main, or until production. By then the context cooled (lesson 4), the fix costs ten times more, main is broken and stops the whole team, and the "who broke main?" begins.

CI closes the three at once: it runs the suite automatically (kills forgetting), in a clean and declared environment (kills the leak), on every PR before merging (keeps main green and catches the bug while the context is warm). My file above is, literally, the list of steps that pipeline is going to run for me —the same flow, done by a machine that doesn't forget and that starts without my assumptions—.

Criteria for a good delivery

Your file is good if it meets this —it's the "calculation memo" of your first pipeline—:

  • The five pieces are there. Flow, forgettable mark, mapping to stages, environment risk, and justification. Without piece 4 (the "on my machine" risk), the file lies by omission: every manual flow has at least one leak.
  • The flow is concrete. Real commands (git clone, pip install -r requirements.txt, python3 -m pytest), not "I install things and run the tests". Module 2's pipeline is written with commands, so your map must be in commands.
  • The mapping covers the four stages. Each stage —checkout, install, test, report— has at least one of your steps assigned. If one is missing, either your flow is incomplete or you didn't recognize a stage.
  • The risk is named precisely. Not "it could fail on another machine" plainly, but which leak (version, variable, dependency, path, clock, order) and how the clean environment erases it.
  • The justification uses the module's concepts. Forgetting, the environment leak, the feedback loop, the green branch. If you can explain what breaks without CI using those words, you master the module.

Common mistakes

Delivering the flow without piece 4 (the environment risk). What happens: someone lists their steps, maps them to stages neatly, and omits identifying where their flow depends on the environment —because "on my machine everything works"—. The file looks complete but is missing exactly what gives CI meaning. Why it happens: the environment leak is invisible from inside your own machine, which is where everything works. How to spot it: if your file names not a single leak, it's not that you have none; it's that you didn't look for it. How to fix it: every manual flow has at least one leak —start with the most common, the Python version, which your own pytest output gives away in the platform ... -- Python X header—. Check it by running something in a clean environment (env -u, or thinking about which version the runner uses).

Mapping the steps but not marking the "forgettable" ones. What happens: someone does the mapping to stages correctly but skips piece 2, so they don't distinguish the steps a human does reliably from the ones they forget. They lose the central argument for why to automate. Why it happens: the mapping feels like "the answer", and the forgettable mark seems like an extra. How to spot it: if you couldn't say which of your steps is the one people skip most, you're missing piece 2. How to fix it: ask yourself, step by step, "could someone in a hurry on a Friday skip this or do it differently?". The step that most often answers "yes" —running the suite— is CI's number-one reason for existing. Automating is, above all, taking the forgettable steps away from the human.

Putting deployment in the file. What happens: someone, excited, adds a final step "deploy Reservo to production" to their flow. But Reservo has no app to deploy, and deployment is CD, outside the module's boundary (lesson 7). The file goes out of scope. Why it happens: "CI/CD" invites you to think of deployment as the natural ending. How to spot it: if your flow ends in "deploy" or "publish", you crossed into CD. How to fix it: your file is about the CI of the tests: it ends in reporting the verdict (green/red) and deciding the merge, not in deploying. The green branch is this module's goal; what's done with that green branch afterward (CD) is another path, and with Reservo there isn't even anywhere to demonstrate it.

Exercises

Exercise 1 — The step that matters most to automate. From your manual flow, choose the single step that, if you had to automate only one, you'd automate first, and defend the choice with two arguments from the module. Then say which crack of a local green that automated step closes.

See solution

The step to automate first is running the suite (python3 -m pytest), step 6. Two arguments:

  1. It's the most forgettable and the most important at once. It's the step people skip in a hurry —"it's a small change"— and it's exactly the one that decides whether the code works. Automating it guarantees the suite is run always, not when someone remembers.
  2. It's the heart of the feedback loop (lesson 4). Running it automatically on every push is what makes a failure's warning arrive at step 3, early and with the context warm, instead of in production.

The crack it closes: the forgetting one (pillar 1 of lesson 3). The machine runs the suite on every change without depending on a human remembering.

(Note: automating "run the suite" drags installing the environment along with it, because without an environment there's no suite; that's why in practice the first pipeline does checkout + install + test together. But the motive for setting it up is step 6.)

Exercise 2 — Reproduce your own leak. Take the environment risk you identified in piece 4 and design a one-line experiment that demonstrates it on your machine —that makes your local green turn red by simulating CI's clean environment—. Describe it and predict the result. (Hint: lesson 2 used env -u VARIABLE to remove a variable; what would you use for your leak?)

See solution

It depends on your leak. Examples of one-line experiments that simulate the clean environment:

  • Environment variable leak: env -u RESERVO_PRO_DISCOUNT python3 -m pytest. Removes the variable from your shell for that run, as the clean runner would. Prediction: if some test depended on it, it goes from green to red (we saw it: assert 7500 == 6000).
  • Python version leak: run the suite with another version, for example python3.11 -m pytest (if you have it installed) or inside a container with the runner's version. Prediction: if the code uses something that doesn't exist in that version, red; if not, green —and then that specific leak doesn't apply—.
  • Undeclared dependency leak: create a clean venv, install only what requirements.txt says, and run. Prediction: if the code imported a package you had "by coincidence" but that isn't declared, red with an ImportError.

The lesson: reproducing the leak on your own machine —by taking the invisible assumption away from your shell— is the cheapest way to verify it exists, and it's exactly the technique module 3 develops to reproduce CI failures. If you can turn your green red with one line, you just found what CI would have found for you.

Exercise 3 — From prose to YAML (preview). Without writing YAML (that's module 2), predict: when in module 2 you translate your file to a GitHub Actions workflow, how many steps will your flow correspond to, approximately, and what will each one do? List them in one sentence each.

See solution

Your file will translate, approximately, to these workflow steps (the exact names and syntax are module 2's; here just the what):

  1. Checkout: bring the code of the commit that triggered the pipeline to the clean machine. (Corresponds to your git clone.)
  2. Set up Python: put on the machine the declared Python version —here your version leak from piece 4 is closed—.
  3. Install dependencies: pip install -r requirements.txt on the clean machine. (Your step 5; the venv is no longer needed because the whole machine is ephemeral.)
  4. Run the suite: python -m pytest, the same command as your step 6, the heart of the pipeline.
  5. Report: this isn't a step you write; GitHub does it on its own, reading pytest's exit code to paint the check green or red (your steps 7–8, automatic).

In other words: about four written steps (checkout, set up Python, install, test) plus the automatic report. Notice how neat: your file in prose already is the workflow, it just lacks the syntax. That's why you did this exercise before the YAML —so module 2 is transcribing something you already understand, not learning something new—.

The lesson: there's almost a one-to-one correspondence between the manual steps you mapped and the steps of your first workflow. Module 1 gave you the blueprint; module 2 gives you the syntax to build it.

Summary and next step

In this mini-project you pulled the module's seven concepts together into a file that's yours: you took your manual flow for testing Reservo —clone, create the environment, install, run pytest, read, decide— and mapped it to the four pipeline stages —checkout, install, test, report—, marking the forgettable steps that gain most from automation, precisely naming your flow's "on my machine" risk (starting with the Python version your own output gives away), and justifying what breaks today without CI: forgetting, the environment leak, the long loop, and the broken branch. You ran the suite for real to see the 12 passed the pipeline is going to guard, and reproduced a leak to verify that your local green isn't universal.

The most important thing you take away is this: your file in prose already is your first pipeline, it just lacks the syntax. There's almost a one-to-one correspondence between the steps you mapped and the steps you'll write in module 2. That's the exercise's purpose: entering module 2 with the problem and the concept so clear that writing the YAML is transcribing something you already know.

With this you close module 1. You know why CI exists (the "it works on my machine" problem), what it is (running the suite automatically, cleanly, and shared on every change), why it's worth it (the feedback loop), how it works inside (the stages and the exit code), what it protects (the green branch), and where it ends (the boundary with CD). What's next is making it real: in module 2 you write your first GitHub Actions workflow —the YAML file that declares the stages you just mapped— and watch it run your Reservo suite on every push, with its real log. The blueprint is ready; time to build.

Resources