From 2a021eb3bd1a79fc5b5342b8f72f7271ca9e186d Mon Sep 17 00:00:00 2001 From: Yugan Date: Thu, 11 Dec 2025 13:40:19 +0530 Subject: [PATCH 1/4] Add workflow to comment on PRs when released - Automatically comments on PRs included in a release - Only comments on PRs (not issues) using GitHub API validation - Compares current and previous release tags to find PRs - Minimal permissions: only requires pull-requests:write Fixes #1759 --- .github/workflows/comment-on-release.yml | 126 +++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 .github/workflows/comment-on-release.yml diff --git a/.github/workflows/comment-on-release.yml b/.github/workflows/comment-on-release.yml new file mode 100644 index 0000000000..1ec09221a6 --- /dev/null +++ b/.github/workflows/comment-on-release.yml @@ -0,0 +1,126 @@ +name: Comment on PRs in Release + +on: + release: + types: [published] + +permissions: + pull-requests: write + contents: read + +jobs: + comment-on-prs: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Get previous release tag + id: previous_tag + run: | + # Get all tags sorted by version + CURRENT_TAG="${{ github.event.release.tag_name }}" + PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -A 1 "^${CURRENT_TAG}$" | tail -n 1) + + # If there's no previous tag, use the first commit + if [ -z "$PREVIOUS_TAG" ] || [ "$PREVIOUS_TAG" = "$CURRENT_TAG" ]; then + PREVIOUS_TAG=$(git rev-list --max-parents=0 HEAD) + fi + + echo "previous_tag=${PREVIOUS_TAG}" >> $GITHUB_OUTPUT + echo "Found previous tag/commit: ${PREVIOUS_TAG}" + + - name: Get merged PRs between releases + id: get_prs + uses: actions/github-script@v7 + with: + script: | + const currentTag = '${{ github.event.release.tag_name }}'; + const previousTag = '${{ steps.previous_tag.outputs.previous_tag }}'; + + console.log(`Finding PRs between ${previousTag} and ${currentTag}`); + + // Get commits between previous and current release + const comparison = await github.rest.repos.compareCommits({ + owner: context.repo.owner, + repo: context.repo.repo, + base: previousTag, + head: currentTag + }); + + const commits = comparison.data.commits; + console.log(`Found ${commits.length} commits`); + + // Extract PR numbers from commit messages + const prNumbers = new Set(); + const prPattern = /#(\d+)/g; + + for (const commit of commits) { + const message = commit.commit.message; + const matches = message.matchAll(prPattern); + + for (const match of matches) { + const prNumber = parseInt(match[1]); + prNumbers.add(prNumber); + } + + // Also check if the commit is associated with a PR + if (commit.commit.message.includes('Merge pull request #')) { + const match = commit.commit.message.match(/Merge pull request #(\d+)/); + if (match) { + prNumbers.add(parseInt(match[1])); + } + } + } + + // Verify these are actually PRs (not issues) + const validPRs = []; + + for (const prNumber of prNumbers) { + try { + const { data: pr } = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: prNumber + }); + + // Only include merged PRs + if (pr.merged_at) { + validPRs.push(prNumber); + console.log(`Found valid merged PR: #${prNumber}`); + } + } catch (error) { + console.log(`#${prNumber} is not a valid PR or was not found`); + } + } + + console.log(`Found ${validPRs.length} valid merged PRs`); + return validPRs; + + - name: Comment on PRs + uses: actions/github-script@v7 + with: + script: | + const prNumbers = ${{ steps.get_prs.outputs.result }}; + const releaseTag = '${{ github.event.release.tag_name }}'; + const releaseUrl = '${{ github.event.release.html_url }}'; + + const comment = `🎉 This pull request is included in [${releaseTag}](${releaseUrl})!`; + + for (const prNumber of prNumbers) { + try { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body: comment + }); + console.log(`Successfully commented on PR #${prNumber}`); + } catch (error) { + console.error(`Failed to comment on PR #${prNumber}:`, error.message); + } + } + + console.log(`Commented on ${prNumbers.length} PRs`); From 1c601d1f4a8daa5a159a98a627125d67fcd88b0a Mon Sep 17 00:00:00 2001 From: Yugan Date: Thu, 11 Dec 2025 14:03:37 +0530 Subject: [PATCH 2/4] fix: Apply prettier formatting --- .github/workflows/comment-on-release.yml | 26 ++++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/comment-on-release.yml b/.github/workflows/comment-on-release.yml index 1ec09221a6..3c248ff75a 100644 --- a/.github/workflows/comment-on-release.yml +++ b/.github/workflows/comment-on-release.yml @@ -23,12 +23,12 @@ jobs: # Get all tags sorted by version CURRENT_TAG="${{ github.event.release.tag_name }}" PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -A 1 "^${CURRENT_TAG}$" | tail -n 1) - + # If there's no previous tag, use the first commit if [ -z "$PREVIOUS_TAG" ] || [ "$PREVIOUS_TAG" = "$CURRENT_TAG" ]; then PREVIOUS_TAG=$(git rev-list --max-parents=0 HEAD) fi - + echo "previous_tag=${PREVIOUS_TAG}" >> $GITHUB_OUTPUT echo "Found previous tag/commit: ${PREVIOUS_TAG}" @@ -39,9 +39,9 @@ jobs: script: | const currentTag = '${{ github.event.release.tag_name }}'; const previousTag = '${{ steps.previous_tag.outputs.previous_tag }}'; - + console.log(`Finding PRs between ${previousTag} and ${currentTag}`); - + // Get commits between previous and current release const comparison = await github.rest.repos.compareCommits({ owner: context.repo.owner, @@ -49,14 +49,14 @@ jobs: base: previousTag, head: currentTag }); - + const commits = comparison.data.commits; console.log(`Found ${commits.length} commits`); - + // Extract PR numbers from commit messages const prNumbers = new Set(); const prPattern = /#(\d+)/g; - + for (const commit of commits) { const message = commit.commit.message; const matches = message.matchAll(prPattern); @@ -74,10 +74,10 @@ jobs: } } } - + // Verify these are actually PRs (not issues) const validPRs = []; - + for (const prNumber of prNumbers) { try { const { data: pr } = await github.rest.pulls.get({ @@ -95,7 +95,7 @@ jobs: console.log(`#${prNumber} is not a valid PR or was not found`); } } - + console.log(`Found ${validPRs.length} valid merged PRs`); return validPRs; @@ -106,9 +106,9 @@ jobs: const prNumbers = ${{ steps.get_prs.outputs.result }}; const releaseTag = '${{ github.event.release.tag_name }}'; const releaseUrl = '${{ github.event.release.html_url }}'; - + const comment = `🎉 This pull request is included in [${releaseTag}](${releaseUrl})!`; - + for (const prNumber of prNumbers) { try { await github.rest.issues.createComment({ @@ -122,5 +122,5 @@ jobs: console.error(`Failed to comment on PR #${prNumber}:`, error.message); } } - + console.log(`Commented on ${prNumbers.length} PRs`); From 37410a1b832c53451922c406ac6fcb8dd4c234fc Mon Sep 17 00:00:00 2001 From: Yugan Date: Thu, 11 Dec 2025 18:13:48 +0530 Subject: [PATCH 3/4] refacored --- .github/workflows/comment-on-release.yml | 122 +++++++++++++---------- 1 file changed, 72 insertions(+), 50 deletions(-) diff --git a/.github/workflows/comment-on-release.yml b/.github/workflows/comment-on-release.yml index 3c248ff75a..fa4cd3adb6 100644 --- a/.github/workflows/comment-on-release.yml +++ b/.github/workflows/comment-on-release.yml @@ -17,20 +17,38 @@ jobs: with: fetch-depth: 0 - - name: Get previous release tag - id: previous_tag - run: | - # Get all tags sorted by version - CURRENT_TAG="${{ github.event.release.tag_name }}" - PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -A 1 "^${CURRENT_TAG}$" | tail -n 1) - - # If there's no previous tag, use the first commit - if [ -z "$PREVIOUS_TAG" ] || [ "$PREVIOUS_TAG" = "$CURRENT_TAG" ]; then - PREVIOUS_TAG=$(git rev-list --max-parents=0 HEAD) - fi - - echo "previous_tag=${PREVIOUS_TAG}" >> $GITHUB_OUTPUT - echo "Found previous tag/commit: ${PREVIOUS_TAG}" + - name: Get previous release + id: previous_release + uses: actions/github-script@v7 + with: + script: | + const currentTag = '${{ github.event.release.tag_name }}'; + + // Get all releases + const { data: releases } = await github.rest.repos.listReleases({ + owner: context.repo.owner, + repo: context.repo.repo, + per_page: 100 + }); + + // Find current release index + const currentIndex = releases.findIndex(r => r.tag_name === currentTag); + + if (currentIndex === -1) { + console.log('Current release not found in list'); + return null; + } + + // Get previous release (next in the list since they're sorted by date desc) + const previousRelease = releases[currentIndex + 1]; + + if (!previousRelease) { + console.log('No previous release found, this might be the first release'); + return null; + } + + console.log(`Found previous release: ${previousRelease.tag_name}`); + return previousRelease.tag_name; - name: Get merged PRs between releases id: get_prs @@ -38,7 +56,12 @@ jobs: with: script: | const currentTag = '${{ github.event.release.tag_name }}'; - const previousTag = '${{ steps.previous_tag.outputs.previous_tag }}'; + const previousTag = ${{ steps.previous_release.outputs.result }}; + + if (!previousTag) { + console.log('No previous release found, skipping'); + return []; + } console.log(`Finding PRs between ${previousTag} and ${currentTag}`); @@ -53,51 +76,30 @@ jobs: const commits = comparison.data.commits; console.log(`Found ${commits.length} commits`); - // Extract PR numbers from commit messages + // Get PRs associated with each commit using GitHub API const prNumbers = new Set(); - const prPattern = /#(\d+)/g; for (const commit of commits) { - const message = commit.commit.message; - const matches = message.matchAll(prPattern); - - for (const match of matches) { - const prNumber = parseInt(match[1]); - prNumbers.add(prNumber); - } - - // Also check if the commit is associated with a PR - if (commit.commit.message.includes('Merge pull request #')) { - const match = commit.commit.message.match(/Merge pull request #(\d+)/); - if (match) { - prNumbers.add(parseInt(match[1])); - } - } - } - - // Verify these are actually PRs (not issues) - const validPRs = []; - - for (const prNumber of prNumbers) { try { - const { data: pr } = await github.rest.pulls.get({ + const { data: prs } = await github.rest.repos.listPullRequestsAssociatedWithCommit({ owner: context.repo.owner, repo: context.repo.repo, - pull_number: prNumber + commit_sha: commit.sha }); - - // Only include merged PRs - if (pr.merged_at) { - validPRs.push(prNumber); - console.log(`Found valid merged PR: #${prNumber}`); + + for (const pr of prs) { + if (pr.merged_at) { + prNumbers.add(pr.number); + console.log(`Found merged PR: #${pr.number}`); + } } } catch (error) { - console.log(`#${prNumber} is not a valid PR or was not found`); + console.log(`Failed to get PRs for commit ${commit.sha}: ${error.message}`); } } - console.log(`Found ${validPRs.length} valid merged PRs`); - return validPRs; + console.log(`Found ${prNumbers.size} merged PRs`); + return Array.from(prNumbers); - name: Comment on PRs uses: actions/github-script@v7 @@ -107,20 +109,40 @@ jobs: const releaseTag = '${{ github.event.release.tag_name }}'; const releaseUrl = '${{ github.event.release.html_url }}'; - const comment = `🎉 This pull request is included in [${releaseTag}](${releaseUrl})!`; + const comment = `This pull request is included in [${releaseTag}](${releaseUrl})`; + + let commentedCount = 0; for (const prNumber of prNumbers) { try { + // Check if we've already commented on this PR for this release + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + per_page: 100 + }); + + const alreadyCommented = comments.some(c => + c.user.type === 'Bot' && c.body.includes(releaseTag) + ); + + if (alreadyCommented) { + console.log(`Skipping PR #${prNumber} - already commented for ${releaseTag}`); + continue; + } + await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: prNumber, body: comment }); + commentedCount++; console.log(`Successfully commented on PR #${prNumber}`); } catch (error) { console.error(`Failed to comment on PR #${prNumber}:`, error.message); } } - console.log(`Commented on ${prNumbers.length} PRs`); + console.log(`Commented on ${commentedCount} of ${prNumbers.length} PRs`); From 632e6fbc99ca913ab04e8a851e2b8c5f14d1fcc2 Mon Sep 17 00:00:00 2001 From: Yugan Date: Thu, 11 Dec 2025 18:21:18 +0530 Subject: [PATCH 4/4] Removed whitespaces --- .github/workflows/comment-on-release.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/comment-on-release.yml b/.github/workflows/comment-on-release.yml index fa4cd3adb6..f8b1751e53 100644 --- a/.github/workflows/comment-on-release.yml +++ b/.github/workflows/comment-on-release.yml @@ -23,31 +23,32 @@ jobs: with: script: | const currentTag = '${{ github.event.release.tag_name }}'; - + // Get all releases const { data: releases } = await github.rest.repos.listReleases({ owner: context.repo.owner, repo: context.repo.repo, per_page: 100 }); - + // Find current release index const currentIndex = releases.findIndex(r => r.tag_name === currentTag); - + if (currentIndex === -1) { console.log('Current release not found in list'); return null; } - + // Get previous release (next in the list since they're sorted by date desc) const previousRelease = releases[currentIndex + 1]; - + if (!previousRelease) { console.log('No previous release found, this might be the first release'); return null; } - + console.log(`Found previous release: ${previousRelease.tag_name}`); + return previousRelease.tag_name; - name: Get merged PRs between releases @@ -57,7 +58,7 @@ jobs: script: | const currentTag = '${{ github.event.release.tag_name }}'; const previousTag = ${{ steps.previous_release.outputs.result }}; - + if (!previousTag) { console.log('No previous release found, skipping'); return [];