feat: add IDE choice (VSCode, Google Antigravity, SSH) #20
+22
-3
@@ -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
|
||||
|
||||
@@ -20,6 +20,7 @@ The secret is picked up automatically via `envFrom`. Keys recognised:
|
||||
| `GITHUB_TOKEN` | PAT for private repo access (`repo` scope) |
|
||||
| `VNC_PASSWORD` | Password for the VNC web UI |
|
||||
| `ANTHROPIC_API_KEY` | API key — alternative to browser-based Claude login |
|
||||
| `SSH_AUTHORIZED_KEYS` | Public key(s) for SSH access (required when `ssh: true`) |
|
||||
|
||||
```bash
|
||||
kubectl create secret generic devcontainer-mydev-secrets-env \
|
||||
@@ -75,16 +76,55 @@ 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 `none` (see below) |
|
||||
| `ssh` | `false` | Also start an OpenSSH server on port 22 (additive, any `ide`) |
|
||||
| `image.repository` | `ghcr.io/cpfarhood/devcontainer` | Container image |
|
||||
| `image.tag` | `latest` | Image tag |
|
||||
|
||||
### IDE choice
|
||||
|
||||
`ide` controls what GUI is launched in the VNC session:
|
||||
|
||||
| Value | Port | Description |
|
||||
|-------|------|-------------|
|
||||
| `vscode` (default) | 5800 (VNC) | VSCode desktop via browser-based VNC |
|
||||
| `antigravity` | 5800 (VNC) | Google Antigravity (VSCode fork with AI) via VNC |
|
||||
| `none` | — | No IDE; container stays alive (useful when `ssh: true`) |
|
||||
|
||||
### SSH access
|
||||
|
||||
`ssh: true` starts OpenSSH on port 22 **in addition to** the IDE. It works with any `ide` value:
|
||||
|
||||
```bash
|
||||
# SSH-only (no VNC)
|
||||
helm install mydev ./chart --set name=mydev --set ide=none --set ssh=true
|
||||
|
||||
# VSCode in VNC + SSH access at the same time
|
||||
helm install mydev ./chart --set name=mydev --set ssh=true
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```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 +173,18 @@ 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 SSH=true
|
||||
→ /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=none: sleep infinity
|
||||
(SSH=true: sshd also running as root on port 22)
|
||||
```
|
||||
|
||||
### Storage
|
||||
@@ -149,7 +194,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 +210,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
@@ -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.6
|
||||
appVersion: "latest"
|
||||
|
||||
@@ -25,10 +25,21 @@ spec:
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
ports:
|
||||
{{- if ne (.Values.ide | default "vscode") "none" }}
|
||||
- containerPort: 5800
|
||||
name: vnc-web
|
||||
protocol: TCP
|
||||
{{- end }}
|
||||
{{- if .Values.ssh }}
|
||||
- containerPort: 22
|
||||
name: ssh
|
||||
protocol: TCP
|
||||
{{- end }}
|
||||
env:
|
||||
- name: IDE
|
||||
value: {{ .Values.ide | default "vscode" | quote }}
|
||||
- name: SSH
|
||||
value: {{ .Values.ssh | toString | quote }}
|
||||
- name: USER_ID
|
||||
value: {{ .Values.userId | quote }}
|
||||
- name: GROUP_ID
|
||||
@@ -60,6 +71,7 @@ spec:
|
||||
mountPath: /home
|
||||
- name: workspace
|
||||
mountPath: /workspace
|
||||
{{- if ne (.Values.ide | default "vscode") "none" }}
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
@@ -72,6 +84,18 @@ spec:
|
||||
port: 5800
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
{{- else if .Values.ssh }}
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: 22
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 22
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
{{- end }}
|
||||
volumes:
|
||||
- name: workspace
|
||||
emptyDir: {}
|
||||
|
||||
@@ -6,9 +6,17 @@ metadata:
|
||||
{{- include "antigravity.labels" . | nindent 4 }}
|
||||
spec:
|
||||
ports:
|
||||
{{- if ne (.Values.ide | default "vscode") "none" }}
|
||||
- port: 5800
|
||||
name: vnc-web
|
||||
protocol: TCP
|
||||
targetPort: vnc-web
|
||||
{{- end }}
|
||||
{{- if .Values.ssh }}
|
||||
- port: 22
|
||||
name: ssh
|
||||
protocol: TCP
|
||||
targetPort: ssh
|
||||
{{- end }}
|
||||
selector:
|
||||
{{- include "antigravity.labels" . | nindent 4 }}
|
||||
|
||||
@@ -9,6 +9,17 @@ 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
|
||||
# none — no IDE; useful when ssh: true is the sole access method
|
||||
ide: vscode
|
||||
|
||||
# Start an OpenSSH server on port 22 in addition to the IDE.
|
||||
# Set SSH_AUTHORIZED_KEYS in the env secret to allow key-based login.
|
||||
ssh: false
|
||||
|
||||
# Happy Coder endpoints
|
||||
happyServerUrl: "https://happy.farh.net"
|
||||
happyWebappUrl: "https://happy-coder.farh.net"
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#!/bin/sh
|
||||
# Start OpenSSH server when SSH=true.
|
||||
# Runs as root during container initialisation (cont-init.d).
|
||||
[ "${SSH:-false}" = "true" ] || exit 0
|
||||
|
||||
echo "=== SSH enabled: 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
@@ -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"
|
||||
;;
|
||||
none)
|
||||
echo "IDE=none: no IDE launched, keeping container alive."
|
||||
exec sleep infinity
|
||||
;;
|
||||
*)
|
||||
echo "Opening VSCode in: $WORKSPACE_DIR"
|
||||
exec code --new-window --wait "$WORKSPACE_DIR"
|
||||
;;
|
||||
esac
|
||||
|
||||
Reference in New Issue
Block a user