diff options
Diffstat (limited to 'packages/cli/src/ui')
| -rw-r--r-- | packages/cli/src/ui/commands/setupGithubCommand.test.ts | 66 | ||||
| -rw-r--r-- | packages/cli/src/ui/commands/setupGithubCommand.ts | 60 |
2 files changed, 126 insertions, 0 deletions
diff --git a/packages/cli/src/ui/commands/setupGithubCommand.test.ts b/packages/cli/src/ui/commands/setupGithubCommand.test.ts new file mode 100644 index 00000000..fe68be0c --- /dev/null +++ b/packages/cli/src/ui/commands/setupGithubCommand.test.ts @@ -0,0 +1,66 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { vi, describe, expect, it, afterEach, beforeEach } from 'vitest'; +import * as child_process from 'child_process'; +import { setupGithubCommand } from './setupGithubCommand.js'; +import { CommandContext, ToolActionReturn } from './types.js'; + +vi.mock('child_process'); + +describe('setupGithubCommand', () => { + beforeEach(() => { + vi.resetAllMocks(); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + it('returns a tool action to download github workflows and handles paths', () => { + const fakeRepoRoot = '/github.com/fake/repo/root'; + vi.mocked(child_process.execSync).mockReturnValue(fakeRepoRoot); + + const result = setupGithubCommand.action?.( + {} as CommandContext, + '', + ) as ToolActionReturn; + + expect(result.type).toBe('tool'); + expect(result.toolName).toBe('run_shell_command'); + expect(child_process.execSync).toHaveBeenCalledWith( + 'git rev-parse --show-toplevel', + { + encoding: 'utf-8', + }, + ); + expect(child_process.execSync).toHaveBeenCalledWith('git remote -v', { + encoding: 'utf-8', + }); + + const { command } = result.toolArgs; + + const expectedSubstrings = [ + `mkdir -p "${fakeRepoRoot}/.github/workflows"`, + `curl -fsSL -o "${fakeRepoRoot}/.github/workflows/gemini-cli.yml"`, + `curl -fsSL -o "${fakeRepoRoot}/.github/workflows/gemini-issue-automated-triage.yml"`, + `curl -fsSL -o "${fakeRepoRoot}/.github/workflows/gemini-issue-scheduled-triage.yml"`, + `curl -fsSL -o "${fakeRepoRoot}/.github/workflows/gemini-pr-review.yml"`, + 'https://raw.githubusercontent.com/google-github-actions/run-gemini-cli/refs/heads/main/workflows/', + ]; + + for (const substring of expectedSubstrings) { + expect(command).toContain(substring); + } + }); + + it('throws an error if git root cannot be determined', () => { + vi.mocked(child_process.execSync).mockReturnValue(''); + expect(() => { + setupGithubCommand.action?.({} as CommandContext, ''); + }).toThrow('Unable to determine the Git root directory.'); + }); +}); diff --git a/packages/cli/src/ui/commands/setupGithubCommand.ts b/packages/cli/src/ui/commands/setupGithubCommand.ts new file mode 100644 index 00000000..14314423 --- /dev/null +++ b/packages/cli/src/ui/commands/setupGithubCommand.ts @@ -0,0 +1,60 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import path from 'path'; +import { execSync } from 'child_process'; +import { isGitHubRepository } from '../../utils/gitUtils.js'; + +import { + CommandKind, + SlashCommand, + SlashCommandActionReturn, +} from './types.js'; + +export const setupGithubCommand: SlashCommand = { + name: 'setup-github', + description: 'Set up GitHub Actions', + kind: CommandKind.BUILT_IN, + action: (): SlashCommandActionReturn => { + const gitRootRepo = execSync('git rev-parse --show-toplevel', { + encoding: 'utf-8', + }).trim(); + + if (!isGitHubRepository()) { + throw new Error('Unable to determine the Git root directory.'); + } + + // TODO(#5198): pin workflow versions for release controls + const version = 'main'; + const workflowBaseUrl = `https://raw.githubusercontent.com/google-github-actions/run-gemini-cli/refs/heads/${version}/workflows/`; + + const workflows = [ + 'gemini-cli/gemini-cli.yml', + 'issue-triage/gemini-issue-automated-triage.yml', + 'issue-triage/gemini-issue-scheduled-triage.yml', + 'pr-review/gemini-pr-review.yml', + ]; + + const command = [ + 'set -e', + `mkdir -p "${gitRootRepo}/.github/workflows"`, + ...workflows.map((workflow) => { + const fileName = path.basename(workflow); + return `curl -fsSL -o "${gitRootRepo}/.github/workflows/${fileName}" "${workflowBaseUrl}/${workflow}"`; + }), + 'echo "Workflows downloaded successfully."', + ].join(' && '); + return { + type: 'tool', + toolName: 'run_shell_command', + toolArgs: { + description: + 'Setting up GitHub Actions to triage issues and review PRs with Gemini.', + command, + }, + }; + }, +}; |
