Compare commits

...

11 Commits

Author SHA1 Message Date
Chris Farhood 4984e5200c chore(deps): replace Dependabot with Renovate
Adds renovate.json modelled after the kubernetes repo config.
Removes .github/dependabot.yml.

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>
2026-02-20 07:17:18 -05:00
Chris Farhood 256a6871e8 Add Helm chart, replace flux/ variable-substitution templates
Introduces chart/ with deployment, service, and PVC templates driven
by Helm values instead of Kustomize variable substitution. Removes
the flux/ directory which is superseded by the chart.

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>
2026-02-20 06:14:40 -05:00
Chris Farhood 720a2a982f try change to deployment 2026-02-19 20:49:58 -05:00
Chris Farhood fdfccb17bd catch up 2026-02-19 20:00:37 -05:00
Chris Farhood dfc0a3c1e3 catchup 2026-02-19 19:55:05 -05:00
Chris Farhood 7003d860b7 Move deployment to cpfarhood namespace
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>
2026-02-19 18:20:48 -05:00
Chris Farhood 8eab159e6f Rename user from claude to user, remove HTTPRoute
- Rename container user from 'claude' to 'user' throughout Dockerfile
- Remove httproute.yaml from kustomization (Authentik outpost will manage routing)

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>
2026-02-19 18:08:39 -05:00
Chris Farhood 13f1878ce5 Fix hostname and TLS config for gateway
- Change hostname from antigravity.dev.farh.net to antigravity.farh.net
  to match the gateway's *.farh.net listener
- Set SECURE_CONNECTION=0 since TLS is terminated at the gateway,
  avoiding the redirect loop caused by the app doing its own HTTP->HTTPS redirect

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>
2026-02-19 18:01:52 -05:00
Chris Farhood c01f85ef24 Remove sudo from startup scripts
baseimage-gui already runs startapp.sh as the app user (UID 1000),
so sudo -u is unnecessary and fails since 'app' is not in sudoers.

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>
2026-02-19 17:54:35 -05:00
Chris Farhood 3db5961b1c Fix chown errors in startup script
- Remove chown on /home mount root (PVC root not owned by container user)
- Change imagePullPolicy to Always to pick up new images on pod restart

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>
2026-02-19 17:51:56 -05:00
Chris Farhood 3794ec60d7 Fix startup scripts and k8s config for initial deployment
- Fix chown/sudo to use numeric UID/GID instead of hardcoded 'claude'
  username (baseimage-gui creates users dynamically, name not available
  at script runtime)
- Fix image name: ghcr.io/cpfarhood/devcontainer (matches github.repository)
- Fix ConfigMap name: antigravity-config -> antigravity (matches statefulset refs)
- Set github-repo in ConfigMap to headlamp-polaris-plugin
- Set HTTPRoute to external gateway at antigravity.dev.farh.net
- Add CLAUDE.md for Claude Code guidance

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>
2026-02-19 17:39:59 -05:00
19 changed files with 431 additions and 240 deletions
-19
View File
@@ -1,19 +0,0 @@
version: 2
updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
labels:
- "dependencies"
- "github-actions"
# Maintain dependencies for Docker
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "weekly"
labels:
- "dependencies"
- "docker"
+29
View File
@@ -0,0 +1,29 @@
{
"mcpServers": {
"github": {
"command": "github-mcp-server",
"args": ["stdio"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "${CLAUDE_GITHUB_TOKEN}"
}
},
"kubernetes (local)": {
"command": "npx",
"args": [
"-y",
"kubernetes-mcp-server@latest"
]
},
"flux (local)":{
"command":"flux-operator-mcp",
"args":["serve"],
"env":{
"KUBECONFIG":"/Users/cpfarhood/.kube/config"
}
},
"playwright": {
"command": "npx",
"args": ["-y", "@playwright/mcp@latest"]
}
}
}
+107
View File
@@ -0,0 +1,107 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
Antigravity is a Docker-based cloud development environment that provides:
- Web-based GUI IDE (VSCode/Antigravity) via VNC on port 5800
- Happy Coder AI assistant integration
- Automatic GitHub repository cloning on startup
- Kubernetes-native deployment with persistent home storage
The stack is primarily **Bash scripts + YAML** — there is no Node.js package, compiled language, or test framework.
## Common Commands
### Building
```bash
make build # Build Docker image
make build REGISTRY=ghcr.io/myuser IMAGE_TAG=v1.0 # Custom registry/tag
docker build -t ghcr.io/cpfarhood/antigravity:latest . # Direct build
```
### Running Locally
```bash
GITHUB_REPO="https://github.com/user/repo" make run # Run with Docker
make stop # Stop container
make clean # Remove volumes
```
### Kubernetes Deployment
```bash
make k8s-deploy # Deploy via kustomize
kubectl apply -k k8s/ # Direct kustomize apply
make k8s-delete # Tear down
make k8s-port-forward # Forward port 5800 to localhost
make k8s-logs # Stream container logs
make k8s-shell # Open interactive shell in pod
```
### Other Useful Targets
```bash
make help # List all Makefile targets with descriptions
make push # Push image to registry (build first)
```
## Architecture
### Startup Flow
```
Container start
→ scripts/startapp.sh
→ scripts/init-repo.sh (clone GITHUB_REPO, start Happy Coder)
→ launch VSCode as user `claude` in /workspace
```
### Key Files
| File | Purpose |
|------|---------|
| `Dockerfile` | Image definition — installs Chrome, Node.js, VSCode, Happy Coder; creates non-root user `claude` (UID 1000) |
| `scripts/init-repo.sh` | Clones GitHub repo, authenticates with token, starts Happy Coder background service |
| `scripts/startapp.sh` | Calls init-repo.sh then opens VSCode in the workspace |
| `k8s/statefulset.yaml` | StatefulSet + headless Service; mounts `/home` (PVC) and `/workspace` (emptyDir) |
| `k8s/configmap.yaml` | `GITHUB_REPO`, `HAPPY_SERVER_URL`, `HAPPY_WEBAPP_URL` |
| `k8s/httproute.yaml` | Gateway API HTTPRoute for external browser access |
| `k8s/secrets-example.yaml` | Template for SealedSecrets (GitHub token, VNC password) |
| `Makefile` | Build/deploy automation |
### Storage Model
- `/home` — ReadWriteMany PVC (persists across pod restarts, holds user config/dotfiles)
- `/workspace` — emptyDir by default (ephemeral; can be changed to PVC)
### Environment Variables
**Required:**
- `GITHUB_REPO` — URL of repository to clone into `/workspace`
**Optional:**
- `GITHUB_TOKEN` — PAT for private repo access
- `VNC_PASSWORD` — VNC web interface password
- `DISPLAY_WIDTH` / `DISPLAY_HEIGHT` — VNC resolution
- `USER_ID` / `GROUP_ID` — Override UID/GID (default 1000)
- `HAPPY_SERVER_URL` / `HAPPY_WEBAPP_URL` — Custom Happy Coder endpoints
- `HAPPY_HOME_DIR` / `HAPPY_EXPERIMENTAL`
### CI/CD
- **`build-and-push.yaml`** — Builds and pushes to GHCR on every push to `main`, version tags (`v*`), and PRs. Tags: `latest` (main), semver, branch name, commit SHA.
- **`release.yaml`** — Creates a GitHub Release with docker pull instructions when a version tag is pushed.
- **`dependabot.yml`** — Weekly updates for GitHub Actions and Docker base image.
Image registry: `ghcr.io/cpfarhood/devcontainer`
## Kubernetes Notes
- Uses Kustomize (`kubectl apply -k k8s/`)
- Storage class is `ceph-filesystem` by default — change in `statefulset.yaml` for other clusters
- Resource limits: 14 CPU, 28Gi memory
- Health checks (liveness/readiness probes) on port 5800
- Secrets managed via SealedSecrets (see `k8s/secrets-example.yaml`)
+9 -9
View File
@@ -8,7 +8,7 @@ ENV APP_NAME="Antigravity Dev Container" \
SECURE_CONNECTION=1 \
USER_ID=1000 \
GROUP_ID=1000 \
CLAUDE_USER=claude
CLAUDE_USER=user
# Install system dependencies
RUN apt-get update && apt-get install -y \
@@ -49,14 +49,14 @@ RUN wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor
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 user user with specific UID/GID
RUN groupadd -g 1000 user && \
useradd -u 1000 -g 1000 -m -s /bin/bash user && \
echo "user ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
# Create workspace directory
RUN mkdir -p /workspace && \
chown -R claude:claude /workspace
chown -R user:user /workspace
# Copy startup script
COPY --chmod=755 scripts/startapp.sh /startapp.sh
@@ -65,9 +65,9 @@ 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
# Configure container to run as user user
ENV HOME=/home/user \
USER=user
# Expose VNC port (baseimage-gui default)
EXPOSE 5800
+6
View File
@@ -0,0 +1,6 @@
apiVersion: v2
name: antigravity
description: Antigravity Dev Container with Happy Coder AI assistant
type: application
version: 0.1.0
appVersion: "latest"
+28
View File
@@ -0,0 +1,28 @@
{{/*
Resource name prefix: devcontainer-{name}
*/}}
{{- define "antigravity.fullname" -}}
{{- printf "devcontainer-%s" .Values.name }}
{{- end }}
{{/*
PVC name: userhome-{name}
*/}}
{{- define "antigravity.pvcName" -}}
{{- printf "userhome-%s" .Values.name }}
{{- end }}
{{/*
Secret name for env vars, default to devcontainer-{name}-secrets-env
*/}}
{{- define "antigravity.envSecretName" -}}
{{- .Values.envSecretName | default (printf "devcontainer-%s-secrets-env" .Values.name) }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "antigravity.labels" -}}
app: devcontainer
instance: {{ .Values.name }}
{{- end }}
+77
View File
@@ -0,0 +1,77 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "antigravity.fullname" . }}
labels:
{{- include "antigravity.labels" . | nindent 4 }}
spec:
replicas: 1
selector:
matchLabels:
{{- include "antigravity.labels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "antigravity.labels" . | nindent 8 }}
spec:
securityContext:
fsGroup: 1000
fsGroupChangePolicy: "OnRootMismatch"
containers:
- name: devcontainer
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: 5800
name: vnc-web
protocol: TCP
env:
- name: USER_ID
value: {{ .Values.userId | quote }}
- name: GROUP_ID
value: {{ .Values.groupId | quote }}
- name: DISPLAY_WIDTH
value: {{ .Values.display.width | quote }}
- name: DISPLAY_HEIGHT
value: {{ .Values.display.height | quote }}
- name: SECURE_CONNECTION
value: {{ .Values.secureConnection | quote }}
- name: HAPPY_HOME_DIR
value: {{ .Values.happyHomeDir | quote }}
- name: HAPPY_EXPERIMENTAL
value: {{ .Values.happyExperimental | quote }}
- name: HAPPY_SERVER_URL
value: {{ .Values.happyServerUrl | quote }}
- name: HAPPY_WEBAPP_URL
value: {{ .Values.happyWebappUrl | quote }}
- name: GITHUB_REPO
value: {{ .Values.githubRepo | quote }}
envFrom:
- secretRef:
name: {{ include "antigravity.envSecretName" . }}
optional: true
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumeMounts:
- name: userhome
mountPath: /home
- name: workspace
mountPath: /workspace
livenessProbe:
httpGet:
path: /
port: 5800
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: 5800
initialDelaySeconds: 10
periodSeconds: 5
volumes:
- name: workspace
emptyDir: {}
- name: userhome
persistentVolumeClaim:
claimName: {{ include "antigravity.pvcName" . }}
+13
View File
@@ -0,0 +1,13 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ include "antigravity.pvcName" . }}
labels:
{{- include "antigravity.labels" . | nindent 4 }}
spec:
accessModes:
- ReadWriteMany
storageClassName: {{ .Values.storage.className }}
resources:
requests:
storage: {{ .Values.storage.size }}
+14
View File
@@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "antigravity.fullname" . }}
labels:
{{- include "antigravity.labels" . | nindent 4 }}
spec:
ports:
- port: 5800
name: vnc-web
protocol: TCP
targetPort: vnc-web
selector:
{{- include "antigravity.labels" . | nindent 4 }}
+43
View File
@@ -0,0 +1,43 @@
# Instance name — used to generate resource names (devcontainer-{name}, userhome-{name})
name: ""
image:
repository: ghcr.io/cpfarhood/devcontainer
tag: latest
pullPolicy: Always
# GitHub repository to clone into /workspace
githubRepo: ""
# Happy Coder endpoints
happyServerUrl: "https://happy.farh.net"
happyWebappUrl: "https://happy-coder.farh.net"
happyHomeDir: "/home/user/.happy"
happyExperimental: "true"
# VNC display
display:
width: "1920"
height: "1080"
# Set to "0" when TLS is terminated at the gateway layer
secureConnection: "0"
userId: "1000"
groupId: "1000"
storage:
size: 32Gi
className: ceph-filesystem
resources:
requests:
memory: "2Gi"
cpu: "1000m"
limits:
memory: "8Gi"
cpu: "4000m"
# Name of existing Secret containing env vars (GITHUB_TOKEN, VNC_PASSWORD, etc.)
# Defaults to: devcontainer-{name}-secrets-env
envSecretName: ""
-13
View File
@@ -1,13 +0,0 @@
---
apiVersion: v1
kind: ConfigMap
metadata:
name: antigravity-config
data:
# GitHub repository to clone on startup
# Example: "https://github.com/username/repository"
github-repo: ""
# Happy Coder configuration (optional)
# happy-server-url: "https://api.cluster-fluster.com"
# happy-webapp-url: "https://app.happy.engineering"
-20
View File
@@ -1,20 +0,0 @@
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: antigravity
spec:
parentRefs:
- name: gateway # Replace with your Gateway name
namespace: gateway-system # Replace with your Gateway namespace
hostnames:
- "antigravity.example.com" # Replace with your domain
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: antigravity
port: 5800
weight: 1
-25
View File
@@ -1,25 +0,0 @@
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: default
resources:
- configmap.yaml
- statefulset.yaml
- httproute.yaml
# Uncomment to create secrets from files
# secretGenerator:
# - name: antigravity-secrets
# literals:
# - github-token=ghp_your_token
# - vnc-password=your_password
commonLabels:
app: antigravity
environment: production
commonAnnotations:
managed-by: kustomize
description: "Antigravity Dev Container with Happy Coder"
-15
View File
@@ -1,15 +0,0 @@
---
# 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"
# VNC Password (optional, for secure VNC access)
vnc-password: "your_vnc_password"
-127
View File
@@ -1,127 +0,0 @@
---
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 (optional)
- name: HAPPY_SERVER_URL
valueFrom:
configMapKeyRef:
name: antigravity
key: happy-server-url
optional: true
- name: HAPPY_WEBAPP_URL
valueFrom:
configMapKeyRef:
name: antigravity
key: happy-webapp-url
optional: true
- name: HAPPY_HOME_DIR
value: "/home/claude/.happy"
- name: HAPPY_EXPERIMENTAL
value: "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
+20
View File
@@ -0,0 +1,20 @@
# Antigravity Dev Container - Session Notes
## Key Architecture Facts
- Image: `ghcr.io/cpfarhood/devcontainer:latest` (repo name is `devcontainer`, not `antigravity`)
- `imagePullPolicy: Always` in statefulset (set during initial deployment debugging)
- Service must NOT be headless (`clusterIP: None`) — Cilium gateway can't route to headless services
- `SECURE_CONNECTION=0` — TLS is terminated at the gateway, not the app
- Container user is `user` (UID 1000) — baseimage-gui runs startapp.sh as `app` user, sudo is not available
- HTTPRoute is managed by Authentik outpost, not in kustomization
## Cluster Patterns
- External gateway: `external` in `gateway-system`, handles `*.farh.net` on port 443 HTTPS only
- Hostnames must be exactly `*.farh.net` (not `*.subdomain.farh.net`) to match gateway listener
- Authentik outpost Terraform lives in `../kubernetes/terraform/authentik-*-proxy/`
- Outpost config uses `external` gateway for public apps, `internal` for internal apps
## Common Gotchas
- `baseimage-gui` creates user dynamically — don't hardcode usernames in scripts, use numeric UID/GID
- `chown /home` fails (PVC root not owned by container) — only chown subdirectories
- `sudo` not available in startapp.sh — script already runs as correct user
+72
View File
@@ -0,0 +1,72 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended",
":gitSignOff"
],
"semanticCommits": "enabled",
"dependencyDashboard": true,
"suppressNotifications": [
"prEditedNotification"
],
"rebaseWhen": "conflicted",
"commitMessagePrefix": "chore(deps):",
"commitMessageAction": "update",
"commitMessageTopic": "{{depName}}",
"prConcurrentLimit": 5,
"prHourlyLimit": 2,
"schedule": [
"before 6am on monday"
],
"packageRules": [
{
"description": "GitHub Actions",
"matchManagers": [
"github-actions"
],
"groupName": "github-actions",
"additionalBranchPrefix": "github-actions-",
"semanticCommitScope": "github-actions",
"pinDigests": true
},
{
"description": "Docker base image",
"matchManagers": [
"dockerfile"
],
"groupName": "docker",
"additionalBranchPrefix": "docker-",
"semanticCommitScope": "docker"
},
{
"description": "Automerge patch updates",
"matchUpdateTypes": [
"patch"
],
"automerge": true,
"automergeType": "pr",
"platformAutomerge": true
},
{
"description": "Automerge minor updates for stable packages",
"matchUpdateTypes": [
"minor"
],
"matchCurrentVersion": "!/^0/",
"automerge": true,
"automergeType": "pr",
"platformAutomerge": true
},
{
"description": "Separate major updates - require manual review",
"matchUpdateTypes": [
"major"
],
"automerge": false,
"additionalBranchPrefix": "major-"
}
],
"ignorePaths": [
"**/node_modules/**"
]
}
+12 -11
View File
@@ -25,8 +25,8 @@ else
# 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
echo "https://oauth2:${GITHUB_TOKEN}@github.com" > /home/.git-credentials
chmod 600 /home/.git-credentials
fi
git pull || echo "Pull failed, continuing anyway..."
@@ -42,29 +42,30 @@ else
# 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
echo "https://oauth2:${GITHUB_TOKEN}@github.com" > /home/.git-credentials
chmod 600 /home/.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
# Set ownership using numeric IDs (username may not exist yet in baseimage-gui)
RUN_UID="${USER_ID:-1000}"
RUN_GID="${GROUP_ID:-1000}"
chown -R "$RUN_UID:$RUN_GID" "$WORKSPACE_DIR"
# Start Happy Coder in background as claude user
# Start Happy Coder in background as the app 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"
chown "$RUN_UID:$RUN_GID" "$HAPPY_LOG"
# Start Happy Coder as claude user
sudo -u claude bash -c "cd '$WORKSPACE_DIR' && happy-coder > '$HAPPY_LOG' 2>&1 &"
# Start Happy Coder (already running as the correct user via baseimage-gui)
bash -c "cd '$WORKSPACE_DIR' && happy-coder > '$HAPPY_LOG' 2>&1 &"
# Save PID for monitoring
echo $! > /tmp/happy-coder.pid
+1 -1
View File
@@ -18,4 +18,4 @@ 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"
exec code --new-window --wait "$WORKSPACE_DIR"