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.
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:
[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
Run the gate locally before automating it
5 guided stepsThe 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
Author the lint job in a GitHub Actions workflow
5 guided stepsLint 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
Add a test job per service so failures point at the culprit
5 guided stepsBeacon 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
Make the checks required with branch protection
5 guided stepsCI 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
You'll walk away with
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