Compare commits

..

21 Commits

Author SHA1 Message Date
github-actions[bot] b27487d844 fix: update URLs from cpfarhood to farhoodliquor org 2026-03-22 09:43:13 -04:00
Chris Farhood 24b31e061a Delete CNAME 2026-03-22 08:59:24 -04:00
Chris Farhood aad4ec4287 Create CNAME 2026-03-22 08:59:01 -04:00
Chris Farhood 4915fce118 Delete CNAME 2026-03-22 08:58:54 -04:00
Chris Farhood 7940419c4e Create CNAME 2026-03-22 08:58:47 -04:00
github-actions[bot] 9ddbcc1e68 Publish chart 2.6.0 2026-03-11 12:15:44 +00:00
github-actions[bot] 07a6baefd7 Publish chart 2.4.0 2026-03-03 16:46:43 +00:00
github-actions[bot] 720e6d7de9 Publish chart 2.3.0 2026-03-01 15:32:02 +00:00
github-actions[bot] c89d7e4ed6 Publish chart 2.2.5 2026-02-28 19:41:38 +00:00
github-actions[bot] 80763b817d Publish chart 2.2.4 2026-02-28 19:40:18 +00:00
github-actions[bot] 2bb60745ce Publish chart 2.2.3 2026-02-28 17:49:23 +00:00
github-actions[bot] 46cef775b1 Publish chart 2.2.2 2026-02-28 16:12:27 +00:00
github-actions[bot] b83265f131 Publish chart 2.2.1 2026-02-28 14:25:39 +00:00
github-actions[bot] 695850469d Publish chart 2.2.0 2026-02-27 14:26:42 +00:00
github-actions[bot] eca8d10772 Publish chart 2.1.1 2026-02-27 02:46:40 +00:00
github-actions[bot] 0eaf1645e1 Publish chart 2.1.0 2026-02-27 02:11:32 +00:00
github-actions[bot] 8b8af85558 Publish chart 2.0.5 2026-02-27 01:02:59 +00:00
github-actions[bot] a9593a763f Publish chart 2.0.4 2026-02-25 23:03:26 +00:00
github-actions[bot] 129f49abe8 Publish chart 2.0.3 2026-02-25 16:48:57 +00:00
github-actions[bot] 58ee2d6770 Publish chart 2.0.1-dev 2026-02-25 15:34:55 +00:00
github-actions[bot] 955f26ab37 Publish chart 2.0.0-dev 2026-02-25 14:01:56 +00:00
48 changed files with 297 additions and 3138 deletions
-7
View File
@@ -1,7 +0,0 @@
{
"enabledMcpjsonServers": [
"kubernetes",
"flux",
"playwright"
]
}
-24
View File
@@ -1,24 +0,0 @@
.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
-36
View File
@@ -1,36 +0,0 @@
## Description
<!-- Describe your changes in detail -->
## Type of Change
<!-- Mark with an `x` all that apply -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation update
- [ ] CI/CD update
## Testing
<!-- Describe the tests you ran to verify your changes -->
- [ ] Built Docker image locally
- [ ] Tested container startup
- [ ] Tested repository cloning
- [ ] Tested Happy Coder integration
- [ ] Tested VNC web interface
## Checklist
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have tested that the Docker image builds successfully
## Screenshots (if applicable)
<!-- Add screenshots to help explain your changes -->
-64
View File
@@ -1,64 +0,0 @@
name: Build and Push Docker Image
on:
push:
branches:
- main
tags:
- 'v*'
pull_request:
branches:
- main
workflow_dispatch:
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha,prefix=sha-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64
-57
View File
@@ -1,57 +0,0 @@
name: Publish Helm Chart
on:
push:
branches:
- main
paths:
- 'chart/**'
workflow_dispatch:
permissions:
contents: write
packages: write
jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Helm
uses: azure/setup-helm@v4
- name: Bump patch version
id: bump
run: |
CURRENT=$(grep '^version:' chart/Chart.yaml | awk '{print $2}')
MAJOR=$(echo $CURRENT | cut -d. -f1)
MINOR=$(echo $CURRENT | cut -d. -f2)
PATCH=$(echo $CURRENT | cut -d. -f3)
NEW_VERSION="${MAJOR}.${MINOR}.$((PATCH + 1))"
sed -i "s/^version: .*/version: ${NEW_VERSION}/" chart/Chart.yaml
echo "version=${NEW_VERSION}" >> $GITHUB_OUTPUT
- name: Commit version bump
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add chart/Chart.yaml
git commit -m "chore: bump chart version to ${{ steps.bump.outputs.version }} [skip ci]"
git push
- name: Log in to GHCR
run: |
helm registry login ghcr.io \
--username ${{ github.actor }} \
--password ${{ secrets.GITHUB_TOKEN }}
- name: Package chart
run: helm package chart/
- name: Push chart to GHCR
run: |
helm push devcontainer-${{ steps.bump.outputs.version }}.tgz oci://ghcr.io/cpfarhood/charts
-51
View File
@@ -1,51 +0,0 @@
name: Release
on:
push:
tags:
- 'v*'
permissions:
contents: write
packages: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Generate Release Notes
id: notes
run: |
# Get the tag message or generate from commits
TAG_MESSAGE=$(git tag -l --format='%(contents)' ${{ github.ref_name }})
if [ -z "$TAG_MESSAGE" ]; then
# Generate from commit messages since last tag
PREV_TAG=$(git describe --tags --abbrev=0 ${{ github.ref_name }}^ 2>/dev/null || echo "")
if [ -z "$PREV_TAG" ]; then
COMMITS=$(git log --pretty=format:"- %s (%h)" ${{ github.ref_name }})
else
COMMITS=$(git log --pretty=format:"- %s (%h)" ${PREV_TAG}..${{ github.ref_name }})
fi
NOTES="## Changes\n\n${COMMITS}\n\n## Docker Image\n\n\`\`\`bash\ndocker pull ghcr.io/${{ github.repository }}:${{ github.ref_name }}\n\`\`\`"
else
NOTES="${TAG_MESSAGE}\n\n## Docker Image\n\n\`\`\`bash\ndocker pull ghcr.io/${{ github.repository }}:${{ github.ref_name }}\n\`\`\`"
fi
echo "notes<<EOF" >> $GITHUB_OUTPUT
echo -e "$NOTES" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Create Release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref_name }}
release_name: Release ${{ github.ref_name }}
body: ${{ steps.notes.outputs.notes }}
draft: false
prerelease: false
-259
View File
@@ -1,259 +0,0 @@
# Release Process
This document describes how to create releases for this project.
## Semantic Versioning
We follow [Semantic Versioning 2.0.0](https://semver.org/):
- **MAJOR** version (v2.0.0): Incompatible API/breaking changes
- **MINOR** version (v1.1.0): New features, backwards compatible
- **PATCH** version (v1.0.1): Bug fixes, backwards compatible
## Creating a Release
### Method 1: Using GitHub CLI (Recommended)
```bash
# Ensure you're on main branch and up to date
git checkout main
git pull
# Create and push a tag
VERSION="v1.0.0" # Change this
git tag -a "$VERSION" -m "Release $VERSION
## What's New
- Feature 1
- Feature 2
- Bug fix 1
## Docker Image
\`\`\`bash
docker pull ghcr.io/cpfarhood/devcontainer:$VERSION
\`\`\`
"
git push origin "$VERSION"
# The GitHub Actions workflow will automatically:
# 1. Build the Docker image
# 2. Push to ghcr.io with multiple tags
# 3. Create a GitHub release with notes
```
### Method 2: Using Git Tags Only
```bash
git checkout main
git pull
# Create annotated tag
git tag -a v1.0.0 -m "Release v1.0.0"
# Push tag
git push origin v1.0.0
```
### Method 3: Using GitHub Web UI
1. Go to https://github.com/cpfarhood/devcontainer/releases
2. Click "Draft a new release"
3. Click "Choose a tag"
4. Type the new version (e.g., `v1.0.0`)
5. Click "Create new tag on publish"
6. Fill in the release title and description
7. Click "Publish release"
## What Happens Automatically
When you push a version tag (`v*`), GitHub Actions will:
1. **Build Docker image** with multiple tags:
- `ghcr.io/cpfarhood/devcontainer:v1.2.3` (exact version)
- `ghcr.io/cpfarhood/devcontainer:1.2` (minor version)
- `ghcr.io/cpfarhood/devcontainer:1` (major version)
- `ghcr.io/cpfarhood/devcontainer:latest` (if on default branch)
2. **Create GitHub Release** with:
- Auto-generated release notes from commits
- Docker pull command in the description
## Version Bump Guidelines
### Patch Release (v1.0.X)
- Bug fixes
- Documentation updates
- Minor dependency updates
- No new features
- No breaking changes
**Example:** v1.0.1
```bash
git tag -a v1.0.1 -m "Release v1.0.1 - Bug fixes"
git push origin v1.0.1
```
### Minor Release (v1.X.0)
- New features
- New optional configuration variables
- Enhancements to existing features
- Backwards compatible
- No breaking changes
**Example:** v1.1.0
```bash
git tag -a v1.1.0 -m "Release v1.1.0 - New Happy Coder features"
git push origin v1.1.0
```
### Major Release (vX.0.0)
- Breaking changes
- Required configuration changes
- Removal of deprecated features
- Incompatible API changes
**Example:** v2.0.0
```bash
git tag -a v2.0.0 -m "Release v2.0.0 - Breaking: New storage architecture"
git push origin v2.0.0
```
## Pre-releases
For alpha, beta, or release candidates:
```bash
# Alpha
git tag -a v1.1.0-alpha.1 -m "Release v1.1.0-alpha.1"
git push origin v1.1.0-alpha.1
# Beta
git tag -a v1.1.0-beta.1 -m "Release v1.1.0-beta.1"
git push origin v1.1.0-beta.1
# Release Candidate
git tag -a v1.1.0-rc.1 -m "Release v1.1.0-rc.1"
git push origin v1.1.0-rc.1
```
## Release Checklist
Before creating a release:
- [ ] All tests pass
- [ ] Documentation is up to date
- [ ] CHANGELOG.md is updated (if you maintain one)
- [ ] Version number follows semver
- [ ] On main/master branch
- [ ] All changes are committed
- [ ] Tag message includes release notes
## Docker Image Tags
Each release creates multiple Docker tags for flexibility:
| Git Tag | Docker Tags Created |
|---------|---------------------|
| v1.2.3 | `:v1.2.3`, `:1.2`, `:1`, `:latest` |
| v2.0.0 | `:v2.0.0`, `:2.0`, `:2`, `:latest` |
| v1.2.4-beta.1 | `:v1.2.4-beta.1`, `:1.2-beta` |
**Usage examples:**
```bash
# Specific version (recommended for production)
docker pull ghcr.io/cpfarhood/devcontainer:v1.2.3
# Minor version (gets patches automatically)
docker pull ghcr.io/cpfarhood/devcontainer:1.2
# Major version (gets minor updates and patches)
docker pull ghcr.io/cpfarhood/devcontainer:1
# Latest (always gets newest stable release)
docker pull ghcr.io/cpfarhood/devcontainer:latest
```
## Viewing Releases
- **GitHub Releases:** https://github.com/cpfarhood/devcontainer/releases
- **Docker Images:** https://github.com/cpfarhood/devcontainer/pkgs/container/devcontainer
- **Git Tags:** `git tag -l`
## Deleting a Release
If you need to delete a bad release:
```bash
# Delete local tag
git tag -d v1.0.0
# Delete remote tag
git push origin :refs/tags/v1.0.0
# Delete GitHub release (use web UI or gh CLI)
gh release delete v1.0.0
```
**Note:** Docker images pushed to ghcr.io cannot be easily deleted. It's better to create a new patch version.
## First Release
For the initial v1.0.0 release:
```bash
git checkout main
git pull
git tag -a v1.0.0 -m "Release v1.0.0 - Initial Release
## Features
- Antigravity IDE with web-based VNC access
- Happy Coder AI assistant integration
- Automatic GitHub repository cloning
- Persistent home directory with ReadWriteMany PVC
- Secure non-root execution (claude user, UID 1000)
- Support for private repositories with GitHub token
- HTTPRoute (Gateway API) support
- Multi-platform Docker images
- Comprehensive deployment documentation
## Docker Image
\`\`\`bash
docker pull ghcr.io/cpfarhood/devcontainer:v1.0.0
\`\`\`
## Deployment
See DEPLOYMENT.md for complete deployment instructions.
"
git push origin v1.0.0
```
## Example Release Workflow
```bash
# 1. Finish your feature/fix on a branch
git checkout feature/new-feature
git commit -m "feat: Add new feature"
git push
# 2. Create PR and merge to main
gh pr create
# ... get approval and merge ...
# 3. Pull latest main
git checkout main
git pull
# 4. Create release tag
git tag -a v1.1.0 -m "Release v1.1.0 - New feature"
git push origin v1.1.0
# 5. Wait for GitHub Actions
# - Check: https://github.com/cpfarhood/devcontainer/actions
# 6. Verify release
# - GitHub: https://github.com/cpfarhood/devcontainer/releases
# - Docker: docker pull ghcr.io/cpfarhood/devcontainer:v1.1.0
```
-24
View File
@@ -1,24 +0,0 @@
# 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
-21
View File
@@ -1,21 +0,0 @@
{
"mcpServers": {
"kubernetes": {
"type": "sse",
"url": "http://localhost:8080/sse"
},
"flux": {
"type": "sse",
"url": "http://localhost:8081/sse"
},
"homeassistant": {
"type": "sse",
"url": "http://localhost:8087/sse"
},
"playwright": {
"type": "sse",
"url": "http://playwright-mcp.playwright.svc.cluster.local:3000/sse"
}
}
}
-98
View File
@@ -1,98 +0,0 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- Initial project setup
- Antigravity IDE (VSCode) with web-based VNC access
- Happy Coder AI assistant integration
- Automatic GitHub repository cloning on startup
- Persistent home directory with ReadWriteMany PVC support
- Secure non-root execution (claude user, UID 1000, GID 1000)
- Support for private repositories via GitHub token
- HTTPRoute (Gateway API) support
- VNC password protection
- Multi-platform Docker image builds
- GitHub Actions CI/CD pipeline
- Automated releases on version tags
- Comprehensive deployment documentation (DEPLOYMENT.md)
- Complete variables reference (VARIABLES.md)
### Container Features
- Base: jlesage/baseimage-gui:ubuntu-22.04-v4
- Antigravity IDE (VSCode)
- Happy Coder npm package
- Chrome browser
- Node.js (LTS)
- Python 3
- Git
### Kubernetes Resources
- StatefulSet with volumeClaimTemplates
- ReadWriteMany PVC for /home directory
- ConfigMap for configuration
- Sealed Secrets support
- HTTPRoute for external access
- Service (headless)
### Configuration Options
- GitHub repository URL (required)
- GitHub token (optional, for private repos)
- VNC password (optional)
- Happy Coder server URL (optional)
- Happy Coder webapp URL (optional)
- Display resolution (configurable)
- Resource limits (configurable)
- Storage size (configurable)
### Documentation
- README.md with quick start guide
- DEPLOYMENT.md with step-by-step instructions
- VARIABLES.md with complete variable reference
- Release process documentation
- Pull request template
- Dependabot configuration
## Version History
No releases yet. See [Unreleased] section above for planned v1.0.0 features.
---
## Release Template
Use this template for future releases:
```markdown
## [1.0.0] - YYYY-MM-DD
### Added
- New features
- New configuration options
### Changed
- Changes to existing features
- Updated dependencies
### Deprecated
- Features that will be removed in future versions
### Removed
- Removed features
- Breaking changes
### Fixed
- Bug fixes
- Security patches
### Security
- Security improvements
- Vulnerability fixes
```
[Unreleased]: https://github.com/cpfarhood/devcontainer/compare/v1.0.0...HEAD
-168
View File
@@ -1,168 +0,0 @@
# 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
GITHUB_REPO="https://github.com/user/repo" make helm-deploy # Deploy with Helm
make helm-delete # Tear down Helm release
make helm-port-forward # Forward port 5800 to localhost
make helm-logs # Stream container logs
make helm-shell # Open interactive shell in pod
# Or use Helm directly
helm install mydev ./chart --set name=mydev --set githubRepo=https://github.com/user/repo
```
### 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
→ Configure git user & credentials
→ Clone GITHUB_REPO (if set)
→ Start Happy Coder
→ Launch VSCode as user `user` in /workspace
```
### Key Files
| File | Purpose |
|------|---------|
| `Dockerfile` | Image definition — installs Chrome, Node.js, VSCode, Happy Coder; creates non-root user (UID 1000) |
| `scripts/init-repo.sh` | Configures git credentials, clones GitHub repo, starts Happy Coder background service |
| `scripts/startapp.sh` | Calls init-repo.sh then opens VSCode in the workspace |
| `chart/` | Helm chart for Kubernetes deployment |
| `chart/templates/deployment.yaml` | Deployment spec — main container + MCP sidecar containers |
| `chart/templates/rbac.yaml` | ServiceAccount, Role/ClusterRole based on `clusterAccess` value |
| `chart/templates/pvc.yaml` | PersistentVolumeClaim for user home |
| `chart/templates/service.yaml` | ClusterIP Service (VNC + optional SSH) |
| `chart/values.yaml` | Default Helm values |
| `.mcp.json` | MCP server connection config (Kubernetes, Flux, Playwright) |
| `Makefile` | Build/deploy automation |
### MCP Sidecars
MCP (Model Context Protocol) servers run as sidecar containers in the pod, enabling AI assistants to interact with various services:
| Sidecar | Image | Version | Port | Endpoint | Default |
|---------|-------|---------|------|----------|---------|
| `kubernetes-mcp` | `quay.io/containers/kubernetes_mcp_server` | v0.0.57 | 8080 | `http://localhost:8080/sse` | Enabled |
| `flux-mcp` | `ghcr.io/controlplaneio-fluxcd/flux-operator-mcp` | v0.41.1 | 8081 | `http://localhost:8081/sse` | Enabled |
| `homeassistant-mcp` | `ghcr.io/homeassistant-ai/ha-mcp` | v6.7.1 | 8087 | `http://localhost:8087/sse` | Disabled |
**Note:**
- Kubernetes and Flux sidecars require `clusterAccess` != `none` to be deployed (they need RBAC permissions)
- Kubernetes and Flux sidecars inherit the pod's ServiceAccount RBAC permissions
- Home Assistant sidecar requires `HOMEASSISTANT_URL` and `HOMEASSISTANT_TOKEN` in the env secret
- Playwright MCP remains an external service
#### Enabling/Disabling MCP Servers
To control MCP sidecars, set the `enabled` flag in your values override:
```yaml
# Disable all MCP sidecars
mcpSidecars:
kubernetes:
enabled: false
flux:
enabled: false
homeassistant:
enabled: false
# Or selectively enable/disable
mcpSidecars:
kubernetes:
enabled: true # Keep Kubernetes MCP enabled
flux:
enabled: false # Disable Flux MCP
homeassistant:
enabled: true # Enable Home Assistant MCP (requires secrets)
```
When deploying via Helm:
```bash
# Using --set flag
helm install my-devcontainer ./chart --set mcpSidecars.kubernetes.enabled=false --set mcpSidecars.flux.enabled=false
# Or with a values file
helm install my-devcontainer ./chart -f custom-values.yaml
```
### Storage Model
- `/config` — 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 (automatically configures git credentials)
- `GIT_USER_NAME` — Git user name for commits (default: "DevContainer User")
- `GIT_USER_EMAIL` — Git user email for commits (default: "devcontainer@example.com")
- `GITLAB_HOST` — GitLab hostname if using GitLab with same token
- `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
- Deployed via Helm chart (`chart/`), published as OCI artifact to GHCR, reconciled by Flux
- Storage class is `ceph-filesystem` by default — change via `storage.className` in values
- Resource limits: 14 CPU, 28Gi memory
- Health checks (liveness/readiness probes) on port 5800
- Secrets: optional env Secret (`devcontainer-{name}-secrets-env`) for `GITHUB_TOKEN`, `VNC_PASSWORD`, etc.
- RBAC: controlled by `clusterAccess` value (`none`, `readonlyns`, `readwritens`, `readonly`, `readwrite`)
-449
View File
@@ -1,449 +0,0 @@
# Deployment Guide
This guide provides step-by-step instructions for deploying the Antigravity Dev Container using Helm.
## Prerequisites
- Kubernetes cluster (1.19+)
- `kubectl` configured to access your cluster
- `helm` CLI installed (3.0+)
- ReadWriteMany storage class available (e.g., `ceph-filesystem`, `nfs-client`, `efs-sc`)
- GitHub Container Registry access (images are public)
## Quick Start
### 1. Clone the Repository
```bash
git clone https://github.com/cpfarhood/devcontainer.git
cd devcontainer
```
### 2. Create Secret (Optional)
For private repos or VNC password:
```bash
kubectl create secret generic devcontainer-mydev-secrets-env \
--from-literal=GITHUB_TOKEN='ghp_...' \
--from-literal=VNC_PASSWORD='changeme' \
--from-literal=ANTHROPIC_API_KEY='sk-ant-...'
```
### 3. Deploy with Helm
```bash
# Basic deployment
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo
# With custom storage class
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set storage.className=nfs-client
# With cluster access for kubectl
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set clusterAccess=readwritens
```
### 4. Access the Container
```bash
# Port forward
kubectl port-forward deployment/devcontainer-mydev 5800:5800
open http://localhost:5800
```
## Deployment Options
### Using Values File
Create a custom `values.yaml`:
```yaml
name: mydev
githubRepo: https://github.com/youruser/yourrepo
ide: vscode
ssh: false
# Storage
storage:
size: 32Gi
className: ceph-filesystem
# Resources
resources:
requests:
memory: "4Gi"
cpu: "2000m"
limits:
memory: "16Gi"
cpu: "8000m"
# Kubernetes access
clusterAccess: readwritens
# MCP sidecars
mcpSidecars:
kubernetes:
enabled: true
flux:
enabled: false
```
Deploy:
```bash
helm install mydev ./chart -f values.yaml
```
### SSH Access Setup
Enable SSH and add your public key:
```bash
# Create secret with SSH key
kubectl create secret generic devcontainer-mydev-secrets-env \
--from-literal=SSH_AUTHORIZED_KEYS='ssh-ed25519 AAAA...'
# Deploy with SSH enabled
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set ssh=true
# Connect via SSH
kubectl port-forward deployment/devcontainer-mydev 2222:22
ssh -p 2222 user@localhost
```
### MCP Sidecar Configuration
Control MCP servers for AI-assisted operations.
**Important:** Kubernetes and Flux MCP sidecars are only deployed when:
1. They are enabled in values (`mcpSidecars.<name>.enabled: true`)
2. AND `clusterAccess` is not `none` (they need RBAC permissions to function)
```bash
# Disable all MCP sidecars
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set mcpSidecars.kubernetes.enabled=false \
--set mcpSidecars.flux.enabled=false \
--set mcpSidecars.homeassistant.enabled=false
# Enable only Kubernetes MCP
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set mcpSidecars.kubernetes.enabled=true \
--set mcpSidecars.flux.enabled=false
# Enable Home Assistant MCP (requires credentials)
kubectl create secret generic devcontainer-mydev-secrets-env \
--from-literal=homeassistant-url='http://homeassistant.local:8123' \
--from-literal=homeassistant-token='your_long_lived_token'
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set mcpSidecars.homeassistant.enabled=true
```
### Cluster Access Levels
Configure Kubernetes RBAC permissions:
| Value | Scope | Permissions | Use Case |
|-------|-------|-------------|----------|
| `none` | No access | None | Default, isolated development |
| `readonlyns` | Namespace | Read-only | View resources in namespace |
| `readwritens` | Namespace | Full access | Deploy apps in namespace |
| `readonly` | Cluster-wide | Read-only | View all cluster resources |
| `readwrite` | Cluster-wide | Full access | Cluster administration |
```bash
# Example: Full access within namespace
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set clusterAccess=readwritens
```
## Ingress Configuration
### Using Gateway API HTTPRoute
Create an HTTPRoute for external access:
```yaml
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: devcontainer-mydev
spec:
parentRefs:
- name: your-gateway
namespace: your-gateway-namespace
hostnames:
- devcontainer.example.com
rules:
- backendRefs:
- name: devcontainer-mydev
port: 5800
```
### Using Traditional Ingress
Create an Ingress resource:
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: devcontainer-mydev
spec:
rules:
- host: devcontainer.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: devcontainer-mydev
port:
number: 5800
```
## Advanced Configurations
### Custom Happy Coder Endpoints
For self-hosted Happy instances:
```bash
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set happyServerUrl=https://your-happy-server.com \
--set happyWebappUrl=https://your-happy-webapp.com
```
### Custom Display Resolution
```bash
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set display.width=2560 \
--set display.height=1440
```
### Different IDE Options
```bash
# Use Google Antigravity
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set ide=antigravity
# SSH-only mode (no GUI)
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set ide=none \
--set ssh=true
```
## Helm Operations
### List Deployments
```bash
helm list
```
### Upgrade Deployment
```bash
# Change values
helm upgrade mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/newrepo
# Upgrade with new chart version
git pull
helm upgrade mydev ./chart
```
### Uninstall
```bash
helm uninstall mydev
# Note: PVC persists by default
kubectl delete pvc userhome-mydev
```
### Rollback
```bash
# View history
helm history mydev
# Rollback to previous version
helm rollback mydev
# Rollback to specific revision
helm rollback mydev 3
```
## Troubleshooting
### Pod Not Starting
```bash
# Check pod status
kubectl get pods -l app.kubernetes.io/instance=mydev
# Describe pod for events
kubectl describe pod -l app.kubernetes.io/instance=mydev
# Check logs
kubectl logs deployment/devcontainer-mydev
```
### Repository Not Cloning
```bash
# Check init logs
kubectl logs deployment/devcontainer-mydev | grep "Repository Initialization"
# Verify secret exists
kubectl get secret devcontainer-mydev-secrets-env
# Check environment
kubectl exec deployment/devcontainer-mydev -- env | grep GITHUB
```
### VNC Not Accessible
```bash
# Check service
kubectl get svc devcontainer-mydev
kubectl describe svc devcontainer-mydev
# Test with port-forward
kubectl port-forward deployment/devcontainer-mydev 5800:5800
```
### MCP Sidecar Issues
```bash
# Check all containers
kubectl get pod -l app.kubernetes.io/instance=mydev -o jsonpath='{.items[0].spec.containers[*].name}'
# Check MCP container logs
kubectl logs deployment/devcontainer-mydev -c kubernetes-mcp
kubectl logs deployment/devcontainer-mydev -c flux-mcp
kubectl logs deployment/devcontainer-mydev -c homeassistant-mcp
# Verify RBAC permissions (for Kubernetes/Flux MCP)
kubectl auth can-i --list --as system:serviceaccount:default:devcontainer-mydev
# Check Home Assistant MCP credentials
kubectl get secret devcontainer-mydev-secrets-env -o jsonpath='{.data.homeassistant-url}' | base64 -d
# Verify the URL is accessible from the pod
kubectl exec deployment/devcontainer-mydev -- curl -s http://homeassistant.local:8123/api/
```
### Storage Issues
```bash
# Check PVC
kubectl get pvc userhome-mydev
kubectl describe pvc userhome-mydev
# Check available storage classes
kubectl get storageclass
# Verify ReadWriteMany support
kubectl get storageclass <class-name> -o yaml | grep -i accessmodes
```
## Best Practices
### Production Deployment
1. **Use specific image tags** instead of `latest`:
```bash
helm install mydev ./chart --set image.tag=v1.0.0
```
2. **Set resource limits** appropriately:
```yaml
resources:
requests:
memory: "4Gi"
cpu: "2000m"
limits:
memory: "8Gi"
cpu: "4000m"
```
3. **Enable VNC password**:
```bash
kubectl create secret generic devcontainer-mydev-secrets-env \
--from-literal=VNC_PASSWORD='strong-password-here'
```
4. **Use dedicated namespace**:
```bash
kubectl create namespace dev-environments
helm install mydev ./chart -n dev-environments
```
5. **Configure appropriate cluster access**:
- Use `readonlyns` or `readwritens` for namespace-scoped work
- Avoid `readwrite` cluster-wide access unless necessary
### Multi-User Deployment
For teams, create separate deployments per user:
```bash
# User 1
helm install alice-dev ./chart \
--set name=alice-dev \
--set githubRepo=https://github.com/alice/project
# User 2
helm install bob-dev ./chart \
--set name=bob-dev \
--set githubRepo=https://github.com/bob/project
```
### Backup and Recovery
The home directory persists on PVC. To backup:
```bash
# Create backup pod
kubectl run backup --image=busybox --restart=Never --rm -i --tty \
-- tar czf - -C /home . | gzip > home-backup.tar.gz
```
## Support
For issues or questions:
- GitHub Issues: https://github.com/cpfarhood/devcontainer/issues
- Documentation: https://github.com/cpfarhood/devcontainer
-122
View File
@@ -1,122 +0,0 @@
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=user
# 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 and xdg-utils (needed for xdg-open to work in VNC)
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 xdg-utils && \
rm -rf /var/lib/apt/lists/*
# Chrome wrapper: adds flags required for running inside a Docker container.
# xdg-open (used by Claude Code on Linux) respects $BROWSER, so pointing it
# here ensures the OAuth popup works without manual --no-sandbox invocations.
# Cleans up crash lock files and suppresses the crash-restore bubble so that
# sessions/cookies survive unclean pod shutdowns (SIGKILL).
RUN printf '#!/bin/bash\n\
CHROME_DIR="/config/userdata/.config/google-chrome"\n\
mkdir -p "$CHROME_DIR"\n\
# Remove stale lock files left by unclean container shutdown\n\
rm -f "$CHROME_DIR/SingletonLock" "$CHROME_DIR/SingletonSocket" "$CHROME_DIR/SingletonCookie"\n\
# Mark the previous session as clean so Chrome does not clear cookies\n\
PREFS="$CHROME_DIR/Default/Preferences"\n\
if [ -f "$PREFS" ]; then\n\
sed -i '\''s/"exit_type":"Crashed"/"exit_type":"Normal"/g; s/"exited_cleanly":false/"exited_cleanly":true/g'\'' "$PREFS"\n\
fi\n\
exec /usr/bin/google-chrome-stable \\\n\
--no-sandbox \\\n\
--disable-dev-shm-usage \\\n\
--disable-gpu \\\n\
--disable-session-crashed-bubble \\\n\
--user-data-dir="$CHROME_DIR" \\\n\
"$@"\n' > /usr/local/bin/google-chrome && \
chmod +x /usr/local/bin/google-chrome
# 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 and Claude Code globally
RUN npm install -g happy-coder @anthropic-ai/claude-code
# 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 && \
echo "user ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
# Create workspace directory
RUN mkdir -p /workspace && \
chown -R user:user /workspace
# Copy startup scripts
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
# Configure container to run as user user
ENV HOME=/config/userdata \
USER=user \
BROWSER=/usr/local/bin/google-chrome
# Expose VNC port (baseimage-gui default)
EXPOSE 5800
# Set app name for baseimage-gui
RUN set-cont-env APP_NAME "Antigravity"
-120
View File
@@ -1,120 +0,0 @@
.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 VNC_PASSWORD="${VNC_PASSWORD}" \
-e HAPPY_EXPERIMENTAL="true" \
-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
# Helm deployment
RELEASE_NAME ?= mydev
NAMESPACE ?= default
helm-deploy:
@echo "Deploying with Helm (release: $(RELEASE_NAME))..."
@if [ -z "$(GITHUB_REPO)" ]; then \
echo "ERROR: GITHUB_REPO environment variable is required"; \
echo "Usage: GITHUB_REPO=https://github.com/user/repo make helm-deploy"; \
exit 1; \
fi
helm upgrade --install $(RELEASE_NAME) ./chart \
--namespace $(NAMESPACE) \
--set name=$(RELEASE_NAME) \
--set githubRepo="$(GITHUB_REPO)" \
--set image.repository=$(REGISTRY)/$(IMAGE_NAME) \
--set image.tag=$(IMAGE_TAG)
helm-delete:
@echo "Deleting Helm release $(RELEASE_NAME)..."
helm uninstall $(RELEASE_NAME) --namespace $(NAMESPACE)
@echo "Note: PVC persists. To delete: kubectl delete pvc userhome-$(RELEASE_NAME) -n $(NAMESPACE)"
helm-logs:
@echo "Showing logs for $(RELEASE_NAME)..."
kubectl logs -f deployment/devcontainer-$(RELEASE_NAME) -n $(NAMESPACE)
helm-shell:
@echo "Opening shell in $(RELEASE_NAME)..."
kubectl exec -it deployment/devcontainer-$(RELEASE_NAME) -n $(NAMESPACE) -- bash
helm-port-forward:
@echo "Port forwarding $(RELEASE_NAME) to localhost:5800..."
kubectl port-forward deployment/devcontainer-$(RELEASE_NAME) 5800:5800 -n $(NAMESPACE)
# 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 "Helm/Kubernetes Targets:"
@echo " helm-deploy - Deploy with Helm chart (requires GITHUB_REPO)"
@echo " helm-delete - Delete Helm release"
@echo " helm-logs - Show container logs"
@echo " helm-shell - Open shell in container"
@echo " helm-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 " RELEASE_NAME - Helm release name (default: mydev)"
@echo " NAMESPACE - Kubernetes namespace (default: default)"
@echo " GITHUB_REPO - GitHub repository URL (required for helm-deploy)"
@echo ""
@echo "Environment Variables for 'make run':"
@echo " GITHUB_REPO - GitHub repository URL"
@echo " GITHUB_TOKEN - GitHub token (optional)"
@echo " VNC_PASSWORD - VNC password (optional)"
@echo ""
@echo "Examples:"
@echo " make build"
@echo " make push REGISTRY=ghcr.io/myuser IMAGE_TAG=v1.0"
@echo " GITHUB_REPO=https://github.com/user/repo make run"
@echo " GITHUB_REPO=https://github.com/user/repo make helm-deploy"
@echo " RELEASE_NAME=alice-dev GITHUB_REPO=https://github.com/alice/project make helm-deploy"
-365
View File
@@ -1,365 +0,0 @@
# Antigravity Dev Container
![Build and Push](https://github.com/cpfarhood/devcontainer/actions/workflows/build-and-push.yaml/badge.svg)
A containerized cloud development environment with web-based GUI access, featuring:
- **VSCode or Google Antigravity** via browser-based VNC (port 5800)
- **SSH access** option (OpenSSH on port 22, additive with any IDE)
- **Happy Coder** AI assistant backed by Claude
- **Automatic GitHub repo cloning** on startup
- **Persistent home directory** via ReadWriteMany PVC
- **Kubernetes-native** Helm chart deployment
## Quick Start
### 1. Create a secret
The secret is picked up automatically via `envFrom`. Keys recognised:
| Key | Purpose |
|-----|---------|
| `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`) |
| `homeassistant-url` | Home Assistant URL (required when `mcpSidecars.homeassistant.enabled: true`) |
| `homeassistant-token` | Home Assistant long-lived access token (required when `mcpSidecars.homeassistant.enabled: true`) |
```bash
kubectl create secret generic devcontainer-mydev-secrets-env \
--from-literal=GITHUB_TOKEN='ghp_...' \
--from-literal=VNC_PASSWORD='changeme'
```
Or use SealedSecrets:
```bash
kubectl create secret generic devcontainer-mydev-secrets-env \
--from-literal=GITHUB_TOKEN='ghp_...' \
--from-literal=VNC_PASSWORD='changeme' \
--dry-run=client -o yaml | \
kubeseal --format=yaml | kubectl apply -f -
```
### 2. Deploy with Helm
```bash
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo
```
### 3. Access
```bash
# Local port-forward
kubectl port-forward deployment/devcontainer-mydev 5800:5800
open http://localhost:5800
```
Or configure an ingress / Gateway API HTTPRoute pointing at port 5800.
### 4. Authenticate Claude
On first launch, open a terminal in the VSCode GUI and run:
```bash
claude
```
A Chrome browser window will open inside VNC for the Claude Max OAuth login. Credentials are stored on the home PVC and persist across pod restarts.
---
## Helm Chart Reference
### Core values
| Value | Default | Description |
|-------|---------|-------------|
| `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` | `/home/user/.happy` | Happy runtime state directory (persists on the home PVC) |
| `happyExperimental` | `true` | Enable experimental Happy features |
### Kubernetes cluster access
The `clusterAccess` value provisions a ServiceAccount, Role/ClusterRole, and binding so the devcontainer pod can interact with the Kubernetes API. The default is `none` — no RBAC resources are created.
| Value | Scope | Verbs |
|-------|-------|-------|
| `none` (default) | — | no access |
| `readonlyns` | release namespace | `get`, `list`, `watch` |
| `readwritens` | release namespace | `*` |
| `readonly` | cluster-wide | `get`, `list`, `watch` |
| `readwrite` | cluster-wide | `*` |
```bash
# Give the pod read-only access to its own namespace
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set clusterAccess=readonlyns
```
With any non-`none` value, a `ServiceAccount` named `devcontainer-{name}` is created and set as the pod's `serviceAccountName`, so `kubectl` and any in-cluster API calls use it automatically.
### MCP Sidecars
The devcontainer includes MCP (Model Context Protocol) servers as sidecar containers that enable AI assistants to interact with various services:
| Sidecar | Default | Purpose |
|---------|---------|---------|
| `mcpSidecars.kubernetes.enabled` | `true` | Kubernetes API access via MCP |
| `mcpSidecars.flux.enabled` | `true` | Flux GitOps operations via MCP |
| `mcpSidecars.homeassistant.enabled` | `false` | Home Assistant smart home control via MCP |
**Notes:**
- Kubernetes and Flux sidecars require `clusterAccess` != `none` to be deployed (automatically disabled when no cluster access)
- Kubernetes and Flux sidecars inherit the pod's ServiceAccount RBAC permissions (controlled by `clusterAccess`)
- Home Assistant sidecar requires additional configuration (see below)
**Disable MCP sidecars:**
```bash
# Disable both sidecars
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set mcpSidecars.kubernetes.enabled=false \
--set mcpSidecars.flux.enabled=false
# Or selectively disable
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set mcpSidecars.flux.enabled=false # Disable only Flux MCP
```
**Enable Home Assistant MCP:**
```bash
# Create secret with Home Assistant credentials
kubectl create secret generic devcontainer-mydev-secrets-env \
--from-literal=GITHUB_TOKEN='ghp_...' \
--from-literal=homeassistant-url='http://homeassistant.local:8123' \
--from-literal=homeassistant-token='your_long_lived_access_token'
# Deploy with Home Assistant MCP enabled
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set mcpSidecars.homeassistant.enabled=true
```
**Custom MCP configuration:**
```yaml
# values.yaml override
mcpSidecars:
kubernetes:
enabled: true
image:
repository: quay.io/containers/kubernetes_mcp_server
tag: latest
port: 8080
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "500m"
flux:
enabled: false # Disabled in this example
homeassistant:
enabled: true
image:
repository: ghcr.io/homeassistant-ai/ha-mcp
tag: v6.7.1 # Override the pinned version if needed
port: 8087
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
```
### Display and resources
| Value | Default | Description |
|-------|---------|-------------|
| `display.width` | `1920` | VNC width (px) |
| `display.height` | `1080` | VNC height (px) |
| `secureConnection` | `0` | Set to `1` if TLS is not terminated upstream |
| `userId` | `1000` | UID for the app user |
| `groupId` | `1000` | GID for the app user |
| `storage.size` | `32Gi` | Home PVC size |
| `storage.className` | `ceph-filesystem` | StorageClass (must be ReadWriteMany) |
| `shm.sizeLimit` | `2Gi` | `/dev/shm` size (memory-backed; used by Electron apps) |
| `resources.requests.memory` | `2Gi` | |
| `resources.requests.cpu` | `1000m` | |
| `resources.limits.memory` | `8Gi` | |
| `resources.limits.cpu` | `4000m` | |
| `envSecretName` | `devcontainer-{name}-secrets-env` | Override the secret name |
---
## Architecture
### Startup flow
```
Container start
→ cont-init.d/20-fix-user-shell.sh — fix shell/home on baseimage-gui app user
→ 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}
→ 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 --no-sandbox --user-data-dir ~/.config/antigravity ... /workspace/{repo}
IDE=none: sleep infinity
(SSH=true: sshd also running as root on port 22; host keys persisted on PVC)
```
### Storage
| Mount | Source | Persistence |
|-------|--------|-------------|
| `/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 `/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.
---
## Troubleshooting
### Happy Coder daemon not starting
```bash
# Check daemon status
happy daemon status
# Start manually (also clears any stale lock)
happy daemon start
# View daemon logs
ls ~/.happy/logs/
```
### Claude not authenticated
Browser-based OAuth login is the primary method (works inside VNC via the Chrome wrapper). If you prefer API key auth:
```bash
kubectl patch secret devcontainer-mydev-secrets-env \
--type='json' \
-p='[{"op":"add","path":"/data/ANTHROPIC_API_KEY","value":"'$(echo -n "sk-ant-..." | base64)'"}]'
```
Then restart the pod to pick up the new env var.
### VNC not loading
```bash
kubectl port-forward deployment/devcontainer-mydev 5800:5800
kubectl logs deployment/devcontainer-mydev
kubectl describe pod -l app.kubernetes.io/instance=mydev
```
### Pod not picking up new image after upgrade
The chart uses `image.tag: latest`. Kubernetes won't restart the pod on a Helm upgrade unless the Deployment spec changes. Force a restart manually:
```bash
kubectl rollout restart deployment/devcontainer-mydev
```
### Repository not cloning
```bash
kubectl logs deployment/devcontainer-mydev | grep "Repository Initialization"
kubectl exec deployment/devcontainer-mydev -- env | grep GITHUB
```
---
## Local Docker run
```bash
docker run -d \
-p 5800:5800 \
-e GITHUB_REPO="https://github.com/youruser/yourrepo" \
-e GITHUB_TOKEN="ghp_..." \
-e VNC_PASSWORD="changeme" \
-v $(pwd)/home:/home \
ghcr.io/cpfarhood/devcontainer:latest
```
---
## Building
```bash
docker build -t ghcr.io/cpfarhood/devcontainer:latest .
docker push ghcr.io/cpfarhood/devcontainer:latest
```
The image is also built and pushed automatically by CI on every push to `main` and on version tags (`v*`).
---
## Credits
- Base image: [jlesage/docker-baseimage-gui](https://github.com/jlesage/docker-baseimage-gui)
- AI assistant: [Happy Coder](https://happy.engineering) + [Claude](https://claude.ai)
-444
View File
@@ -1,444 +0,0 @@
# Helm Chart Values Reference
Complete reference for all configurable values in the Antigravity Dev Container Helm chart.
## Core Configuration
### name
- **Type:** String
- **Default:** `""`
- **Required:** Yes
- **Description:** Instance name used to generate resource names (`devcontainer-{name}`, `userhome-{name}`)
- **Example:** `mydev`, `alice-dev`, `team-workspace`
### githubRepo
- **Type:** String
- **Default:** `""`
- **Required:** Yes
- **Description:** GitHub repository URL to clone into `/workspace`
- **Example:** `https://github.com/username/repository`
### ide
- **Type:** String
- **Default:** `vscode`
- **Options:** `vscode`, `antigravity`, `none`
- **Description:** IDE to launch inside the container
- `vscode` — VSCode via VNC browser UI on port 5800
- `antigravity` — Google Antigravity (VSCode fork) via VNC on port 5800
- `none` — No IDE; useful when `ssh: true` is the sole access method
### ssh
- **Type:** Boolean
- **Default:** `false`
- **Description:** Start an OpenSSH server on port 22 in addition to the IDE
- **Note:** Requires `SSH_AUTHORIZED_KEYS` in env secret for key-based login
## Image Configuration
### image.repository
- **Type:** String
- **Default:** `ghcr.io/cpfarhood/devcontainer`
- **Description:** Container image repository
### image.tag
- **Type:** String
- **Default:** `latest`
- **Description:** Container image tag
- **Best Practice:** Use specific version tags for production
### image.pullPolicy
- **Type:** String
- **Default:** `Always`
- **Options:** `Always`, `IfNotPresent`, `Never`
- **Description:** Image pull policy
## Happy Coder Configuration
### happyServerUrl
- **Type:** String
- **Default:** `https://happy.farh.net`
- **Description:** Happy Coder server endpoint
- **When to Change:** Self-hosted Happy instance
### happyWebappUrl
- **Type:** String
- **Default:** `https://happy-coder.farh.net`
- **Description:** Happy Coder webapp URL
- **When to Change:** Self-hosted Happy instance
### happyHomeDir
- **Type:** String
- **Default:** `/config/userdata/.happy`
- **Description:** Happy runtime state directory (persists on PVC)
### happyExperimental
- **Type:** String
- **Default:** `"true"`
- **Description:** Enable experimental Happy features
## Display Configuration
### display.width
- **Type:** String
- **Default:** `"1920"`
- **Description:** VNC display width in pixels
### display.height
- **Type:** String
- **Default:** `"1080"`
- **Description:** VNC display height in pixels
### secureConnection
- **Type:** String
- **Default:** `"0"`
- **Options:** `"0"`, `"1"`
- **Description:** Set to `"0"` when TLS is terminated at the gateway layer
## User Configuration
### userId
- **Type:** String
- **Default:** `"1000"`
- **Description:** UID for the app user
### groupId
- **Type:** String
- **Default:** `"1000"`
- **Description:** GID for the app user
## Storage Configuration
### storage.size
- **Type:** String
- **Default:** `32Gi`
- **Description:** Size of the persistent home directory
- **Format:** Kubernetes quantity (e.g., `10Gi`, `100Gi`, `1Ti`)
### storage.className
- **Type:** String
- **Default:** `ceph-filesystem`
- **Description:** StorageClass name (must support ReadWriteMany)
- **Examples:** `ceph-filesystem`, `nfs-client`, `efs-sc`, `azurefile`
### shm.sizeLimit
- **Type:** String
- **Default:** `2Gi`
- **Description:** `/dev/shm` size (memory-backed emptyDir for Electron apps)
## Resource Limits
### resources.requests.memory
- **Type:** String
- **Default:** `2Gi`
- **Description:** Minimum memory to reserve
- **Format:** Kubernetes quantity
### resources.requests.cpu
- **Type:** String
- **Default:** `1000m`
- **Description:** Minimum CPU to reserve
- **Format:** Millicores (`1000m` = 1 CPU core)
### resources.limits.memory
- **Type:** String
- **Default:** `8Gi`
- **Description:** Maximum memory allowed
- **Format:** Kubernetes quantity
### resources.limits.cpu
- **Type:** String
- **Default:** `4000m`
- **Description:** Maximum CPU allowed
- **Format:** Millicores (`4000m` = 4 CPU cores)
## Kubernetes Access
### clusterAccess
- **Type:** String
- **Default:** `none`
- **Options:**
- `none` — No cluster access
- `readonlyns` — Read-only access to release namespace
- `readwritens` — Full access to release namespace
- `readonly` — Read-only access cluster-wide
- `readwrite` — Full access cluster-wide
- **Description:** RBAC permissions for the pod's ServiceAccount
## Secrets
### envSecretName
- **Type:** String
- **Default:** `""` (auto-generates as `devcontainer-{name}-secrets-env`)
- **Description:** Name of existing Secret containing environment variables
- **Keys Recognized:**
- `GITHUB_TOKEN` — PAT for private repo access
- `VNC_PASSWORD` — Password for VNC web UI
- `ANTHROPIC_API_KEY` — API key for Claude
- `SSH_AUTHORIZED_KEYS` — Public keys for SSH access
- `homeassistant-url` — Home Assistant base URL (e.g., http://homeassistant.local:8123)
- `homeassistant-token` — Home Assistant long-lived access token
## MCP Sidecars
### mcpSidecars.kubernetes.enabled
- **Type:** Boolean
- **Default:** `true`
- **Description:** Enable Kubernetes MCP server sidecar
### mcpSidecars.kubernetes.image.repository
- **Type:** String
- **Default:** `quay.io/containers/kubernetes_mcp_server`
- **Description:** Kubernetes MCP server image
### mcpSidecars.kubernetes.image.tag
- **Type:** String
- **Default:** `latest`
- **Description:** Kubernetes MCP server image tag
### mcpSidecars.kubernetes.port
- **Type:** Integer
- **Default:** `8080`
- **Description:** Port for Kubernetes MCP server
### mcpSidecars.kubernetes.resources
- **Type:** Object
- **Default:**
```yaml
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "500m"
```
- **Description:** Resource limits for Kubernetes MCP sidecar
### mcpSidecars.flux.enabled
- **Type:** Boolean
- **Default:** `true`
- **Description:** Enable Flux MCP server sidecar
### mcpSidecars.flux.image.repository
- **Type:** String
- **Default:** `ghcr.io/controlplaneio-fluxcd/flux-operator-mcp`
- **Description:** Flux MCP server image
### mcpSidecars.flux.image.tag
- **Type:** String
- **Default:** `v0.41.1`
- **Description:** Flux MCP server image tag
### mcpSidecars.flux.port
- **Type:** Integer
- **Default:** `8081`
- **Description:** Port for Flux MCP server
### mcpSidecars.flux.resources
- **Type:** Object
- **Default:**
```yaml
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "500m"
```
- **Description:** Resource limits for Flux MCP sidecar
### mcpSidecars.homeassistant.enabled
- **Type:** Boolean
- **Default:** `false`
- **Description:** Enable Home Assistant MCP server sidecar
- **Note:** Requires `homeassistant-url` and `homeassistant-token` in env secret
### mcpSidecars.homeassistant.image.repository
- **Type:** String
- **Default:** `ghcr.io/homeassistant-ai/ha-mcp`
- **Description:** Home Assistant MCP server image
### mcpSidecars.homeassistant.image.tag
- **Type:** String
- **Default:** `stable`
- **Description:** Home Assistant MCP server image tag
- **Options:** `stable` (recommended), `latest` (dev builds), `v{version}` (specific version)
### mcpSidecars.homeassistant.port
- **Type:** Integer
- **Default:** `8087`
- **Description:** Port for Home Assistant MCP server (SSE mode)
### mcpSidecars.homeassistant.resources
- **Type:** Object
- **Default:**
```yaml
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "500m"
```
- **Description:** Resource limits for Home Assistant MCP sidecar
## Usage Examples
### Minimal Configuration
```yaml
name: mydev
githubRepo: https://github.com/user/repo
```
### Production Configuration
```yaml
name: prod-workspace
githubRepo: https://github.com/company/application
ide: vscode
ssh: true
image:
tag: v1.0.0
storage:
size: 100Gi
className: ceph-filesystem
resources:
requests:
memory: "4Gi"
cpu: "2000m"
limits:
memory: "16Gi"
cpu: "8000m"
clusterAccess: readwritens
mcpSidecars:
kubernetes:
enabled: true
flux:
enabled: false
```
### Development Team Configuration
```yaml
name: team-dev
githubRepo: https://github.com/team/project
ide: antigravity
display:
width: "2560"
height: "1440"
storage:
size: 50Gi
className: nfs-client
clusterAccess: readonly
happyServerUrl: https://happy.internal.company.com
happyWebappUrl: https://happy-app.internal.company.com
```
### Smart Home Development Configuration
```yaml
name: smarthome-dev
githubRepo: https://github.com/user/home-automation
ide: vscode
clusterAccess: readwritens
mcpSidecars:
kubernetes:
enabled: true
flux:
enabled: false
homeassistant:
enabled: true
image:
tag: stable
# Requires secrets:
# homeassistant-url: http://homeassistant.local:8123
# homeassistant-token: <long-lived-access-token>
```
## Helm CLI Examples
### Using --set Flags
```bash
# Basic deployment
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/user/repo
# With multiple values
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/user/repo \
--set ide=antigravity \
--set storage.size=50Gi \
--set clusterAccess=readwritens \
--set mcpSidecars.flux.enabled=false
```
### Using Values File
Create `custom-values.yaml`:
```yaml
name: mydev
githubRepo: https://github.com/user/repo
storage:
size: 50Gi
clusterAccess: readwritens
```
Deploy:
```bash
helm install mydev ./chart -f custom-values.yaml
```
### Combining Methods
```bash
helm install mydev ./chart \
-f base-values.yaml \
-f prod-values.yaml \
--set githubRepo=https://github.com/user/repo \
--set image.tag=v2.0.0
```
## Value Precedence
Values are applied in order of precedence (highest to lowest):
1. `--set` flags on command line
2. `-f` values files (later files override earlier)
3. `chart/values.yaml` defaults
## Environment Variables
These environment variables are set in the container based on chart values:
| Environment Variable | Source Value | Description |
|---------------------|--------------|-------------|
| `GITHUB_REPO` | `githubRepo` | Repository to clone |
| `GITHUB_TOKEN` | Secret: `github-token` | PAT for private repos |
| `VNC_PASSWORD` | Secret: `vnc-password` | VNC access password |
| `ANTHROPIC_API_KEY` | Secret: `anthropic-api-key` | Claude API key |
| `SSH_AUTHORIZED_KEYS` | Secret: `ssh-authorized-keys` | SSH public keys |
| `HAPPY_SERVER_URL` | `happyServerUrl` | Happy server endpoint |
| `HAPPY_WEBAPP_URL` | `happyWebappUrl` | Happy webapp URL |
| `HAPPY_HOME_DIR` | `happyHomeDir` | Happy data directory |
| `HAPPY_EXPERIMENTAL` | `happyExperimental` | Experimental features |
| `DISPLAY_WIDTH` | `display.width` | VNC width |
| `DISPLAY_HEIGHT` | `display.height` | VNC height |
| `SECURE_CONNECTION` | `secureConnection` | TLS termination |
| `USER_ID` | `userId` | App user UID |
| `GROUP_ID` | `groupId` | App user GID |
| `IDE` | `ide` | IDE to launch |
| `SSH` | `ssh` | SSH server enabled |
-6
View File
@@ -1,6 +0,0 @@
apiVersion: v2
name: devcontainer
description: Antigravity Dev Container with Happy Coder AI assistant
type: application
version: 0.1.22
appVersion: "latest"
-28
View File
@@ -1,28 +0,0 @@
{{/*
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 }}
-193
View File
@@ -1,193 +0,0 @@
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:
{{- if ne (.Values.clusterAccess | default "none") "none" }}
serviceAccountName: {{ include "antigravity.fullname" . }}
{{- end }}
securityContext:
fsGroup: 1000
fsGroupChangePolicy: "OnRootMismatch"
containers:
- name: devcontainer
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
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: /config
- name: workspace
mountPath: /workspace
- name: shm
mountPath: /dev/shm
{{- if ne (.Values.ide | default "vscode") "none" }}
livenessProbe:
httpGet:
path: /
port: 5800
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /
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 }}
{{- if and .Values.mcpSidecars.kubernetes.enabled (ne .Values.clusterAccess "none") }}
- name: kubernetes-mcp
image: "{{ .Values.mcpSidecars.kubernetes.image.repository }}:{{ .Values.mcpSidecars.kubernetes.image.tag }}"
args:
- --port
- {{ .Values.mcpSidecars.kubernetes.port | quote }}
ports:
- containerPort: {{ .Values.mcpSidecars.kubernetes.port }}
name: k8s-mcp
protocol: TCP
livenessProbe:
httpGet:
path: /healthz
port: {{ .Values.mcpSidecars.kubernetes.port }}
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /healthz
port: {{ .Values.mcpSidecars.kubernetes.port }}
initialDelaySeconds: 5
periodSeconds: 5
resources:
{{- toYaml .Values.mcpSidecars.kubernetes.resources | nindent 12 }}
{{- end }}
{{- if and .Values.mcpSidecars.flux.enabled (ne .Values.clusterAccess "none") }}
- name: flux-mcp
image: "{{ .Values.mcpSidecars.flux.image.repository }}:{{ .Values.mcpSidecars.flux.image.tag }}"
args:
- serve
- --transport=sse
- --port={{ .Values.mcpSidecars.flux.port }}
ports:
- containerPort: {{ .Values.mcpSidecars.flux.port }}
name: flux-mcp
protocol: TCP
livenessProbe:
tcpSocket:
port: {{ .Values.mcpSidecars.flux.port }}
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
tcpSocket:
port: {{ .Values.mcpSidecars.flux.port }}
initialDelaySeconds: 5
periodSeconds: 5
resources:
{{- toYaml .Values.mcpSidecars.flux.resources | nindent 12 }}
{{- end }}
{{- if .Values.mcpSidecars.homeassistant.enabled }}
- name: homeassistant-mcp
image: "{{ .Values.mcpSidecars.homeassistant.image.repository }}:{{ .Values.mcpSidecars.homeassistant.image.tag }}"
imagePullPolicy: Always
command: ["fastmcp", "run", "ha_mcp.main:app", "--transport", "sse", "--sse-server-host", "0.0.0.0", "--sse-server-port", "{{ .Values.mcpSidecars.homeassistant.port }}"]
ports:
- name: homeassistant
containerPort: {{ .Values.mcpSidecars.homeassistant.port }}
env:
- name: HOMEASSISTANT_URL
valueFrom:
secretKeyRef:
name: {{ include "antigravity.envSecretName" . }}
key: homeassistant-url
optional: true
- name: HOMEASSISTANT_TOKEN
valueFrom:
secretKeyRef:
name: {{ include "antigravity.envSecretName" . }}
key: homeassistant-token
optional: true
livenessProbe:
tcpSocket:
port: {{ .Values.mcpSidecars.homeassistant.port }}
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
tcpSocket:
port: {{ .Values.mcpSidecars.homeassistant.port }}
initialDelaySeconds: 5
periodSeconds: 5
resources:
{{- toYaml .Values.mcpSidecars.homeassistant.resources | nindent 12 }}
{{- end }}
volumes:
- name: workspace
emptyDir: {}
- name: shm
emptyDir:
medium: Memory
sizeLimit: {{ .Values.shm.sizeLimit }}
- name: userhome
persistentVolumeClaim:
claimName: {{ include "antigravity.pvcName" . }}
-13
View File
@@ -1,13 +0,0 @@
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 }}
-97
View File
@@ -1,97 +0,0 @@
{{- $access := .Values.clusterAccess | default "none" }}
{{- $name := include "antigravity.fullname" . }}
{{- $ns := .Release.Namespace }}
{{- $labels := include "antigravity.labels" . }}
{{- if ne $access "none" }}
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ $name }}
namespace: {{ $ns }}
labels:
{{- $labels | nindent 4 }}
{{- if or (eq $access "readonlyns") (eq $access "readwritens") }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ $name }}
namespace: {{ $ns }}
labels:
{{- $labels | nindent 4 }}
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs:
{{- if eq $access "readonlyns" }}
- get
- list
- watch
{{- else }}
- "*"
{{- end }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ $name }}
namespace: {{ $ns }}
labels:
{{- $labels | nindent 4 }}
subjects:
- kind: ServiceAccount
name: {{ $name }}
namespace: {{ $ns }}
roleRef:
kind: Role
name: {{ $name }}
apiGroup: rbac.authorization.k8s.io
{{- end }}
{{- if or (eq $access "readonly") (eq $access "readwrite") }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: {{ $name }}
labels:
{{- $labels | nindent 4 }}
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs:
{{- if eq $access "readonly" }}
- get
- list
- watch
{{- else }}
- "*"
{{- end }}
- nonResourceURLs: ["*"]
verbs:
{{- if eq $access "readonly" }}
- get
{{- else }}
- "*"
{{- end }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ $name }}
labels:
{{- $labels | nindent 4 }}
subjects:
- kind: ServiceAccount
name: {{ $name }}
namespace: {{ $ns }}
roleRef:
kind: ClusterRole
name: {{ $name }}
apiGroup: rbac.authorization.k8s.io
{{- end }}
{{- end }}
-22
View File
@@ -1,22 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "antigravity.fullname" . }}
labels:
{{- 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 }}
-110
View File
@@ -1,110 +0,0 @@
# 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: ""
# 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"
happyHomeDir: "/config/userdata/.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
# Shared memory size — mounted at /dev/shm as a memory-backed emptyDir.
# Electron apps (Antigravity, Chrome) use /dev/shm for GPU/IPC buffers.
shm:
sizeLimit: 2Gi
resources:
requests:
memory: "2Gi"
cpu: "1000m"
limits:
memory: "8Gi"
cpu: "4000m"
# Kubernetes cluster access granted to the devcontainer pod via RBAC.
# Options:
# none — no cluster access (default)
# readonlyns — get/list/watch all resources in the release namespace
# readwritens — full access to all resources in the release namespace
# readonly — get/list/watch all resources cluster-wide
# readwrite — full access to all resources cluster-wide
clusterAccess: none
# Name of existing Secret containing env vars (GITHUB_TOKEN, VNC_PASSWORD, etc.)
# Defaults to: devcontainer-{name}-secrets-env
envSecretName: ""
# MCP server sidecars — run alongside the devcontainer to inherit pod RBAC.
mcpSidecars:
kubernetes:
enabled: true
image:
repository: quay.io/containers/kubernetes_mcp_server
tag: v0.0.57 # Pinned version (Jan 27, 2025) with token exchange and field selector support
port: 8080
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "500m"
flux:
enabled: true
image:
repository: ghcr.io/controlplaneio-fluxcd/flux-operator-mcp
tag: v0.41.1
port: 8081
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "500m"
homeassistant:
enabled: false # Disabled by default, requires HOMEASSISTANT_URL and HOMEASSISTANT_TOKEN
image:
repository: ghcr.io/homeassistant-ai/ha-mcp
tag: v6.7.1 # Pinned version (Feb 20, 2026) - latest stable release
port: 8087
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "500m"
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head><title>Dev Container Helm Chart Repository</title></head>
<body>
<h1>Dev Container Helm Chart Repository</h1>
<p>Add this repository to Helm:</p>
<pre>helm repo add devcontainer https://farhoodliquor.github.io/devcontainer</pre>
<p>Install the chart:</p>
<pre>helm install mydev devcontainer/devcontainer --set name=mydev</pre>
</body>
</html>
+286
View File
@@ -0,0 +1,286 @@
apiVersion: v1
entries:
devcontainer:
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.634530101Z"
description: Dev Container with AI coding agents and MCP sidecars
digest: 0c46b5d7a889cc21c28b8b088eb6c0c785a3f9324a4b39d14813c4eefe3f8e7a
keywords:
- development
- devcontainer
- vscode
- ai
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.6.0.tgz
version: 2.6.0
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.633861353Z"
description: Dev Container with AI coding agents and MCP sidecars
digest: 010294a1a9b4bb6a92c685d819a0cd34dd03bc111da715d0807799168d0aba66
keywords:
- development
- devcontainer
- vscode
- ai
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.4.0.tgz
version: 2.4.0
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.633145718Z"
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
and dynamic deployment modes
digest: a650e9c7a8feb961232aee4048d3bf0ff1d04c55100f51a7db138e1a0f8b524e
keywords:
- development
- devcontainer
- vscode
- ai
- knative
- serverless
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.3.0.tgz
version: 2.3.0
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.631873087Z"
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
and dynamic deployment modes
digest: f5c440846e7672239a6f7b14a393888988ef627d896bc967bfc018130d65921d
keywords:
- development
- devcontainer
- vscode
- ai
- knative
- serverless
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.2.5.tgz
version: 2.2.5
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.6304485Z"
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
and dynamic deployment modes
digest: 2bea7dc5c198a5b4dab0b74f0a75089210c7ba49b56176ba2af205b7dac3fe23
keywords:
- development
- devcontainer
- vscode
- ai
- knative
- serverless
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.2.4.tgz
version: 2.2.4
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.62965028Z"
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
and dynamic deployment modes
digest: afde89b22d7e4a5dfd4c918a06258d9f27f1b17493a70dba98d1ae544280505a
keywords:
- development
- devcontainer
- vscode
- ai
- knative
- serverless
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.2.3.tgz
version: 2.2.3
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.628801186Z"
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
and dynamic deployment modes
digest: 5b3b994b74da01579156021fcfb718c61989def7c16cafadb36e7ddc90cbeea7
keywords:
- development
- devcontainer
- vscode
- ai
- knative
- serverless
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.2.2.tgz
version: 2.2.2
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.627970857Z"
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
and dynamic deployment modes
digest: 8db383b24252edd37998c56bbba76793d1b6eeb37365a6894a713eef6af81210
keywords:
- development
- devcontainer
- vscode
- ai
- knative
- serverless
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.2.1.tgz
version: 2.2.1
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.627169642Z"
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
and dynamic deployment modes
digest: a1ea207bc96a35cc545d12fa8aca00452792de54e3fae74993260cd69afee0fa
keywords:
- development
- devcontainer
- vscode
- ai
- knative
- serverless
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.2.0.tgz
version: 2.2.0
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.626328122Z"
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
and dynamic deployment modes
digest: beac1182a39f158fb9aa1f3308b4b030bf378d612f2aa860f792fad62aa30321
keywords:
- development
- devcontainer
- vscode
- ai
- knative
- serverless
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.1.1.tgz
version: 2.1.1
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.625121312Z"
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
and dynamic deployment modes
digest: efb6cbd932a7ac082853d305e970db72e0086543a6963baabef16ebd2e8498f8
keywords:
- development
- devcontainer
- vscode
- ai
- knative
- serverless
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.1.0.tgz
version: 2.1.0
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.623735055Z"
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
and dynamic deployment modes
digest: 46f25124d9045802d0e50d34209d15a7fa15e1ef1c9d3f0e93ac4bb39b7c9b17
keywords:
- development
- devcontainer
- vscode
- ai
- knative
- serverless
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.0.5.tgz
version: 2.0.5
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.622870733Z"
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
and dynamic deployment modes
digest: db82381ffe831b07ce7777e8e6e05455a8eaeccfcd0afcd87825433a96cb2d65
keywords:
- development
- devcontainer
- vscode
- ai
- knative
- serverless
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.0.4.tgz
version: 2.0.4
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.622004867Z"
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
and dynamic deployment modes
digest: 872919ea64531b35dfa7f956d8a6e4130a1c7f0f80c50141b6f2d1cddd49682e
keywords:
- development
- devcontainer
- vscode
- ai
- knative
- serverless
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.0.3.tgz
version: 2.0.3
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.621180038Z"
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
and dynamic deployment modes
digest: 7cbb5379f8b41bc938a29b44c563757358c25fd843c75fb6d2f1a2b9365c0cf1
keywords:
- development
- devcontainer
- vscode
- ai
- knative
- serverless
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.0.1-dev.tgz
version: 2.0.1-dev
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.620303012Z"
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
and dynamic deployment modes
digest: 5736c92626e2c3edb8e81ddba5be581360dbf3901ccc607db8bad238ea831cd0
keywords:
- development
- devcontainer
- vscode
- ai
- knative
- serverless
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.0.0-dev.tgz
version: 2.0.0-dev
generated: "2026-03-11T12:15:44.619306973Z"
-30
View File
@@ -1,30 +0,0 @@
# Antigravity Dev Container - Session Notes
## Key Architecture Facts
- Image: `ghcr.io/cpfarhood/devcontainer:latest` (repo name is `devcontainer`, not `antigravity`)
- Deployed via Helm chart (`chart/`), not kustomize anymore
- 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
## Deployment Method
- **Primary**: Helm chart in `chart/` directory
- **Makefile targets**: `helm-deploy`, `helm-delete`, `helm-logs`, `helm-shell`, `helm-port-forward`
- **Old kustomize** (`k8s/` directory) has been removed — all deployments use Helm now
- Chart published as OCI artifact to GHCR, reconciled by Flux
## MCP Sidecars
- **Kubernetes MCP** (v0.0.57, port 8080): Only deployed when enabled AND `clusterAccess` != `none`
- **Flux MCP** (v0.41.1, port 8081): Only deployed when enabled AND `clusterAccess` != `none`
- **Home Assistant MCP** (v6.7.1, port 8087): Disabled by default, requires secrets:
- `homeassistant-url`: Base URL like `http://homeassistant.local:8123`
- `homeassistant-token`: Long-lived access token
- **Playwright MCP**: External service, not a sidecar
- Configure via `mcpSidecars.<name>.enabled` in values
- **Version Strategy**: All MCP images use pinned versions for stability (no `latest` tags)
## 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
- MCP sidecars need appropriate secrets and RBAC permissions to function
-72
View File
@@ -1,72 +0,0 @@
{
"$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/**"
]
}
-45
View File
@@ -1,45 +0,0 @@
#!/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 ==="
HOME_DIR="/config/userdata"
HOST_KEY_STORE="$HOME_DIR/.ssh/host_keys"
# Persist host keys on the home PVC so clients don't see a "host key
# changed" warning after pod restarts.
if [ -d "$HOST_KEY_STORE" ] && [ -n "$(ls "$HOST_KEY_STORE"/ssh_host_* 2>/dev/null)" ]; then
# Restore previously generated host keys
echo "Restoring SSH host keys from PVC..."
cp "$HOST_KEY_STORE"/ssh_host_* /etc/ssh/
chmod 600 /etc/ssh/ssh_host_*_key
chmod 644 /etc/ssh/ssh_host_*_key.pub
else
# First boot: generate and save host keys to PVC
echo "Generating SSH host keys (first boot)..."
ssh-keygen -A 2>/dev/null || true
mkdir -p "$HOST_KEY_STORE"
cp /etc/ssh/ssh_host_* "$HOST_KEY_STORE/"
chmod 700 "$HOST_KEY_STORE"
chown -R 1000:1000 "$HOST_KEY_STORE"
echo "SSH host keys saved to PVC."
fi
# Populate authorized_keys from env var (injected via Kubernetes secret)
if [ -n "$SSH_AUTHORIZED_KEYS" ]; then
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 $!)"
-6
View File
@@ -1,6 +0,0 @@
#!/bin/sh
# Fix the app user (UID 1000) created by baseimage-gui at runtime.
# baseimage-gui sets shell=/sbin/nologin and home=/dev/null, which
# prevents VSCode from opening terminals.
usermod -s /bin/bash app
usermod -d /config/userdata app
-121
View File
@@ -1,121 +0,0 @@
#!/bin/bash
# Initialize repository and start Happy Coder
set -e
echo "=== Repository Initialization ==="
# Set up basic git configuration
echo "Configuring git user settings..."
# Use environment variables if provided, otherwise use defaults
GIT_USER_NAME="${GIT_USER_NAME:-DevContainer User}"
GIT_USER_EMAIL="${GIT_USER_EMAIL:-devcontainer@example.com}"
git config --global user.name "$GIT_USER_NAME"
git config --global user.email "$GIT_USER_EMAIL"
# Set up git credentials early if GITHUB_TOKEN is provided
# This ensures all git operations have proper authentication
if [ -n "$GITHUB_TOKEN" ]; then
echo "Setting up git credentials..."
# Configure git to use credential store globally
git config --global credential.helper store
# Create or update the credentials file
CREDENTIALS_FILE="/config/userdata/.git-credentials"
# Support multiple git hosting providers
# GitHub supports both oauth2 and token as username
echo "https://oauth2:${GITHUB_TOKEN}@github.com" > "$CREDENTIALS_FILE"
echo "https://${GITHUB_TOKEN}:x-oauth-basic@github.com" >> "$CREDENTIALS_FILE"
echo "https://token:${GITHUB_TOKEN}@github.com" >> "$CREDENTIALS_FILE"
# GitLab format (if same token works)
if [ -n "$GITLAB_HOST" ]; then
echo "https://oauth2:${GITHUB_TOKEN}@${GITLAB_HOST}" >> "$CREDENTIALS_FILE"
fi
chmod 600 "$CREDENTIALS_FILE"
# Also create a symlink in the home directory if it doesn't exist
# This handles cases where git might look in different locations
if [ ! -f "$HOME/.git-credentials" ] && [ "$HOME" != "/config/userdata" ]; then
ln -sf "$CREDENTIALS_FILE" "$HOME/.git-credentials"
fi
echo "Git credentials configured"
else
# Even without a token, ensure git has a proper credential helper configured
# This prevents errors when credentials are added later
echo "No GITHUB_TOKEN provided, configuring basic git settings..."
git config --global credential.helper store
# Create an empty credentials file with proper permissions
CREDENTIALS_FILE="/config/userdata/.git-credentials"
touch "$CREDENTIALS_FILE"
chmod 600 "$CREDENTIALS_FILE"
# Create symlink if needed
if [ ! -f "$HOME/.git-credentials" ] && [ "$HOME" != "/config/userdata" ]; then
ln -sf "$CREDENTIALS_FILE" "$HOME/.git-credentials"
fi
fi
# 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"
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"
else
git clone "$GITHUB_REPO" "$WORKSPACE_DIR"
fi
fi
fi
# 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"
# Ensure home directory exists on the PVC (may be absent on a fresh volume)
mkdir -p "$HOME"
chown "$RUN_UID:$RUN_GID" "$HOME"
# Start Happy Coder daemon. startapp.sh already runs as the app user (UID 1000),
# so no sudo needed — Happy/Claude Code will find credentials in the correct home dir.
echo "Starting Happy Coder..."
# Remove stale lock file. HAPPY_HOME_DIR lives on the home PVC so it survives
# pod restarts — without this cleanup the daemon refuses to start after a crash.
rm -f "${HAPPY_HOME_DIR:-$HOME/.happy}/daemon.state.json.lock"
cd "$WORKSPACE_DIR"
happy daemon start || echo "Happy Coder daemon failed to start, continuing anyway..."
echo "Happy Coder daemon started"
# Export workspace directory for startapp.sh
echo "$WORKSPACE_DIR" > /tmp/workspace-dir
echo "=== Initialization Complete ==="
-40
View File
@@ -1,40 +0,0 @@
#!/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
IDE="${IDE:-vscode}"
echo "IDE mode: $IDE"
echo "Workspace: $WORKSPACE_DIR"
case "$IDE" in
antigravity)
echo "Opening Google Antigravity in: $WORKSPACE_DIR"
# --no-sandbox is required for Electron apps in Docker (no kernel sandbox available).
# Explicit --user-data-dir and --extensions-dir pin config to the home PVC so
# settings and the setup wizard state survive pod restarts.
exec antigravity --no-sandbox \
--user-data-dir "$HOME/.config/antigravity" \
--extensions-dir "$HOME/.antigravity/extensions" \
--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
-46
View File
@@ -1,46 +0,0 @@
#!/bin/bash
# Test script to verify git credentials configuration
set -e
echo "=== Git Credentials Test ==="
# Check git configuration
echo "1. Git user configuration:"
git config --global user.name || echo " ❌ user.name not set"
git config --global user.email || echo " ❌ user.email not set"
echo ""
echo "2. Git credential helper:"
git config --global credential.helper || echo " ❌ credential.helper not set"
echo ""
echo "3. Credentials file locations:"
CREDENTIALS_FILE="/config/userdata/.git-credentials"
if [ -f "$CREDENTIALS_FILE" ]; then
echo "$CREDENTIALS_FILE exists"
echo " Permissions: $(stat -c %a $CREDENTIALS_FILE)"
echo " Lines in file: $(wc -l < $CREDENTIALS_FILE)"
else
echo "$CREDENTIALS_FILE does not exist"
fi
if [ -f "$HOME/.git-credentials" ]; then
if [ -L "$HOME/.git-credentials" ]; then
echo "$HOME/.git-credentials is a symlink to $(readlink -f $HOME/.git-credentials)"
else
echo "$HOME/.git-credentials exists (not a symlink)"
fi
else
echo "$HOME/.git-credentials does not exist"
fi
echo ""
echo "4. Environment check:"
echo " HOME=$HOME"
echo " GITHUB_TOKEN=${GITHUB_TOKEN:+[SET]}"
echo " GIT_USER_NAME=${GIT_USER_NAME:-[NOT SET]}"
echo " GIT_USER_EMAIL=${GIT_USER_EMAIL:-[NOT SET]}"
echo ""
echo "=== Test Complete ==="