-
Notifications
You must be signed in to change notification settings - Fork 423
ci: don't flag private org members as external contributors #2546
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -46,18 +46,34 @@ jobs: | |||||||||
| if (item.draft) { core.info(`Skipping draft PR #${item.number}`); return; } | ||||||||||
| // Skip bots (Dependabot, etc.). | ||||||||||
| if (item.user?.type === 'Bot') { core.info(`Skipping bot ${item.user?.login}`); return; } | ||||||||||
| // External == author has no write association with the repo. | ||||||||||
| const INTERNAL = ['OWNER', 'MEMBER', 'COLLABORATOR']; | ||||||||||
| if (INTERNAL.includes(item.author_association)) { | ||||||||||
| core.info(`#${item.number} by ${item.user?.login} is internal (${item.author_association}); skipping.`); | ||||||||||
| // External == author has no write access to the repo. | ||||||||||
| const { owner, repo } = context.repo; | ||||||||||
| // Fast path (free): public members & direct collaborators. | ||||||||||
| let internal = ['OWNER', 'MEMBER', 'COLLABORATOR'].includes(item.author_association); | ||||||||||
| // Slow path: catches PRIVATE org members via their granted repo access. | ||||||||||
| // author_association only reports MEMBER for *public* members, so a | ||||||||||
| // member with concealed membership would otherwise be misflagged as | ||||||||||
| // external. Public repos grant everyone implicit 'read', so require | ||||||||||
| // triage+ (not permission != 'none'). | ||||||||||
| if (!internal) { | ||||||||||
| try { | ||||||||||
| const { data } = await github.rest.repos.getCollaboratorPermissionLevel({ | ||||||||||
| owner, repo, username: item.user.login, | ||||||||||
| }); | ||||||||||
| const p = data.user?.permissions ?? {}; | ||||||||||
| internal = !!(p.admin || p.maintain || p.push || p.triage); | ||||||||||
|
Comment on lines
+63
to
+64
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
| } catch (e) { | ||||||||||
| if (e.status !== 404) throw e; // 404 = not a collaborator => external | ||||||||||
| } | ||||||||||
| } | ||||||||||
| if (internal) { | ||||||||||
| core.info(`#${item.number} by ${item.user?.login} is internal; skipping.`); | ||||||||||
| return; | ||||||||||
| } | ||||||||||
| // The `external` label doubles as our "already alerted" marker, so a | ||||||||||
| // reopen (label persists) won't post to Slack twice. | ||||||||||
| if (item.labels?.some(l => l.name === LABEL)) { core.info(`#${item.number} already handled.`); return; } | ||||||||||
|
|
||||||||||
| const { owner, repo } = context.repo; | ||||||||||
|
|
||||||||||
| // Deliver the alert FIRST. We only mark the item handled (label it) | ||||||||||
| // after a confirmed send, so an unset/failing webhook leaves it | ||||||||||
| // unlabeled and a later event (reopen / ready_for_review) retries, | ||||||||||
|
|
||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
item.userin this file uses optional chaining (item.user?.login,item.user?.type), but this call site accessesitem.user.loginwithout it. Ifitem.useris somehow null (e.g. a deleted account triggering an event), this throws aTypeErrorinside the try/catch; since aTypeErrorhas no.status,e.status !== 404is true and the error is re-thrown, failing the workflow run rather than quietly passing. Usingitem.user?.loginis consistent with the rest of the file and avoids the hard throw.