feat: remove node installation from gemini (#374)

Closes #

## Description
Standardising this across all modules:
- remove default node & nvm installation

<!-- Briefly describe what this PR does and why -->

## Type of Change

- [ ] New module
- [ ] Bug fix
- [x] Feature/enhancement
- [ ] Documentation
- [ ] Other

## Module Information

<!-- Delete this section if not applicable -->

**Path:** `registry/coder-labs/modules/gemini`  
**New version:** `v2.0.0`  
**Breaking change:** [x] Yes [ ] No

## Testing & Validation

- [x] Tests pass (`bun test`)
- [x] Code formatted (`bun run fmt`)
- [ ] Changes tested locally

## Related Issues

<!-- Link related issues or write "None" if not applicable -->

---------

Co-authored-by: DevCats <christofer@coder.com>
This commit is contained in:
35C4n0r 2025-08-25 23:23:48 +05:30 committed by GitHub
parent 68f881e220
commit 240643d3b0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 130 additions and 142 deletions

View File

@ -13,7 +13,7 @@ Run [Gemini CLI](https://github.com/google-gemini/gemini-cli) in your workspace
```tf ```tf
module "gemini" { module "gemini" {
source = "registry.coder.com/coder-labs/gemini/coder" source = "registry.coder.com/coder-labs/gemini/coder"
version = "1.1.0" version = "2.0.0"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
folder = "/home/coder/project" folder = "/home/coder/project"
} }
@ -30,7 +30,7 @@ module "gemini" {
## Prerequisites ## Prerequisites
- Node.js and npm will be installed automatically if not present - **Node.js and npm must be sourced/available before the gemini module installs** - ensure they are installed in your workspace image or via earlier provisioning steps
- The [Coder Login](https://registry.coder.com/modules/coder/coder-login) module is required - The [Coder Login](https://registry.coder.com/modules/coder/coder-login) module is required
## Examples ## Examples
@ -46,7 +46,7 @@ variable "gemini_api_key" {
module "gemini" { module "gemini" {
source = "registry.coder.com/coder-labs/gemini/coder" source = "registry.coder.com/coder-labs/gemini/coder"
version = "1.1.0" version = "2.0.0"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
gemini_api_key = var.gemini_api_key gemini_api_key = var.gemini_api_key
folder = "/home/coder/project" folder = "/home/coder/project"
@ -94,7 +94,7 @@ data "coder_parameter" "ai_prompt" {
module "gemini" { module "gemini" {
count = data.coder_workspace.me.start_count count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder-labs/gemini/coder" source = "registry.coder.com/coder-labs/gemini/coder"
version = "1.1.0" version = "2.0.0"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
gemini_api_key = var.gemini_api_key gemini_api_key = var.gemini_api_key
gemini_model = "gemini-2.5-flash" gemini_model = "gemini-2.5-flash"
@ -118,7 +118,7 @@ For enterprise users who prefer Google's Vertex AI platform:
```tf ```tf
module "gemini" { module "gemini" {
source = "registry.coder.com/coder-labs/gemini/coder" source = "registry.coder.com/coder-labs/gemini/coder"
version = "1.1.0" version = "2.0.0"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
gemini_api_key = var.gemini_api_key gemini_api_key = var.gemini_api_key
folder = "/home/coder/project" folder = "/home/coder/project"

View File

@ -153,7 +153,7 @@ describe("gemini", async () => {
}, },
}); });
await execModuleScript(id); await execModuleScript(id);
const resp = await readFileContainer(id, "/home/coder/.gemini-module/install.log"); const resp = await readFileContainer(id, "/home/coder/.gemini-module/agentapi-start.log");
expect(resp).toContain('GOOGLE_GENAI_USE_VERTEXAI=\'true\''); expect(resp).toContain('GOOGLE_GENAI_USE_VERTEXAI=\'true\'');
}); });
@ -166,7 +166,7 @@ describe("gemini", async () => {
}, },
}); });
await execModuleScript(id); await execModuleScript(id);
const resp = await readFileContainer(id, "/home/coder/.gemini-module/install.log"); const resp = await readFileContainer(id, "/home/coder/.gemini-module/agentapi-start.log");
expect(resp).toContain(model); expect(resp).toContain(model);
}); });
@ -193,7 +193,7 @@ describe("gemini", async () => {
}, },
}); });
await execModuleScript(id); await execModuleScript(id);
const resp = await readFileContainer(id, "/home/coder/.gemini-module/install.log"); const resp = await readFileContainer(id, "/home/coder/.gemini-module/agentapi-start.log");
expect(resp).toContain(folder); expect(resp).toContain(folder);
}); });

View File

@ -1,9 +1,9 @@
#!/bin/bash #!/bin/bash
BOLD='\033[0;1m' BOLD='\033[0;1m'
source "$HOME"/.bashrc
command_exists() { command_exists() {
command -v "$1" >/dev/null 2>&1 command -v "$1" > /dev/null 2>&1
} }
set -o nounset set -o nounset
@ -21,144 +21,132 @@ echo "--------------------------------"
set +o nounset set +o nounset
function install_node() { function check_dependencies() {
if ! command_exists npm; then if ! command_exists node; then
printf "npm not found, checking for Node.js installation...\n" printf "Error: Node.js is not installed. Please install Node.js manually or use the pre_install_script to install it.\n"
if ! command_exists node; then exit 1
printf "Node.js not found, installing Node.js via NVM...\n" fi
export NVM_DIR="$HOME/.nvm"
if [ ! -d "$NVM_DIR" ]; then
mkdir -p "$NVM_DIR"
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
else
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
fi
nvm install --lts if ! command_exists npm; then
nvm use --lts printf "Error: npm is not installed. Please install npm manually or use the pre_install_script to install it.\n"
nvm alias default node exit 1
fi
printf "Node.js installed: %s\n" "$(node --version)" printf "Node.js version: %s\n" "$(node --version)"
printf "npm installed: %s\n" "$(npm --version)" printf "npm version: %s\n" "$(npm --version)"
else
printf "Node.js is installed but npm is not available. Please install npm manually.\n"
exit 1
fi
fi
} }
function install_gemini() { function install_gemini() {
if [ "${ARG_INSTALL}" = "true" ]; then if [ "${ARG_INSTALL}" = "true" ]; then
install_node check_dependencies
if ! command_exists nvm; then
printf "which node: %s\n" "$(which node)"
printf "which npm: %s\n" "$(which npm)"
mkdir -p "$HOME"/.npm-global
npm config set prefix "$HOME/.npm-global"
export PATH="$HOME/.npm-global/bin:$PATH"
if ! grep -q "export PATH=$HOME/.npm-global/bin:\$PATH" ~/.bashrc; then
echo "export PATH=$HOME/.npm-global/bin:\$PATH" >> ~/.bashrc
fi
fi
printf "%s Installing Gemini CLI\n" "${BOLD}" printf "%s Installing Gemini CLI\n" "${BOLD}"
NPM_GLOBAL_PREFIX="${HOME}/.npm-global"
if [ ! -d "$NPM_GLOBAL_PREFIX" ]; then
mkdir -p "$NPM_GLOBAL_PREFIX"
fi
npm config set prefix "$NPM_GLOBAL_PREFIX"
export PATH="$NPM_GLOBAL_PREFIX/bin:$PATH"
if [ -n "$ARG_GEMINI_VERSION" ]; then if [ -n "$ARG_GEMINI_VERSION" ]; then
npm install -g "@google/gemini-cli@$ARG_GEMINI_VERSION" npm install -g "@google/gemini-cli@$ARG_GEMINI_VERSION"
else else
npm install -g "@google/gemini-cli" npm install -g "@google/gemini-cli"
fi fi
if ! grep -q "export PATH=\"\$HOME/.npm-global/bin:\$PATH\"" "$HOME/.bashrc"; then
echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> "$HOME/.bashrc"
fi
printf "%s Successfully installed Gemini CLI. Version: %s\n" "${BOLD}" "$(gemini --version)" printf "%s Successfully installed Gemini CLI. Version: %s\n" "${BOLD}" "$(gemini --version)"
fi fi
} }
function populate_settings_json() { function populate_settings_json() {
if [ "${ARG_GEMINI_CONFIG}" != "" ]; then if [ "${ARG_GEMINI_CONFIG}" != "" ]; then
SETTINGS_PATH="$HOME/.gemini/settings.json" SETTINGS_PATH="$HOME/.gemini/settings.json"
mkdir -p "$(dirname "$SETTINGS_PATH")" mkdir -p "$(dirname "$SETTINGS_PATH")"
printf "Custom gemini_config is provided !\n" printf "Custom gemini_config is provided !\n"
echo "${ARG_GEMINI_CONFIG}" > "$HOME/.gemini/settings.json" echo "${ARG_GEMINI_CONFIG}" > "$HOME/.gemini/settings.json"
else else
printf "No custom gemini_config provided, using default settings.json.\n" printf "No custom gemini_config provided, using default settings.json.\n"
append_extensions_to_settings_json append_extensions_to_settings_json
fi fi
} }
function append_extensions_to_settings_json() { function append_extensions_to_settings_json() {
SETTINGS_PATH="$HOME/.gemini/settings.json" SETTINGS_PATH="$HOME/.gemini/settings.json"
mkdir -p "$(dirname "$SETTINGS_PATH")" mkdir -p "$(dirname "$SETTINGS_PATH")"
printf "[append_extensions_to_settings_json] Starting extension merge process...\n" printf "[append_extensions_to_settings_json] Starting extension merge process...\n"
if [ -z "${BASE_EXTENSIONS:-}" ]; then if [ -z "${BASE_EXTENSIONS:-}" ]; then
printf "[append_extensions_to_settings_json] BASE_EXTENSIONS is empty, skipping merge.\n" printf "[append_extensions_to_settings_json] BASE_EXTENSIONS is empty, skipping merge.\n"
return return
fi fi
if [ ! -f "$SETTINGS_PATH" ]; then if [ ! -f "$SETTINGS_PATH" ]; then
printf "%s does not exist. Creating with merged mcpServers structure.\n" "$SETTINGS_PATH" printf "%s does not exist. Creating with merged mcpServers structure.\n" "$SETTINGS_PATH"
ADD_EXT_JSON='{}'
if [ -n "${ADDITIONAL_EXTENSIONS:-}" ]; then
ADD_EXT_JSON="$ADDITIONAL_EXTENSIONS"
fi
printf '{"mcpServers":%s}\n' "$(jq -s 'add' <(echo "$BASE_EXTENSIONS") <(echo "$ADD_EXT_JSON"))" > "$SETTINGS_PATH"
fi
TMP_SETTINGS=$(mktemp)
ADD_EXT_JSON='{}' ADD_EXT_JSON='{}'
if [ -n "${ADDITIONAL_EXTENSIONS:-}" ]; then if [ -n "${ADDITIONAL_EXTENSIONS:-}" ]; then
printf "[append_extensions_to_settings_json] ADDITIONAL_EXTENSIONS is set.\n"
ADD_EXT_JSON="$ADDITIONAL_EXTENSIONS" ADD_EXT_JSON="$ADDITIONAL_EXTENSIONS"
else
printf "[append_extensions_to_settings_json] ADDITIONAL_EXTENSIONS is empty or not set.\n"
fi fi
printf '{"mcpServers":%s}\n' "$(jq -s 'add' <(echo "$BASE_EXTENSIONS") <(echo "$ADD_EXT_JSON"))" > "$SETTINGS_PATH"
fi
printf "[append_extensions_to_settings_json] Merging BASE_EXTENSIONS and ADDITIONAL_EXTENSIONS into mcpServers...\n" TMP_SETTINGS=$(mktemp)
jq --argjson base "$BASE_EXTENSIONS" --argjson add "$ADD_EXT_JSON" \ ADD_EXT_JSON='{}'
'.mcpServers = (.mcpServers // {} + $base + $add)' \ if [ -n "${ADDITIONAL_EXTENSIONS:-}" ]; then
"$SETTINGS_PATH" > "$TMP_SETTINGS" && mv "$TMP_SETTINGS" "$SETTINGS_PATH" printf "[append_extensions_to_settings_json] ADDITIONAL_EXTENSIONS is set.\n"
ADD_EXT_JSON="$ADDITIONAL_EXTENSIONS"
else
printf "[append_extensions_to_settings_json] ADDITIONAL_EXTENSIONS is empty or not set.\n"
fi
jq '.theme = "Default" | .selectedAuthType = "gemini-api-key"' "$SETTINGS_PATH" > "$TMP_SETTINGS" && mv "$TMP_SETTINGS" "$SETTINGS_PATH" printf "[append_extensions_to_settings_json] Merging BASE_EXTENSIONS and ADDITIONAL_EXTENSIONS into mcpServers...\n"
jq --argjson base "$BASE_EXTENSIONS" --argjson add "$ADD_EXT_JSON" \
'.mcpServers = (.mcpServers // {} + $base + $add)' \
"$SETTINGS_PATH" > "$TMP_SETTINGS" && mv "$TMP_SETTINGS" "$SETTINGS_PATH"
printf "[append_extensions_to_settings_json] Merge complete.\n" jq '.theme = "Default" | .selectedAuthType = "gemini-api-key"' "$SETTINGS_PATH" > "$TMP_SETTINGS" && mv "$TMP_SETTINGS" "$SETTINGS_PATH"
printf "[append_extensions_to_settings_json] Merge complete.\n"
} }
function add_system_prompt_if_exists() { function add_system_prompt_if_exists() {
if [ -n "${GEMINI_SYSTEM_PROMPT:-}" ]; then if [ -n "${GEMINI_SYSTEM_PROMPT:-}" ]; then
if [ -d "${GEMINI_START_DIRECTORY}" ]; then if [ -d "${GEMINI_START_DIRECTORY}" ]; then
printf "Directory '%s' exists. Changing to it.\\n" "${GEMINI_START_DIRECTORY}" printf "Directory '%s' exists. Changing to it.\\n" "${GEMINI_START_DIRECTORY}"
cd "${GEMINI_START_DIRECTORY}" || { cd "${GEMINI_START_DIRECTORY}" || {
printf "Error: Could not change to directory '%s'.\\n" "${GEMINI_START_DIRECTORY}" printf "Error: Could not change to directory '%s'.\\n" "${GEMINI_START_DIRECTORY}"
exit 1 exit 1
} }
else
printf "Directory '%s' does not exist. Creating and changing to it.\\n" "${GEMINI_START_DIRECTORY}"
mkdir -p "${GEMINI_START_DIRECTORY}" || {
printf "Error: Could not create directory '%s'.\\n" "${GEMINI_START_DIRECTORY}"
exit 1
}
cd "${GEMINI_START_DIRECTORY}" || {
printf "Error: Could not change to directory '%s'.\\n" "${GEMINI_START_DIRECTORY}"
exit 1
}
fi
touch GEMINI.md
printf "Setting GEMINI.md\n"
echo "${GEMINI_SYSTEM_PROMPT}" > GEMINI.md
else else
printf "GEMINI.md is not set.\n" printf "Directory '%s' does not exist. Creating and changing to it.\\n" "${GEMINI_START_DIRECTORY}"
mkdir -p "${GEMINI_START_DIRECTORY}" || {
printf "Error: Could not create directory '%s'.\\n" "${GEMINI_START_DIRECTORY}"
exit 1
}
cd "${GEMINI_START_DIRECTORY}" || {
printf "Error: Could not change to directory '%s'.\\n" "${GEMINI_START_DIRECTORY}"
exit 1
}
fi fi
touch GEMINI.md
printf "Setting GEMINI.md\n"
echo "${GEMINI_SYSTEM_PROMPT}" > GEMINI.md
else
printf "GEMINI.md is not set.\n"
fi
} }
function configure_mcp() { function configure_mcp() {
export CODER_MCP_APP_STATUS_SLUG="gemini" export CODER_MCP_APP_STATUS_SLUG="gemini"
export CODER_MCP_AI_AGENTAPI_URL="http://localhost:3284" export CODER_MCP_AI_AGENTAPI_URL="http://localhost:3284"
coder exp mcp configure gemini "${GEMINI_START_DIRECTORY}" coder exp mcp configure gemini "${GEMINI_START_DIRECTORY}"
} }
install_gemini install_gemini
gemini --version
populate_settings_json populate_settings_json
add_system_prompt_if_exists add_system_prompt_if_exists
configure_mcp configure_mcp

View File

@ -5,7 +5,7 @@ set -o pipefail
source "$HOME"/.bashrc source "$HOME"/.bashrc
command_exists() { command_exists() {
command -v "$1" >/dev/null 2>&1 command -v "$1" > /dev/null 2>&1
} }
if [ -f "$HOME/.nvm/nvm.sh" ]; then if [ -f "$HOME/.nvm/nvm.sh" ]; then
@ -20,55 +20,55 @@ MODULE_DIR="$HOME/.gemini-module"
mkdir -p "$MODULE_DIR" mkdir -p "$MODULE_DIR"
if command_exists gemini; then if command_exists gemini; then
printf "Gemini is installed\n" printf "Gemini is installed\n"
else else
printf "Error: Gemini is not installed. Please enable install_gemini or install it manually :)\n" printf "Error: Gemini is not installed. Please enable install_gemini or install it manually :)\n"
exit 1 exit 1
fi fi
if [ -d "${GEMINI_START_DIRECTORY}" ]; then if [ -d "${GEMINI_START_DIRECTORY}" ]; then
printf "Directory '%s' exists. Changing to it.\\n" "${GEMINI_START_DIRECTORY}" printf "Directory '%s' exists. Changing to it.\\n" "${GEMINI_START_DIRECTORY}"
cd "${GEMINI_START_DIRECTORY}" || { cd "${GEMINI_START_DIRECTORY}" || {
printf "Error: Could not change to directory '%s'.\\n" "${GEMINI_START_DIRECTORY}" printf "Error: Could not change to directory '%s'.\\n" "${GEMINI_START_DIRECTORY}"
exit 1 exit 1
} }
else else
printf "Directory '%s' does not exist. Creating and changing to it.\\n" "${GEMINI_START_DIRECTORY}" printf "Directory '%s' does not exist. Creating and changing to it.\\n" "${GEMINI_START_DIRECTORY}"
mkdir -p "${GEMINI_START_DIRECTORY}" || { mkdir -p "${GEMINI_START_DIRECTORY}" || {
printf "Error: Could not create directory '%s'.\\n" "${GEMINI_START_DIRECTORY}" printf "Error: Could not create directory '%s'.\\n" "${GEMINI_START_DIRECTORY}"
exit 1 exit 1
} }
cd "${GEMINI_START_DIRECTORY}" || { cd "${GEMINI_START_DIRECTORY}" || {
printf "Error: Could not change to directory '%s'.\\n" "${GEMINI_START_DIRECTORY}" printf "Error: Could not change to directory '%s'.\\n" "${GEMINI_START_DIRECTORY}"
exit 1 exit 1
} }
fi fi
if [ -n "$GEMINI_TASK_PROMPT" ]; then if [ -n "$GEMINI_TASK_PROMPT" ]; then
printf "Running automated task: %s\n" "$GEMINI_TASK_PROMPT" printf "Running automated task: %s\n" "$GEMINI_TASK_PROMPT"
PROMPT="Every step of the way, report tasks to Coder with proper descriptions and statuses. Your task at hand: $GEMINI_TASK_PROMPT" PROMPT="Every step of the way, report tasks to Coder with proper descriptions and statuses. Your task at hand: $GEMINI_TASK_PROMPT"
PROMPT_FILE="$MODULE_DIR/prompt.txt" PROMPT_FILE="$MODULE_DIR/prompt.txt"
echo -n "$PROMPT" >"$PROMPT_FILE" echo -n "$PROMPT" > "$PROMPT_FILE"
GEMINI_ARGS=(--prompt-interactive "$PROMPT") GEMINI_ARGS=(--prompt-interactive "$PROMPT")
else else
printf "Starting Gemini CLI in interactive mode.\n" printf "Starting Gemini CLI in interactive mode.\n"
GEMINI_ARGS=() GEMINI_ARGS=()
fi fi
if [ -n "$GEMINI_YOLO_MODE" ] && [ "$GEMINI_YOLO_MODE" = "true" ]; then if [ -n "$GEMINI_YOLO_MODE" ] && [ "$GEMINI_YOLO_MODE" = "true" ]; then
printf "YOLO mode enabled - will auto-approve all tool calls\n" printf "YOLO mode enabled - will auto-approve all tool calls\n"
GEMINI_ARGS+=(--yolo) GEMINI_ARGS+=(--yolo)
fi fi
if [ -n "$GEMINI_API_KEY" ] || [ -n "$GOOGLE_API_KEY" ]; then if [ -n "$GEMINI_API_KEY" ] || [ -n "$GOOGLE_API_KEY" ]; then
if [ -n "$GOOGLE_GENAI_USE_VERTEXAI" ] && [ "$GOOGLE_GENAI_USE_VERTEXAI" = "true" ]; then if [ -n "$GOOGLE_GENAI_USE_VERTEXAI" ] && [ "$GOOGLE_GENAI_USE_VERTEXAI" = "true" ]; then
printf "Using Vertex AI with API key\n" printf "Using Vertex AI with API key\n"
else else
printf "Using direct Gemini API with API key\n" printf "Using direct Gemini API with API key\n"
fi fi
else else
printf "No API key provided (neither GEMINI_API_KEY nor GOOGLE_API_KEY)\n" printf "No API key provided (neither GEMINI_API_KEY nor GOOGLE_API_KEY)\n"
fi fi
agentapi server --term-width 67 --term-height 1190 -- \ agentapi server --term-width 67 --term-height 1190 -- \
bash -c "$(printf '%q ' gemini "${GEMINI_ARGS[@]}")" bash -c "$(printf '%q ' gemini "${GEMINI_ARGS[@]}")"