diff --git a/app/gitspace/orchestrator/container/embedded_docker_container_orchestrator.go b/app/gitspace/orchestrator/container/embedded_docker_container_orchestrator.go index 6fe92abce..77a47eda9 100644 --- a/app/gitspace/orchestrator/container/embedded_docker_container_orchestrator.go +++ b/app/gitspace/orchestrator/container/embedded_docker_container_orchestrator.go @@ -675,6 +675,17 @@ func (e *EmbeddedDockerOrchestrator) buildSetupSteps( }, StopOnFailure: true, }, + { + Name: "Install AI agents", + Execute: func( + ctx context.Context, + exec *devcontainer.Exec, + gitspaceLogger gitspaceTypes.GitspaceLogger, + ) error { + return utils.InstallAIAgents(ctx, exec, gitspaceLogger, gitspaceConfig.AIAgents) + }, + StopOnFailure: true, + }, { Name: "Setup IDE", Execute: func( diff --git a/app/gitspace/orchestrator/utils/ai_agent.go b/app/gitspace/orchestrator/utils/ai_agent.go new file mode 100644 index 000000000..1a1e3ecfc --- /dev/null +++ b/app/gitspace/orchestrator/utils/ai_agent.go @@ -0,0 +1,80 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import ( + "context" + "fmt" + + "github.com/harness/gitness/app/gitspace/orchestrator/devcontainer" + "github.com/harness/gitness/app/gitspace/types" + "github.com/harness/gitness/types/enum" +) + +const ( + templateClaudeCodeInstallScript = "install_claude_code.sh" +) + +type installAgentFun func(context.Context, *devcontainer.Exec, types.GitspaceLogger) error + +var installationMap = map[enum.AIAgent]installAgentFun{ + enum.AIAgentClaudeCode: installClaudeCode, +} + +func InstallAIAgents( + ctx context.Context, + exec *devcontainer.Exec, + gitspaceLogger types.GitspaceLogger, + aiAgents []enum.AIAgent, +) error { + gitspaceLogger.Info(fmt.Sprintf("Installing ai agents: %v...", aiAgents)) + for _, aiAgent := range aiAgents { + installFun, ok := installationMap[aiAgent] + if !ok { + return fmt.Errorf("installation not available for %s", aiAgent) + } + + if err := installFun(ctx, exec, gitspaceLogger); err != nil { + return err + } + } + + gitspaceLogger.Info("Successfully installed ai agents") + return nil +} + +func installClaudeCode( + ctx context.Context, + exec *devcontainer.Exec, + gitspaceLogger types.GitspaceLogger, +) error { + script, err := GenerateScriptFromTemplate( + templateClaudeCodeInstallScript, &types.SetupClaudeCodePayload{ + OSInfoScript: GetOSInfoScript(), + }) + if err != nil { + return fmt.Errorf( + "failed to generate script to install claude code from template %s: %w", + templateClaudeCodeInstallScript, err) + } + gitspaceLogger.Info("Installing claude code output...") + gitspaceLogger.Info("Installing claude code inside container") + err = exec.ExecuteCommandInHomeDirAndLog(ctx, script, true, gitspaceLogger, true) + if err != nil { + return fmt.Errorf("failed to install claude code : %w", err) + } + gitspaceLogger.Info("Successfully Installed claude code") + return nil +} diff --git a/app/gitspace/orchestrator/utils/script_templates/install_claude_code.sh b/app/gitspace/orchestrator/utils/script_templates/install_claude_code.sh new file mode 100644 index 000000000..b64d9c1ed --- /dev/null +++ b/app/gitspace/orchestrator/utils/script_templates/install_claude_code.sh @@ -0,0 +1,76 @@ +#!/bin/sh +osInfoScript={{ .OSInfoScript }} + +eval "$osInfoScript" + +# Function to install Node.js and npm +install_nodejs() { + echo "Checking if Node.js is installed..." + if ! command -v node >/dev/null 2>&1; then + echo "Installing Node.js for $(distro)" + case "$(distro)" in + debian) + export DEBIAN_FRONTEND=noninteractive + # Install Node.js 18.x LTS + curl -fsSL https://deb.nodesource.com/setup_18.x | bash - + apt-get install -y nodejs + ;; + fedora) + dnf install -y nodejs npm + ;; + opensuse) + zypper install -y nodejs18 npm18 + ;; + alpine) + apk add --no-cache nodejs npm + ;; + arch) + pacman -Syu --noconfirm nodejs npm + ;; + freebsd) + pkg install -y node18 npm + ;; + *) + echo "Unsupported distribution: $(distro). Please install Node.js manually." + exit 1 + ;; + esac + echo "Node.js installation completed." + else + echo "Node.js is already installed." + fi + # Verify installation + if command -v node >/dev/null && command -v npm >/dev/null; then + echo "Successfully installed Node.js and npm" + return 0 + else + echo "Failed to install Node.js and npm" + return 1 + fi +} + +# Function to install Claude Code CLI +install_claude_code() { + echo "Installing Claude Code CLI..." + npm install -g @anthropic-ai/claude-code + if command -v claude >/dev/null; then + echo "Claude Code CLI installed successfully!" + claude --version + return 0 + else + echo "ERROR: Claude Code CLI installation failed!" + return 1 + fi +} + +# Install required dependencies +if ! install_nodejs; then + echo "ERROR: Failed to install Node.js dependencies" + exit 1 +fi + +# Install Claude agent +if ! install_claude_code; then + echo "ERROR: Failed to install Claude Code CLI" + exit 1 +fi diff --git a/app/gitspace/types/script_template_payload.go b/app/gitspace/types/script_template_payload.go index b783c6a98..5301f2007 100644 --- a/app/gitspace/types/script_template_payload.go +++ b/app/gitspace/types/script_template_payload.go @@ -98,3 +98,7 @@ type RunIntellijIDEPayload struct { IdeDownloadURL string IdeDirName string } + +type SetupClaudeCodePayload struct { + OSInfoScript string +}