Lesson 5 of 9 · about 15 minutes
CI/CD and branches
A robot reviewer that tests every branch before it reaches main, and a robot publisher that ships main when it changes.
pytest on every pull request, see a pull request go red and then green, and understand how main becomes a deployment.Why integrate continuously?
Martin Fowler's definition: "Continuous Integration is a software development practice where each member of a team merges their changes into a codebase together with their colleagues changes at least daily. Each of these integrations is verified by an automated build (including test) to detect integration errors as quickly as possible."1 GitHub makes the same case: "Committing code more often detects errors sooner and reduces the amount of code a developer needs to debug."2
Two patterns from Fowler's branching essay give the vocabulary:3
- Mainline: "A single, shared, branch that acts as the current state of the product." For us, that's
main. - Healthy branch: "On each commit, perform automated checks, usually building and running tests, to ensure there are no defects on the branch."
His key point about branches: "Frequent integration increases the frequency of merges but reduces their complexity and risk."3 That's why the course keeps saying short-lived branches: a one-task branch merged within a day is easy to test and easy to merge.
How a workflow attaches to branches
A workflow is a YAML file in .github/workflows/. This one is the minimal Python CI from GitHub's tutorial,4 with the actions bumped to their current major versions (checkout@v7, setup-python@v7):12
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main] # run after anything lands on main
pull_request:
branches: [main] # run on every PR whose BASE is main
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: "3.12"
- run: pip install -r requirements.txt
- run: pytest
Three details trip people up:
- The
pull_requestfilter matches the base branch, not your branch name.branches: [main]means "pull requests that targetmain."5 Any feature branch you open a PR from is covered. - CI tests the merged result. For a PR, the workflow checks out
refs/pull/N/merge, a simulated merge of your branch intomain. "Your CI tests run against the merged result, not just the head branch alone."6 A green check means "this will still work after merging," not just "my branch works." - Conflicts stop CI. "Workflows will not run on
pull_requestactivity if the pull request has a merge conflict."6 Fix the conflict first (Lesson 3).
Each job reports a status check that appears on the PR's Checks tab and next to the merge button.7
Do it for real: red, then green
Back in penguins-lab. main now has clean.py and test_clean.py from Lesson 4.
-
$ git switch main $ git pull $ git switch -c add-ci -
pandas pytest -
Wait for the$ git add .github requirements.txt $ git commit -m "Add pytest CI workflow" $ git push -u origin add-ci $ gh pr create --fill $ gh pr checks --watchtestcheck to pass. It takes a minute or two. -
return df.reset_index(drop=True) # bug: no dropna
The check goes red. Open the PR in the browser ($ git commit -am "Simulate a bad change" $ git push $ gh pr checks --watchgh pr view --web) and findassert 3 == 2in the job log. This is exactly what CI does when an agent's change breaks something. -
$ git restore --source=HEAD~1 clean.py # bring back the good version $ git commit -am "Restore dropna" $ git push $ gh pr checks --watch -
The$ gh pr merge --squash --delete-branch $ git switch main $ git pullpush: branches: [main]trigger now runs CI once more onmainitself. Check the repo's Actions tab.
Making green mandatory: branch protection
Right now a red PR can still be merged. Branch protection closes that gap. With "require status checks before merging," required checks "must have a successful, skipped, or neutral status before collaborators can make changes to a protected branch."8 You can also require approving reviews, so changes reach main only "via a pull request that is approved by the required number of reviewers."8 Newer repositories can do the same with rulesets, "a named list of rules" that can stack on one branch.9
On GitHub Free, protected branches and rulesets work in public repositories only.89 A private student repo on Free can't enforce them, so you have to enforce the habit yourself: never merge on red.
One gotcha: if a required check's workflow is skipped by a branch or path filter, its check stays "Pending," and the PR "will be blocked from merging."5 Keep the triggers of required workflows broad.
The CD half: deploying from main
GitHub's definition: "Continuous deployment (CD) is the practice of using automation to publish and deploy software updates. As part of the typical CD process, the code is automatically built and tested before deployment." A CD workflow often runs "when new code is pushed to the default branch."10
main; CD ships whatever is on main. Branches are where the risky work happens.The site you're reading is deployed this way. Here is a trimmed version of its Pages workflow (the full files are in the course repository):
name: Deploy Pages
on:
push:
branches: [main] # only main deploys; PRs never do
workflow_dispatch:
permissions:
contents: read
pages: write # required by deploy-pages
id-token: write # required by deploy-pages
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/configure-pages@v6
- uses: actions/upload-pages-artifact@v5
with:
path: .
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@v5
The deploy job "must have a minimum of pages: write and id-token: write permissions" and runs in the github-pages environment.11 GitHub's Pages docs also note the deploy step "is skipped if the workflow was triggered by a pull request."11 Branches stay previews of work; only main goes live.
Why this matters for agentic coding
When Claude Code pushes commits to an agent branch, CI is a reviewer that never gets tired. It re-runs your tests on every push and puts a red or green mark on the PR before you have read a line. Your job shifts to reading the diff and the check results. The agent's job is to make the check green. In Lesson 6 you'll tell Claude to do exactly that.
Check yourself
Watch the current branch's pull request checks in the terminal until they finish.
A workflow has on: pull_request: branches: [main]. You open a PR from add-ci into main. Does it run?
- Yes, the filter matches the base
- No, the filter matches the head
- Only after the PR gets merged
- Only if add-ci is renamed main
For pull_request, branches filters on the branch the PR targets: the base.
What code does CI test for a pull request, by default?
- Your branch merged into main
- Your branch alone, without main
- Main alone, without your branch
- Only files you changed today
actions/checkout checks out refs/pull/N/merge, so tests run "against the merged result, not just the head branch alone."
Your private repo is on GitHub Free. Can you require green checks before merging?
- No; Free protects public repos only
- Yes; every plan protects every repo
- Yes, but only for draft PRs
- No; Actions never runs on private
Protected branches and rulesets are available for public repositories on GitHub Free; private repositories need Pro, Team, or Enterprise.
In this course's Pages workflow, what triggers a deployment?
- A push landing on main
- Opening any new pull request
- Pushing to any feature branch
- Running pytest on your laptop
CD runs on push to main. Pull requests get CI, not deployments.
gh pr checks and summarise the error."
Want more practice before moving on? Paste the branch practice tutor prompt into Claude. It drills everything from Lessons 1 to 5, including red and green checks on real pull requests, before you hand the keyboard to an agent in Lesson 6.
Sources
- Martin Fowler, Continuous Integration (2024).
- GitHub Docs, Continuous integration.
- Martin Fowler, Patterns for Managing Source Code Branches (mainline, healthy branch, integration frequency).
- GitHub Docs, Building and testing Python.
- GitHub Docs, Workflow syntax: on.pull_request.branches.
- GitHub Docs, Events that trigger workflows: pull_request.
- GitHub Docs, Status checks.
- GitHub Docs, About protected branches.
- GitHub Docs, About rulesets.
- GitHub Docs, Continuous deployment.
- GitHub Docs, Using custom workflows with GitHub Pages and Configuring a publishing source.
- Release pages: actions/checkout, actions/setup-python, configure-pages, upload-pages-artifact, deploy-pages (checked October 2026).