Initial commit: Antigravity dev container with Happy Coder
Add containerized GUI development environment featuring: - Antigravity IDE (VSCode) accessible via web browser - Happy Coder AI assistant integration - Automatic GitHub repository cloning on startup - Persistent user home directory (ReadWriteMany PVC) - Secure non-root execution as user claude (UID 1000) Components: - Dockerfile based on jlesage/baseimage-gui - Startup scripts for repo initialization and app launch - Kubernetes manifests (StatefulSet, ConfigMap, Secrets) - Makefile for build and deployment automation - Comprehensive documentation Features: - Web-based VNC interface (port 5800) - GitHub token authentication for private repos - Happy Coder runs as background service in workspace - CephFS storage for persistent home directory - Configurable display resolution and security Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
This commit is contained in:
@@ -0,0 +1,24 @@
|
|||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
.dockerignore
|
||||||
|
*.md
|
||||||
|
README.md
|
||||||
|
LICENSE
|
||||||
|
Makefile
|
||||||
|
docker-compose.yml
|
||||||
|
|
||||||
|
# Kubernetes files
|
||||||
|
k8s/
|
||||||
|
|
||||||
|
# Local development
|
||||||
|
home/
|
||||||
|
workspace/
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
# Secrets
|
||||||
|
*.env
|
||||||
|
.env.local
|
||||||
|
secrets.yaml
|
||||||
|
k8s/sealedsecrets.yaml
|
||||||
|
|
||||||
|
# Local volumes
|
||||||
|
home/
|
||||||
|
workspace/
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Build artifacts
|
||||||
|
*.tar
|
||||||
|
*.tar.gz
|
||||||
+76
@@ -0,0 +1,76 @@
|
|||||||
|
FROM jlesage/baseimage-gui:ubuntu-22.04-v4
|
||||||
|
|
||||||
|
# Set environment variables
|
||||||
|
ENV APP_NAME="Antigravity Dev Container" \
|
||||||
|
KEEP_APP_RUNNING=1 \
|
||||||
|
DISPLAY_WIDTH=1920 \
|
||||||
|
DISPLAY_HEIGHT=1080 \
|
||||||
|
SECURE_CONNECTION=1 \
|
||||||
|
USER_ID=1000 \
|
||||||
|
GROUP_ID=1000 \
|
||||||
|
CLAUDE_USER=claude
|
||||||
|
|
||||||
|
# Install system dependencies
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
curl \
|
||||||
|
wget \
|
||||||
|
gnupg \
|
||||||
|
ca-certificates \
|
||||||
|
git \
|
||||||
|
build-essential \
|
||||||
|
python3 \
|
||||||
|
python3-pip \
|
||||||
|
jq \
|
||||||
|
unzip \
|
||||||
|
sudo \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Install Chrome
|
||||||
|
RUN wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | gpg --dearmor -o /usr/share/keyrings/google-chrome-keyring.gpg && \
|
||||||
|
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/google-chrome-keyring.gpg] http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list && \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get install -y google-chrome-stable && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Install Node.js (LTS version for Happy Coder)
|
||||||
|
RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - && \
|
||||||
|
apt-get install -y nodejs && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Install Happy Coder globally
|
||||||
|
RUN npm install -g @happy-sdk/happy-coder
|
||||||
|
|
||||||
|
# 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
|
||||||
|
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/*
|
||||||
|
|
||||||
|
# Create claude user with specific UID/GID
|
||||||
|
RUN groupadd -g 1000 claude && \
|
||||||
|
useradd -u 1000 -g 1000 -m -s /bin/bash claude && \
|
||||||
|
echo "claude ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
|
||||||
|
|
||||||
|
# Create workspace directory
|
||||||
|
RUN mkdir -p /workspace && \
|
||||||
|
chown -R claude:claude /workspace
|
||||||
|
|
||||||
|
# Copy startup script
|
||||||
|
COPY --chmod=755 scripts/startapp.sh /startapp.sh
|
||||||
|
COPY --chmod=755 scripts/init-repo.sh /usr/local/bin/init-repo
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /workspace
|
||||||
|
|
||||||
|
# Configure container to run as claude user
|
||||||
|
ENV HOME=/home/claude \
|
||||||
|
USER=claude
|
||||||
|
|
||||||
|
# Expose VNC port (baseimage-gui default)
|
||||||
|
EXPOSE 5800
|
||||||
|
|
||||||
|
# Set app name for baseimage-gui
|
||||||
|
RUN set-cont-env APP_NAME "Antigravity"
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
.PHONY: build push run stop clean help
|
||||||
|
|
||||||
|
# Variables
|
||||||
|
REGISTRY ?= ghcr.io/cpfarhood
|
||||||
|
IMAGE_NAME ?= antigravity
|
||||||
|
IMAGE_TAG ?= latest
|
||||||
|
FULL_IMAGE = $(REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG)
|
||||||
|
|
||||||
|
.DEFAULT_GOAL := help
|
||||||
|
|
||||||
|
# Build the Docker image
|
||||||
|
build:
|
||||||
|
@echo "Building $(FULL_IMAGE)..."
|
||||||
|
docker build -t $(FULL_IMAGE) .
|
||||||
|
|
||||||
|
# Push the image to registry
|
||||||
|
push: build
|
||||||
|
@echo "Pushing $(FULL_IMAGE)..."
|
||||||
|
docker push $(FULL_IMAGE)
|
||||||
|
|
||||||
|
# Run locally with Docker
|
||||||
|
run:
|
||||||
|
@echo "Running $(FULL_IMAGE) locally..."
|
||||||
|
docker run -d \
|
||||||
|
-p 5800:5800 \
|
||||||
|
-e GITHUB_REPO="${GITHUB_REPO}" \
|
||||||
|
-e GITHUB_TOKEN="${GITHUB_TOKEN}" \
|
||||||
|
-e HAPPY_CODER_API_KEY="${HAPPY_CODER_API_KEY}" \
|
||||||
|
-e VNC_PASSWORD="${VNC_PASSWORD}" \
|
||||||
|
-v $(PWD)/home:/home \
|
||||||
|
-v $(PWD)/workspace:/workspace \
|
||||||
|
--name antigravity \
|
||||||
|
$(FULL_IMAGE)
|
||||||
|
@echo "Access at http://localhost:5800"
|
||||||
|
|
||||||
|
# Stop the running container
|
||||||
|
stop:
|
||||||
|
@echo "Stopping antigravity container..."
|
||||||
|
docker stop antigravity || true
|
||||||
|
docker rm antigravity || true
|
||||||
|
|
||||||
|
# Clean up local volumes
|
||||||
|
clean: stop
|
||||||
|
@echo "Cleaning up..."
|
||||||
|
rm -rf ./home ./workspace
|
||||||
|
|
||||||
|
# Kubernetes deployment
|
||||||
|
k8s-deploy:
|
||||||
|
@echo "Deploying to Kubernetes..."
|
||||||
|
kubectl apply -k k8s/
|
||||||
|
|
||||||
|
k8s-delete:
|
||||||
|
@echo "Deleting from Kubernetes..."
|
||||||
|
kubectl delete -k k8s/
|
||||||
|
|
||||||
|
k8s-logs:
|
||||||
|
@echo "Showing logs..."
|
||||||
|
kubectl logs -f antigravity-0
|
||||||
|
|
||||||
|
k8s-shell:
|
||||||
|
@echo "Opening shell..."
|
||||||
|
kubectl exec -it antigravity-0 -- bash
|
||||||
|
|
||||||
|
k8s-port-forward:
|
||||||
|
@echo "Port forwarding to localhost:5800..."
|
||||||
|
kubectl port-forward antigravity-0 5800:5800
|
||||||
|
|
||||||
|
# Show help
|
||||||
|
help:
|
||||||
|
@echo "Antigravity Dev Container Makefile"
|
||||||
|
@echo ""
|
||||||
|
@echo "Usage: make [target]"
|
||||||
|
@echo ""
|
||||||
|
@echo "Docker Targets:"
|
||||||
|
@echo " build - Build the Docker image"
|
||||||
|
@echo " push - Push image to registry"
|
||||||
|
@echo " run - Run container locally (requires env vars)"
|
||||||
|
@echo " stop - Stop running container"
|
||||||
|
@echo " clean - Clean up containers and volumes"
|
||||||
|
@echo ""
|
||||||
|
@echo "Kubernetes Targets:"
|
||||||
|
@echo " k8s-deploy - Deploy to Kubernetes"
|
||||||
|
@echo " k8s-delete - Delete from Kubernetes"
|
||||||
|
@echo " k8s-logs - Show container logs"
|
||||||
|
@echo " k8s-shell - Open shell in container"
|
||||||
|
@echo " k8s-port-forward - Port forward to localhost"
|
||||||
|
@echo ""
|
||||||
|
@echo "Variables:"
|
||||||
|
@echo " REGISTRY - Docker registry (default: ghcr.io/cpfarhood)"
|
||||||
|
@echo " IMAGE_NAME - Image name (default: antigravity)"
|
||||||
|
@echo " IMAGE_TAG - Image tag (default: latest)"
|
||||||
|
@echo ""
|
||||||
|
@echo "Environment Variables for 'make run':"
|
||||||
|
@echo " GITHUB_REPO - GitHub repository URL"
|
||||||
|
@echo " GITHUB_TOKEN - GitHub token (optional)"
|
||||||
|
@echo " HAPPY_CODER_API_KEY - Happy Coder API key"
|
||||||
|
@echo " VNC_PASSWORD - VNC password (optional)"
|
||||||
|
@echo ""
|
||||||
|
@echo "Example:"
|
||||||
|
@echo " make build"
|
||||||
|
@echo " make push REGISTRY=ghcr.io/myuser IMAGE_TAG=v1.0"
|
||||||
|
@echo " GITHUB_REPO=https://github.com/user/repo make run"
|
||||||
@@ -0,0 +1,342 @@
|
|||||||
|
# Antigravity Dev Container
|
||||||
|
|
||||||
|
A containerized development environment with GUI access, featuring:
|
||||||
|
- **Antigravity** (VSCode/Cloud IDE) via web browser
|
||||||
|
- **Happy Coder** - AI-powered development assistant
|
||||||
|
- **Automatic GitHub repo cloning**
|
||||||
|
- **Persistent user home directory**
|
||||||
|
- **Secure non-root execution**
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
### GUI Access
|
||||||
|
- Web-based VNC interface (port 5800)
|
||||||
|
- Full desktop environment in your browser
|
||||||
|
- Secure connections with optional password protection
|
||||||
|
|
||||||
|
### Development Tools
|
||||||
|
- Antigravity IDE (VSCode-based)
|
||||||
|
- Happy Coder AI assistant
|
||||||
|
- Git integration
|
||||||
|
- Node.js and npm
|
||||||
|
- Python 3
|
||||||
|
- Chrome browser
|
||||||
|
|
||||||
|
### Security
|
||||||
|
- Runs as non-root user `claude` (UID 1000, GID 1000)
|
||||||
|
- Secure VNC connections
|
||||||
|
- Token-based GitHub authentication
|
||||||
|
- Isolated workspace
|
||||||
|
|
||||||
|
### Persistence
|
||||||
|
- ReadWriteMany PVC for `/home` (user data persists)
|
||||||
|
- Workspace mounted at `/workspace`
|
||||||
|
- Repository cloned on first startup
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### 1. Build the Image
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -t ghcr.io/cpfarhood/antigravity:latest .
|
||||||
|
docker push ghcr.io/cpfarhood/antigravity:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Configure Secrets
|
||||||
|
|
||||||
|
Edit `k8s/secrets-example.yaml` and create a sealed secret:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl create secret generic antigravity-secrets \
|
||||||
|
--from-literal=github-token='ghp_your_token' \
|
||||||
|
--from-literal=happy-coder-api-key='your_key' \
|
||||||
|
--from-literal=vnc-password='your_password' \
|
||||||
|
--dry-run=client -o yaml | \
|
||||||
|
kubeseal --format=yaml > k8s/sealedsecrets.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Configure Repository
|
||||||
|
|
||||||
|
Edit `k8s/configmap.yaml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
data:
|
||||||
|
github-repo: "https://github.com/yourusername/yourrepo"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Deploy to Kubernetes
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl apply -k k8s/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Access the Interface
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Port forward for local access
|
||||||
|
kubectl port-forward statefulset/antigravity 5800:5800
|
||||||
|
|
||||||
|
# Open in browser
|
||||||
|
open http://localhost:5800
|
||||||
|
```
|
||||||
|
|
||||||
|
Or configure Ingress for external access.
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
### Required
|
||||||
|
- `GITHUB_REPO` - GitHub repository URL to clone
|
||||||
|
|
||||||
|
### Optional
|
||||||
|
- `GITHUB_TOKEN` - GitHub Personal Access Token (for private repos)
|
||||||
|
- `HAPPY_CODER_API_KEY` - API key for Happy Coder
|
||||||
|
- `VNC_PASSWORD` - Password for VNC access
|
||||||
|
- `USER_ID` - UID for claude user (default: 1000)
|
||||||
|
- `GROUP_ID` - GID for claude user (default: 1000)
|
||||||
|
- `DISPLAY_WIDTH` - VNC display width (default: 1920)
|
||||||
|
- `DISPLAY_HEIGHT` - VNC display height (default: 1080)
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ Web Browser (Port 5800) │
|
||||||
|
└──────────────┬──────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ VNC Web Interface │
|
||||||
|
│ (jlesage/baseimage-gui) │
|
||||||
|
└──────────────┬──────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ Antigravity IDE │
|
||||||
|
│ (VSCode + Extensions) │
|
||||||
|
│ Running as user: claude (1000) │
|
||||||
|
└──────────────┬──────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ Happy Coder (Background Process) │
|
||||||
|
│ AI Development Assistant │
|
||||||
|
└─────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ Workspace: /workspace/{repo} │
|
||||||
|
│ Home: /home/claude (RWX PVC) │
|
||||||
|
└─────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Startup Flow
|
||||||
|
|
||||||
|
1. **Container starts** - baseimage-gui initializes
|
||||||
|
2. **init-repo.sh runs**:
|
||||||
|
- Checks for `GITHUB_REPO` environment variable
|
||||||
|
- Clones repository to `/workspace/{repo-name}` if not exists
|
||||||
|
- Configures git credentials with `GITHUB_TOKEN`
|
||||||
|
- Starts Happy Coder in background
|
||||||
|
3. **startapp.sh runs**:
|
||||||
|
- Opens Antigravity IDE in the cloned repository
|
||||||
|
- Happy Coder is already running and accessible
|
||||||
|
|
||||||
|
## Happy Coder Integration
|
||||||
|
|
||||||
|
Happy Coder runs as a background service and is accessible within the IDE:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check Happy Coder status
|
||||||
|
ps aux | grep happy-coder
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
cat /tmp/happy-coder.log
|
||||||
|
|
||||||
|
# Restart Happy Coder
|
||||||
|
sudo -u claude bash -c "cd /workspace/your-repo && happy-coder &"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Local Development
|
||||||
|
|
||||||
|
### Run with Docker Compose
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3.8'
|
||||||
|
services:
|
||||||
|
antigravity:
|
||||||
|
build: .
|
||||||
|
ports:
|
||||||
|
- "5800:5800"
|
||||||
|
environment:
|
||||||
|
- GITHUB_REPO=https://github.com/yourusername/yourrepo
|
||||||
|
- GITHUB_TOKEN=ghp_your_token
|
||||||
|
- HAPPY_CODER_API_KEY=your_key
|
||||||
|
- VNC_PASSWORD=yourpassword
|
||||||
|
volumes:
|
||||||
|
- ./home:/home
|
||||||
|
- ./workspace:/workspace
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose up
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run with Docker
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d \
|
||||||
|
-p 5800:5800 \
|
||||||
|
-e GITHUB_REPO="https://github.com/yourusername/yourrepo" \
|
||||||
|
-e GITHUB_TOKEN="ghp_your_token" \
|
||||||
|
-e HAPPY_CODER_API_KEY="your_key" \
|
||||||
|
-e VNC_PASSWORD="yourpassword" \
|
||||||
|
-v $(pwd)/home:/home \
|
||||||
|
-v $(pwd)/workspace:/workspace \
|
||||||
|
ghcr.io/cpfarhood/antigravity:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
## Kubernetes Deployment
|
||||||
|
|
||||||
|
### With Flux
|
||||||
|
|
||||||
|
See the animaniacs cluster configuration for GitOps deployment patterns.
|
||||||
|
|
||||||
|
### Standalone
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Apply manifests
|
||||||
|
kubectl apply -k k8s/
|
||||||
|
|
||||||
|
# Check status
|
||||||
|
kubectl get statefulset antigravity
|
||||||
|
kubectl get pods -l app=antigravity
|
||||||
|
|
||||||
|
# Access logs
|
||||||
|
kubectl logs antigravity-0
|
||||||
|
|
||||||
|
# Access shell
|
||||||
|
kubectl exec -it antigravity-0 -- bash
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Repository not cloning
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check logs
|
||||||
|
kubectl logs antigravity-0 | grep "Repository Initialization"
|
||||||
|
|
||||||
|
# Verify GITHUB_REPO is set
|
||||||
|
kubectl exec antigravity-0 -- env | grep GITHUB
|
||||||
|
|
||||||
|
# Check git credentials
|
||||||
|
kubectl exec antigravity-0 -- cat /home/claude/.git-credentials
|
||||||
|
```
|
||||||
|
|
||||||
|
### Happy Coder not starting
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check Happy Coder logs
|
||||||
|
kubectl exec antigravity-0 -- cat /tmp/happy-coder.log
|
||||||
|
|
||||||
|
# Verify API key
|
||||||
|
kubectl exec antigravity-0 -- env | grep HAPPY_CODER
|
||||||
|
|
||||||
|
# Restart Happy Coder
|
||||||
|
kubectl exec antigravity-0 -- sudo -u claude bash -c "cd /workspace/repo && happy-coder &"
|
||||||
|
```
|
||||||
|
|
||||||
|
### VNC not accessible
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check port forwarding
|
||||||
|
kubectl port-forward antigravity-0 5800:5800
|
||||||
|
|
||||||
|
# Verify service
|
||||||
|
kubectl get svc antigravity
|
||||||
|
|
||||||
|
# Check pod status
|
||||||
|
kubectl describe pod antigravity-0
|
||||||
|
```
|
||||||
|
|
||||||
|
### Permission issues
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check ownership
|
||||||
|
kubectl exec antigravity-0 -- ls -la /home/claude
|
||||||
|
kubectl exec antigravity-0 -- ls -la /workspace
|
||||||
|
|
||||||
|
# Fix ownership
|
||||||
|
kubectl exec antigravity-0 -- chown -R claude:claude /home/claude
|
||||||
|
kubectl exec antigravity-0 -- chown -R claude:claude /workspace
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
1. **Secrets Management**: Use SealedSecrets or external secret managers
|
||||||
|
2. **Network Policies**: Restrict ingress/egress as needed
|
||||||
|
3. **RBAC**: Limit who can access the namespace
|
||||||
|
4. **VNC Password**: Always set a strong VNC password
|
||||||
|
5. **GitHub Token**: Use fine-grained tokens with minimal permissions
|
||||||
|
6. **Container Security**: Runs as non-root user (claude:1000)
|
||||||
|
|
||||||
|
## Storage
|
||||||
|
|
||||||
|
### Home Directory (`/home`)
|
||||||
|
- Mounted from ReadWriteMany PVC (`userhome`)
|
||||||
|
- Persists user settings, credentials, history
|
||||||
|
- Survives pod restarts
|
||||||
|
|
||||||
|
### Workspace (`/workspace`)
|
||||||
|
- ephemeral emptyDir (can be changed to PVC)
|
||||||
|
- Contains cloned repository
|
||||||
|
- Rebuild on pod restart
|
||||||
|
|
||||||
|
To persist workspace:
|
||||||
|
1. Create a PVC for workspace
|
||||||
|
2. Update `statefulset.yaml` to use PVC instead of emptyDir
|
||||||
|
|
||||||
|
## Customization
|
||||||
|
|
||||||
|
### Add More Tools
|
||||||
|
|
||||||
|
Edit `Dockerfile`:
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
your-package-here \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
```
|
||||||
|
|
||||||
|
### Change Display Resolution
|
||||||
|
|
||||||
|
Set environment variables:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
env:
|
||||||
|
- name: DISPLAY_WIDTH
|
||||||
|
value: "2560"
|
||||||
|
- name: DISPLAY_HEIGHT
|
||||||
|
value: "1440"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Auto-clone Multiple Repos
|
||||||
|
|
||||||
|
Modify `init-repo.sh` to support `GITHUB_REPOS` (comma-separated):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
IFS=',' read -ra REPOS <<< "$GITHUB_REPOS"
|
||||||
|
for repo in "${REPOS[@]}"; do
|
||||||
|
# Clone each repo
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
- Built on [jlesage/baseimage-gui](https://github.com/jlesage/docker-baseimage-gui)
|
||||||
|
- Uses [Happy Coder](https://happy.engineering)
|
||||||
|
- Inspired by Google's Project IDX
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: antigravity-config
|
||||||
|
data:
|
||||||
|
# GitHub repository to clone on startup
|
||||||
|
# Example: "https://github.com/username/repository"
|
||||||
|
github-repo: ""
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: antigravity
|
||||||
|
annotations:
|
||||||
|
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
||||||
|
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
||||||
|
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
|
||||||
|
nginx.ingress.kubernetes.io/websocket-services: "antigravity"
|
||||||
|
spec:
|
||||||
|
ingressClassName: nginx
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
- antigravity.example.com # Replace with your domain
|
||||||
|
secretName: antigravity-tls
|
||||||
|
rules:
|
||||||
|
- host: antigravity.example.com # Replace with your domain
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: antigravity
|
||||||
|
port:
|
||||||
|
number: 5800
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- configmap.yaml
|
||||||
|
- statefulset.yaml
|
||||||
|
- ingress.yaml
|
||||||
|
|
||||||
|
# Uncomment to create secrets from files
|
||||||
|
# secretGenerator:
|
||||||
|
# - name: antigravity-secrets
|
||||||
|
# literals:
|
||||||
|
# - github-token=ghp_your_token
|
||||||
|
# - happy-coder-api-key=your_key
|
||||||
|
# - vnc-password=your_password
|
||||||
|
|
||||||
|
commonLabels:
|
||||||
|
app: antigravity
|
||||||
|
environment: production
|
||||||
|
|
||||||
|
commonAnnotations:
|
||||||
|
managed-by: kustomize
|
||||||
|
description: "Antigravity Dev Container with Happy Coder"
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
# Example secrets - DO NOT commit actual secrets!
|
||||||
|
# Use SealedSecrets or another secret management solution
|
||||||
|
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: antigravity-secrets
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
# GitHub Personal Access Token (for private repos)
|
||||||
|
github-token: "ghp_your_token_here"
|
||||||
|
|
||||||
|
# Happy Coder API Key
|
||||||
|
happy-coder-api-key: "your_happy_coder_api_key"
|
||||||
|
|
||||||
|
# VNC Password (optional, for secure VNC access)
|
||||||
|
vnc-password: "your_vnc_password"
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: antigravity
|
||||||
|
labels:
|
||||||
|
app: antigravity
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 5800
|
||||||
|
name: vnc-web
|
||||||
|
protocol: TCP
|
||||||
|
clusterIP: None
|
||||||
|
selector:
|
||||||
|
app: antigravity
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: antigravity
|
||||||
|
spec:
|
||||||
|
serviceName: "antigravity"
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: antigravity
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: antigravity
|
||||||
|
spec:
|
||||||
|
securityContext:
|
||||||
|
fsGroup: 1000
|
||||||
|
fsGroupChangePolicy: "OnRootMismatch"
|
||||||
|
containers:
|
||||||
|
- name: antigravity
|
||||||
|
image: ghcr.io/cpfarhood/antigravity:latest
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
ports:
|
||||||
|
- containerPort: 5800
|
||||||
|
name: vnc-web
|
||||||
|
protocol: TCP
|
||||||
|
volumeMounts:
|
||||||
|
- name: userhome
|
||||||
|
mountPath: /home
|
||||||
|
- name: workspace
|
||||||
|
mountPath: /workspace
|
||||||
|
env:
|
||||||
|
# User/Group IDs for the claude user
|
||||||
|
- name: USER_ID
|
||||||
|
value: "1000"
|
||||||
|
- name: GROUP_ID
|
||||||
|
value: "1000"
|
||||||
|
# VNC display settings
|
||||||
|
- name: DISPLAY_WIDTH
|
||||||
|
value: "1920"
|
||||||
|
- name: DISPLAY_HEIGHT
|
||||||
|
value: "1080"
|
||||||
|
- name: SECURE_CONNECTION
|
||||||
|
value: "1"
|
||||||
|
- name: VNC_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: antigravity
|
||||||
|
key: vnc-password
|
||||||
|
optional: true
|
||||||
|
# GitHub configuration
|
||||||
|
- name: GITHUB_REPO
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: antigravity
|
||||||
|
key: github-repo
|
||||||
|
optional: true
|
||||||
|
- name: GITHUB_TOKEN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: antigravity
|
||||||
|
key: github-token
|
||||||
|
optional: true
|
||||||
|
# Happy Coder configuration
|
||||||
|
- name: HAPPY_CODER_API_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: antigravity
|
||||||
|
key: happy-coder-api-key
|
||||||
|
optional: true
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "2Gi"
|
||||||
|
cpu: "1000m"
|
||||||
|
limits:
|
||||||
|
memory: "8Gi"
|
||||||
|
cpu: "4000m"
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: 5800
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 10
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: 5800
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 5
|
||||||
|
volumes:
|
||||||
|
- name: workspace
|
||||||
|
emptyDir: {}
|
||||||
|
volumeClaimTemplates:
|
||||||
|
- metadata:
|
||||||
|
name: userhome
|
||||||
|
spec:
|
||||||
|
accessModes: [ "ReadWriteMany" ]
|
||||||
|
storageClassName: "ceph-filesystem"
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 10Gi
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Initialize repository and start Happy Coder
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "=== Repository Initialization ==="
|
||||||
|
|
||||||
|
# Check if GITHUB_REPO is set
|
||||||
|
if [ -z "$GITHUB_REPO" ]; then
|
||||||
|
echo "GITHUB_REPO not set, skipping repository clone"
|
||||||
|
WORKSPACE_DIR="/workspace/default"
|
||||||
|
mkdir -p "$WORKSPACE_DIR"
|
||||||
|
else
|
||||||
|
# Parse repo name from URL
|
||||||
|
REPO_NAME=$(basename "$GITHUB_REPO" .git)
|
||||||
|
WORKSPACE_DIR="/workspace/$REPO_NAME"
|
||||||
|
|
||||||
|
echo "Repository: $GITHUB_REPO"
|
||||||
|
echo "Target directory: $WORKSPACE_DIR"
|
||||||
|
|
||||||
|
# Check if repo already exists
|
||||||
|
if [ -d "$WORKSPACE_DIR/.git" ]; then
|
||||||
|
echo "Repository already exists, pulling latest changes..."
|
||||||
|
cd "$WORKSPACE_DIR"
|
||||||
|
|
||||||
|
# Configure git to use token if provided
|
||||||
|
if [ -n "$GITHUB_TOKEN" ]; then
|
||||||
|
git config credential.helper store
|
||||||
|
echo "https://oauth2:${GITHUB_TOKEN}@github.com" > /home/claude/.git-credentials
|
||||||
|
chmod 600 /home/claude/.git-credentials
|
||||||
|
fi
|
||||||
|
|
||||||
|
git pull || echo "Pull failed, continuing anyway..."
|
||||||
|
else
|
||||||
|
echo "Cloning repository..."
|
||||||
|
mkdir -p "$(dirname "$WORKSPACE_DIR")"
|
||||||
|
|
||||||
|
# Clone with token if provided
|
||||||
|
if [ -n "$GITHUB_TOKEN" ]; then
|
||||||
|
# Replace https://github.com/ with https://oauth2:token@github.com/
|
||||||
|
CLONE_URL=$(echo "$GITHUB_REPO" | sed "s|https://github.com/|https://oauth2:${GITHUB_TOKEN}@github.com/|")
|
||||||
|
git clone "$CLONE_URL" "$WORKSPACE_DIR"
|
||||||
|
|
||||||
|
# Configure credentials for future use
|
||||||
|
git config --global credential.helper store
|
||||||
|
echo "https://oauth2:${GITHUB_TOKEN}@github.com" > /home/claude/.git-credentials
|
||||||
|
chmod 600 /home/claude/.git-credentials
|
||||||
|
else
|
||||||
|
git clone "$GITHUB_REPO" "$WORKSPACE_DIR"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set ownership
|
||||||
|
chown -R claude:claude "$WORKSPACE_DIR"
|
||||||
|
chown -R claude:claude /home/claude
|
||||||
|
|
||||||
|
# Start Happy Coder in background as claude user
|
||||||
|
echo "Starting Happy Coder..."
|
||||||
|
cd "$WORKSPACE_DIR"
|
||||||
|
|
||||||
|
# Create Happy Coder log file
|
||||||
|
HAPPY_LOG="/tmp/happy-coder.log"
|
||||||
|
touch "$HAPPY_LOG"
|
||||||
|
chown claude:claude "$HAPPY_LOG"
|
||||||
|
|
||||||
|
# Start Happy Coder as claude user
|
||||||
|
sudo -u claude bash -c "cd '$WORKSPACE_DIR' && happy-coder > '$HAPPY_LOG' 2>&1 &"
|
||||||
|
|
||||||
|
# Save PID for monitoring
|
||||||
|
echo $! > /tmp/happy-coder.pid
|
||||||
|
|
||||||
|
echo "Happy Coder started (PID: $(cat /tmp/happy-coder.pid))"
|
||||||
|
echo "Logs available at: $HAPPY_LOG"
|
||||||
|
|
||||||
|
# Export workspace directory for startapp.sh
|
||||||
|
echo "$WORKSPACE_DIR" > /tmp/workspace-dir
|
||||||
|
|
||||||
|
echo "=== Initialization Complete ==="
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Start application script for baseimage-gui
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "=== Starting Antigravity Dev Container ==="
|
||||||
|
|
||||||
|
# Initialize repository and Happy Coder
|
||||||
|
/usr/local/bin/init-repo
|
||||||
|
|
||||||
|
# Get workspace directory
|
||||||
|
if [ -f /tmp/workspace-dir ]; then
|
||||||
|
WORKSPACE_DIR=$(cat /tmp/workspace-dir)
|
||||||
|
else
|
||||||
|
WORKSPACE_DIR="/workspace/default"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Opening Antigravity in: $WORKSPACE_DIR"
|
||||||
|
|
||||||
|
# Start Antigravity (VSCode) in the workspace directory as claude user
|
||||||
|
# The baseimage-gui will handle the GUI display
|
||||||
|
exec sudo -u claude code --new-window --wait "$WORKSPACE_DIR"
|
||||||
Reference in New Issue
Block a user