Professional Collaboration on GitHub
Protect a branch, require review, and make the pipeline the thing that decides whether code can merge.
- Time
- 45 min
- Level
- Intermediate
- Objectives
- 4 objectives
- Cost
- Free
Before you start
You will need
- A GitHub repository you own
- git 2.30+
You will be able to
- Configure branch protection that CI actually enforces
- Route review automatically with CODEOWNERS
- Write commit messages a release process can read
Cost — Free
— a GitHub account.
Success criteria
0 of 4
The scenario#
Trunk-based development only works when main is always releasable. That is not a matter of discipline — it is a matter of configuration.
This lab makes the repository enforce it.
1. A check for the branch to depend on#
# .github/workflows/ci.yml
name: CI
on:
pull_request:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
- run: npm ci
- run: npm testBranch protection can only require a check that has run at least once, so merge this first.
2. Protect the branch#
Settings → Branches → Add rule, pattern main:
- Require a pull request before merging — no direct pushes.
- Require approvals: 1 — for a solo repository this can be 0; keep the PR requirement regardless, because it is what makes the checks run.
- Require status checks to pass — select
test. - Require branches to be up to date before merging.
- Do not allow bypassing the above settings — including for administrators.
Prove it:
git switch main
echo "direct" >> README.md && git commit -qam "direct push"
git push origin main
# ! [remote rejected] main -> main (protected branch hook declined)"Up to date before merging" is the one worth understanding. Without it, two
PRs that each pass independently can break main together — one renames a
function, the other adds a caller. Neither PR ever saw the other. With it, the
branch must be rebased onto current main and the checks re-run against the
combination that will actually exist.
3. CODEOWNERS#
# .github/CODEOWNERS
* @Waleeddarwesh
/infrastructure/ @Waleeddarwesh
/content/learn/ @Waleeddarwesh
*.tf @WaleeddarweshLast matching pattern wins — the opposite of .gitignore. Combined with
"Require review from Code Owners", a change to /infrastructure/ cannot merge
without the person responsible for it seeing it.
4. Commit messages a machine can read#
feat(labs): add incident tier
fix(ci): accept GitHub's immutable OIDC subject
docs(readme): correct the lab countConventional Commits are worth adopting for one concrete reason: the prefix
drives release automation. fix produces a patch release, feat a minor, and
! or a BREAKING CHANGE: footer a major. The changelog writes itself.
git log --oneline --grep '^feat' | head5. The pull request#
git switch -c fix/typo
# ... change, commit, push ...
gh pr create --fill # or open it in the browserTwo habits that make review useful rather than ceremonial:
- Say what a reader can now do that they could not before. A diff shows what changed; only you can say why.
- Keep it small. A 40-line PR gets a real review. A 2,000-line PR gets "LGTM", which is not a review.
When it goes wrong#
The status check cannot be selected in branch protection
It has never run. Merge the workflow first, or open one PR so GitHub learns the check name.
Protection does not apply to you
Enable 'Do not allow bypassing'. Administrators are exempt by default.
CODEOWNERS is ignored
It must be on the default branch, in .github/, docs/ or the root, and the owner needs write access.
PRs stay blocked after checks pass
'Up to date' is on and main moved. Rebase and let the checks re-run.
The concept behind it
Phase complete · 01 Foundations
You can now: You can administer a server, diagnose a connection, and recover work you thought you had destroyed.
Next phase
Lab 8 of 58 on the project path