Download scripts/releasing/patch-create-comment.js from SaylorTwift/gemini-cli: direct link, hf CLI and curl.
- Browser
- Download file 13.5 kB
-
https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/scripts/releasing/patch-create-comment.js
- Command line
-
hf download hf://SaylorTwift/gemini-cli/scripts/releasing/patch-create-comment.js
-
curl -L -o patch-create-comment.js https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/scripts/releasing/patch-create-comment.js
13.5 kB
| /** | |
| * @license | |
| * Copyright 2025 Google LLC | |
| * SPDX-License-Identifier: Apache-2.0 | |
| */ | |
| /** | |
| * Script for commenting on the original PR after patch creation (step 1). | |
| * Handles parsing create-patch-pr.js output and creating appropriate feedback. | |
| */ | |
| import yargs from 'yargs'; | |
| import { hideBin } from 'yargs/helpers'; | |
| async function main() { | |
| const argv = await yargs(hideBin(process.argv)) | |
| .option('original-pr', { | |
| description: 'The original PR number to comment on', | |
| type: 'number', | |
| demandOption: !process.env.GITHUB_ACTIONS, | |
| }) | |
| .option('exit-code', { | |
| description: 'Exit code from patch creation step', | |
| type: 'number', | |
| demandOption: !process.env.GITHUB_ACTIONS, | |
| }) | |
| .option('commit', { | |
| description: 'The commit SHA being patched', | |
| type: 'string', | |
| demandOption: !process.env.GITHUB_ACTIONS, | |
| }) | |
| .option('channel', { | |
| description: 'The channel (stable or preview)', | |
| type: 'string', | |
| choices: ['stable', 'preview'], | |
| demandOption: !process.env.GITHUB_ACTIONS, | |
| }) | |
| .option('repository', { | |
| description: 'The GitHub repository (owner/repo format)', | |
| type: 'string', | |
| demandOption: !process.env.GITHUB_ACTIONS, | |
| }) | |
| .option('run-id', { | |
| description: 'The GitHub workflow run ID', | |
| type: 'string', | |
| }) | |
| .option('environment', { | |
| choices: ['prod', 'dev'], | |
| type: 'string', | |
| default: process.env.ENVIRONMENT || 'prod', | |
| }) | |
| .option('test', { | |
| description: 'Test mode - validate logic without GitHub API calls', | |
| type: 'boolean', | |
| default: false, | |
| }) | |
| .example( | |
| '$0 --original-pr 8655 --exit-code 0 --commit abc1234 --channel preview --repository google-gemini/gemini-cli --test', | |
| 'Test success comment', | |
| ) | |
| .example( | |
| '$0 --original-pr 8655 --exit-code 1 --commit abc1234 --channel stable --repository google-gemini/gemini-cli --test', | |
| 'Test failure comment', | |
| ) | |
| .help() | |
| .alias('help', 'h').argv; | |
| const testMode = argv.test || process.env.TEST_MODE === 'true'; | |
| // GitHub CLI is available in the workflow environment | |
| const hasGitHubCli = !testMode; | |
| // Get inputs from CLI args or environment | |
| const originalPr = argv.originalPr || process.env.ORIGINAL_PR; | |
| const exitCode = | |
| argv.exitCode !== undefined | |
| ? argv.exitCode | |
| : parseInt(process.env.EXIT_CODE || '1'); | |
| const commit = argv.commit || process.env.COMMIT; | |
| const channel = argv.channel || process.env.CHANNEL; | |
| const environment = argv.environment; | |
| const repository = | |
| argv.repository || process.env.REPOSITORY || 'google-gemini/gemini-cli'; | |
| const runId = argv.runId || process.env.GITHUB_RUN_ID || '0'; | |
| // Validate required parameters | |
| if (!runId || runId === '0') { | |
| console.warn( | |
| 'Warning: No valid GitHub run ID found, workflow links may not work correctly', | |
| ); | |
| } | |
| if (!originalPr) { | |
| console.log('No original PR specified, skipping comment'); | |
| return; | |
| } | |
| console.log( | |
| `Analyzing patch creation result for PR ${originalPr} (exit code: ${exitCode})`, | |
| ); | |
| const [_owner, _repo] = repository.split('/'); | |
| const npmTag = channel === 'stable' ? 'latest' : 'preview'; | |
| if (testMode) { | |
| console.log('\nπ§ͺ TEST MODE - No API calls will be made'); | |
| console.log('\nπ Inputs:'); | |
| console.log(` - Original PR: ${originalPr}`); | |
| console.log(` - Exit Code: ${exitCode}`); | |
| console.log(` - Commit: ${commit}`); | |
| console.log(` - Channel: ${channel} β npm tag: ${npmTag}`); | |
| console.log(` - Repository: ${repository}`); | |
| console.log(` - Run ID: ${runId}`); | |
| } | |
| let commentBody; | |
| let logContent = ''; | |
| // Get log content from environment variable or generate mock content for testing | |
| if (testMode && !process.env.LOG_CONTENT) { | |
| // Create mock log content for testing only if LOG_CONTENT is not provided | |
| if (exitCode === 0) { | |
| logContent = `Creating hotfix branch hotfix/v0.5.3/${channel}/cherry-pick-${commit.substring(0, 7)} from release/v0.5.3`; | |
| } else { | |
| logContent = 'Error: Failed to create patch'; | |
| } | |
| } else { | |
| // Use log content from environment variable | |
| logContent = process.env.LOG_CONTENT || ''; | |
| } | |
| if ( | |
| logContent.includes( | |
| 'Failed to create release branch due to insufficient GitHub App permissions', | |
| ) | |
| ) { | |
| // GitHub App permission error - extract manual commands | |
| const manualCommandsMatch = logContent.match( | |
| /π Please run these commands manually to create the branch:[\s\S]*?```bash\s*([\s\S]*?)\s*```/, | |
| ); | |
| let manualCommands = ''; | |
| if (manualCommandsMatch) { | |
| manualCommands = manualCommandsMatch[1].trim(); | |
| } | |
| commentBody = `π **[Step 2/4] GitHub App Permission Issue** | |
| The patch creation failed due to insufficient GitHub App permissions for creating workflow files. | |
| **π Manual Action Required:** | |
| ${ | |
| manualCommands | |
| ? `Please run these commands manually to create the release branch: | |
| \`\`\`bash | |
| ${manualCommands} | |
| \`\`\` | |
| After running these commands, you can re-run the patch workflow.` | |
| : 'Please check the workflow logs for manual commands to run.' | |
| } | |
| **π Links:** | |
| - [View workflow run](https://github.com/${repository}/actions/runs/${runId})`; | |
| } else if (logContent.includes('already has an open PR')) { | |
| // Branch exists with existing PR | |
| const prMatch = logContent.match(/Found existing PR #(\d+): (.*)/); | |
| if (prMatch) { | |
| const [, prNumber, prUrl] = prMatch; | |
| commentBody = `βΉοΈ **[Step 2/4] Patch PR already exists!** | |
| A patch PR for this change already exists: [#${prNumber}](${prUrl}). | |
| **π Next Steps:** | |
| 1. Review and approve the existing patch PR | |
| 2. If it's incorrect, close it and run the patch command again | |
| **π Links:** | |
| - [View existing patch PR #${prNumber}](${prUrl})`; | |
| } | |
| } else if (logContent.includes('exists but has no open PR')) { | |
| // Branch exists but no PR | |
| const branchMatch = logContent.match(/Hotfix branch (.*) already exists/); | |
| if (branchMatch) { | |
| const [, branch] = branchMatch; | |
| 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. | |
| **π Issue:** This might indicate an incomplete patch process. | |
| **π Next Steps:** | |
| 1. Delete the branch: \`git branch -D ${branch}\` | |
| 2. Run the patch command again | |
| **π Links:** | |
| - [View branch on GitHub](https://github.com/${repository}/tree/${branch})`; | |
| } | |
| } else if (exitCode === 0) { | |
| // Success - extract branch info | |
| const branchMatch = logContent.match(/Creating hotfix branch (.*) from/); | |
| if (branchMatch) { | |
| const [, branch] = branchMatch; | |
| if (testMode) { | |
| // Mock PR info for testing | |
| const mockPrNumber = Math.floor(Math.random() * 1000) + 8000; | |
| const mockPrUrl = `https://github.com/${repository}/pull/${mockPrNumber}`; | |
| const hasConflicts = | |
| logContent.includes('Cherry-pick has conflicts') || | |
| logContent.includes('[CONFLICTS]'); | |
| commentBody = `π **[Step 2/4] Patch PR Created!** | |
| **π Patch Details:** | |
| - **Environment**: \`${environment}\` | |
| - **Channel**: \`${channel}\` β will publish to npm tag \`${npmTag}\` | |
| - **Commit**: \`${commit}\` | |
| - **Hotfix Branch**: [\`${branch}\`](https://github.com/${repository}/tree/${branch}) | |
| - **Hotfix PR**: [#${mockPrNumber}](${mockPrUrl})${hasConflicts ? '\n- **β οΈ Status**: Cherry-pick conflicts detected - manual resolution required' : ''} | |
| **π Next Steps:** | |
| 1. ${hasConflicts ? 'β οΈ **Resolve conflicts** in the hotfix PR first' : 'Review and approve the hotfix PR'}: [#${mockPrNumber}](${mockPrUrl})${hasConflicts ? '\n2. **Test your changes** after resolving conflicts' : ''} | |
| ${hasConflicts ? '3' : '2'}. Once merged, the patch release will automatically trigger | |
| ${hasConflicts ? '4' : '3'}. You'll receive updates here when the release completes | |
| **π Track Progress:** | |
| - [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 { | |
| const { spawnSync } = await import('node:child_process'); | |
| const result = spawnSync( | |
| 'gh', | |
| [ | |
| 'pr', | |
| 'list', | |
| '--head', | |
| branch, | |
| '--state', | |
| 'open', | |
| '--json', | |
| 'number,title,url', | |
| '--limit', | |
| '1', | |
| ], | |
| { encoding: 'utf8' }, | |
| ); | |
| if (result.error) { | |
| throw result.error; | |
| } | |
| if (result.status !== 0) { | |
| throw new Error( | |
| `gh pr list failed with status ${result.status}: ${result.stderr}`, | |
| ); | |
| } | |
| const prListOutput = result.stdout; | |
| const prList = JSON.parse(prListOutput); | |
| if (prList.length > 0) { | |
| const pr = prList[0]; | |
| const hasConflicts = | |
| logContent.includes('Cherry-pick has conflicts') || | |
| pr.title.includes('[CONFLICTS]'); | |
| commentBody = `π **[Step 2/4] Patch PR Created!** | |
| **π Patch Details:** | |
| - **Environment**: \`${environment}\` | |
| - **Channel**: \`${channel}\` β will publish to npm tag \`${npmTag}\` | |
| - **Commit**: \`${commit}\` | |
| - **Hotfix Branch**: [\`${branch}\`](https://github.com/${repository}/tree/${branch}) | |
| - **Hotfix PR**: [#${pr.number}](${pr.url})${hasConflicts ? '\n- **β οΈ Status**: Cherry-pick conflicts detected - manual resolution required' : ''} | |
| **π Next Steps:** | |
| 1. ${hasConflicts ? 'β οΈ **Resolve conflicts** in the hotfix PR first' : 'Review and approve the hotfix PR'}: [#${pr.number}](${pr.url})${hasConflicts ? '\n2. **Test your changes** after resolving conflicts' : ''} | |
| ${hasConflicts ? '3' : '2'}. Once merged, the patch release will automatically trigger | |
| ${hasConflicts ? '4' : '3'}. You'll receive updates here when the release completes | |
| **π Track Progress:** | |
| - [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 = `π **[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}). | |
| **π Next Steps:** | |
| 1. Review and approve the patch PR | |
| 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) | |
| - [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 = `π **[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) | |
| - [This patch creation workflow run](https://github.com/${repository}/actions/runs/${runId})`; | |
| } | |
| } | |
| } | |
| } else { | |
| // Failure | |
| commentBody = `β **[Step 2/4] Patch creation failed!** | |
| There was an error creating the patch release. | |
| **π Troubleshooting:** | |
| - Check the workflow logs for detailed error information | |
| - Verify the commit SHA is valid and accessible | |
| - Ensure you have permissions to create branches and PRs | |
| **π Links:** | |
| - [View workflow run](https://github.com/${repository}/actions/runs/${runId})`; | |
| } | |
| if (!commentBody) { | |
| commentBody = `β **[Step 2/4] Patch creation failed!** | |
| No output was generated during patch creation. | |
| **π Links:** | |
| - [View workflow run](https://github.com/${repository}/actions/runs/${runId})`; | |
| } | |
| if (testMode) { | |
| console.log('\n㪠Would post comment:'); | |
| console.log('----------------------------------------'); | |
| console.log(commentBody); | |
| console.log('----------------------------------------'); | |
| console.log('\nβ Comment generation working correctly!'); | |
| } else if (hasGitHubCli) { | |
| const { spawnSync } = await import('node:child_process'); | |
| const { writeFileSync, unlinkSync } = await import('node:fs'); | |
| const { join } = await import('node:path'); | |
| // Write comment to temporary file to avoid shell escaping issues | |
| const tmpFile = join(process.cwd(), `comment-${Date.now()}.md`); | |
| writeFileSync(tmpFile, commentBody); | |
| try { | |
| const result = spawnSync( | |
| 'gh', | |
| ['pr', 'comment', originalPr.toString(), '--body-file', tmpFile], | |
| { | |
| stdio: 'inherit', | |
| }, | |
| ); | |
| if (result.error) { | |
| throw result.error; | |
| } | |
| if (result.status !== 0) { | |
| throw new Error(`gh pr comment failed with status ${result.status}`); | |
| } | |
| console.log(`Successfully commented on PR ${originalPr}`); | |
| } finally { | |
| // Clean up temp file | |
| try { | |
| unlinkSync(tmpFile); | |
| } catch { | |
| // Ignore cleanup errors | |
| } | |
| } | |
| } else { | |
| console.log('No GitHub CLI available'); | |
| } | |
| } | |
| main().catch((error) => { | |
| console.error('Error commenting on PR:', error); | |
| process.exit(1); | |
| }); | |