Skip to content

Commit d365897

Browse files
committed
Add Claude Code and Gemini CLI to VSCode, RStudio, JupyterLab, NeMo Jupyter and Parabricks Jupyter. Install AI extensions (Claude Code, Gemini) in VSCode app
1 parent bf7a78e commit d365897

13 files changed

Lines changed: 184 additions & 2 deletions

File tree

features/src/gemini-cli/README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
2+
# Gemini CLI (gemini-cli)
3+
4+
Installs the Gemini CLI globally
5+
6+
## Example Usage
7+
8+
```json
9+
"features": {
10+
"./.devcontainer/features/gemini-cli": {}
11+
}
12+
```
13+
14+
## Options
15+
16+
| Options Id | Description | Type | Default Value |
17+
|-----|-----|-----|-----|
18+
| username | Username of the container user | string | root |
19+
20+
21+
22+
---
23+
24+
_Note: This file was auto-generated from the [devcontainer-feature.json](devcontainer-feature.json). Add additional notes to a `NOTES.md`._
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "Gemini CLI",
3+
"id": "gemini-cli",
4+
"version": "1.0.0",
5+
"description": "Installs the Gemini CLI globally",
6+
"documentationURL": "https://github.com/google-gemini/gemini-cli",
7+
"options": {
8+
"username": {
9+
"type": "string",
10+
"default": "root",
11+
"description": "Username of the container user"
12+
}
13+
},
14+
"installsAfter": [
15+
"ghcr.io/devcontainers/features/node"
16+
]
17+
}

features/src/gemini-cli/install.sh

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#!/usr/bin/env bash
2+
set -eu
3+
4+
# Function to install Gemini CLI
5+
install_gemini_cli() {
6+
echo "Installing Gemini CLI..."
7+
npm install -g @google/gemini-cli
8+
9+
if command -v gemini >/dev/null; then
10+
echo "Gemini CLI installed successfully!"
11+
return 0
12+
else
13+
echo "ERROR: Gemini CLI installation failed!"
14+
return 1
15+
fi
16+
}
17+
18+
# Function to fix permissions for non-root users
19+
fix_permissions() {
20+
local username="${1:-root}"
21+
22+
if [ "${username}" = "root" ]; then
23+
return 0
24+
fi
25+
26+
# Fix NVM permissions: node feature installs as root, causing "Permission denied" in non-root containers
27+
local nvm_dir="${NVM_DIR:-/usr/local/share/nvm}"
28+
if [ -d "${nvm_dir}" ]; then
29+
echo "Fixing NVM permissions for user ${username}..."
30+
chown -R "${username}:" "${nvm_dir}"
31+
fi
32+
33+
# Fix npm cache: npm install -g as root creates root-owned files in user's ~/.npm
34+
local user_home
35+
user_home=$(eval echo "~${username}" 2>/dev/null || echo "/home/${username}")
36+
if [ -d "${user_home}/.npm" ]; then
37+
echo "Fixing npm cache ownership for user ${username}..."
38+
chown -R "${username}:" "${user_home}/.npm"
39+
fi
40+
41+
# Edge case: Disable auto-update to prevent gemini from trying to re-exec
42+
# itself on first run, which fails on freshly provisioned machines.
43+
mkdir -p "${user_home}/.gemini"
44+
printf '{"general.enableAutoUpdate": false}\n' > "${user_home}/.gemini/settings.json"
45+
chown -R "${username}:" "${user_home}/.gemini"
46+
}
47+
48+
# Print error message about requiring Node.js feature
49+
print_nodejs_requirement() {
50+
cat <<EOF
51+
52+
ERROR: Node.js and npm are required but not found!
53+
Please add the Node.js feature to your devcontainer.json:
54+
55+
"features": {
56+
"ghcr.io/devcontainers/features/node:1": {},
57+
"./.devcontainer/features/gemini-cli": { "username": "your-user" }
58+
}
59+
60+
EOF
61+
exit 1
62+
}
63+
64+
echo "Activating feature 'gemini-cli'"
65+
66+
if ! command -v node >/dev/null || ! command -v npm >/dev/null; then
67+
print_nodejs_requirement
68+
fi
69+
70+
install_gemini_cli || exit 1
71+
72+
fix_permissions "${USERNAME:-root}"
73+
74+
echo "Done!"

features/src/workbench-tools/install.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,18 @@ chown -R "${USERNAME}:" "${LIBRARIES_ENV_DIR}"
180180

181181
# Set CROMWELL_JAR environment variable
182182
printf 'export CROMWELL_JAR="%s"\n' "${BINARIES_ENV_DIR}/share/cromwell/cromwell.jar"
183+
184+
# Wrap gcloud to unset DISPLAY per-invocation so auth commands don't try
185+
# to open a browser via X11 in headless devcontainer environments.
186+
printf 'function gcloud() { DISPLAY= command gcloud "$@"; }\n'
187+
188+
# Wrap claude to clear BROWSER per-invocation so any app-managed browser
189+
# handler does not intercept the auth URL in headless environments.
190+
printf 'function claude() { BROWSER= command claude "$@"; }\n'
191+
192+
# Wrap gemini to set NO_BROWSER=1 per-invocation to force device code flow
193+
# instead of opening a browser window.
194+
printf 'function gemini() { NO_BROWSER=1 NO_COLOR=1 command gemini "$@"; }\n'
183195
} >> "${USER_HOME_DIR}/.bashrc"
184196

185197
# Allow .bashrc to be sourced in non-interactive shells

src/custom-workbench-jupyter-template/.devcontainer.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@
2121
"${templateOption:login}"
2222
],
2323
"features": {
24+
"ghcr.io/devcontainers/features/node": {
25+
"version": "24.11.0"
26+
},
27+
"ghcr.io/anthropics/devcontainer-features/claude-code:1.0": {},
28+
"./.devcontainer/features/gemini-cli": { "username": "jupyter" },
2429
"./.devcontainer/features/workbench-tools": {
2530
"libEnv": "/opt/conda/envs/jupyter", // Use the jupyter conda environment
2631
"cloud": "${templateOption:cloud}",

src/custom-workbench-jupyter-template/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM us-west2-docker.pkg.dev/shared-pub-buckets-94mvrf/workbench-artifacts/app-workbench-jupyter@sha256:229166b2be902aef0a2a621fae38037fe0e069875df1b8bfd5bcff8766d6036c
1+
FROM us-west2-docker.pkg.dev/shared-pub-buckets-94mvrf/workbench-artifacts/app-workbench-jupyter@sha256:229166b2be902aef0a2a621fae38037fe0e069875df1b8bfd5bcff8766d6036c
22

33
# Install jupyter extensions
44
RUN --mount=type=bind,from=jupyter-extension-builder,source=/dist,target=/tmp/extensions \

src/nemo_jupyter/.devcontainer.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@
2424
"version": "17"
2525
},
2626
"ghcr.io/dhoeric/features/google-cloud-cli@sha256:fa5d894718825c5ad8009ac8f2c9f0cea3d1661eb108a9d465cba9f3fc48965f": {},
27+
"ghcr.io/devcontainers/features/node": {
28+
"version": "24.11.0"
29+
},
30+
"ghcr.io/anthropics/devcontainer-features/claude-code:1.0": {},
31+
"./.devcontainer/features/gemini-cli": { "username": "jupyter" },
2732
"./.devcontainer/features/workbench-tools": {
2833
"libPythonVersion": "3.12", // Must match python version in nemo image
2934
"cloud": "${templateOption:cloud}",

src/r-analysis/.devcontainer.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@
2929
},
3030
"ghcr.io/devcontainers/features/aws-cli@sha256:17cb4a40151f59144b46957b9264683663b0214371a041ecd53dccc015a4b923": {},
3131
"ghcr.io/dhoeric/features/google-cloud-cli@sha256:fa5d894718825c5ad8009ac8f2c9f0cea3d1661eb108a9d465cba9f3fc48965f": {},
32+
"ghcr.io/devcontainers/features/node": {
33+
"version": "24.11.0"
34+
},
35+
"ghcr.io/anthropics/devcontainer-features/claude-code:1.0": {},
36+
"./.devcontainer/features/gemini-cli": { "username": "rstudio" },
3237
"./.devcontainer/features/workbench-tools": {
3338
"cloud": "${templateOption:cloud}",
3439
"username": "rstudio",

src/vscode/.devcontainer.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,13 @@
1818
"ghcr.io/devcontainers/features/java@sha256:e75d274ac969b29a59ba6f34c2d098f6a52144d0ec027ef326b724ea4b8b7b4e": {
1919
"version": "17"
2020
},
21+
"ghcr.io/devcontainers/features/node": {
22+
"version": "24.11.0"
23+
},
2124
"ghcr.io/devcontainers/features/aws-cli@sha256:17cb4a40151f59144b46957b9264683663b0214371a041ecd53dccc015a4b923": {},
2225
"ghcr.io/dhoeric/features/google-cloud-cli@sha256:fa5d894718825c5ad8009ac8f2c9f0cea3d1661eb108a9d465cba9f3fc48965f": {},
26+
"ghcr.io/anthropics/devcontainer-features/claude-code:1.0": {},
27+
"./.devcontainer/features/gemini-cli": { "username": "abc" },
2328
"./.devcontainer/features/workbench-tools": {
2429
"cloud": "${templateOption:cloud}",
2530
"username": "abc",

src/vscode/Dockerfile

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
FROM lscr.io/linuxserver/code-server:4.100.3
2+
3+
# Gemini: https://open-vsx.org/extension/Google/geminicodeassist
4+
# Claude: https://open-vsx.org/extension/Anthropic/claude-code
5+
RUN GEMINI_VERSION=$(curl -fsSL "https://open-vsx.org/api/Google/geminicodeassist/latest" | grep -o '"version":"[^"]*"' | head -1 | cut -d'"' -f4) && \
6+
curl -fL --compressed \
7+
"https://open-vsx.org/api/Google/geminicodeassist/${GEMINI_VERSION}/file/Google.geminicodeassist-${GEMINI_VERSION}.vsix" \
8+
-o /opt/geminicodeassist.vsix && \
9+
CLAUDE_VERSION=$(curl -fsSL "https://open-vsx.org/api/Anthropic/claude-code/latest" | grep -o '"version":"[^"]*"' | head -1 | cut -d'"' -f4) && \
10+
curl -fL --compressed \
11+
"https://open-vsx.org/api/Anthropic/claude-code/${CLAUDE_VERSION}/file/Anthropic.claude-code-${CLAUDE_VERSION}.vsix" \
12+
-o /opt/claudecode.vsix
13+
14+
# Install extensions during container init, before code-server starts
15+
COPY install-extensions.sh /etc/cont-init.d/99-install-extensions
16+
RUN chmod +x /etc/cont-init.d/99-install-extensions
17+
18+
WORKDIR /config

0 commit comments

Comments
 (0)