feat: add IDE choice — VSCode (default), Google Antigravity, SSH

- Add `ide` Helm value with options: vscode, antigravity, ssh
- Dockerfile: install Google Antigravity via apt and openssh-server
- scripts/startapp.sh: branch on IDE env var to launch the right app
- scripts/cont-init-sshd.sh: start sshd as root in SSH mode, set up
  authorized_keys from SSH_AUTHORIZED_KEYS env var
- chart/templates/deployment.yaml: pass IDE env var, conditional ports
  and probes (HTTP for VNC modes, TCP socket for SSH mode)
- chart/templates/service.yaml: expose port 5800 (VNC) or 22 (SSH)
- chart/values.yaml: add ide field with documentation
- README.md: document IDE choice, fix stale happyHomeDir references
- chart/Chart.yaml: bump to 0.1.5

Closes #10

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-20 13:49:31 -05:00
parent f33c7e1ae8
commit 298a1ce6ec
8 changed files with 138 additions and 15 deletions
+22 -3
View File
@@ -46,15 +46,33 @@ RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - && \
# Install Happy Coder and Claude Code globally
RUN npm install -g happy-coder @anthropic-ai/claude-code
# Install Antigravity (Google's Project IDX / Cloud Code alternative)
# Note: Antigravity might be packaged differently - adjust as needed
# For now, we'll use VSCode with Project IDX extensions as a placeholder
# Install VSCode
RUN wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o /usr/share/keyrings/packages.microsoft.gpg && \
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/packages.microsoft.gpg] https://packages.microsoft.com/repos/code stable main" > /etc/apt/sources.list.d/vscode.list && \
apt-get update && \
apt-get install -y code && \
rm -rf /var/lib/apt/lists/*
# Install Google Antigravity IDE
RUN mkdir -p /etc/apt/keyrings && \
curl -fsSL https://us-central1-apt.pkg.dev/doc/repo-signing-key.gpg | \
gpg --dearmor --yes -o /etc/apt/keyrings/antigravity-repo-key.gpg && \
echo "deb [signed-by=/etc/apt/keyrings/antigravity-repo-key.gpg] https://us-central1-apt.pkg.dev/projects/antigravity-auto-updater-dev/ antigravity-debian main" \
> /etc/apt/sources.list.d/antigravity.list && \
apt-get update && \
apt-get install -y antigravity && \
rm -rf /var/lib/apt/lists/*
# Install OpenSSH server (for SSH IDE mode)
RUN apt-get update && \
apt-get install -y openssh-server && \
rm -rf /var/lib/apt/lists/* && \
mkdir -p /var/run/sshd && \
sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/' /etc/ssh/sshd_config && \
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config && \
sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config && \
echo "PermitRootLogin no" >> /etc/ssh/sshd_config
# Create user user with specific UID/GID
RUN groupadd -g 1000 user && \
useradd -u 1000 -g 1000 -m -s /bin/bash user && \
@@ -69,6 +87,7 @@ COPY --chmod=755 scripts/startapp.sh /startapp.sh
COPY --chmod=755 scripts/init-repo.sh /usr/local/bin/init-repo
# Fix app user shell after baseimage-gui creates it at runtime
COPY --chmod=755 scripts/cont-init-user.sh /etc/cont-init.d/20-fix-user-shell.sh
COPY --chmod=755 scripts/cont-init-sshd.sh /etc/cont-init.d/25-start-sshd.sh
# Set working directory
WORKDIR /workspace
+35 -7
View File
@@ -75,16 +75,40 @@ A Chrome browser window will open inside VNC for the Claude Max OAuth login. Cre
|-------|---------|-------------|
| `name` | `""` | Instance name — used in all resource names (`devcontainer-{name}`) |
| `githubRepo` | `""` | Repository to clone into `/workspace` on startup |
| `ide` | `vscode` | IDE to launch — `vscode`, `antigravity`, or `ssh` (see below) |
| `image.repository` | `ghcr.io/cpfarhood/devcontainer` | Container image |
| `image.tag` | `latest` | Image tag |
### IDE choice
| Value | Port | Description |
|-------|------|-------------|
| `vscode` (default) | 5800 (VNC) | VSCode desktop via browser-based VNC |
| `antigravity` | 5800 (VNC) | Google Antigravity (VSCode fork with AI) via VNC |
| `ssh` | 22 (SSH) | OpenSSH server — no VNC GUI, connect via SSH |
For `ssh` mode, add your public key to the env secret:
```bash
kubectl create secret generic devcontainer-mydev-secrets-env \
--from-literal=GITHUB_TOKEN='ghp_...' \
--from-literal=SSH_AUTHORIZED_KEYS='ssh-ed25519 AAAA...'
```
Then connect with:
```bash
kubectl port-forward deployment/devcontainer-mydev 2222:22
ssh -p 2222 user@localhost
```
### Happy Coder
| Value | Default | Description |
|-------|---------|-------------|
| `happyServerUrl` | `https://happy.farh.net` | Happy Coder server endpoint |
| `happyWebappUrl` | `https://happy-coder.farh.net` | Happy Coder webapp URL |
| `happyHomeDir` | `/workspace/.happy` | Happy runtime state directory (ephemeral — lives in emptyDir) |
| `happyHomeDir` | `/home/user/.happy` | Happy runtime state directory (persists on the home PVC) |
| `happyExperimental` | `true` | Enable experimental Happy features |
### Kubernetes cluster access
@@ -133,13 +157,17 @@ With any non-`none` value, a `ServiceAccount` named `devcontainer-{name}` is cre
### Startup flow
```
Container start (as app user, UID 1000)
Container start
→ cont-init.d/20-fix-user-shell.sh — fix shell/home on baseimage-gui app user
/startapp.sh
cont-init.d/25-start-sshd.sh — start sshd if IDE=ssh
→ /startapp.sh (runs as app user, UID 1000)
→ init-repo.sh
→ clone / pull GITHUB_REPO into /workspace/{repo}
happy daemon start — starts Happy Coder background daemon
→ code --new-window /workspace/{repo} — opens VSCode in VNC
rm daemon.state.json.lock — clear stale Happy lock
→ happy daemon start — starts Happy Coder background daemon
→ IDE=vscode: code --new-window --wait /workspace/{repo}
IDE=antigravity: antigravity --new-window --wait /workspace/{repo}
IDE=ssh: sleep infinity (sshd already running as root)
```
### Storage
@@ -149,7 +177,7 @@ Container start (as app user, UID 1000)
| `/home` | ReadWriteMany PVC (`userhome-{name}`) | Survives pod restarts — stores Claude credentials, dotfiles, git config |
| `/workspace` | `emptyDir` | Ephemeral — repo is re-cloned on each pod start |
Happy Coder's runtime state (`HAPPY_HOME_DIR`) is kept in `/workspace/.happy` so stale lock files never survive a pod restart.
Happy Coder's runtime state (`HAPPY_HOME_DIR`) is kept in `/home/user/.happy` on the persistent home PVC, so auth credentials and settings survive pod restarts. A stale lock file (`daemon.state.json.lock`) is removed automatically on each startup.
---
@@ -165,7 +193,7 @@ happy daemon status
happy daemon start
# View daemon logs
ls ~/.happy/logs/ || ls /workspace/.happy/logs/
ls ~/.happy/logs/
```
### Claude not authenticated
+1 -1
View File
@@ -2,5 +2,5 @@ apiVersion: v2
name: devcontainer
description: Antigravity Dev Container with Happy Coder AI assistant
type: application
version: 0.1.4
version: 0.1.5
appVersion: "latest"
+21
View File
@@ -25,10 +25,18 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
{{- if eq (.Values.ide | default "vscode") "ssh" }}
- containerPort: 22
name: ssh
protocol: TCP
{{- else }}
- containerPort: 5800
name: vnc-web
protocol: TCP
{{- end }}
env:
- name: IDE
value: {{ .Values.ide | default "vscode" | quote }}
- name: USER_ID
value: {{ .Values.userId | quote }}
- name: GROUP_ID
@@ -60,6 +68,7 @@ spec:
mountPath: /home
- name: workspace
mountPath: /workspace
{{- if ne (.Values.ide | default "vscode") "ssh" }}
livenessProbe:
httpGet:
path: /
@@ -72,6 +81,18 @@ spec:
port: 5800
initialDelaySeconds: 10
periodSeconds: 5
{{- else }}
livenessProbe:
tcpSocket:
port: 22
initialDelaySeconds: 15
periodSeconds: 10
readinessProbe:
tcpSocket:
port: 22
initialDelaySeconds: 5
periodSeconds: 5
{{- end }}
volumes:
- name: workspace
emptyDir: {}
+7
View File
@@ -6,9 +6,16 @@ metadata:
{{- include "antigravity.labels" . | nindent 4 }}
spec:
ports:
{{- if eq (.Values.ide | default "vscode") "ssh" }}
- port: 22
name: ssh
protocol: TCP
targetPort: ssh
{{- else }}
- port: 5800
name: vnc-web
protocol: TCP
targetPort: vnc-web
{{- end }}
selector:
{{- include "antigravity.labels" . | nindent 4 }}
+8
View File
@@ -9,6 +9,14 @@ image:
# GitHub repository to clone into /workspace
githubRepo: ""
# IDE to launch inside the container.
# Options:
# vscode — VSCode via VNC browser UI on port 5800 (default)
# antigravity — Google Antigravity (VSCode fork) via VNC on port 5800
# ssh — OpenSSH server on port 22 (no VNC GUI)
# Requires SSH_AUTHORIZED_KEYS in the env secret.
ide: vscode
# Happy Coder endpoints
happyServerUrl: "https://happy.farh.net"
happyWebappUrl: "https://happy-coder.farh.net"
+27
View File
@@ -0,0 +1,27 @@
#!/bin/sh
# Start OpenSSH server for SSH IDE mode.
# Runs as root during container initialisation (cont-init.d).
[ "${IDE:-vscode}" = "ssh" ] || exit 0
echo "=== SSH IDE mode: starting sshd ==="
# Generate host keys if missing (first boot or ephemeral /etc/ssh)
ssh-keygen -A 2>/dev/null || true
# Populate authorized_keys from env var (injected via Kubernetes secret)
if [ -n "$SSH_AUTHORIZED_KEYS" ]; then
HOME_DIR="/home/user"
mkdir -p "$HOME_DIR/.ssh"
chmod 700 "$HOME_DIR/.ssh"
printf '%s\n' "$SSH_AUTHORIZED_KEYS" > "$HOME_DIR/.ssh/authorized_keys"
chmod 600 "$HOME_DIR/.ssh/authorized_keys"
chown -R 1000:1000 "$HOME_DIR/.ssh"
echo "SSH authorized keys configured."
else
echo "WARNING: SSH_AUTHORIZED_KEYS not set — you will not be able to log in."
fi
# Start sshd in background (root required to bind :22 and fork sessions)
/usr/sbin/sshd -D &
echo "sshd started (PID $!)"
+17 -4
View File
@@ -14,8 +14,21 @@ else
WORKSPACE_DIR="/workspace/default"
fi
echo "Opening Antigravity in: $WORKSPACE_DIR"
IDE="${IDE:-vscode}"
echo "IDE mode: $IDE"
echo "Workspace: $WORKSPACE_DIR"
# Start Antigravity (VSCode) in the workspace directory as claude user
# The baseimage-gui will handle the GUI display
exec code --new-window --wait "$WORKSPACE_DIR"
case "$IDE" in
antigravity)
echo "Opening Google Antigravity in: $WORKSPACE_DIR"
exec antigravity --new-window --wait "$WORKSPACE_DIR"
;;
ssh)
echo "SSH mode: sshd started by cont-init. Keeping container alive."
exec sleep infinity
;;
*)
echo "Opening VSCode in: $WORKSPACE_DIR"
exec code --new-window --wait "$WORKSPACE_DIR"
;;
esac