From 538abd6b8fad0f0ade1d7d1383776b2f1c84c6ea Mon Sep 17 00:00:00 2001 From: o-az Date: Tue, 10 Jun 2025 09:20:37 -0700 Subject: [PATCH 1/4] feat: auto-assign PR to author --- .github/workflows/auto-assign-pr.yml | 43 ++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/auto-assign-pr.yml diff --git a/.github/workflows/auto-assign-pr.yml b/.github/workflows/auto-assign-pr.yml new file mode 100644 index 0000000..088a459 --- /dev/null +++ b/.github/workflows/auto-assign-pr.yml @@ -0,0 +1,43 @@ +name: Auto Assign PR to Author + +on: + workflow_call: + inputs: + SKIP_IF_ASSIGNED: + description: 'Skip assignment if PR already has assignees' + required: false + default: true + type: boolean + +env: + SKIP_IF_ASSIGNED: ${{ inputs.SKIP_IF_ASSIGNED || true }} + +jobs: + auto-assign: + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - name: Auto assign PR to author + uses: actions/github-script@v7 + with: + script: | + const pr = context.payload.pull_request + const author = pr.user.login + const skipIfAssigned = process.env.SKIP_IF_ASSIGNED === Boolean(true) + + // Check if PR already has assignees + if (skipIfAssigned && pr.assignees && pr.assignees.length > 0) { + console.info(`PR #${pr.number} already has assignees. Skipping assignment.`) + return + } + + // Assign the PR to the author + await github.rest.issues.addAssignees({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pr.number, + assignees: [author] + }) + + console.info(`Successfully assigned PR #${pr.number} to ${author}`) From 7d5ddbf2c3bb7f6f3f13dc0e21ed4dfcf163d754 Mon Sep 17 00:00:00 2001 From: o-az Date: Tue, 16 Sep 2025 08:09:09 -0700 Subject: [PATCH 2/4] feat(ci): auto-assign PR to author --- .github/workflows/auto-assign-pr.yml | 59 ++++++++++++++++------------ 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/.github/workflows/auto-assign-pr.yml b/.github/workflows/auto-assign-pr.yml index 088a459..c5ec906 100644 --- a/.github/workflows/auto-assign-pr.yml +++ b/.github/workflows/auto-assign-pr.yml @@ -1,43 +1,52 @@ name: Auto Assign PR to Author on: + pull_request: + types: [opened, reopened] workflow_call: - inputs: - SKIP_IF_ASSIGNED: - description: 'Skip assignment if PR already has assignees' - required: false - default: true - type: boolean -env: - SKIP_IF_ASSIGNED: ${{ inputs.SKIP_IF_ASSIGNED || true }} +defaults: + run: + shell: bash jobs: auto-assign: - runs-on: ubuntu-latest permissions: + issues: write pull-requests: write + runs-on: ubuntu-latest steps: - name: Auto assign PR to author + if: > + github.event.pull_request.user.type != 'Bot' && + ( + github.event.pull_request.author_association == 'MEMBER' || + github.event.pull_request.author_association == 'OWNER' || + github.event.pull_request.author_association == 'COLLABORATOR' + ) uses: actions/github-script@v7 with: script: | const pr = context.payload.pull_request - const author = pr.user.login - const skipIfAssigned = process.env.SKIP_IF_ASSIGNED === Boolean(true) - - // Check if PR already has assignees - if (skipIfAssigned && pr.assignees && pr.assignees.length > 0) { - console.info(`PR #${pr.number} already has assignees. Skipping assignment.`) + const prAuthor = pr.user.login + + console.log(`PR #${pr.number} author: ${prAuthor}`) + console.log(`Current assignees: ${pr.assignees.map(a => a.login).join(', ') || 'none'}`) + + if (pr.assignees && pr.assignees.length > 0) { + console.log('PR already has assignees, skipping') return } - - // Assign the PR to the author - await github.rest.issues.addAssignees({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number, - assignees: [author] - }) - - console.info(`Successfully assigned PR #${pr.number} to ${author}`) + + try { + await github.rest.issues.addAssignees({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pr.number, + assignees: [prAuthor] + }) + console.log(`Successfully assigned PR #${pr.number} to ${prAuthor}`) + } catch (error) { + console.error(`Failed to assign PR: ${error.message}`) + throw error + } From 402731d39cfdad6f1cfc3cb0c6464477b7dd4a06 Mon Sep 17 00:00:00 2001 From: o-az Date: Tue, 16 Sep 2025 08:25:55 -0700 Subject: [PATCH 3/4] chore: add diagnostics info --- .github/workflows/auto-assign-pr.yml | 75 ++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 15 deletions(-) diff --git a/.github/workflows/auto-assign-pr.yml b/.github/workflows/auto-assign-pr.yml index c5ec906..1172aeb 100644 --- a/.github/workflows/auto-assign-pr.yml +++ b/.github/workflows/auto-assign-pr.yml @@ -11,33 +11,52 @@ defaults: jobs: auto-assign: + name: Auto Assign + # Permissions the called workflow requests (Caller workflow must still grant these scopes to GITHUB_TOKEN) permissions: issues: write pull-requests: write runs-on: ubuntu-latest steps: - - name: Auto assign PR to author - if: > - github.event.pull_request.user.type != 'Bot' && - ( - github.event.pull_request.author_association == 'MEMBER' || - github.event.pull_request.author_association == 'OWNER' || - github.event.pull_request.author_association == 'COLLABORATOR' - ) + - name: Auto assign PR to author (with permission checks) uses: actions/github-script@v7 with: script: | const pr = context.payload.pull_request - const prAuthor = pr.user.login - console.log(`PR #${pr.number} author: ${prAuthor}`) - console.log(`Current assignees: ${pr.assignees.map(a => a.login).join(', ') || 'none'}`) + if (!pr) { + core.warning('No pull_request payload detected. Ensure this is triggered by a pull_request event or called from a workflow triggered by pull_request. Skipping.') + return + } + + const prAuthor = pr.user?.login + const isBot = pr.user?.type === 'Bot' + const assoc = pr.author_association + const allowedAssocs = ['MEMBER', 'OWNER', 'COLLABORATOR'] + + core.info(`PR #${pr.number} author: ${prAuthor}`) + core.info(`Author association: ${assoc}`) + core.info(`Current assignees: ${pr.assignees?.map(a => a.login).join(', ') || 'none'}`) - if (pr.assignees && pr.assignees.length > 0) { - console.log('PR already has assignees, skipping') + // Skip for bots + if (isBot) { + core.info('Skipping: PR author is a Bot.') return } + // Skip for non-internal contributors (e.g., forks) + if (!allowedAssocs.includes(assoc)) { + core.info(`Skipping: author association (${assoc}) is not internal (MEMBER/OWNER/COLLABORATOR).`) + return + } + + // Skip if assignees already present + if (Array.isArray(pr.assignees) && pr.assignees.length > 0) { + core.info('PR already has assignees, skipping.') + return + } + + // Attempt to assign; provide descriptive errors on permission failure try { await github.rest.issues.addAssignees({ owner: context.repo.owner, @@ -45,8 +64,34 @@ jobs: issue_number: pr.number, assignees: [prAuthor] }) - console.log(`Successfully assigned PR #${pr.number} to ${prAuthor}`) + core.info(`Successfully assigned PR #${pr.number} to ${prAuthor}`) } catch (error) { - console.error(`Failed to assign PR: ${error.message}`) + if (error.status === 403 || error.status === 404) { + const lines = [ + `Failed to assign PR #${pr.number} to ${prAuthor} due to insufficient permissions for GITHUB_TOKEN.`, + `Error: ${error.message}`, + ].join('\n') + core.setFailed(lines) + return + } + + core.setFailed(`Failed to assign PR: ${error.message}`) throw error } + + // Try to fetch repository default workflow permissions for helpful diagnostics + let defaultWorkflowPermissions = 'unknown' + try { + const { data } = await github.request('GET /repos/{owner}/{repo}/actions/permissions/workflow', { + owner: context.repo.owner, + repo: context.repo.repo + }) + defaultWorkflowPermissions = data.default_workflow_permissions + core.info(`Repository default workflow permissions: ${defaultWorkflowPermissions}`) + core.info(`Can approve pull request reviews: ${data.can_approve_pull_request_reviews}`) + if (defaultWorkflowPermissions !== 'write') { + core.warning('Default workflow permissions are not "write". If the caller workflow does not explicitly grant write permissions to GITHUB_TOKEN, assigning will fail.') + } + } catch (err) { + core.warning(`Could not read repository Actions workflow permissions (not fatal): ${err.message}`) + } From b38cdca843c35031cad0b6e01c5b069cc28cc18a Mon Sep 17 00:00:00 2001 From: o-az Date: Tue, 16 Sep 2025 08:34:44 -0700 Subject: [PATCH 4/4] chore: cleanup --- .github/workflows/auto-assign-pr.yml | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/.github/workflows/auto-assign-pr.yml b/.github/workflows/auto-assign-pr.yml index 1172aeb..baf4522 100644 --- a/.github/workflows/auto-assign-pr.yml +++ b/.github/workflows/auto-assign-pr.yml @@ -76,22 +76,7 @@ jobs: } core.setFailed(`Failed to assign PR: ${error.message}`) - throw error - } - - // Try to fetch repository default workflow permissions for helpful diagnostics - let defaultWorkflowPermissions = 'unknown' - try { - const { data } = await github.request('GET /repos/{owner}/{repo}/actions/permissions/workflow', { - owner: context.repo.owner, - repo: context.repo.repo - }) - defaultWorkflowPermissions = data.default_workflow_permissions - core.info(`Repository default workflow permissions: ${defaultWorkflowPermissions}`) - core.info(`Can approve pull request reviews: ${data.can_approve_pull_request_reviews}`) - if (defaultWorkflowPermissions !== 'write') { - core.warning('Default workflow permissions are not "write". If the caller workflow does not explicitly grant write permissions to GITHUB_TOKEN, assigning will fail.') - } - } catch (err) { - core.warning(`Could not read repository Actions workflow permissions (not fatal): ${err.message}`) + core.warning('Possible reasons:') + console.info('- Default workflow permissions are not "write"') + console.info('- Caller workflow does not explicitly grant write permissions to GITHUB_TOKEN') }