CODEOWNERS
CODEOWNERS is the one ownership file every pull request already consults. If the org graph is
right and CODEOWNERS is wrong, the graph is decoration — reviewers still get routed by whatever
someone typed months ago.
teamapi generate codeowners /path/to/your/org --out ./codeowners --org acme
What it writes#
One file per repository, at the path it belongs to, so the output can be copied straight across:
codeowners/acme/checkout-api/CODEOWNERS
codeowners/acme/ledger/CODEOWNERS
codeowners/acme/onboarding-api/CODEOWNERS
codeowners/acme/payments-api/CODEOWNERS
Each one says who owns the repository and why:
# Generated by TeamAPI — edit the team's teamapi.yml, not this file.
# Owner: Stream Checkout (stream-checkout)
# Because it owns: checkout-api
* @acme/stream-checkout
Two ways to name the owner#
--org acme |
@acme/stream-checkout — the same team slug teamapi apply provisions, so the two commands agree |
no --org |
the members' githubUsername handles, which needs no org setup but drifts as people move |
Prefer --org. A team slug survives someone changing teams; a list of handles does not.
The index is inverted#
Team API is written per team. CODEOWNERS lives per repository. Going from one to the other means inverting the index, and that exposes a question the per-team view can hide: what if two teams declare services in the same repository?
There is no correct CODEOWNERS for that repo, so none is written:
! acme/checkout-api is claimed by platform-payments and stream-checkout — no CODEOWNERS written
The command exits non-zero on a conflict, unlike the other generate targets, because the output
is incomplete and shipping it would quietly hand the repo to whichever team the tool happened to
sort first. Fix the ownership in teamapi.yml and run it again.
Limits#
- Only the root
*rule. Team API models which team owns a service, not which directories inside a repository belong to whom. A path-level rule would be invented. - Services with no
repositoryare skipped, and named in the output. Nothing to write a file for. - Non-GitHub URLs still work as long as they end in
owner/repo— GitLab uses the same file format — but the@org/teamform is GitHub-specific.
Suggested loop#
- Declare
services[].repositoryon the team that owns each service. - Run
teamapi apply --org acmeso the GitHub teams the owner slugs point at exist. - Run
teamapi generate codeowners --org acmeon merge and open a pull request per repository. - Let a conflict fail the build. It means two teams think they own the same code.