Skip to content

DRU-1145 -- Ask GitHub whether a reviewer can write when the delivery hides it - #1174

Open
dserbano wants to merge 1 commit into
mainfrom
issues/1145
Open

dserbano wants to merge 1 commit into
mainfrom
issues/1145

Conversation

@dserbano

Copy link
Copy Markdown
Collaborator

A person with write access who approved a build's pull request on an org-owned repo left the build parked on ReviewWork, and no log line said why. GitHub hides private org membership in webhook payloads, so that person's author_association is CONTRIBUTOR. GitHubEvents set author_can_write only for OWNER, MEMBER, and COLLABORATOR, so pr_review_answers_the_gate never got the approval. The GitHub chat channel dropped the same person's issue and PR comments for the same reason.

  • When the association is not a writer's, GitHubEvents._can_write asks GitHub for the person's permission on the repo: GET /repos/{owner}/{repo}/collaborators/{username}/permission, with the App's installation token. admin or write sets author_can_write. GitHub reports maintain as write.
  • A writer association or a bot sender skips the call.
  • Reviews, issue and PR comments, and pull request events use _can_write, so the review gate and the chat channel follow one rule.
  • GitHubClient.get_permission makes the read. It needs only the Metadata read permission, which every GitHub App has. For someone who is not a collaborator, GitHub answers read, not an error.

Each review or comment from someone who is not a writer by association now costs a permission read. On a public repo, that includes every passer-by who comments.

@dserbano

Copy link
Copy Markdown
Collaborator Author

DRU-1145: the fix, the tests, and one question

The fix

On an org repo, a webhook hides a reviewer's private membership, so the reviewer's author_association arrives as CONTRIBUTOR. GitHubEvents set author_can_write only for OWNER, MEMBER, and COLLABORATOR. So pr_review_answers_the_gate never received an approval from an org member with private membership, and the build stayed parked with no log line.

  • GitHubEvents._can_write decides author_can_write for reviews, issue and PR comments, and pull request events. A writer association returns true and a bot returns false, with no call. For anyone else, it asks GitHub for the person's permission on the repo. admin or write can write, and _WRITE_PERMISSIONS holds those two values beside _WRITERS.
  • GitHubClient.get_permission reads GET /repos/{owner}/{repo}/collaborators/{username}/permission with the App's installation token. It needs only the Metadata read permission. For someone who is not a collaborator, GitHub answers read, not an error.
  • A review or comment from someone who is not a writer by association now costs one permission read. On a public repo, that includes every passer-by who comments.

Automated tests

  • ruff check, ruff format --check, and druks-house-rules check --base origin/main pass. GitHub CI passes all six checks.

  • pytest backend/ passed 2646 tests. Three tests fail, and each also fails on main on my Mac:

    • test_upload_dir_tar_survives_more_warnings_than_a_pipe_holds, which is macOS-only;
    • the two test_druks_sandbox_script tests, where the script exits with 127 locally.
  • New tests in test_webhooks_github.py cover:

    • a review and a comment from a private member with write access;
    • a review from a person with read access only;
    • a writer association or a bot, which makes no lookup.

    The review and comment tests fail without the fix. The fake subclasses GitHubClient. The comment tests in test_github_channel.py get a permission answer from their existing local-GitHub fixture.

Live tests: the bug on main, then the fix

I set up the case from team.druks.ai on a local install:

  • a new organization, dserbano-druks-test, where my account is an admin with private membership, GitHub's default;
  • a second account that is a member with private membership and the organization's base permission, Read;
  • both Apps installed on the organization.

Each build started from a board ticket that I filed in the dashboard, and I approved each plan there. I ran some cases on an image of main, then the same cases on an image of this PR. I read the Delivered as column, the author_association that GitHub sent, from the App's delivery log. Druks returned 200 for every delivery.

# Case Image Delivered as Result
1 I approve a build's PR on the org repo main CONTRIBUTOR ❌ The build stayed parked on review_work two minutes later. This reproduces the bug.
2 I approve the same PR again this PR CONTRIBUTOR ✅ In the same second, the gate was answered, the draft was cleared, and the build finished.
3 I request changes on another org build this PR CONTRIBUTOR ✅ The gate was answered, and triage ran.
4 I approve a build's PR on my personal repo, where I am the owner this PR OWNER ✅ The gate was answered with no lookup, and on_approval: merge merged the PR.
5 The member with read access approves a parked org build this PR NONE ✅ The lookup returned read, and the build stayed parked two minutes later.
6 I tag the App in an inline review comment on the org repo main CONTRIBUTOR ❌ No reaction and no reply three minutes later.
7 The same inline comment again this PR CONTRIBUTOR ✅ The App reacted and replied in the thread 10 seconds later.
8 I tag the App in a top-level PR comment on the org repo main MEMBER The App answered. On main too, a top-level comment already reached the chat channel.

GitHub sent different associations for the same person on the same PR. A review and an inline review comment came as CONTRIBUTOR, and a top-level comment came as MEMBER. So for a private member, the chat channel lost only inline review comments, not top-level ones. The reader in case 5 came as NONE, because CONTRIBUTOR needs a commit in the repo.

A question

_WRITERS includes COLLABORATOR. GitHub reports a person added to the repo as a collaborator as COLLABORATOR, whatever their role, including read. So a read-only collaborator's approval answers the review gate, and their comments reach the chat channel, with no lookup. That holds on main and on this branch. I did not test it live.

Do you want this PR to drop COLLABORATOR from _WRITERS, so that a collaborator's permission is looked up too? Or should it stay as it is, with a ticket of its own?

@dserbano
dserbano marked this pull request as ready for review November 10, 2026 19:26
@dserbano
dserbano requested a review from czpython as a code owner November 10, 2026 19:26

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant