From 80cf2fe4445a9a1f1d55e9a558b27a85aa58ea21 Mon Sep 17 00:00:00 2001 From: Jerop Kipruto Date: Tue, 10 Mar 2026 13:15:04 -0400 Subject: [PATCH] fix(release): Improve Patch Release Workflow Comments: Clearer Approval Guidance (#21894) --- .../release-patch-0-from-comment.yml | 22 +++++++--- scripts/releasing/patch-comment.js | 19 ++++---- scripts/releasing/patch-create-comment.js | 30 +++++++------ scripts/releasing/patch-trigger.js | 8 ++-- scripts/tests/patch-create-comment.test.js | 44 ++++++++++++------- 5 files changed, 77 insertions(+), 46 deletions(-) diff --git a/.github/workflows/release-patch-0-from-comment.yml b/.github/workflows/release-patch-0-from-comment.yml index d73ba82abd..2bb7c27c7b 100644 --- a/.github/workflows/release-patch-0-from-comment.yml +++ b/.github/workflows/release-patch-0-from-comment.yml @@ -120,6 +120,9 @@ jobs: if (recentRuns.length > 0) { core.setOutput('dispatched_run_urls', recentRuns.map(r => r.html_url).join(',')); core.setOutput('dispatched_run_ids', recentRuns.map(r => r.id).join(',')); + + const markdownLinks = recentRuns.map(r => `- [View dispatched workflow run](${r.html_url})`).join('\n'); + core.setOutput('dispatched_run_links', markdownLinks); } - name: 'Comment on Failure' @@ -138,16 +141,19 @@ jobs: token: '${{ secrets.GITHUB_TOKEN }}' issue-number: '${{ github.event.issue.number }}' body: | - โœ… **Patch workflow(s) dispatched successfully!** + ๐Ÿš€ **[Step 1/4] Patch workflow(s) waiting for approval!** **๐Ÿ“‹ Details:** - **Channels**: `${{ steps.dispatch_patch.outputs.dispatched_channels }}` - **Commit**: `${{ steps.pr_status.outputs.MERGE_COMMIT_SHA }}` - **Workflows Created**: ${{ steps.dispatch_patch.outputs.dispatched_run_count }} + **โณ Status:** The patch creation workflow has been triggered and is waiting for deployment approval. Please visit the specific workflow links below and approve the runs. + **๐Ÿ”— Track Progress:** - - [View patch workflows](https://github.com/${{ github.repository }}/actions/workflows/release-patch-1-create-pr.yml) - - [This workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) + ${{ steps.dispatch_patch.outputs.dispatched_run_links }} + - [View patch workflow history](https://github.com/${{ github.repository }}/actions/workflows/release-patch-1-create-pr.yml) + - [This trigger workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) - name: 'Final Status Comment - Dispatch Success (No URL)' if: "always() && startsWith(github.event.comment.body, '/patch') && steps.dispatch_patch.outcome == 'success' && !steps.dispatch_patch.outputs.dispatched_run_urls" @@ -156,16 +162,18 @@ jobs: token: '${{ secrets.GITHUB_TOKEN }}' issue-number: '${{ github.event.issue.number }}' body: | - โœ… **Patch workflow(s) dispatched successfully!** + ๐Ÿš€ **[Step 1/4] Patch workflow(s) waiting for approval!** **๐Ÿ“‹ Details:** - **Channels**: `${{ steps.dispatch_patch.outputs.dispatched_channels }}` - **Commit**: `${{ steps.pr_status.outputs.MERGE_COMMIT_SHA }}` - **Workflows Created**: ${{ steps.dispatch_patch.outputs.dispatched_run_count }} + **โณ Status:** The patch creation workflow has been triggered and is waiting for deployment approval. Please visit the workflow history link below and approve the runs. + **๐Ÿ”— Track Progress:** - - [View patch workflows](https://github.com/${{ github.repository }}/actions/workflows/release-patch-1-create-pr.yml) - - [This workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) + - [View patch workflow history](https://github.com/${{ github.repository }}/actions/workflows/release-patch-1-create-pr.yml) + - [This trigger workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) - name: 'Final Status Comment - Failure' if: "always() && startsWith(github.event.comment.body, '/patch') && (steps.dispatch_patch.outcome == 'failure' || steps.dispatch_patch.outcome == 'cancelled')" @@ -174,7 +182,7 @@ jobs: token: '${{ secrets.GITHUB_TOKEN }}' issue-number: '${{ github.event.issue.number }}' body: | - โŒ **Patch workflow dispatch failed!** + โŒ **[Step 1/4] Patch workflow dispatch failed!** There was an error dispatching the patch creation workflow. diff --git a/scripts/releasing/patch-comment.js b/scripts/releasing/patch-comment.js index 7c7fe7d5ed..98a26cd917 100644 --- a/scripts/releasing/patch-comment.js +++ b/scripts/releasing/patch-comment.js @@ -128,7 +128,7 @@ async function main() { let commentBody; if (success) { - commentBody = `โœ… **Patch Release Complete!** + commentBody = `โœ… **[Step 4/4] Patch Release Complete!** **๐Ÿ“ฆ Release Details:** - **Version**: [\`${releaseVersion}\`](https://github.com/${repo.owner}/${repo.repo}/releases/tag/${releaseTag}) @@ -144,9 +144,10 @@ async function main() { **๐Ÿ”— Links:** - [GitHub Release](https://github.com/${repo.owner}/${repo.repo}/releases/tag/${releaseTag}) -- [Workflow Run](https://github.com/${repo.owner}/${repo.repo}/actions/runs/${runId})`; +- [This release workflow run](https://github.com/${repo.owner}/${repo.repo}/actions/runs/${runId}) +- [Workflow History](https://github.com/${repo.owner}/${repo.repo}/actions/workflows/release-patch-3-release.yml)`; } else if (raceConditionFailure) { - commentBody = `โš ๏ธ **Patch Release Cancelled - Concurrent Release Detected** + commentBody = `โš ๏ธ **[Step 4/4] Patch Release Cancelled - Concurrent Release Detected** **๐Ÿšฆ What Happened:** Another patch release completed while this one was in progress, causing a version conflict. @@ -163,7 +164,7 @@ Another patch release completed while this one was in progress, causing a versio - **Next patch should be**: \`${currentReleaseVersion}\` - **New release tag**: \`${currentReleaseTag || 'unknown'}\`` : ` -- **Status**: Version information updated since this release started` +- **Status**: Version information updated since this release was triggered` } **๐Ÿ”„ Next Steps:** @@ -175,9 +176,10 @@ Another patch release completed while this one was in progress, causing a versio Multiple patch releases can't run simultaneously. When they do, the second one is automatically cancelled to maintain version consistency. **๐Ÿ”— Details:** -- [View cancelled workflow run](https://github.com/${repo.owner}/${repo.repo}/actions/runs/${runId})`; +- [This release workflow run](https://github.com/${repo.owner}/${repo.repo}/actions/runs/${runId}) +- [Workflow History](https://github.com/${repo.owner}/${repo.repo}/actions/workflows/release-patch-3-release.yml)`; } else { - commentBody = `โŒ **Patch Release Failed!** + commentBody = `โŒ **[Step 4/4] Patch Release Failed!** **๐Ÿ“‹ Details:** - **Version**: \`${releaseVersion || 'Unknown'}\` @@ -190,8 +192,9 @@ Multiple patch releases can't run simultaneously. When they do, the second one i 3. You may need to retry the patch once the issue is resolved **๐Ÿ”— Troubleshooting:** -- [View workflow run](https://github.com/${repo.owner}/${repo.repo}/actions/runs/${runId}) -- [View workflow logs](https://github.com/${repo.owner}/${repo.repo}/actions/runs/${runId})`; +- [This release workflow run](https://github.com/${repo.owner}/${repo.repo}/actions/runs/${runId}) +- [View workflow logs](https://github.com/${repo.owner}/${repo.repo}/actions/runs/${runId}) +- [Workflow History](https://github.com/${repo.owner}/${repo.repo}/actions/workflows/release-patch-3-release.yml)`; } if (testMode) { diff --git a/scripts/releasing/patch-create-comment.js b/scripts/releasing/patch-create-comment.js index c7b8422c6b..32a0b329e2 100644 --- a/scripts/releasing/patch-create-comment.js +++ b/scripts/releasing/patch-create-comment.js @@ -145,7 +145,7 @@ async function main() { manualCommands = manualCommandsMatch[1].trim(); } - commentBody = `๐Ÿ”’ **GitHub App Permission Issue** + commentBody = `๐Ÿ”’ **[Step 2/4] GitHub App Permission Issue** The patch creation failed due to insufficient GitHub App permissions for creating workflow files. @@ -169,7 +169,7 @@ After running these commands, you can re-run the patch workflow.` const prMatch = logContent.match(/Found existing PR #(\d+): (.*)/); if (prMatch) { const [, prNumber, prUrl] = prMatch; - commentBody = `โ„น๏ธ **Patch PR already exists!** + commentBody = `โ„น๏ธ **[Step 2/4] Patch PR already exists!** A patch PR for this change already exists: [#${prNumber}](${prUrl}). @@ -185,7 +185,7 @@ A patch PR for this change already exists: [#${prNumber}](${prUrl}). const branchMatch = logContent.match(/Hotfix branch (.*) already exists/); if (branchMatch) { const [, branch] = branchMatch; - commentBody = `โ„น๏ธ **Patch branch exists but no PR found!** + commentBody = `โ„น๏ธ **[Step 2/4] Patch branch exists but no PR found!** A patch branch [\`${branch}\`](https://github.com/${repository}/tree/${branch}) exists but has no open PR. @@ -213,7 +213,7 @@ A patch branch [\`${branch}\`](https://github.com/${repository}/tree/${branch}) logContent.includes('Cherry-pick has conflicts') || logContent.includes('[CONFLICTS]'); - commentBody = `๐Ÿš€ **Patch PR Created!** + commentBody = `๐Ÿš€ **[Step 2/4] Patch PR Created!** **๐Ÿ“‹ Patch Details:** - **Environment**: \`${environment}\` @@ -228,7 +228,8 @@ ${hasConflicts ? '3' : '2'}. Once merged, the patch release will automatically t ${hasConflicts ? '4' : '3'}. You'll receive updates here when the release completes **๐Ÿ”— Track Progress:** -- [View hotfix PR #${mockPrNumber}](${mockPrUrl})`; +- [View hotfix PR #${mockPrNumber}](${mockPrUrl}) +- [This patch creation workflow run](https://github.com/${repository}/actions/runs/${runId})`; } else if (hasGitHubCli) { // Find the actual PR for the new branch using gh CLI try { @@ -269,7 +270,7 @@ ${hasConflicts ? '4' : '3'}. You'll receive updates here when the release comple logContent.includes('Cherry-pick has conflicts') || pr.title.includes('[CONFLICTS]'); - commentBody = `๐Ÿš€ **Patch PR Created!** + commentBody = `๐Ÿš€ **[Step 2/4] Patch PR Created!** **๐Ÿ“‹ Patch Details:** - **Environment**: \`${environment}\` @@ -284,10 +285,11 @@ ${hasConflicts ? '3' : '2'}. Once merged, the patch release will automatically t ${hasConflicts ? '4' : '3'}. You'll receive updates here when the release completes **๐Ÿ”— Track Progress:** -- [View hotfix PR #${pr.number}](${pr.url})`; +- [View hotfix PR #${pr.number}](${pr.url}) +- [This patch creation workflow run](https://github.com/${repository}/actions/runs/${runId})`; } else { // Fallback if PR not found yet - commentBody = `๐Ÿš€ **Patch PR Created!** + commentBody = `๐Ÿš€ **[Step 2/4] Patch PR Created!** The patch release PR for this change has been created on branch [\`${branch}\`](https://github.com/${repository}/tree/${branch}). @@ -296,23 +298,25 @@ The patch release PR for this change has been created on branch [\`${branch}\`]( 2. Once merged, the patch release will automatically trigger **๐Ÿ”— Links:** -- [View all patch PRs](https://github.com/${repository}/pulls?q=is%3Apr+is%3Aopen+label%3Apatch)`; +- [View all patch PRs](https://github.com/${repository}/pulls?q=is%3Apr+is%3Aopen+label%3Apatch) +- [This patch creation workflow run](https://github.com/${repository}/actions/runs/${runId})`; } } catch (error) { console.log('Error finding PR for branch:', error.message); // Fallback - commentBody = `๐Ÿš€ **Patch PR Created!** + commentBody = `๐Ÿš€ **[Step 2/4] Patch PR Created!** The patch release PR for this change has been created. **๐Ÿ”— Links:** -- [View all patch PRs](https://github.com/${repository}/pulls?q=is%3Apr+is%3Aopen+label%3Apatch)`; +- [View all patch PRs](https://github.com/${repository}/pulls?q=is%3Apr+is%3Aopen+label%3Apatch) +- [This patch creation workflow run](https://github.com/${repository}/actions/runs/${runId})`; } } } } else { // Failure - commentBody = `โŒ **Patch creation failed!** + commentBody = `โŒ **[Step 2/4] Patch creation failed!** There was an error creating the patch release. @@ -326,7 +330,7 @@ There was an error creating the patch release. } if (!commentBody) { - commentBody = `โŒ **Patch creation failed!** + commentBody = `โŒ **[Step 2/4] Patch creation failed!** No output was generated during patch creation. diff --git a/scripts/releasing/patch-trigger.js b/scripts/releasing/patch-trigger.js index a6e831b6ee..b8dfa97dfb 100644 --- a/scripts/releasing/patch-trigger.js +++ b/scripts/releasing/patch-trigger.js @@ -115,6 +115,7 @@ async function main() { const isDryRun = argv.dryRun || body.includes('[DRY RUN]'); const forceSkipTests = argv.forceSkipTests || process.env.FORCE_SKIP_TESTS === 'true'; + const runId = process.env.GITHUB_RUN_ID || '0'; if (!headRef) { throw new Error( @@ -264,7 +265,7 @@ async function main() { console.log(`Commenting on original PR ${originalPr}...`); const npmTag = channel === 'stable' ? 'latest' : 'preview'; - const commentBody = `๐Ÿš€ **Patch Release Started!** + const commentBody = `๐Ÿš€ **[Step 3/4] Patch Release ${environment === 'prod' ? 'Waiting for Approval' : 'Triggered'}!** **๐Ÿ“‹ Release Details:** - **Environment**: \`${environment}\` @@ -273,10 +274,11 @@ async function main() { - **Hotfix PR**: Merged โœ… - **Release Branch**: [\`${releaseRef}\`](https://github.com/${context.repo.owner}/${context.repo.repo}/tree/${releaseRef}) -**โณ Status:** The patch release is now running. You'll receive another update when it completes. +**โณ Status:** The patch release has been triggered${environment === 'prod' ? ' and is waiting for deployment approval. Please visit the specific workflow run link below and approve the deployment' : ''}. You'll receive another update when it completes. **๐Ÿ”— Track Progress:** -- [View release workflow](https://github.com/${context.repo.owner}/${context.repo.repo}/actions)`; +- [View release workflow history](https://github.com/${context.repo.owner}/${context.repo.repo}/actions/workflows/${workflowId}) +- [This trigger workflow run](https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId})`; if (!testMode) { let tempDir; diff --git a/scripts/tests/patch-create-comment.test.js b/scripts/tests/patch-create-comment.test.js index e38cd4ed10..befced4ee8 100644 --- a/scripts/tests/patch-create-comment.test.js +++ b/scripts/tests/patch-create-comment.test.js @@ -57,7 +57,7 @@ describe('patch-create-comment', () => { ); expect(result.success).toBe(true); - expect(result.stdout).toContain('๐Ÿš€ **Patch PR Created!**'); + expect(result.stdout).toContain('๐Ÿš€ **[Step 2/4] Patch PR Created!**'); expect(result.stdout).toContain('Environment**: `prod`'); }); @@ -68,7 +68,7 @@ describe('patch-create-comment', () => { ); expect(result.success).toBe(true); - expect(result.stdout).toContain('๐Ÿš€ **Patch PR Created!**'); + expect(result.stdout).toContain('๐Ÿš€ **[Step 2/4] Patch PR Created!**'); expect(result.stdout).toContain('Environment**: `dev`'); }); @@ -90,7 +90,7 @@ describe('patch-create-comment', () => { ); expect(result.success).toBe(true); - expect(result.stdout).toContain('๐Ÿš€ **Patch PR Created!**'); + expect(result.stdout).toContain('๐Ÿš€ **[Step 2/4] Patch PR Created!**'); expect(result.stdout).toContain('Environment**: `prod`'); }); }); @@ -106,7 +106,7 @@ describe('patch-create-comment', () => { ); expect(result.success).toBe(true); - expect(result.stdout).toContain('๐Ÿš€ **Patch PR Created!**'); + expect(result.stdout).toContain('๐Ÿš€ **[Step 2/4] Patch PR Created!**'); expect(result.stdout).toContain('Channel**: `preview`'); expect(result.stdout).toContain('Commit**: `abc1234`'); }); @@ -118,7 +118,9 @@ describe('patch-create-comment', () => { ); expect(result.success).toBe(true); - expect(result.stdout).toContain('โŒ **Patch creation failed!**'); + expect(result.stdout).toContain( + 'โŒ **[Step 2/4] Patch creation failed!**', + ); expect(result.stdout).toContain( 'There was an error creating the patch release', ); @@ -136,7 +138,7 @@ describe('patch-create-comment', () => { ); expect(result.success).toBe(true); - expect(result.stdout).toContain('๐Ÿš€ **Patch PR Created!**'); + expect(result.stdout).toContain('๐Ÿš€ **[Step 2/4] Patch PR Created!**'); expect(result.stdout).toContain('Channel**: `stable`'); expect(result.stdout).toContain('Commit**: `abc1234`'); expect(result.stdout).not.toContain('โš ๏ธ Status'); @@ -152,7 +154,7 @@ describe('patch-create-comment', () => { ); expect(result.success).toBe(true); - expect(result.stdout).toContain('๐Ÿš€ **Patch PR Created!**'); + expect(result.stdout).toContain('๐Ÿš€ **[Step 2/4] Patch PR Created!**'); expect(result.stdout).toContain( 'โš ๏ธ Status**: Cherry-pick conflicts detected', ); @@ -174,7 +176,9 @@ describe('patch-create-comment', () => { ); expect(result.success).toBe(true); - expect(result.stdout).toContain('โ„น๏ธ **Patch PR already exists!**'); + expect(result.stdout).toContain( + 'โ„น๏ธ **[Step 2/4] Patch PR already exists!**', + ); expect(result.stdout).toContain( 'A patch PR for this change already exists: [#8700](https://github.com/google-gemini/gemini-cli/pull/8700)', ); @@ -194,7 +198,7 @@ describe('patch-create-comment', () => { expect(result.success).toBe(true); expect(result.stdout).toContain( - 'โ„น๏ธ **Patch branch exists but no PR found!**', + 'โ„น๏ธ **[Step 2/4] Patch branch exists but no PR found!**', ); expect(result.stdout).toContain( 'Delete the branch: `git branch -D hotfix/v0.5.0-preview.2/preview/cherry-pick-jkl3456`', @@ -213,7 +217,9 @@ describe('patch-create-comment', () => { ); expect(result.success).toBe(true); - expect(result.stdout).toContain('โŒ **Patch creation failed!**'); + expect(result.stdout).toContain( + 'โŒ **[Step 2/4] Patch creation failed!**', + ); expect(result.stdout).toContain( 'There was an error creating the patch release', ); @@ -231,7 +237,9 @@ describe('patch-create-comment', () => { ); expect(result.success).toBe(true); - expect(result.stdout).toContain('โŒ **Patch creation failed!**'); + expect(result.stdout).toContain( + 'โŒ **[Step 2/4] Patch creation failed!**', + ); expect(result.stdout).toContain( 'There was an error creating the patch release', ); @@ -292,7 +300,9 @@ describe('patch-create-comment', () => { ); expect(result.success).toBe(true); - expect(result.stdout).toContain('โŒ **Patch creation failed!**'); + expect(result.stdout).toContain( + 'โŒ **[Step 2/4] Patch creation failed!**', + ); expect(result.stdout).toContain( 'There was an error creating the patch release', ); @@ -316,7 +326,9 @@ git push origin hotfix/v0.4.1/stable/cherry-pick-abc1234 ); expect(result.success).toBe(true); - expect(result.stdout).toContain('๐Ÿ”’ **GitHub App Permission Issue**'); + expect(result.stdout).toContain( + '๐Ÿ”’ **[Step 2/4] GitHub App Permission Issue**', + ); expect(result.stdout).toContain( 'Please run these commands manually to create the release branch:', ); @@ -339,7 +351,7 @@ git push origin hotfix/v0.4.1/stable/cherry-pick-abc1234 expect(result.stdout).toContain( '๐Ÿงช TEST MODE - No API calls will be made', ); - expect(result.stdout).toContain('๐Ÿš€ **Patch PR Created!**'); + expect(result.stdout).toContain('๐Ÿš€ **[Step 2/4] Patch PR Created!**'); }); it('should generate mock content in test mode for failure', () => { @@ -348,7 +360,9 @@ git push origin hotfix/v0.4.1/stable/cherry-pick-abc1234 ); expect(result.success).toBe(true); - expect(result.stdout).toContain('โŒ **Patch creation failed!**'); + expect(result.stdout).toContain( + 'โŒ **[Step 2/4] Patch creation failed!**', + ); }); }); });