From f65d2cc00d6c4f7d5734a2023e5753ac807b04e0 Mon Sep 17 00:00:00 2001 From: Mounil Date: Tue, 6 Jan 2026 12:48:16 +0530 Subject: [PATCH 1/4] fix: LinkBot fork permission issue Signed-off-by: Mounil --- .../scripts/bot-pr-missing-linked-issue.js | 86 +++++++++++++------ .../workflows/bot-pr-missing-linked-issue.yml | 20 ++++- CHANGELOG.md | 1 + 3 files changed, 78 insertions(+), 29 deletions(-) diff --git a/.github/scripts/bot-pr-missing-linked-issue.js b/.github/scripts/bot-pr-missing-linked-issue.js index ec8f9764c..3a3e2f82b 100644 --- a/.github/scripts/bot-pr-missing-linked-issue.js +++ b/.github/scripts/bot-pr-missing-linked-issue.js @@ -1,11 +1,30 @@ module.exports = async ({ github, context }) => { - const body = context.payload.pull_request.body || ""; + const isDryRun = process.env.DRY_RUN === 'true'; + const prNumber = parseInt(process.env.PR_NUMBER) || context.payload.pull_request.number; + + console.log(`Processing PR #${prNumber} (Dry run: ${isDryRun})`); + + // For workflow_dispatch, we need to fetch PR details + let prData; + if (context.payload.pull_request) { + prData = context.payload.pull_request; + } else { + // workflow_dispatch case - fetch PR data + const prResponse = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: prNumber, + }); + prData = prResponse.data; + } + + const body = prData.body || ""; const regex = /\bFixes\s*:?\s*(#\d+)(\s*,\s*#\d+)*/i; const comments = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.payload.pull_request.number, + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, }); const alreadyCommented = comments.data.some(comment => @@ -13,33 +32,46 @@ module.exports = async ({ github, context }) => { ); if (alreadyCommented) { + console.log('LinkBot already commented on this PR'); return; } if (!regex.test(body)) { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.payload.pull_request.number, - body: [ - `Hi @${context.payload.pull_request.user.login}, this is **LinkBot** 👋`, - ``, - `Linking pull requests to issues helps us significantly with reviewing pull requests and keeping the repository healthy.`, - ``, - `🚨 **This pull request does not have an issue linked.**`, - ``, - `Please link an issue using the following format:`, - `- Fixes #123`, - ``, - `📖 Guide:`, - `[docs/sdk_developers/training/workflow/how_to_link_issues.md](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/main/docs/sdk_developers/training/workflow/how_to_link_issues.md)`, - ``, - `If no issue exists yet, please create one:`, - `[docs/sdk_developers/creating_issues.md](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/main/docs/sdk_developers/creating_issues.md)`, - ``, - `Thanks!` - ].join('\n') - }); + const commentBody = [ + `Hi @${prData.user.login}, this is **LinkBot** 👋`, + ``, + `Linking pull requests to issues helps us significantly with reviewing pull requests and keeping the repository healthy.`, + ``, + `🚨 **This pull request does not have an issue linked.**`, + ``, + `Please link an issue using the following format:`, + `- Fixes #123`, + ``, + `📖 Guide:`, + `[docs/sdk_developers/training/workflow/how_to_link_issues.md](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/main/docs/sdk_developers/training/workflow/how_to_link_issues.md)`, + ``, + `If no issue exists yet, please create one:`, + `[docs/sdk_developers/creating_issues.md](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/main/docs/sdk_developers/creating_issues.md)`, + ``, + `Thanks!` + ].join('\n'); + + if (isDryRun) { + console.log('DRY RUN: Would post the following comment:'); + console.log('---'); + console.log(commentBody); + console.log('---'); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body: commentBody, + }); + console.log('LinkBot comment posted successfully'); + } + } else { + console.log('PR has linked issue - no comment needed'); } }; diff --git a/.github/workflows/bot-pr-missing-linked-issue.yml b/.github/workflows/bot-pr-missing-linked-issue.yml index baea7362f..6353ee1b0 100644 --- a/.github/workflows/bot-pr-missing-linked-issue.yml +++ b/.github/workflows/bot-pr-missing-linked-issue.yml @@ -1,19 +1,31 @@ name: PR Missing Linked Issue Reminder on: - pull_request: + pull_request_target: types: [opened, edited, reopened] + workflow_dispatch: + inputs: + pr_number: + description: 'PR number to check' + required: true + type: number + dry_run: + description: 'Dry run (only log, no comments)' + required: false + type: boolean + default: false permissions: pull-requests: write contents: read + issues: write jobs: check-linked-issue: runs-on: ubuntu-latest concurrency: - group: bot-pr-missing-linked-issue-${{ github.event.pull_request.number }} + group: bot-pr-missing-linked-issue-${{ github.event.pull_request.number || github.event.inputs.pr_number }} cancel-in-progress: true steps: @@ -24,10 +36,14 @@ jobs: - name: Checkout repository uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + ref: main - name: Check PR body for linked issue env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DRY_RUN: ${{ github.event.inputs.dry_run || 'false' }} + PR_NUMBER: ${{ github.event.pull_request.number || github.event.inputs.pr_number }} uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: | diff --git a/CHANGELOG.md b/CHANGELOG.md index ba152e27d..9e2706a79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -155,6 +155,7 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. - Workflow does not contain permissions for `pr-check-test-files` and `pr-check-codecov` - Fixed `cron-check-broken-links.yml` string parsing issue in context input `dry_run` (#1235) - Flaky tests by disabling TLS in mock Hedera nodes in `mock_server.py` +- Fixed LinkBot permission issue for fork PRs by changing trigger to pull_request_target and adding proper permissions. ### Breaking Change From f59bdb98e7295aee9bffa1cd636718960daf4598 Mon Sep 17 00:00:00 2001 From: Mounil Date: Tue, 6 Jan 2026 13:19:34 +0530 Subject: [PATCH 2/4] refactor: improve LinkBot workflow safety Signed-off-by: Mounil --- .github/workflows/bot-pr-missing-linked-issue.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/bot-pr-missing-linked-issue.yml b/.github/workflows/bot-pr-missing-linked-issue.yml index 6353ee1b0..903fcf3e4 100644 --- a/.github/workflows/bot-pr-missing-linked-issue.yml +++ b/.github/workflows/bot-pr-missing-linked-issue.yml @@ -1,3 +1,5 @@ +# This workflow checks if a PR has a linked issue and posts a reminder comment if missing. +# It runs on pull_request_target to have write permissions for fork PRs. name: PR Missing Linked Issue Reminder on: @@ -13,7 +15,7 @@ on: description: 'Dry run (only log, no comments)' required: false type: boolean - default: false + default: true permissions: pull-requests: write From 7383924f968de0981745ffe100aa67c1a3871152 Mon Sep 17 00:00:00 2001 From: Mounil Date: Tue, 6 Jan 2026 13:21:08 +0530 Subject: [PATCH 3/4] refactor: add error handling to LinkBot script Signed-off-by: Mounil --- .../scripts/bot-pr-missing-linked-issue.js | 136 +++++++++--------- 1 file changed, 72 insertions(+), 64 deletions(-) diff --git a/.github/scripts/bot-pr-missing-linked-issue.js b/.github/scripts/bot-pr-missing-linked-issue.js index 3a3e2f82b..4e0726f08 100644 --- a/.github/scripts/bot-pr-missing-linked-issue.js +++ b/.github/scripts/bot-pr-missing-linked-issue.js @@ -1,77 +1,85 @@ module.exports = async ({ github, context }) => { - const isDryRun = process.env.DRY_RUN === 'true'; - const prNumber = parseInt(process.env.PR_NUMBER) || context.payload.pull_request.number; - - console.log(`Processing PR #${prNumber} (Dry run: ${isDryRun})`); - - // For workflow_dispatch, we need to fetch PR details - let prData; - if (context.payload.pull_request) { - prData = context.payload.pull_request; - } else { - // workflow_dispatch case - fetch PR data - const prResponse = await github.rest.pulls.get({ + let prNumber; + try { + const isDryRun = process.env.DRY_RUN === 'true'; + prNumber = parseInt(process.env.PR_NUMBER) || context.payload.pull_request.number; + + console.log(`Processing PR #${prNumber} (Dry run: ${isDryRun})`); + + // For workflow_dispatch, we need to fetch PR details + let prData; + if (context.payload.pull_request) { + prData = context.payload.pull_request; + } else { + // workflow_dispatch case - fetch PR data + const prResponse = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: prNumber, + }); + prData = prResponse.data; + } + + const body = prData.body || ""; + const regex = /\bFixes\s*:?\s*(#\d+)(\s*,\s*#\d+)*/i; + + const comments = await github.rest.issues.listComments({ owner: context.repo.owner, repo: context.repo.repo, - pull_number: prNumber, + issue_number: prNumber, }); - prData = prResponse.data; - } - const body = prData.body || ""; - const regex = /\bFixes\s*:?\s*(#\d+)(\s*,\s*#\d+)*/i; + const alreadyCommented = comments.data.some(comment => + comment.body.includes("this is LinkBot") + ); - const comments = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: prNumber, - }); - - const alreadyCommented = comments.data.some(comment => - comment.body.includes("this is LinkBot") - ); - - if (alreadyCommented) { - console.log('LinkBot already commented on this PR'); - return; - } + if (alreadyCommented) { + console.log('LinkBot already commented on this PR'); + return; + } - if (!regex.test(body)) { - const commentBody = [ - `Hi @${prData.user.login}, this is **LinkBot** 👋`, - ``, - `Linking pull requests to issues helps us significantly with reviewing pull requests and keeping the repository healthy.`, - ``, - `🚨 **This pull request does not have an issue linked.**`, - ``, - `Please link an issue using the following format:`, - `- Fixes #123`, - ``, - `📖 Guide:`, - `[docs/sdk_developers/training/workflow/how_to_link_issues.md](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/main/docs/sdk_developers/training/workflow/how_to_link_issues.md)`, - ``, - `If no issue exists yet, please create one:`, - `[docs/sdk_developers/creating_issues.md](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/main/docs/sdk_developers/creating_issues.md)`, - ``, - `Thanks!` - ].join('\n'); + if (!regex.test(body)) { + const commentBody = [ + `Hi @${prData.user.login}, this is **LinkBot** 👋`, + ``, + `Linking pull requests to issues helps us significantly with reviewing pull requests and keeping the repository healthy.`, + ``, + `🚨 **This pull request does not have an issue linked.**`, + ``, + `Please link an issue using the following format:`, + `- Fixes #123`, + ``, + `📖 Guide:`, + `[docs/sdk_developers/training/workflow/how_to_link_issues.md](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/main/docs/sdk_developers/training/workflow/how_to_link_issues.md)`, + ``, + `If no issue exists yet, please create one:`, + `[docs/sdk_developers/creating_issues.md](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/main/docs/sdk_developers/creating_issues.md)`, + ``, + `Thanks!` + ].join('\n'); - if (isDryRun) { - console.log('DRY RUN: Would post the following comment:'); - console.log('---'); - console.log(commentBody); - console.log('---'); + if (isDryRun) { + console.log('DRY RUN: Would post the following comment:'); + console.log('---'); + console.log(commentBody); + console.log('---'); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body: commentBody, + }); + console.log('LinkBot comment posted successfully'); + } } else { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: prNumber, - body: commentBody, - }); - console.log('LinkBot comment posted successfully'); + console.log('PR has linked issue - no comment needed'); } - } else { - console.log('PR has linked issue - no comment needed'); + } catch (error) { + console.error('Error processing PR:', error); + console.error('PR number:', prNumber); + console.error('Repository:', `${context.repo.owner}/${context.repo.repo}`); + throw error; } }; From 8dbb2572237562cf4550541b91ee122d1bbe254e Mon Sep 17 00:00:00 2001 From: Mounil Date: Tue, 6 Jan 2026 13:22:59 +0530 Subject: [PATCH 4/4] refactor: enhance LinkBot workflow documentation Signed-off-by: Mounil --- .github/workflows/bot-pr-missing-linked-issue.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/bot-pr-missing-linked-issue.yml b/.github/workflows/bot-pr-missing-linked-issue.yml index 903fcf3e4..fd39c4431 100644 --- a/.github/workflows/bot-pr-missing-linked-issue.yml +++ b/.github/workflows/bot-pr-missing-linked-issue.yml @@ -1,5 +1,6 @@ -# This workflow checks if a PR has a linked issue and posts a reminder comment if missing. -# It runs on pull_request_target to have write permissions for fork PRs. +# This workflow checks PRs for linked issues and posts a reminder comment if missing. +# Uses pull_request_target to enable write permissions for fork PRs while checking out +# only the base branch code (ref: main) to avoid executing untrusted fork code. name: PR Missing Linked Issue Reminder on: