Repository navigation
chore: fix CODEOWNERS again, and make the next break loud - #22
Conversation
guks-trl
left a comment
There was a problem hiding this comment.
The rename itself is right and should go in: @hongjoonahn-tri returns 404, @hongjoonahn-trl exists, and all three owners have admin access. GitHub's own CODEOWNERS check agrees. GET /repos/trillion-labs/trida-stack/codeowners/errors lists 12 "Unknown owner" errors on main and none on this branch.
I'd change the guard before merging, though. GitHub already exposes exactly this check through that endpoint, and it covers more than the script does:
- Owners without write access. CODEOWNERS also silently ignores an owner who exists but can't write to the repo. The script can't see that, because
/users/{handle}returns 200 for them. The errors endpoint reports both cases. - GitHub's own parser. The endpoint reads the file the way GitHub does. The script relies on a hand-written regex, which matters for email owners and trailing comments (see inline).
- About 90 lines less code:
- name: CODEOWNERS has no errors
env:
GH_TOKEN: ${{ github.token }}
run: |
errs=$(gh api "repos/${{ github.repository }}/codeowners/errors?ref=${{ github.sha }}")
echo "$errs" | jq -r '.errors[].message'
test "$(echo "$errs" | jq '.errors | length')" -eq 0The step then needs no setup-python.
This also needs a schedule. A rename doesn't create a commit, so a check that only runs on push and PR won't fire when the third rename happens; it'll fire on whichever unrelated PR comes next. A daily cron (stale-unanswered.yml and supply-chain.yml already have one) would catch it the day it happens.
The description's claim of "no error, no warning, no annotation" also isn't accurate. GitHub shows the error banner when you view CODEOWNERS on the web, and the endpoint above reports it. Worth fixing in the docstring and the PR text, since that's what points to the simpler fix.
| @@ -29,6 +29,16 @@ jobs: | |||
|
|
|||
| # A code of conduct whose report address does not work is not operational evidence. | |||
There was a problem hiding this comment.
These two comment lines belong to the "Reporting routes are still stated" step below. The insertion split them off, so they now sit above setup-python. Move them back down.
| python-version: "3.10" | ||
| # CODEOWNERS ignores unknown handles silently. Two owners renamed in one week and | ||
| # both breakages were found by accident; this makes the third one loud. | ||
| - name: CODEOWNERS handles resolve |
There was a problem hiding this comment.
Suggest replacing this step with GitHub's codeowners/errors endpoint (snippet in the review summary). It also catches owners who exist but lack write access, which /users/{handle} can't see. Separately, consider adding schedule: to this workflow's on:. A rename isn't a commit, so push/PR triggers won't notice it.
| #!/usr/bin/env python3 | ||
| """Verify every handle in CODEOWNERS still resolves. | ||
|
|
||
| CODEOWNERS ignores unknown handles silently -- no error, no warning, no annotation. |
There was a problem hiding this comment.
Not quite: GitHub shows a CODEOWNERS error banner in the web file view, and GET /repos/{owner}/{repo}/codeowners/errors returns the errors as JSON. Today on main it lists 12 "Unknown owner" errors for @hongjoonahn-tri.
|
|
||
| ROOT = pathlib.Path(__file__).resolve().parent.parent | ||
| CANDIDATES = [".github/CODEOWNERS", "CODEOWNERS", "docs/CODEOWNERS"] | ||
| HANDLE = re.compile(r"@([A-Za-z0-9][A-Za-z0-9-]*(?:/[A-Za-z0-9._-]+)?)") |
There was a problem hiding this comment.
CODEOWNERS also accepts email owners (dev@example.com). This regex would pull out @example and query a user that has nothing to do with the rule, which can fail CI or pass by accident. Trailing # comments on a rule line get scanned too. Moot if you switch to the errors endpoint.
| except urllib.error.HTTPError as e: | ||
| if e.code == 404: | ||
| return False, "404 -- renamed or deleted" | ||
| return True, f"unverified (HTTP {e.code})" |
There was a problem hiding this comment.
A 401 or 403 (expired token, rate limit) also lands here and counts as a pass, so a broken token makes the check pass silently. I'd fail on 401/403 and keep the lenient path for network errors only.
| def main() -> int: | ||
| path = next((ROOT / c for c in CANDIDATES if (ROOT / c).is_file()), None) | ||
| if path is None: | ||
| print("no CODEOWNERS file; nothing to check") |
There was a problem hiding this comment.
If the ruleset is going to require code-owner review, a missing CODEOWNERS file is itself the failure. I'd return 1 here.
hongjoonahn-tri became hongjoonahn-trl. That is the second owner rename in a week -- usik-luke -> usik-luke-trl was #19 six days ago -- and both left every rule in the file one owner short while continuing to look correct, because CODEOWNERS ignores unknown handles with no error, no warning and no annotation. Both were found by accident. Rather than rely on noticing a third time, tools/check_codeowners.py resolves every handle against the API and fails CI when one 404s. Wired into community-check.yml, which already runs on every push and PR. The checker treats network trouble and non-404 errors as unverified rather than dead, so a flaky API cannot fail the build, and skips @org/team handles since a default token cannot read org membership. Verified both directions: it failed on hongjoonahn-tri before the fix and passes on the three current handles after. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K599fUWZZYdiL4ekUq4Vhs
26773df to
56b4181
Compare
hongjoonahn-tri→hongjoonahn-trl. Second owner rename in a week —usik-luke→usik-luke-trlwas #19 six days ago.Both left every rule in the file one owner short while continuing to look correct. CODEOWNERS ignores unknown handles with no error, no warning, no annotation, and GitHub does not redirect a renamed handle:
Both were found by accident. Rather than rely on noticing a third time,
tools/check_codeowners.pyresolves every handle and fails CI when one 404s — wired intocommunity-check.yml, which already runs on every push and PR.Treats network trouble and non-404 errors as unverified rather than dead, so a flaky API can't fail the build. Skips
@org/teamhandles, since a default token can't read org membership.Verified both directions
🤖 Generated with Claude Code
https://claude.ai/code/session_01K599fUWZZYdiL4ekUq4Vhs