Back to path
BeginnerBeacon · Project 2 of 12 ~4h· 4 milestones

Put every change behind CI

Continues from the last build: Releases are now scripted and repeatable, but nothing stops a broken commit from reaching main; you only learn it is broken after you deploy.

Last rung you turned the copy-pasted release into a tidy runbook, so shipping Beacon is now repeatable.

GitHub Actions workflow authoringCI matrix and multi-job pipelinesPython linting with ruff and blackRunning pytest in CIDependency caching in CIBranch protection and required status checks

What you'll build

You will have a working GitHub Actions CI pipeline that runs on every pull request to main: a lint job (ruff plus black in check mode over the whole monorepo) and two parallel test jobs, one for the api service and one for the worker service, each on Python 3.12 with cached dependencies. The status checks surface on every PR, and branch protection makes all three required so no broken commit can reach main without a human override. Beacon now has an automated quality gate guarding trunk for the first time.

See how we teach, before you sign up

You don't just get code dumped on you. Every starter file and every solution is explained line-by-line, in plain English. Here's one real file from this project:

pyproject.tomltoml
[tool.black]
line-length = 100
target-version = ["py312"]

[tool.ruff]
line-length = 100
target-version = "py312"
src = ["api", "worker", "libs"]

[tool.ruff.lint]
select = ["E", "F", "I"]
ignore = ["E501"]

Reading this file

  • line-length = 100black and ruff agree on width; keep them identical or they will fight each other in CI.
  • target-version = "py312"Pins the syntax level to Python 3.12, the same version the CI jobs set up.
  • src = ["api", "worker", "libs"]Tells ruff where first-party code lives so import sorting (the I rules) groups Beacon imports correctly.
  • select = ["E", "F", "I"]E and F catch errors and undefined names; I sorts imports. A small, fast rule set for a first CI.

Repo-wide tool config so the same lint rules apply locally and in CI. CI runs these tools with no extra flags, so this file is the single source of truth for style.

That's 1 of 4 explained code blocks in this single project.

The build, milestone by milestone

  1. 1

    Run the gate locally before automating it

    5 guided steps

    The number one reason a first CI is painful is that people write the workflow and the failing tests at the same time, then cannot tell whether the pipeline or the code is broken. Establishing a green baseline locally makes the workflow a transcription job, not a debugging session.

  2. 2

    Author the lint job in a GitHub Actions workflow

    5 guided steps

    Lint is fast and catches a whole class of trivial mistakes before any test even starts. Putting it in CI means style and obvious errors are enforced by the machine, not argued about in code review.

  3. 3

    Add a test job per service so failures point at the culprit

    5 guided steps

    Beacon is a monorepo with two deployables. One combined test job would still catch the failure, but a developer staring at the PR wants to know it is the worker that broke, not the api. Per-service jobs turn the status check list itself into a diagnosis.

  4. 4

    Make the checks required with branch protection

    5 guided steps

    CI only guards trunk if the platform refuses to merge red. Without required checks, a developer can merge over a failing pipeline and the broken-commit problem from the scenario returns. Branch protection is what converts a signal into an enforced rule.

What's inside when you start

2 starter files, ready to clone
4 guided milestones
2 full reference solutions
4 code blocks explained line-by-line
4 "is it working?" checks
3 interview questions it prepares you for

You'll walk away with

A .github/workflows/ci.yml that triggers on pull_request to main with three jobs: lint, test-api, and test-worker
A lint job running ruff check . and black --check . over the whole monorepo on Python 3.12 with pip caching
Two parallel pytest jobs, one scoped to api/tests and one to worker/tests, each installing only its own service dev deps
A branch protection rule on main requiring a pull request and all three status checks to pass before merge
A short note in the repo (README or runbook) explaining that main is now gated by CI and how to read a red check

This is portfolio-grade. Build it free.

Sign up to unlock every milestone step-by-step, the code skeletons, full reference solutions, and checkable tasks, with your progress saved as you build.

Start building