From 209ec2e66edb6081a67c91477a94586de9036c77 Mon Sep 17 00:00:00 2001 From: Volodymyr Zotov Date: Wed, 3 Sep 2025 17:11:28 -0500 Subject: [PATCH 1/4] Fix getActorPermission to handle team-based permissions --- dist/index.js | 34 +++++++++++++++--------------- src/github-helper.ts | 50 +++++++++++++++++++++++--------------------- 2 files changed, 43 insertions(+), 41 deletions(-) diff --git a/dist/index.js b/dist/index.js index faf637755..491c31080 100644 --- a/dist/index.js +++ b/dist/index.js @@ -294,23 +294,23 @@ class GitHubHelper { } getActorPermission(repo, actor) { return __awaiter(this, void 0, void 0, function* () { - // https://docs.github.com/en/graphql/reference/enums#repositorypermission - // https://docs.github.com/en/graphql/reference/objects#repositorycollaboratoredge - // Returns 'READ', 'TRIAGE', 'WRITE', 'MAINTAIN', 'ADMIN' - const query = `query CollaboratorPermission($owner: String!, $repo: String!, $collaborator: String) { - repository(owner:$owner, name:$repo) { - collaborators(login: $collaborator) { - edges { - permission - } - } - } - }`; - const collaboratorPermission = yield this.octokit.graphql(query, Object.assign(Object.assign({}, repo), { collaborator: actor })); - core.debug(`CollaboratorPermission: ${(0, util_1.inspect)(collaboratorPermission.repository.collaborators.edges)}`); - return collaboratorPermission.repository.collaborators.edges.length > 0 - ? collaboratorPermission.repository.collaborators.edges[0].permission.toLowerCase() - : 'none'; + // Use the REST API approach which can detect both direct and team-based permissions + // This is more reliable than the GraphQL approach for team permissions and works better with default GITHUB_TOKEN + core.debug(`Checking permissions using REST API for user ${actor}`); + try { + const { data: collaboratorPermission } = yield this.octokit.rest.repos.getCollaboratorPermissionLevel(Object.assign(Object.assign({}, repo), { username: actor })); + core.debug(`REST API collaborator permission: ${(0, util_1.inspect)(collaboratorPermission)}`); + if (collaboratorPermission.permission) { + const permission = collaboratorPermission.permission.toLowerCase(); + core.debug(`User ${actor} has ${permission} permission via REST API`); + return permission; + } + return 'none'; + } + catch (restError) { + core.debug(`REST API permission check failed: ${utils.getErrorMessage(restError)}`); + return 'none'; + } }); } tryAddReaction(repo, commentId, reaction) { diff --git a/src/github-helper.ts b/src/github-helper.ts index 31b96237f..d9970e955 100644 --- a/src/github-helper.ts +++ b/src/github-helper.ts @@ -56,31 +56,33 @@ export class GitHubHelper { } async getActorPermission(repo: Repository, actor: string): Promise { - // https://docs.github.com/en/graphql/reference/enums#repositorypermission - // https://docs.github.com/en/graphql/reference/objects#repositorycollaboratoredge - // Returns 'READ', 'TRIAGE', 'WRITE', 'MAINTAIN', 'ADMIN' - const query = `query CollaboratorPermission($owner: String!, $repo: String!, $collaborator: String) { - repository(owner:$owner, name:$repo) { - collaborators(login: $collaborator) { - edges { - permission - } - } + // Use the REST API approach which can detect both direct and team-based permissions + // This is more reliable than the GraphQL approach for team permissions and works better with default GITHUB_TOKEN + core.debug(`Checking permissions using REST API for user ${actor}`) + try { + const {data: collaboratorPermission} = + await this.octokit.rest.repos.getCollaboratorPermissionLevel({ + ...repo, + username: actor + }) + + core.debug( + `REST API collaborator permission: ${inspect(collaboratorPermission)}` + ) + + if (collaboratorPermission.permission) { + const permission = collaboratorPermission.permission.toLowerCase() + core.debug(`User ${actor} has ${permission} permission via REST API`) + return permission } - }` - const collaboratorPermission = - await this.octokit.graphql(query, { - ...repo, - collaborator: actor - }) - core.debug( - `CollaboratorPermission: ${inspect( - collaboratorPermission.repository.collaborators.edges - )}` - ) - return collaboratorPermission.repository.collaborators.edges.length > 0 - ? collaboratorPermission.repository.collaborators.edges[0].permission.toLowerCase() - : 'none' + + return 'none' + } catch (restError) { + core.debug( + `REST API permission check failed: ${utils.getErrorMessage(restError)}` + ) + return 'none' + } } async tryAddReaction( From 56c83f11a9790d28146429b65a1653fa9dbf5b66 Mon Sep 17 00:00:00 2001 From: Volodymyr Zotov Date: Thu, 11 Sep 2025 10:22:50 -0500 Subject: [PATCH 2/4] Use 'user.permissions' object to return permission --- src/github-helper.ts | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/src/github-helper.ts b/src/github-helper.ts index d9970e955..0cbf7b51f 100644 --- a/src/github-helper.ts +++ b/src/github-helper.ts @@ -1,7 +1,7 @@ import * as core from '@actions/core' -import {Octokit, PullsGetResponseData} from './octokit-client' -import {Command, SlashCommandPayload} from './command-helper' -import {inspect} from 'util' +import { Octokit, PullsGetResponseData } from './octokit-client' +import { Command, SlashCommandPayload } from './command-helper' +import { inspect } from 'util' import * as utils from './utils' type ReposCreateDispatchEventParamsClientPayload = { @@ -58,28 +58,40 @@ export class GitHubHelper { async getActorPermission(repo: Repository, actor: string): Promise { // Use the REST API approach which can detect both direct and team-based permissions // This is more reliable than the GraphQL approach for team permissions and works better with default GITHUB_TOKEN - core.debug(`Checking permissions using REST API for user ${actor}`) try { - const {data: collaboratorPermission} = + const { data: collaboratorPermission } = await this.octokit.rest.repos.getCollaboratorPermissionLevel({ ...repo, username: actor }) + const permissions = collaboratorPermission.user?.permissions core.debug( - `REST API collaborator permission: ${inspect(collaboratorPermission)}` + `REST API collaborator permission: ${inspect(permissions)}` ) - if (collaboratorPermission.permission) { - const permission = collaboratorPermission.permission.toLowerCase() - core.debug(`User ${actor} has ${permission} permission via REST API`) - return permission + // Use the detailed permissions object to get the highest permission level + if (permissions) { + // Check permissions in order of highest to lowest + if (permissions.admin) { + return 'admin' + } else if (permissions.maintain) { + return 'maintain' + } else if (permissions.push) { + return 'write' + } else if (permissions.triage) { + core.debug(`User ${actor} has triage permission via REST API`) + return 'triage' + } else if (permissions.pull) { + core.debug(`User ${actor} has read permission via REST API`) + return 'read' + } } return 'none' - } catch (restError) { + } catch (error) { core.debug( - `REST API permission check failed: ${utils.getErrorMessage(restError)}` + `REST API permission check failed: ${utils.getErrorMessage(error)}` ) return 'none' } @@ -114,7 +126,7 @@ export class GitHubHelper { repo: Repository, pullNumber: number ): Promise { - const {data: pullRequest} = await this.octokit.rest.pulls.get({ + const { data: pullRequest } = await this.octokit.rest.pulls.get({ ...repo, pull_number: pullNumber }) @@ -144,7 +156,7 @@ export class GitHubHelper { }) core.info( `Command '${cmd.command}' dispatched to '${cmd.repository}' ` + - `with event type '${eventType}'.` + `with event type '${eventType}'.` ) } @@ -184,7 +196,7 @@ export class GitHubHelper { } private async getDefaultBranch(repository: string): Promise { - const {data: repo} = await this.octokit.rest.repos.get({ + const { data: repo } = await this.octokit.rest.repos.get({ ...this.parseRepository(repository) }) return repo.default_branch From bf24d346a88285077b55b34c199f73c9b13ee533 Mon Sep 17 00:00:00 2001 From: Volodymyr Zotov Date: Thu, 11 Sep 2025 10:27:33 -0500 Subject: [PATCH 3/4] Add RepoPermission type --- src/command-helper.ts | 2 +- src/github-helper.ts | 23 ++++++++++++----------- src/utils.ts | 8 ++++++++ 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/command-helper.ts b/src/command-helper.ts index 6c2f31bac..353a92417 100644 --- a/src/command-helper.ts +++ b/src/command-helper.ts @@ -175,7 +175,7 @@ export function configIsValid(config: Command[]): string | null { } export function actorHasPermission( - actorPermission: string, + actorPermission: utils.RepoPermission, commandPermission: string ): boolean { const permissionLevels = Object.freeze({ diff --git a/src/github-helper.ts b/src/github-helper.ts index 0cbf7b51f..8df006dab 100644 --- a/src/github-helper.ts +++ b/src/github-helper.ts @@ -1,7 +1,7 @@ import * as core from '@actions/core' -import { Octokit, PullsGetResponseData } from './octokit-client' -import { Command, SlashCommandPayload } from './command-helper' -import { inspect } from 'util' +import {Octokit, PullsGetResponseData} from './octokit-client' +import {Command, SlashCommandPayload} from './command-helper' +import {inspect} from 'util' import * as utils from './utils' type ReposCreateDispatchEventParamsClientPayload = { @@ -55,20 +55,21 @@ export class GitHubHelper { } } - async getActorPermission(repo: Repository, actor: string): Promise { + async getActorPermission( + repo: Repository, + actor: string + ): Promise { // Use the REST API approach which can detect both direct and team-based permissions // This is more reliable than the GraphQL approach for team permissions and works better with default GITHUB_TOKEN try { - const { data: collaboratorPermission } = + const {data: collaboratorPermission} = await this.octokit.rest.repos.getCollaboratorPermissionLevel({ ...repo, username: actor }) const permissions = collaboratorPermission.user?.permissions - core.debug( - `REST API collaborator permission: ${inspect(permissions)}` - ) + core.debug(`REST API collaborator permission: ${inspect(permissions)}`) // Use the detailed permissions object to get the highest permission level if (permissions) { @@ -126,7 +127,7 @@ export class GitHubHelper { repo: Repository, pullNumber: number ): Promise { - const { data: pullRequest } = await this.octokit.rest.pulls.get({ + const {data: pullRequest} = await this.octokit.rest.pulls.get({ ...repo, pull_number: pullNumber }) @@ -156,7 +157,7 @@ export class GitHubHelper { }) core.info( `Command '${cmd.command}' dispatched to '${cmd.repository}' ` + - `with event type '${eventType}'.` + `with event type '${eventType}'.` ) } @@ -196,7 +197,7 @@ export class GitHubHelper { } private async getDefaultBranch(repository: string): Promise { - const { data: repo } = await this.octokit.rest.repos.get({ + const {data: repo} = await this.octokit.rest.repos.get({ ...this.parseRepository(repository) }) return repo.default_branch diff --git a/src/utils.ts b/src/utils.ts index 52dc89799..52fb0266d 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,13 @@ import * as core from '@actions/core' +export type RepoPermission = + | 'admin' + | 'maintain' + | 'write' + | 'triage' + | 'read' + | 'none' + export function getInputAsArray( name: string, options?: core.InputOptions From 7c1b623a2b0eba93f684c34f689a441f0be84cf1 Mon Sep 17 00:00:00 2001 From: Volodymyr Zotov Date: Thu, 11 Sep 2025 10:27:54 -0500 Subject: [PATCH 4/4] Make new build --- dist/index.js | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/dist/index.js b/dist/index.js index 491c31080..080286d0d 100644 --- a/dist/index.js +++ b/dist/index.js @@ -293,22 +293,39 @@ class GitHubHelper { }; } getActorPermission(repo, actor) { + var _a; return __awaiter(this, void 0, void 0, function* () { // Use the REST API approach which can detect both direct and team-based permissions // This is more reliable than the GraphQL approach for team permissions and works better with default GITHUB_TOKEN - core.debug(`Checking permissions using REST API for user ${actor}`); try { const { data: collaboratorPermission } = yield this.octokit.rest.repos.getCollaboratorPermissionLevel(Object.assign(Object.assign({}, repo), { username: actor })); - core.debug(`REST API collaborator permission: ${(0, util_1.inspect)(collaboratorPermission)}`); - if (collaboratorPermission.permission) { - const permission = collaboratorPermission.permission.toLowerCase(); - core.debug(`User ${actor} has ${permission} permission via REST API`); - return permission; + const permissions = (_a = collaboratorPermission.user) === null || _a === void 0 ? void 0 : _a.permissions; + core.debug(`REST API collaborator permission: ${(0, util_1.inspect)(permissions)}`); + // Use the detailed permissions object to get the highest permission level + if (permissions) { + // Check permissions in order of highest to lowest + if (permissions.admin) { + return 'admin'; + } + else if (permissions.maintain) { + return 'maintain'; + } + else if (permissions.push) { + return 'write'; + } + else if (permissions.triage) { + core.debug(`User ${actor} has triage permission via REST API`); + return 'triage'; + } + else if (permissions.pull) { + core.debug(`User ${actor} has read permission via REST API`); + return 'read'; + } } return 'none'; } - catch (restError) { - core.debug(`REST API permission check failed: ${utils.getErrorMessage(restError)}`); + catch (error) { + core.debug(`REST API permission check failed: ${utils.getErrorMessage(error)}`); return 'none'; } });