Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions foc-mechanical-rules/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ uv run foc-mechanical-rules --dry-run --rule R-FC-013 --rule R-PR-001 # or a fe

Requires a `GITHUB_TOKEN` (or `--token`) with `read:project` (board reads) and issue/PR write access (`repo` scope, or fine-grained `Issues: write` + `Pull requests: write`) on the blessed orgs. CI uses the org's `FILOZZY_CI_ADD_TO_PROJECT` secret (also used by [`add-issues-and-prs-to-fs-project-board.yml`](../.github/workflows/add-issues-and-prs-to-fs-project-board.yml)).

That org-wide scope isn't sufficient on its own: GitHub still checks the token's account's *per-repo* permission for repo-level writes like `R-PR-001`'s assignee mutation. A new repo added to the board doesn't automatically inherit this -- its `FilOzone/github-mgmt` config (or a direct collaborator grant) needs to give the bot account triage+ access, or `add_assignee` fails with a 404 that reads like the item wasn't found rather than a permissions gap.

## Testing

```bash
Expand Down
18 changes: 17 additions & 1 deletion foc-mechanical-rules/foc_mechanical_rules/github_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,28 @@ def get_issue_events(
def add_assignee(
session: requests.Session, *, owner: str, repo: str, number: str, login: str
) -> None:
"""Add an assignee to an issue/PR."""
"""Add an assignee to an issue/PR.

Raises requests.HTTPError. GitHub returns 404 here (never 403) both when
the calling token lacks write/triage access to `owner/repo` and when
`login` isn't a valid assignee on it -- the two cases are
indistinguishable from the response alone, but in this codebase's usage
(the target is always the PR's own author) it's almost always the
former, so the error is annotated with that hint rather than left as a
bare "Not Found".
Comment on lines +112 to +115
"""
resp = session.post(
f"https://api.github.com/repos/{owner}/{repo}/issues/{number}/assignees",
json={"assignees": [login]},
timeout=30,
)
if resp.status_code == 404:
raise requests.HTTPError(
f"{resp.status_code} {resp.reason} for url: {resp.url} -- likely a "
f"permissions issue: check that this token's account has "
f"triage+ access to {owner}/{repo} (see foc-mechanical-rules/README.md)",
response=resp,
)
resp.raise_for_status()


Expand Down
Loading