Skip to content

Commit 9f6a20a

Browse files
fix: add error handling for GITHUB_TOKEN permissions in pull request comments (#106)
1 parent c7cf8d1 commit 9f6a20a

File tree

5 files changed

+90
-29
lines changed

5 files changed

+90
-29
lines changed

__tests__/index.test.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1511,4 +1511,53 @@ describe('Azure DevOps Commit Validator', () => {
15111511
expect(commentCall.body).toContain('`AB#55555555` (in PR title/body)');
15121512
});
15131513
});
1514+
1515+
describe('GitHub token permissions', () => {
1516+
it('should provide helpful error message when GITHUB_TOKEN lacks pull-requests write permission', async () => {
1517+
// Mock PR data
1518+
mockContext.payload.pull_request = {
1519+
number: 123
1520+
};
1521+
1522+
// Mock commits without AB# pattern
1523+
mockOctokit.paginate.mockResolvedValueOnce([
1524+
{
1525+
sha: 'abc1234567890',
1526+
commit: { message: 'Fix bug without work item' }
1527+
}
1528+
]);
1529+
1530+
// Mock listComments to succeed
1531+
mockOctokit.paginate.mockResolvedValueOnce([]);
1532+
1533+
// Mock createComment to fail with 403 permission error
1534+
const permissionError = new Error('Resource not accessible by integration');
1535+
permissionError.status = 403;
1536+
mockOctokit.rest.issues.createComment.mockRejectedValueOnce(permissionError);
1537+
1538+
// Mock inputs
1539+
mockGetInput.mockImplementation(input => {
1540+
const inputs = {
1541+
'check-commits': 'true',
1542+
'check-pull-request': 'false',
1543+
'fail-if-missing-workitem-commit-link': 'true',
1544+
'link-commits-to-pull-request': 'false',
1545+
'comment-on-failure': 'true',
1546+
'validate-work-item-exists': 'false',
1547+
'github-token': 'fake-token',
1548+
'azure-devops-token': '',
1549+
'azure-devops-organization': ''
1550+
};
1551+
return inputs[input] || '';
1552+
});
1553+
1554+
await run();
1555+
1556+
// Should set a helpful error message about missing permissions
1557+
expect(mockSetFailed).toHaveBeenCalledWith(expect.stringContaining('pull-requests: write'));
1558+
expect(mockSetFailed).toHaveBeenCalledWith(
1559+
expect.stringContaining('GITHUB_TOKEN does not have sufficient permissions')
1560+
);
1561+
});
1562+
});
15141563
});

badges/coverage.svg

Lines changed: 1 addition & 1 deletion
Loading

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "azure-devops-work-item-link-enforcer-and-linker",
3-
"version": "3.0.0",
3+
"version": "3.0.1",
44
"private": true,
55
"type": "module",
66
"description": "GitHub Action to enforce that each commit in a pull request be linked to an Azure DevOps work item and automatically link the pull request to each work item ",

src/index.js

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -509,34 +509,46 @@ async function addOrUpdateComment(octokit, context, pullNumber, commentBody, sea
509509
const commentExtra = `\n<details>\n<summary>Workflow run details</summary>\n\n[View workflow run](${context.payload.repository?.html_url}/actions/runs/${context.runId}) - _Last ran: ${currentDateTime} UTC_\n</details>`;
510510
const commentCombined = commentBody + commentExtra;
511511

512-
// Get all comments
513-
const comments = await octokit.paginate(octokit.rest.issues.listComments, {
514-
owner,
515-
repo,
516-
issue_number: pullNumber
517-
});
518-
519-
// Find existing comment
520-
const existingComment = comments.find(comment => comment.body?.includes(searchText));
521-
522-
if (existingComment) {
523-
console.log(`Comment already exists: ${existingComment.id}`);
524-
console.log('... attempting to update the PR comment');
525-
await octokit.rest.issues.updateComment({
526-
owner,
527-
repo,
528-
comment_id: existingComment.id,
529-
body: commentCombined
530-
});
531-
console.log('... PR comment updated');
532-
} else {
533-
console.log('Comment does not exist. Posting a new comment.');
534-
await octokit.rest.issues.createComment({
512+
try {
513+
// Get all comments
514+
const comments = await octokit.paginate(octokit.rest.issues.listComments, {
535515
owner,
536516
repo,
537-
issue_number: pullNumber,
538-
body: commentCombined
517+
issue_number: pullNumber
539518
});
519+
520+
// Find existing comment
521+
const existingComment = comments.find(comment => comment.body?.includes(searchText));
522+
523+
if (existingComment) {
524+
console.log(`Comment already exists: ${existingComment.id}`);
525+
console.log('... attempting to update the PR comment');
526+
await octokit.rest.issues.updateComment({
527+
owner,
528+
repo,
529+
comment_id: existingComment.id,
530+
body: commentCombined
531+
});
532+
console.log('... PR comment updated');
533+
} else {
534+
console.log('Comment does not exist. Posting a new comment.');
535+
await octokit.rest.issues.createComment({
536+
owner,
537+
repo,
538+
issue_number: pullNumber,
539+
body: commentCombined
540+
});
541+
}
542+
} catch (error) {
543+
if (error.status === 403 && error.message.includes('Resource not accessible by integration')) {
544+
core.setFailed(
545+
'Unable to comment on pull request. The GITHUB_TOKEN does not have sufficient permissions. ' +
546+
'Please add "pull-requests: write" permission to your workflow. ' +
547+
'See: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token'
548+
);
549+
} else {
550+
throw error;
551+
}
540552
}
541553
}
542554

0 commit comments

Comments
 (0)