Skip to content

Commit a63f165

Browse files
authored
Add Claude Code and Gemini CLI to Workbench apps (#342)
* Add Claude Code and Gemini CLI to VSCode, RStudio, JupyterLab, NeMo Jupyter and Parabricks Jupyter. Install AI extensions (Claude Code, Gemini) in VSCode app * Address PR comments -- all fixes Address PR comments, testing separately before merging into PR * Address PR suggestions
1 parent 0bcd227 commit a63f165

13 files changed

Lines changed: 192 additions & 1 deletion

File tree

feature-versions/state.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@
2424
"installed": "sha256:dbf431d6b42d55cde50fa1df75c7f7c3999a90cde6d73f7a7071174b3c3d0cc4",
2525
"filter": ".*\\/\\.devcontainer\\.json"
2626
},
27+
"ghcr.io/devcontainers/features/node": {
28+
"tag": "1",
29+
"installed": "sha256:8c0de46939b61958041700ee89e3493f3b2e4131a06dc46b4d9423427d06e5f6",
30+
"filter": ".*\\/\\.devcontainer\\.json"
31+
},
32+
"ghcr.io/anthropics/devcontainer-features/claude-code": {
33+
"tag": "1.0",
34+
"installed": "sha256:cfc2e7d3e9fd3b9b01f8d5cb158508a884c8c0ede2e23ed10f32dea5d4ffe69a",
35+
"filter": ".*\\/\\.devcontainer\\.json"
36+
},
2737
"us-west2-docker.pkg.dev/shared-pub-buckets-94mvrf/workbench-artifacts/app-wondershaper": {
2838
"tag": "latest",
2939
"installed": "sha256:6cb6bde6ace7dd7e82b9397e6c9d580e00969091e8091d78fea591ebecf0f6cb",

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: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#!/usr/bin/env bash
2+
set -o errexit
3+
set -o nounset
4+
set -o pipefail
5+
set -o xtrace
6+
7+
readonly USERNAME="${USERNAME:-"root"}"
8+
9+
install_gemini_cli() {
10+
local username="${1}"
11+
echo "Installing Gemini CLI..."
12+
13+
if [ "${username}" = "root" ]; then
14+
npm install -g @google/gemini-cli
15+
else
16+
local nvm_dir="${NVM_DIR:-/usr/local/share/nvm}"
17+
local user_home
18+
user_home=$(eval echo "~${username}" 2>/dev/null || echo "/home/${username}")
19+
[ -d "${nvm_dir}" ] && chown -R "${username}:" "${nvm_dir}"
20+
[ -d "${user_home}/.npm" ] && chown -R "${username}:" "${user_home}/.npm"
21+
sudo -u "${username}" env PATH="${PATH}" npm install -g @google/gemini-cli
22+
fi
23+
24+
if which gemini >/dev/null 2>&1; then
25+
echo "Gemini CLI installed successfully!"
26+
return 0
27+
else
28+
echo "ERROR: Gemini CLI installation failed!"
29+
return 1
30+
fi
31+
}
32+
33+
fix_permissions() {
34+
local username="${1}"
35+
36+
if [ "${username}" = "root" ]; then
37+
return 0
38+
fi
39+
40+
local user_home
41+
user_home=$(eval echo "~${username}" 2>/dev/null || echo "/home/${username}")
42+
43+
mkdir -p "${user_home}/.gemini"
44+
printf '{"general.enableAutoUpdate": false, "ui": {"autoThemeSwitching": false, "theme": "ANSI Light"}}\n' > "${user_home}/.gemini/settings.json"
45+
chown -R "${username}:" "${user_home}/.gemini"
46+
}
47+
48+
print_nodejs_requirement() {
49+
cat <<EOF
50+
51+
ERROR: Node.js and npm are required but not found!
52+
Please add the Node.js feature to your devcontainer.json:
53+
54+
"features": {
55+
"ghcr.io/devcontainers/features/node:1": {},
56+
"./.devcontainer/features/gemini-cli": { "username": "your-user" }
57+
}
58+
59+
EOF
60+
exit 1
61+
}
62+
63+
echo "Activating feature 'gemini-cli'"
64+
65+
if ! command -v node >/dev/null || ! command -v npm >/dev/null; then
66+
print_nodejs_requirement
67+
fi
68+
69+
install_gemini_cli "${USERNAME}" || exit 1
70+
71+
fix_permissions "${USERNAME}"
72+
73+
echo "Done!"

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@sha256:8c0de46939b61958041700ee89e3493f3b2e4131a06dc46b4d9423427d06e5f6": {
25+
"version": "24.11.0"
26+
},
27+
"ghcr.io/anthropics/devcontainer-features/claude-code@sha256:cfc2e7d3e9fd3b9b01f8d5cb158508a884c8c0ede2e23ed10f32dea5d4ffe69a": {},
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/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@sha256:8c0de46939b61958041700ee89e3493f3b2e4131a06dc46b4d9423427d06e5f6": {
28+
"version": "24.11.0"
29+
},
30+
"ghcr.io/anthropics/devcontainer-features/claude-code@sha256:cfc2e7d3e9fd3b9b01f8d5cb158508a884c8c0ede2e23ed10f32dea5d4ffe69a": {},
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@sha256:8c0de46939b61958041700ee89e3493f3b2e4131a06dc46b4d9423427d06e5f6": {
33+
"version": "24.11.0"
34+
},
35+
"ghcr.io/anthropics/devcontainer-features/claude-code@sha256:cfc2e7d3e9fd3b9b01f8d5cb158508a884c8c0ede2e23ed10f32dea5d4ffe69a": {},
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@sha256:8c0de46939b61958041700ee89e3493f3b2e4131a06dc46b4d9423427d06e5f6": {
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@sha256:cfc2e7d3e9fd3b9b01f8d5cb158508a884c8c0ede2e23ed10f32dea5d4ffe69a": {},
27+
"./.devcontainer/features/gemini-cli": { "username": "abc" },
2328
"./.devcontainer/features/workbench-tools": {
2429
"cloud": "${templateOption:cloud}",
2530
"username": "abc",

src/vscode/Dockerfile

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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 apt-get update \
6+
&& apt-get install -y --no-install-recommends jq \
7+
&& rm -rf /var/lib/apt/lists/* \
8+
&& curl -fsSL "https://open-vsx.org/api/Google/geminicodeassist/latest" \
9+
| jq -r '.files.download' \
10+
| xargs curl -fL --compressed -o /opt/geminicodeassist.vsix \
11+
&& curl -fsSL "https://open-vsx.org/api/Anthropic/claude-code/latest" \
12+
| jq -r '.files.download' \
13+
| xargs curl -fL --compressed -o /opt/claudecode.vsix
14+
15+
# Install extensions during container init, before code-server starts
16+
COPY install-extensions.sh /etc/cont-init.d/99-install-extensions
17+
RUN chmod +x /etc/cont-init.d/99-install-extensions
18+
19+
WORKDIR /config

src/vscode/docker-compose.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ version: "2.4"
22
services:
33
app:
44
container_name: "application-server"
5-
image: "lscr.io/linuxserver/code-server:4.100.3"
5+
build:
6+
context: .
7+
dockerfile: Dockerfile
68
restart: always
79
volumes:
810
- .:/workspace:cached

0 commit comments

Comments
 (0)