Compare commits

..

201 Commits

Author SHA1 Message Date
github-actions[bot] 673554b393 chore(release): 2.6.0 [skip ci] 2026-03-11 12:05:49 +00:00
DevContainer User 609752e7dc fix: use Eclipse snapshots API for jdtls download
The jdtls GitHub repo has no release assets and the Eclipse milestones
directory listing is JS-rendered. Use snapshots/latest.txt which provides
a reliable tarball filename for automated downloads.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 12:05:21 +00:00
github-actions[bot] fcd959ae1f chore(release): 2.5.0 [skip ci] 2026-03-11 11:57:24 +00:00
DevContainer User 149120ff6c feat: add LSP servers for Claude Code language intelligence
Install pyright, typescript-language-server, gopls, clangd,
rust-analyzer, lua-language-server, jdtls, kotlin-language-server,
and intelephense so Claude Code can provide rich language support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 11:57:00 +00:00
DevContainer User 50770b6e5f feat: add Node.js 22 LTS to container image
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 18:42:49 +00:00
DevContainer User 01d5ebbdb8 perf: cache base image pull only, rebuild all our layers
ARG CACHE_BUST placed immediately after FROM so only the base image
layer is served from GHA cache. All RUN/ENV/COPY layers are rebuilt
every build via CACHE_BUST=$GITHUB_SHA.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:51:22 +00:00
DevContainer User d5338ab836 Revert "perf: enable GHA cache for base image layers in Docker builds"
This reverts commit a378c0f913.
2026-03-03 16:48:47 +00:00
DevContainer User a378c0f913 perf: enable GHA cache for base image layers in Docker builds
Add ARG CACHE_BUST boundary in Dockerfile before curl-latest tool
installs. Layers above (base image, apt, Chrome) are cached via GHA
cache; layers below are rebuilt every build via CACHE_BUST=$GITHUB_SHA.
Replaces the blanket no-cache approach that also prevented caching the
expensive base image.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:48:05 +00:00
github-actions[bot] c1f3fdbb90 chore(release): 2.4.0 [skip ci] 2026-03-03 16:43:26 +00:00
DevContainer User 60f96fc8da feat: add multi-repo cloning, remove dynamic/serverless mode
Add githubRepos list field for cloning multiple repositories into a
single dev container with multi-root workspace file generation.
Remove the unused dynamic deployment mode (Knative, routing proxy,
serverless scripts) to simplify the chart to persistent-only.
Fix release workflow cache-to setting that violated the no-cache policy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:42:53 +00:00
github-actions[bot] a300e8e810 chore(release): 2.3.0 [skip ci] 2026-03-01 15:26:22 +00:00
DevContainer User b5ee7c84de feat: add OpenTofu CLI to container image
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 15:25:56 +00:00
github-actions[bot] 02c4f864f7 chore(release): 2.2.5 [skip ci] 2026-02-28 19:35:54 +00:00
DevContainer User ea966fadab fix: restore Helm CLI in Dockerfile
Only the MCP sidecar was removed, not the CLI itself.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 19:35:22 +00:00
github-actions[bot] f0e70438db chore(release): 2.2.4 [skip ci] 2026-02-28 19:34:40 +00:00
DevContainer User 0fe568a7d6 fix: remove helm MCP sidecar and CLI, disable Docker layer cache
- Remove Helm CLI from Dockerfile (OOMKilled sidecar was the only consumer)
- Disable helm MCP sidecar in chart values (uses 194Mi+ idle, OOMKills at 256Mi)
- Remove helm from .mcp.json
- Disable Docker layer cache in build-and-push workflow (cache serves stale
  layers missing tool binaries - DO NOT RE-ENABLE)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 19:34:00 +00:00
DevContainer User 7235d2dc67 fix: disable Docker layer cache in release builds
Release workflow was using GHA cache which served stale layers,
causing tools like gh CLI to be missing from tagged images despite
being in the Dockerfile. Use no-cache for release builds to ensure
every layer is built fresh. Regular CI builds keep the cache for speed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 17:43:11 +00:00
DevContainer User 7940e80cf0 chore: bump chart version to 2.2.2
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 16:10:39 +00:00
DevContainer User f6cbec05f6 feat: disable Claude Code auto-updater by default
Auto-updater doesn't work inside Docker and produces annoying errors.
Seed ~/.claude/settings.json with DISABLE_AUTOUPDATER=1 via /etc/skel
(new PVCs) and init-repo.sh (existing PVCs).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 15:27:17 +00:00
Chris Farhood 9175d48844 fix: correct ha-mcp image tag from v6.7.1 to 6.7.1 (no v prefix) 2026-02-28 09:23:34 -05:00
DevContainer User cb60f2a428 chore: bump chart version to 2.2.0
Breaking change: removed Happy Coder and Node.js.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 14:22:09 +00:00
DevContainer User 1179897cba feat: remove Happy Coder and Node.js from devcontainer
Happy Coder is no longer used. Node.js was only installed as a
dependency for `npm install -g happy-coder`, so both are removed.
This shrinks the Docker image and simplifies the configuration.

Removed from: Dockerfile, Helm values/schema/templates, serverless
manifests, Makefile, and all documentation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 13:17:47 +00:00
DevContainer User 46dc486cb4 fix: use mcp-helm hardcoded port 8012 and remove invalid -port arg
mcp-helm does not support a -port flag — it always listens on 8012.
The invalid argument caused the container to crashloop.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 11:41:31 +00:00
github-actions[bot] 41ec70c7da chore(release): 2.1.1 [skip ci] 2026-02-27 02:46:33 +00:00
DevContainer User e3f751240a fix: use expanding heredoc for release notes to avoid sed failure
The multi-line COMMITS variable broke sed substitution due to embedded
newlines. Switch to an expanding heredoc that interpolates variables
directly, removing the fragile sed placeholder replacement.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 02:30:03 +00:00
github-actions[bot] e6c3b7f7bf chore(release): 2.1.0 [skip ci] 2026-02-27 02:11:16 +00:00
DevContainer User 41e270ec32 docs: update CLAUDE.md with gh, kubeseal, and Helm MCP sidecar
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 02:08:24 +00:00
DevContainer User 05b06d1d90 feat: add gh CLI, kubeseal CLI, and Helm MCP sidecar
Install GitHub CLI (gh) via official APT repo and kubeseal via GitHub
Releases binary in the Dockerfile. Add mcp-helm sidecar on port 8088
for AI-assisted Helm chart browsing, with corresponding values, schema,
deployment template, and .mcp.json configuration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 01:26:05 +00:00
DevContainer User 8736d5b500 fix: clean up GitHub Actions workflows
- Enable GHA build cache across all workflows (replace no-cache: true)
- Add [skip ci] guard to build-and-push to prevent duplicate latest
  builds during releases
- Remove dead serverless branch trigger and build-routing-proxy job
- Remove unused id-token: write permission
- Add branch guard and contents: read permission to quick-fix workflow
- Fix release notes heredoc indentation so markdown renders correctly
- Fix git describe to use HEAD~1 for accurate changelog after version bump

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 01:17:33 +00:00
github-actions[bot] 713e5eebe6 chore(release): 2.0.5 [skip ci] 2026-02-27 00:59:31 +00:00
Chris Farhood 276477e245 fix: copy claude binary to /usr/local/bin instead of symlinking
Symlink left the original in ~/.local/bin which triggered a PATH
warning at runtime. Copy the binary and remove the original.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 19:11:33 -05:00
Chris Farhood 2136976b8e fix: symlink claude binary to /usr/local/bin after install
The installer puts claude in ~/.local/bin which isn't in PATH during
Docker build.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 18:54:45 -05:00
Chris Farhood e269e19f23 fix: pipe install script to bash, not sh
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 18:51:25 -05:00
Chris Farhood 3109de7e2e fix: switch Claude Code to native binary — npm wrapper breaks remote control
The npm-installed Claude Code runs via Node.js, which causes remote
control to fail with '/usr/bin/node: bad option: --sdk-url'. The native
binary handles subprocess spawning correctly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 18:48:57 -05:00
Chris Farhood 2b9350c86d fix: pin Claude Code to @latest tag and print version at build time
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 18:28:27 -05:00
Chris Farhood 5d62842aec fix: force fresh npm registry lookup for Claude Code install
npm was serving a cached older version even with Docker no-cache.
Clear npm cache and use --prefer-online to force a fresh registry fetch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 18:23:06 -05:00
Chris Farhood 58719cf262 fix: disable all Docker layer caching in CI
GHA cache was serving stale npm install layers despite cache-bust ARG.
Remove all caching — every build is now fully clean.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 18:14:15 -05:00
github-actions[bot] c066aa49be chore(release): 2.0.4 [skip ci] 2026-02-25 23:00:36 +00:00
Chris Farhood 204a673b3d chore: remove 2.0.0-dev image tag from CI
No longer needed — main builds tag as latest only.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 17:52:35 -05:00
Chris Farhood 04ed52bc8d fix: default image tag to latest — 2.0.0-dev was stale
The 2.0.0-dev tag was only built from the now-merged
feature/serverless-2.0.0 branch. Pushes to main only tagged latest,
so the 2.0.0-dev image in the registry was frozen and missing all
recent fixes. Default to latest and also tag main builds as 2.0.0-dev
for backwards compatibility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 17:50:19 -05:00
Chris Farhood c670dd124f fix: ensure Claude Code updates on rebuild and allow GITHUB_REPO from secret
Two fixes:
- Move Claude Code npm install below TOOLS_CACHEBUST ARG so it actually
  gets refreshed when the cache-bust value changes
- Make GITHUB_REPO env conditional so an empty Helm value no longer
  overrides the value provided via the Kubernetes secret (envFrom)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 17:36:57 -05:00
Chris Farhood 219af987ae fix: revert Claude Code back to npm install — binary download breaks container
The direct GCS binary download approach has been unreliable across
multiple attempts. Revert to the proven npm install method. Node.js
is already required for Happy Coder so there is no extra dependency.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 15:03:47 -05:00
DevContainer User c70352dc41 fix: use direct binary download for Claude Code instead of npm
npm install fails in CI due to native dependency compilation issues.
Download the pre-built binary directly from the official GCS distribution
bucket with SHA256 checksum verification. This approach worked previously
(run #135) and avoids npm entirely — Node.js is only needed for Happy Coder.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 19:20:46 +00:00
DevContainer User f689b27b78 fix: revert Claude Code to npm install — native installer unreliable
The native binary installer (both direct GCS download and claude.ai/install.sh)
has been unreliable during Docker builds. Revert to the proven npm approach.
Node.js is already required for Happy Coder, so there's no extra dependency.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 19:11:36 +00:00
DevContainer User a978b505d0 fix: use official Claude Code installer instead of raw GCS bucket URL
The previous native installer approach used a direct GCS bucket download
that was fragile and failing during builds. Switch to the official
install script (claude.ai/install.sh) which handles version discovery,
platform detection, and checksum verification properly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 18:42:17 +00:00
github-actions[bot] 69497b1ec6 chore(release): 2.0.3 [skip ci] 2026-02-25 16:46:05 +00:00
DevContainer User 698c5810a0 fix: update VSCode install to use Microsoft's current repo setup
The legacy GPG key import and .list format was failing with exit code 100
in CI. Switch to the DEB822 .sources format and install -D key method
per Microsoft's current documentation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 16:39:24 +00:00
github-actions[bot] 2582c1d824 chore(release): 2.0.2 [skip ci] 2026-02-25 16:29:21 +00:00
DevContainer User 6dd560f2ad fix: bust Docker cache for tools that fetch latest versions
The native Claude Code installer (and other tools) fetch "latest" at
build time, but Docker layer caching serves stale layers because the
RUN command text never changes. Add TOOLS_CACHEBUST build arg with
github.run_id so every CI run re-downloads fresh tool binaries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 16:16:14 +00:00
github-actions[bot] 19d47da079 chore(release): 2.0.1-dev [skip ci] 2026-02-25 15:30:05 +00:00
DevContainer User 12d3444cc5 feat: switch Claude Code to native installer
Replace npm-based Claude Code installation with the native binary
installer. Downloads directly from Anthropic's distribution bucket to
/usr/local/bin/claude — no Node.js dependency for Claude Code anymore.
Node.js is retained for Happy Coder only.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-25 15:29:14 +00:00
Chris Farhood eeb995e1fc Merge pull request #53 from cpfarhood/feat/helm-github-pages
feat: switch Helm repo to GitHub Pages
2026-02-25 08:55:27 -05:00
Chris Farhood d26b69c587 Merge pull request #52 from cpfarhood/feature/serverless-2.0.0
feat: DevContainer 2.0.0-dev with serverless architecture and unified Helm chart
2026-02-25 08:55:16 -05:00
DevContainer User da40d57e07 fix: overhaul release pipeline — 5 issues resolved
1. version input now optional — auto-increment from release_type works
2. replaced deprecated actions/create-release@v1 with gh release create
3. race condition fixed — release commit uses [skip ci], removed fragile
   github.actor guard from build-and-push.yaml
4. simplified gh-pages publishing — uses clean temp dir + shallow clone
   instead of convoluted git worktree fallback
5. version parsing strips pre-release suffixes (e.g., 2.0.0-dev → 2.0.0)

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-25 13:53:58 +00:00
DevContainer User e99ec65cd9 docs: update all references from OCI registry to GitHub Pages Helm repo
Update CLAUDE.md, README.md, and workflows README to reference the new
GitHub Pages Helm repository at https://cpfarhood.github.io/devcontainer
instead of the old OCI registry at oci://ghcr.io/cpfarhood/charts.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-25 13:38:03 +00:00
DevContainer User 38e481484e feat: switch Helm chart publishing from OCI registry to GitHub Pages
Replaces OCI push (oci://ghcr.io/cpfarhood/charts) with GitHub Pages
Helm repository at https://cpfarhood.github.io/devcontainer. The release
workflow now packages the chart, maintains an index.yaml on the gh-pages
branch, and auto-creates the branch on first run.

Usage: helm repo add devcontainer https://cpfarhood.github.io/devcontainer

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-25 13:34:56 +00:00
DevContainer User cc38a07168 fix: update values schema for deploymentMode and dynamic config
- Add deploymentMode enum (persistent | dynamic)
- Add fileManager schema (enabled, allowedPaths, deniedPaths)
- Add full dynamic mode schema (knative, routingProxy, ingress, authentik)
- Relax githubRepo pattern — not required in dynamic mode

Validated: helm template renders cleanly for both modes.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-25 13:20:08 +00:00
DevContainer User 5c3600a424 feat: add CI/CD support for 2.0.0-dev builds
Updates GitHub Actions to build development images from serverless feature branch:

## GitHub Actions Updates
- Trigger builds on feature/serverless-* branches
- Add 2.0.0-dev tag for feature/serverless-2.0.0 branch
- New routing proxy build job for serverless features
- Parallel builds: main devcontainer + routing proxy

## Chart Updates
- Default image tag changed to 2.0.0-dev
- Routing proxy tag updated to 2.0.0-dev
- Ready for development testing

## Build Outputs
When pushed to feature/serverless-2.0.0:
- ghcr.io/cpfarhood/devcontainer:2.0.0-dev
- ghcr.io/cpfarhood/devcontainer-routing-proxy:2.0.0-dev

This enables immediate testing of serverless features without manual builds.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-25 13:17:37 +00:00
DevContainer User 5565354127 feat: integrate dynamic mode into Helm chart v2.0.0-dev
Implements unified Helm chart supporting both deployment modes:
- persistent: Traditional PVC-based deployment (v1.x behavior)
- dynamic: Serverless Knative with auto-scaling and dynamic routing

## Chart Changes
- Chart.yaml: Bump to v2.0.0-dev with deployment mode support
- values.yaml: Add deploymentMode field and dynamic configuration
- All templates: Conditional rendering based on deploymentMode

## Dynamic Mode Templates
- knative-service.yaml: Auto-scaling dev containers with repo routing
- routing-proxy.yaml: GitHub repo extraction service
- dynamic-ingress.yaml: Ingress with Authentik auth support

## Usage Examples
```bash
# Traditional persistent mode (default)
helm install mydev ./chart --set name=mydev --set githubRepo=...

# Dynamic serverless mode
helm install mydev ./chart -f values-dynamic.yaml \
  --set name=mydev --set dynamic.ingress.host=devcontainer.example.com

# Development builds
helm install mydev ./chart --set deploymentMode=dynamic \
  --set image.tag=2.0.0-dev --set dynamic.ingress.host=...
```

All existing persistent deployments remain compatible (deploymentMode defaults to "persistent").

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-25 13:12:46 +00:00
DevContainer User b69cd80cae feat: serverless 2.0.0 architecture with Authentik auth proxy
Implements a complete serverless development container platform:

## Architecture
- Authentik forward auth for authentication/authorization
- NGINX routing proxy extracts GitHub repo from URL path
- Knative Service auto-scales dev container instances from 0
- Dynamic GitHub repo routing via /github/{owner}/{repo}

## Components
- routing-proxy: NGINX-based service for repo extraction and forwarding
- deployment.yaml: Complete K8s manifests (proxy, Knative, ingress, secrets)
- authentik-config.yaml: Authentik application and provider configs
- serverless scripts: Dynamic repo initialization and startup handling
- Comprehensive documentation and Makefile for ops

## Key Features
- Scale to zero when not in use (cost-effective)
- Per-request isolation (each repo gets own container)
- Built-in file manager for upload/download
- Support for private repos via GitHub tokens
- User attribution via Authentik headers
- WebSocket support for VNC connections

Example usage: https://devcontainer.farh.net/github/microsoft/vscode

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-25 13:04:25 +00:00
DevContainer User 3e46bf5ec1 feat: add Helm CLI and built-in web file manager
- Install Helm v3.17.1 in Dockerfile for chart development (closes #49)
- Add fileManager toggle using base image's WEB_FILE_MANAGER (closes #11)
- Wire WEB_FILE_MANAGER env vars in deployment template
- Update CLAUDE.md, README.md with new features and values

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-24 14:49:03 +00:00
DevContainer User c8a7bbcd6e fix: phase 0 quick wins — safety, naming, and portability
- Add helm.sh/resource-policy: keep to PVC (prevent data loss on uninstall)
- Add fail guard for empty name value in Helm templates
- Fix Makefile IMAGE_NAME from antigravity to devcontainer
- Pin busybox:1.37, homeassistant:v6.7.1, playwright:v0.0.68 (was latest/stable)
- Set imagePullPolicy: IfNotPresent on pinned sidecars
- Remove fetch/sequentialthinking from .mcp.json (sidecars removed from chart)
- Default storage.className to empty (use cluster default, was ceph-filesystem)
- Default Happy Coder URLs to empty (was private farh.net endpoints)
- Broaden githubRepo schema to accept GitLab/Gitea URLs
- Add unknown IDE warning before VSCode fallback
- Add mkdir -p before credential file write (fix fresh PVC boot)
- Guard app user existence in cont-init-user.sh
- Add NOTES.txt post-install template with port-forward and secret hints
- Add standard app.kubernetes.io/* labels and separate selectorLabels

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 04:13:24 +00:00
DevContainer User adb2ee4817 chore: update Claude Code settings and enable voltagent plugins
Add fetch and sequentialthinking MCP servers to allowed list, and enable
voltagent dev-exp and lang subagent plugins.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 03:50:22 +00:00
github-actions[bot] 3637a0a6fc chore: release version 1.0.2 2026-02-24 02:53:07 +00:00
DevContainer User f67066823b fix(docker): correct Crush installation tar extraction
Fixed the Crush AI assistant installation in Dockerfile by replacing the
single-line tar extraction with --strip-components (which was failing)
with a multi-step approach: download to temp, extract, move binary,
and cleanup. This ensures the binary is properly extracted from the
versioned directory structure in the tarball.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-24 02:42:44 +00:00
Chris Farhood 50560652cb feat(helm): rip out sequentialthinking MCP server sidecar 2026-02-23 21:05:40 -05:00
Chris Farhood 0fc4ff503b ci: fix chart version update failing when version already matches 2026-02-23 20:55:38 -05:00
Chris Farhood 04203e4efb feat(helm): rip out fetch MCP server as requested 2026-02-23 20:54:15 -05:00
Chris Farhood b710daac05 fix(helm): allow additionalProperties in values schema to prevent Flux dropping variables and change sidecar container configs 2026-02-23 20:54:15 -05:00
github-actions[bot] 52a29da38d chore: release version 0.4.11 2026-02-24 01:45:24 +00:00
github-actions[bot] ea71f71c74 chore: release version 0.4.9 2026-02-24 01:25:29 +00:00
Chris Farhood f6eceb4d94 fix(helm): shorten sequentialthinking port name (fixes #48) 2026-02-23 20:24:27 -05:00
github-actions[bot] 84bf7841c3 chore: release version 0.4.8 2026-02-23 23:59:03 +00:00
Chris Farhood c823a30c2a fix(chart): add missing MCP sidecars to values schema (#47)
PR #45 added fetch and sequentialthinking MCP sidecars to values.yaml
and the deployment template but missed updating values.schema.json.
The schema has additionalProperties: false on mcp.sidecars, causing
Helm upgrade to fail with validation errors.

Also adds resourceProfile to the schema as it was missing.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Happy <yesreply@happy.engineering>
2026-02-23 18:57:41 -05:00
github-actions[bot] 27af9dc9c4 chore: release version 0.4.7 2026-02-23 23:35:13 +00:00
github-actions[bot] 0944dcec1c chore: release version 0.4.6 2026-02-23 21:19:15 +00:00
Chris Farhood 60a2689658 Merge pull request #46 from cpfarhood/fix/ci-race-condition
fix(ci): resolve Docker build race condition
2026-02-23 16:18:04 -05:00
Chris Farhood 53bc4b68a6 fix(ci): resolve race condition between build and release workflows
Remove tag triggers and duplicate release job from build-and-push.yaml.
The release-unified.yaml workflow handles the full release flow (Docker
build, Helm chart, GitHub release) when triggered via workflow_dispatch.

Previously, release-unified.yaml pushing a commit to main AND a v* tag
would trigger build-and-push.yaml up to twice, causing multiple Docker
builds to race for the :latest tag. The stale GHA layer cache in the
racing build could overwrite :latest with an image missing new tools
(e.g., crush, opencode).

Changes:
- Remove tags: ['v*'] trigger (release-unified handles tag-based releases)
- Remove duplicate release job (Helm chart + GitHub release)
- Remove semver tag patterns from metadata (not needed without tag trigger)
- Skip builds from github-actions[bot] to avoid racing with release commits

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-23 16:05:53 -05:00
Chris Farhood d526a445fd Merge pull request #45 from cpfarhood/feature/add-fetch-sequential-thinking-mcp
feat: add Fetch and Sequential Thinking MCP sidecars
2026-02-23 13:41:46 -05:00
DevContainer User f56b3efb66 feat: add Fetch and Sequential Thinking MCP sidecars
Add two new MCP (Model Context Protocol) sidecars to enable web content fetching
and structured problem-solving capabilities:

- **Fetch MCP**: Web content fetching and HTML to markdown conversion on port 8082
- **Sequential Thinking MCP**: Structured thinking and problem-solving processes on port 8083

Both sidecars are enabled by default and use the official MCP Docker images
(mcp/fetch and mcp/sequentialthinking) with fastmcp SSE transport.

Changes:
- Add fetch and sequentialthinking sidecars to values.yaml
- Add sidecar containers to deployment.yaml template
- Update .mcp.json with new server endpoints
- Update CLAUDE.md documentation with new sidecar details

Closes #43, #44

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-23 18:37:12 +00:00
github-actions[bot] a778d32b3b chore: release version 0.4.5 2026-02-23 01:17:43 +00:00
DevContainer User b48fce97d5 fix: improve Antigravity IDE installation to resolve AI chat issues
- Force fresh APT repository data by clearing package cache before update
- Add debugging output to show available and installed versions
- Use --no-install-recommends to avoid unnecessary packages
- Add version validation during build process
- Disable auto-updates in Antigravity settings to prevent container conflicts

This should resolve the "agentSessions service not found" error that prevents
the AI chat window from responding.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-23 01:13:41 +00:00
github-actions[bot] 47af7acc5e chore: release version 0.4.4 2026-02-23 01:08:30 +00:00
DevContainer User da45415cfe fix: playwright MCP sidecar bind to 0.0.0.0 for probe access (#42)
The Playwright MCP server binds to 127.0.0.1 by default, which makes
it unreachable by Kubernetes liveness/readiness probes from the kubelet.
Add --host 0.0.0.0 to bind to all interfaces.

Fixes #42

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-22 20:43:28 +00:00
DevContainer User 897555b1dc chore: bump chart version to 0.4.2
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-22 20:27:12 +00:00
DevContainer User df1f4d9b50 fix: ensure Docker image is built before release creation
- Move release job into build-and-push workflow with dependency
- Remove separate release.yaml workflow to prevent race condition
- Ensures Docker image is available before GitHub release is published

Fixes the issue where clients see release with docker pull instructions
before the image is uploaded to GHCR.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-22 20:20:26 +00:00
DevContainer User 2f5a8d65d5 fix: playwright MCP sidecar startup args (#41)
The Playwright MCP image (mcr.microsoft.com/playwright/mcp) doesn't
support --transport flag. Use node cli.js entrypoint with --headless,
--browser chromium, --no-sandbox, and --port for SSE mode.

Fixes #41

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-22 20:04:16 +00:00
github-actions[bot] 0d8fe1ec64 chore: release version 0.4.0 2026-02-22 19:39:04 +00:00
DevContainer User 00638d372c docs: comprehensive update for new values format and template names
- Fix CLAUDE.md: update Docker build example from antigravity to devcontainer
- Fix README.md stale value references throughout:
  - ide → ide.type, ssh → ssh.enabled
  - happyServerUrl → happy.serverUrl (and all happy.* values)
  - secureConnection → display.secureConnection
  - userId/groupId → user.id/user.groupId
  - mcpSidecars.* → mcp.sidecars.*
- Remove GitHub MCP sidecar from custom config example (discontinued)
- Fix YAML indentation in MCP config examples
- Fix storage mount path from /home to /config
- Fix Happy state directory from /home/user/.happy to /config/userdata/.happy

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-22 19:38:11 +00:00
DevContainer User 31ec139a8a refactor: rename template helpers from antigravity to devcontainer
Rename all Helm template helper functions from antigravity.* to
devcontainer.* to match the actual chart name. Also update Chart.yaml
description to remove "Antigravity" branding.

Renamed helpers:
- antigravity.fullname → devcontainer.fullname
- antigravity.pvcName → devcontainer.pvcName
- antigravity.envSecretName → devcontainer.envSecretName
- antigravity.labels → devcontainer.labels
- antigravity.smartResources → devcontainer.smartResources
- antigravity.smartDefaults → devcontainer.smartDefaults
- antigravity.mcpDefaults → devcontainer.mcpDefaults

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-22 19:34:09 +00:00
DevContainer User 71c6ca70cc fix: resolve MCP sidecar image failures (issue #40)
Three fixes for broken MCP sidecars:

1. Remove GitHub MCP sidecar entirely - the upstream image
   (ghcr.io/modelcontextprotocol/servers/github) is discontinued.
   GitHub MCP is now accessed via Copilot API instead.

2. Fix Playwright MCP image - changed from non-existent
   microsoft/playwright-mcp to mcr.microsoft.com/playwright/mcp
   (the official Microsoft Container Registry image).

3. Fix pgtuner MCP startup command - changed --transport to --mode
   and added --host 0.0.0.0 to match the current pgtuner_mcp CLI.

Bumps chart version to 0.3.3.

Closes #40

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-22 19:31:08 +00:00
github-actions[bot] b9c30b8e4d chore: release version 0.3.2 2026-02-22 18:05:08 +00:00
DevContainer User 794de6d0e5 fix: update service.yaml to use new nested values format
service.yaml was still using the old flat values format (.Values.ide
and .Values.ssh) while values.yaml had been restructured to nested
objects (ide.type and ssh.enabled). This caused a type comparison
error breaking all devcontainer deployments across cpfarhood and
homeassistant namespaces.

Bumps chart version to 0.3.1.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-22 18:04:23 +00:00
github-actions[bot] fbcd9c1f72 chore: release version 0.3.0 2026-02-22 16:27:17 +00:00
DevContainer User 3be59e56eb docs: add Claude Code to documented AI coding agents
Claude Code was already installed via npm (line 65 of Dockerfile) but
was missing from the feature lists in README.md and CLAUDE.md.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-22 16:25:46 +00:00
DevContainer User be9479ef75 feat: add OpenCode and Crush AI coding agents to Docker image
Install both terminal-based AI coding agents alongside Happy Coder:
- OpenCode (opencode-ai/opencode) - open-source AI coding agent
- Crush (charmbracelet/crush) - OpenCode's active successor by Charm

Both are installed as Go binaries to /usr/local/bin from latest GitHub
releases and available to users via `opencode` and `crush` commands.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-22 16:24:19 +00:00
DevContainer User 065a6534e3 docs: comprehensive updates for new Helm chart structure
Major documentation updates to reflect the reorganized Helm chart:

- Update README.md and CLAUDE.md to use new mcp.sidecars structure
- Reflect removal of Happy daemon automatic startup
- Document new logical values organization (Basic → Access → Infrastructure → Integrations → Smart Defaults)
- Update all MCP sidecar examples to use mcp.sidecars instead of mcpSidecars
- Document new quickstart deployment options and progressive disclosure
- Update troubleshooting sections with current configurations
- Standardize all secret environment variable references to SCREAMING_SNAKE_CASE

This completes the documentation sync following the major Helm chart
user experience improvements implemented in previous commits.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-22 13:32:49 +00:00
DevContainer User d8d83ffa47 feat: major Helm chart user-friendliness improvements
Implements comprehensive enhancements to make the Helm chart more user-friendly
and easier to deploy across different scenarios.

🎯 **IMPLEMENTED IMPROVEMENTS:**

**1. Values Organization & Grouping**
- Reorganized values.yaml with logical sections:
  - Basic Configuration (name, image, githubRepo)
  - Access & Interface (ide, ssh, display, user)
  - Infrastructure & Resources (storage, resources, shm, clusterAccess)
  - Integrations (happy, mcp sidecars)
  - Smart Defaults & Auto-Detection
- Updated deployment templates to use new structure
- Maintains clean, navigable configuration

**2. Simplified Quick-Start Values**
- Added values-quickstart.yaml for 80% of users
- Just 2 required fields: name + githubRepo
- Includes usage instructions and common customizations
- Copy-paste ready deployment experience

**3. Better Documentation Structure**
- Added values.schema.json for IDE validation and autocomplete
- Created comprehensive USAGE.md with real-world examples:
  - Development, team, K8s admin, AI/ML, lightweight scenarios
  - Secret configuration examples
  - Resource sizing by use case
  - Common troubleshooting patterns

**4. Smart Defaults & Auto-Detection**
- Added template helpers for intelligent resource sizing
- Environment auto-detection based on naming patterns
- Smart MCP sidecar selection based on cluster access
- Resource profile auto-selection (auto/small/medium/large/xlarge)
- Enhanced _helpers.tpl with smart default functions

🎫 **CREATED GITHUB ISSUES** for future enhancements:
- #32: Helm chart preset profiles
- #33: Split large deployment.yaml template
- #34: Advanced auto-detection for Kubernetes environments
- #35: Specialized Helm chart variants (basic/team/k8s/ai)
- #36: Installation and configuration helper scripts
- #37: Comprehensive validation and health monitoring
- #38: User experience improvements with better error messages
- #39: Comprehensive examples and template library

📊 **IMPACT:**
- Reduced required configuration from ~20 values to 2 essential fields
- Added IDE support with schema validation
- Created guided examples for common scenarios
- Established foundation for advanced auto-detection
- Planned comprehensive tooling ecosystem

🚀 **USAGE:**
```bash
# Quick start (new users)
cp chart/values-quickstart.yaml my-values.yaml
# Edit name and githubRepo
helm install mydev ./chart -f my-values.yaml

# Full customization (power users)
# Edit chart/values.yaml with organized sections
helm install mydev ./chart
```

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-22 13:16:08 +00:00
DevContainer User 9535886945 feat: remove automatic Happy daemon startup
Removes unreliable automatic Happy Coder daemon startup, allowing users
to start it manually when needed for better reliability and control.

**Changes:**
- scripts/init-repo.sh: Removed Happy daemon startup code and lock cleanup
- scripts/startapp.sh: Updated comment to reflect init-repo only handles git
- README.md: Updated startup flow documentation and troubleshooting section
- CLAUDE.md: Updated startup flow and file descriptions

**Benefits:**
- No more unreliable automatic daemon startup failures
- Users can start Happy daemon manually when needed: `happy daemon start`
- Cleaner container startup without Happy-related delays or errors
- Happy configuration and credentials still persist on PVC when used

**Usage:**
Users can now manually start Happy Coder when needed:
```bash
happy daemon start  # Start when needed
happy daemon status # Check status
happy daemon stop   # Stop if needed
```

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-22 13:01:19 +00:00
DevContainer User 44f30ec03f fix: standardize secret key names to SCREAMING_SNAKE_CASE
Fixes inconsistent variable naming across the codebase by standardizing
all secret keys to use SCREAMING_SNAKE_CASE format.

**Changes:**
- homeassistant-url → HOMEASSISTANT_URL
- homeassistant-token → HOMEASSISTANT_TOKEN
- database-uri → DATABASE_URI
- pgtuner-exclude-userids → PGTUNER_EXCLUDE_USERIDS
- github-token → GITHUB_TOKEN

**Files updated:**
- chart/templates/deployment.yaml - Fixed secret key references
- README.md - Updated documentation and examples

**Result:**
All secret keys now follow the established SCREAMING_SNAKE_CASE
convention (GITHUB_TOKEN, VNC_PASSWORD, SSH_AUTHORIZED_KEYS, etc.)
for consistency and clarity.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-22 12:59:16 +00:00
DevContainer User 76391a8ed0 feat: major improvements to dev container and MCP sidecars
This commit addresses multiple GitHub issues and adds significant enhancements:

🔧 **Issue #8 - Browser Window Title Fix**
- Updated browser window title from "Antigravity Dev Container" to "Dev Container"
- Changed APP_NAME in Dockerfile and startup script for consistency

🚀 **Issue #30 - PostgreSQL Tuner MCP Sidecar**
- Added PostgreSQL performance tuning MCP sidecar (dog830228/pgtuner_mcp)
- Provides AI assistant with database analysis capabilities:
  - Slow query analysis and optimization suggestions
  - Index recommendations with HypoPG virtual testing
  - Table and index bloat detection
  - Vacuum operation tracking and health scoring
- Requires DATABASE_URI in env secret, optional PGTUNER_EXCLUDE_USERIDS
- Disabled by default, configurable via mcpSidecars.pgtuner.enabled
- Updated CLAUDE.md documentation with full configuration examples

🎭 **Playwright: Centralized Service → Sidecar Conversion**
- Converted Playwright from external service to self-contained sidecar
- Updated .mcp.json endpoint: cluster service → http://localhost:8086/sse
- Added deployment configuration with proper health checks
- Enabled by default for immediate browser automation capabilities
- Higher resource allocation (512Mi memory, 1 CPU) for browser workloads

📚 **Documentation Updates**
- Updated README.md: "Antigravity Dev Container" → "Dev Container"
- Added comprehensive MCP sidecars documentation
- Updated secret keys table with database-uri and pgtuner-exclude-userids
- Added configuration examples for all 6 MCP sidecars:
  - kubernetes-mcp (enabled)
  - flux-mcp (enabled)
  - github-mcp (disabled - archived)
  - homeassistant-mcp (disabled - needs secrets)
  - pgtuner-mcp (disabled - needs DATABASE_URI)
  - playwright-mcp (enabled - browser automation)
- Updated CLAUDE.md with complete sidecar reference table
- Added Helm deployment examples and troubleshooting

🏗️ **Architecture Improvements**
- All MCP sidecars now self-contained within pod
- Consistent SSE transport configuration across all sidecars
- Proper health checks and resource limits for all services
- Simplified deployment with no external service dependencies

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-22 12:55:17 +00:00
github-actions[bot] ac1e5074b1 chore: release version 0.2.5 2026-02-22 12:37:45 +00:00
DevContainer User 2a63f227f1 change default value 2026-02-22 12:36:21 +00:00
DevContainer User 5da23def5b fix: add automatic release workflow for tag-based releases
- Triggers on tag pushes (v*) to create GitHub releases
- Publishes Helm chart to OCI registry
- Generates release notes with commit history
- Complements existing build-and-push workflow

Now releases are fully automated:
1. Push tag → build-and-push.yaml builds Docker images
2. Push tag → release.yaml creates GitHub release + publishes Helm chart

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-21 21:17:37 +00:00
DevContainer User 5532eee8cd chore: release version 0.2.4
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-21 19:13:41 +00:00
DevContainer User d32e453f93 feat: add PostgreSQL tuner (pgtuner) MCP sidecar
- Add pgtuner MCP sidecar configuration (disabled by default)
- Supports PostgreSQL performance tuning and optimization
- Analyzes slow queries, recommends indexes, detects bloat
- Requires DATABASE_URI secret to be configured
- Runs in SSE mode on port 8085

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-21 19:11:50 +00:00
DevContainer User f95e8877e8 chore: release version 0.2.3
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-21 17:31:09 +00:00
DevContainer User 46267b6e26 fix: resolve antigravity IDE init and homeassistant MCP issues (fixes #27, #28)
- Fix homeassistant MCP sidecar command by removing incorrect module specification
- Add init container for antigravity IDE to create /config/userdata directory
- Bump chart version to 0.2.2

The homeassistant sidecar was failing with "File not found: /app/ha_mcp.main"
because fastmcp should run without explicit module specification.

The antigravity IDE was failing to initialize due to missing /config/userdata
directory. Added an init container to ensure the directory exists with proper
permissions before the main container starts.

Generated with Claude Code via Happy

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-21 17:29:38 +00:00
github-actions[bot] c4cbd67399 chore: release version 0.2.2 2026-02-21 16:27:25 +00:00
DevContainer User a7799dbb16 hotfix: disable GitHub MCP sidecar - image doesn't exist
The GitHub MCP server has been archived and moved to servers-archived repo.
There is no Docker image available at ghcr.io/modelcontextprotocol/servers/github.

Disabling by default to prevent ImagePullBackOff errors.

TODO: Either build custom image from archived source or find alternative.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-21 16:25:45 +00:00
github-actions[bot] 45b8e5e95e chore: release version 0.2.1 2026-02-21 16:20:17 +00:00
DevContainer User a0b409239e feat: add GitHub MCP sidecar and fix Home Assistant MCP command (fixes #26)
- Fixed Home Assistant MCP command flags from --sse-server-host/port to --host/port
- Added GitHub MCP server as new sidecar (enabled by default)
- Uses existing GITHUB_TOKEN from environment
- Updated documentation and .mcp.json configuration

The GitHub MCP sidecar provides AI assistants with ability to interact with
GitHub repositories, issues, PRs, and more using the same token used for
repository cloning.

Fixes #26

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-21 16:19:11 +00:00
DevContainer User eacf41302c cleanup: remove redundant workflows and simplify CI/CD
Removed 4 redundant workflow files:
- helm-publish.yaml (integrated into release-unified)
- helm-release.yaml.old (the auto-version-bump nightmare)
- release.yaml (replaced by release-unified)
- tag-release.md (old docs, replaced by README.md)

Now down to just 3 clean workflows:
- release-unified.yaml: Complete release process
- quick-fix.yaml: Emergency Docker builds
- build-and-push.yaml: Automatic CI for PRs/tags

Also updated documentation to reflect the simplified pipeline.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-21 16:12:35 +00:00
github-actions[bot] cbdee590bf chore: release version 0.2.0 2026-02-21 16:09:49 +00:00
DevContainer User 5570b2c617 fix: completely overhaul the CI/CD pipeline to not suck
BREAKING CHANGE: Removed auto-version-bump workflow that was causing [skip ci] hell

New features:
- Unified release workflow that handles everything in one go
- Quick fix workflow for emergency deployments
- No more [skip ci] preventing Docker builds
- No more manual tag juggling
- Single button release process

The old pipeline was a disaster with disconnected workflows, auto-version-bumps
with [skip ci] that prevented Docker builds, and required manual tag deletion/
re-pushing to trigger builds. This new pipeline is actually usable.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-21 16:04:46 +00:00
DevContainer User c3f8421d60 cleanup: remove CI trigger file 2026-02-21 15:29:29 +00:00
DevContainer User 21d8fc73e6 ci: trigger build for v0.1.24
Force CI/CD pipeline to build the Docker image for v0.1.24 which includes:
- Git credentials automation fix
- Home Assistant MCP sidecar command fix

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-21 15:29:19 +00:00
DevContainer User 0a63894f6d chore: bump chart version to 0.1.22 [skip ci] 2026-02-21 15:10:36 +00:00
DevContainer User a50a1815e0 fix: correct Home Assistant MCP sidecar command for SSE mode
The Home Assistant MCP was failing with 'fastmcp-sse.json not found' error.
Updated command to use proper fastmcp arguments for SSE transport mode
without requiring a configuration file.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-21 15:10:17 +00:00
github-actions[bot] 131dad8611 chore: bump chart version to 0.1.23 [skip ci] 2026-02-21 15:00:25 +00:00
Chris Farhood 581d0737e4 fix(chart): correct ha-mcp image tag from v6.7.1 to 6.7.1
The ghcr.io/homeassistant-ai/ha-mcp registry publishes tags without
the 'v' prefix, causing ErrImagePull when the sidecar is enabled.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-21 15:00:13 +00:00
DevContainer User 68110d911f chore: bump chart version to 0.1.21 [skip ci] 2026-02-21 14:33:16 +00:00
DevContainer User 427f7a710c fix: automate git credentials setup during container initialization
- Configure git credentials at the beginning of init-repo.sh
- Set up git user name/email with defaults or from environment variables
- Create .git-credentials file with proper permissions (600)
- Support multiple GitHub credential formats for better compatibility
- Create symlinks to handle different credential file locations
- Add test script to verify credentials configuration
- Update documentation with new environment variables

This fixes issues where containers fail due to missing .git-credentials
by ensuring credentials are properly configured before any git operations.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-21 14:28:58 +00:00
github-actions[bot] 745a0cdf59 chore: bump chart version to 0.1.20 [skip ci] 2026-02-21 13:54:03 +00:00
Developer 115907cdc8 fix: pin MCP sidecar versions for stability
- Pin kubernetes-mcp to v0.0.57 (Jan 27, 2025) with token exchange and field selector support
- Pin flux-mcp to v0.41.1 (already pinned)
- Pin homeassistant-mcp to v6.7.1 (Feb 20, 2026) - latest stable release
- Update documentation with version details
- Bump chart version to 0.1.19

This ensures reproducible deployments and prevents unexpected breaking changes
from floating tags (latest/stable).

Generated with Claude Code via Happy

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-21 13:53:49 +00:00
github-actions[bot] 0c4f93c077 chore: bump chart version to 0.1.19 [skip ci] 2026-02-21 13:27:34 +00:00
Developer a83d79bc10 feat: add Home Assistant MCP sidecar and fix K8s/Flux MCP deployment logic
Added features:
- Home Assistant MCP server as optional sidecar (mcpSidecars.homeassistant)
- Requires homeassistant-url and homeassistant-token secrets
- Runs on port 8087 using SSE transport mode
- Disabled by default due to credential requirements

Fixed deployment logic:
- Kubernetes and Flux MCP sidecars now only deploy when:
  1. They are enabled in values (mcpSidecars.<name>.enabled: true)
  2. AND clusterAccess is not "none" (they need RBAC to function)
- Prevents unnecessary container failures when no permissions exist

Documentation updates:
- Complete Helm values reference for all MCP sidecars
- Deployment examples and troubleshooting guides
- Updated memory notes with current architecture

Breaking change:
- K8s/Flux MCP sidecars won't deploy with clusterAccess=none
- This is intentional as they cannot function without RBAC

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-21 13:27:20 +00:00
Developer 2258df4ae3 docs: comprehensive documentation update for Helm chart and MCP sidecars
- Added MCP sidecar configuration documentation across all docs
- Migrated from kustomize to Helm-based deployment instructions
- Updated Makefile with new helm-* targets, removed outdated k8s-* targets
- Rewrote DEPLOYMENT.md to focus on Helm chart deployment
- Transformed VARIABLES.md into complete Helm values reference
- Added MCP sidecar section to README.md with configuration examples

Key improvements:
- Clear instructions for enabling/disabling MCP servers
- Consistent Helm-based deployment throughout documentation
- Comprehensive values reference with examples
- Better organization for different deployment scenarios

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-21 12:48:40 +00:00
github-actions[bot] d4b069cbdc chore: bump chart version to 0.1.17 [skip ci] 2026-02-21 12:11:46 +00:00
Chris Farhood db7e422b96 fix(mcp): use v0.41.1 for flux-operator-mcp instead of latest 2026-02-21 07:11:37 -05:00
Chris Farhood d5bbf21578 docs: update CLAUDE.md for Helm chart and MCP sidecars
Reflect current architecture: Helm chart instead of raw k8s/ manifests,
document MCP sidecar containers, fix storage mount path, and update
Kubernetes notes for Flux-based deployment.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-21 02:01:40 +00:00
github-actions[bot] 1c3398b178 chore: bump chart version to 0.1.15 [skip ci] 2026-02-21 00:33:29 +00:00
Chris Farhood 4e67c48a4c Merge pull request #25 from cpfarhood/feat/mcp-sidecars
feat: add Kubernetes and Flux MCP servers as pod sidecars
2026-02-21 00:33:20 +00:00
Chris Farhood df3413f54e feat: add Kubernetes and Flux MCP servers as pod sidecars
Run MCP servers as sidecar containers so they inherit the pod's
ServiceAccount permissions instead of requiring separate deployments
with their own RBAC. Kubernetes MCP on :8080, Flux MCP on :8081.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-21 00:29:55 +00:00
Chris Farhood 6a35f38a8c add mcp to repo 2026-02-20 23:33:40 +00:00
github-actions[bot] 431b9079ee chore: bump chart version to 0.1.14 [skip ci] 2026-02-20 22:27:56 +00:00
Chris Farhood 00d88b16b5 Merge pull request #24 from cpfarhood/fix/persist-config-to-pvc
fix: mount PVC at /config to persist Chrome and app state
2026-02-20 22:27:47 +00:00
Chris Farhood c10dd718e1 Merge pull request #23 from cpfarhood/docs/final-readme-pass
docs: final README pass
2026-02-20 22:27:05 +00:00
Antigravity Developer b6bf4b6640 fix: mount PVC at /config to persist Chrome and app state across restarts
The jlesage/baseimage-gui sets XDG_CONFIG_HOME=/config/xdg/config at
runtime, so Chrome was writing its profile to /config/xdg/config/google-chrome
which lived on ephemeral storage. This caused Chrome to open as a fresh
install on every pod restart.

Changes:
- Mount the PVC at /config instead of /home (aligns with baseimage-gui convention)
- Move user home directory to /config/userdata (on the PVC)
- Add explicit --user-data-dir for Chrome pointing to PVC path
- Clean up Chrome crash lock files and patch Preferences on startup
  to prevent session/cookie loss after unclean pod shutdown
- Update all scripts (sshd, init-repo, cont-init) to use new paths
- Remove unnecessary cont-init-home.sh

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-20 22:21:36 +00:00
Antigravity Developer c42b47bb56 fix: persist Chrome profile data and settings across container restarts
- Add explicit --user-data-dir flag to Chrome wrapper to ensure profile data
  is stored in the persistent home directory
- Add cont-init-home.sh script to properly initialize home directory structure
  on container startup with correct permissions
- Ensure Chrome config directory exists before Chrome starts
- Bump chart version to 0.1.13

This fixes the issue where Chrome loses authentication and settings after
pod restarts by explicitly managing where Chrome stores its profile data.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-20 21:31:32 +00:00
Chris Farhood 288c1a4103 docs: final README pass
- Add Antigravity and SSH to feature list in intro
- Add shm.sizeLimit to Display/resources table
- Update startup flow to show accurate antigravity launch flags
- Fix kubectl describe label selector
- Add troubleshooting note for latest-tag pod restart

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 15:52:09 -05:00
github-actions[bot] 2caa8a790f chore: bump chart version to 0.1.12 [skip ci] 2026-02-20 20:46:18 +00:00
Chris Farhood 7a6a515b53 Merge pull request #22 from cpfarhood/fix/antigravity-userdata-persistence
fix: pin Antigravity user data to home PVC to survive pod restarts
2026-02-20 15:46:07 -05:00
Chris Farhood 4f126a938b fix: persist SSH host keys on home PVC to avoid known_hosts warnings
On first boot, generated host keys are saved to ~/.ssh/host_keys/ on
the persistent home PVC. On subsequent boots they are restored, so SSH
clients never see a "host key changed" warning after a pod restart.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 15:45:34 -05:00
Chris Farhood 4af38a5d2e fix: pin Antigravity user data to home PVC to survive pod restarts
Without explicit --user-data-dir and --extensions-dir, Antigravity may
default to a path outside /home and lose settings on restart, causing
the setup wizard to reappear. Pinning both to $HOME ensures they land
on the persistent home PVC.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 15:43:21 -05:00
github-actions[bot] 90350a2090 chore: bump chart version to 0.1.10 [skip ci] 2026-02-20 20:25:42 +00:00
Chris Farhood 5b8e6a290b Merge pull request #21 from cpfarhood/fix/antigravity-no-sandbox
fix: add --no-sandbox to antigravity launch command
2026-02-20 15:25:33 -05:00
Chris Farhood e860499757 fix: mount memory-backed emptyDir at /dev/shm for Electron apps
Instead of disabling shared memory usage, mount a proper tmpfs at
/dev/shm so Antigravity (and Chrome) have real shared memory available.
Removes --disable-dev-shm-usage; keeps --no-sandbox (separate issue).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 15:24:15 -05:00
Chris Farhood e90a2fe553 fix: add --no-sandbox to antigravity launch command
Electron apps crash in Docker without --no-sandbox and
--disable-dev-shm-usage, same as Chrome. VSCode handles this
internally; Antigravity does not.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 15:22:19 -05:00
github-actions[bot] 897f1409b5 chore: bump chart version to 0.1.7 [skip ci] 2026-02-20 19:21:23 +00:00
Chris Farhood 32d4fe4944 Merge pull request #20 from cpfarhood/feat/ide-choice
feat: add IDE choice (VSCode, Google Antigravity, SSH)
2026-02-20 14:21:11 -05:00
Chris Farhood e8c263a045 docs: add SSH_AUTHORIZED_KEYS to secret keys reference table
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 14:02:15 -05:00
Chris Farhood 927c9f1051 refactor: make SSH additive boolean, add ide=none
SSH is now a standalone `ssh: true/false` value that starts sshd on
port 22 *in addition to* whatever IDE is running, rather than replacing
it. The `ide` value loses the `ssh` option and gains `none` (keep
container alive with no GUI IDE, useful when ssh: true is the only
access method).

- chart/values.yaml: replace `ide: ssh` with `ssh: false` boolean
- chart/templates/deployment.yaml: expose port 22 when ssh=true,
  port 5800 when ide!=none; probes use HTTP (VNC) or TCP socket (SSH-only)
- chart/templates/service.yaml: include both ports when both enabled
- scripts/cont-init-sshd.sh: check SSH=true instead of IDE=ssh
- scripts/startapp.sh: add ide=none case (sleep infinity), drop ssh case
- chart/Chart.yaml: bump to 0.1.6
- README.md: update IDE choice and SSH access docs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 14:01:05 -05:00
Chris Farhood 298a1ce6ec feat: add IDE choice — VSCode (default), Google Antigravity, SSH
- Add `ide` Helm value with options: vscode, antigravity, ssh
- Dockerfile: install Google Antigravity via apt and openssh-server
- scripts/startapp.sh: branch on IDE env var to launch the right app
- scripts/cont-init-sshd.sh: start sshd as root in SSH mode, set up
  authorized_keys from SSH_AUTHORIZED_KEYS env var
- chart/templates/deployment.yaml: pass IDE env var, conditional ports
  and probes (HTTP for VNC modes, TCP socket for SSH mode)
- chart/templates/service.yaml: expose port 5800 (VNC) or 22 (SSH)
- chart/values.yaml: add ide field with documentation
- README.md: document IDE choice, fix stale happyHomeDir references
- chart/Chart.yaml: bump to 0.1.5

Closes #10

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 13:49:31 -05:00
github-actions[bot] f33c7e1ae8 chore: bump chart version to 0.1.4 [skip ci] 2026-02-20 18:22:44 +00:00
Chris Farhood b0d4b98bb4 fix: restore HAPPY_HOME_DIR to PVC to preserve settings across restarts (#19)
Moving it to /workspace (emptyDir) wiped Happy Coder's auth, config,
and state on every pod restart. The daemon also failed to start on boot
because the settings were gone.

Keep HAPPY_HOME_DIR on the home PVC (/home/user/.happy) for persistence.
The stale lock cleanup in init-repo.sh already handles the daemon.state.json.lock
problem that motivated the workspace move.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 13:22:33 -05:00
github-actions[bot] b5820cfc7f chore: bump chart version to 0.1.3 [skip ci] 2026-02-20 16:31:45 +00:00
Chris Farhood bace308394 Merge pull request #18 from cpfarhood/feat/cluster-access-rbac
feat: add clusterAccess option for Kubernetes RBAC
2026-02-20 11:31:35 -05:00
Chris Farhood 9c964e7069 docs: rewrite README with current architecture and clusterAccess docs
- Replace all stale references (claude user, k8s/ dir, StatefulSet,
  old Happy paths) with current state (app user, Helm chart, Deployment)
- Add full Helm chart reference table including new clusterAccess option
  with usage examples for all five access levels
- Document Happy Coder daemon setup and startup flow accurately
- Update troubleshooting commands to match actual resource names
- Add Claude authentication notes (browser OAuth + API key fallback)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 11:26:45 -05:00
Chris Farhood d7210fb4e5 feat: add clusterAccess option for Kubernetes RBAC in Helm chart
Adds a clusterAccess value with five levels:
  none        — no cluster access (default, no RBAC resources created)
  readonlyns  — Role + RoleBinding: get/list/watch in release namespace
  readwritens — Role + RoleBinding: full access in release namespace
  readonly    — ClusterRole + ClusterRoleBinding: get/list/watch cluster-wide
  readwrite   — ClusterRole + ClusterRoleBinding: full access cluster-wide

A ServiceAccount is created for the pod whenever access != none and
referenced in the Deployment's serviceAccountName.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 11:25:29 -05:00
github-actions[bot] 7a96f5156c chore: bump chart version to 0.1.2 [skip ci] 2026-02-20 15:22:03 +00:00
Chris Farhood 8df46d6b6f Merge pull request #17 from cpfarhood/fix/happy-daemon-stale-lock
fix: remove stale Happy daemon lock file on startup
2026-02-20 10:21:53 -05:00
Chris Farhood 5d8b1369c3 fix: move HAPPY_HOME_DIR to /workspace so lock never persists across restarts
The daemon.state.json.lock lives on the home PVC and survives pod
restarts, causing happy daemon start to fail on every reboot. Moving
HAPPY_HOME_DIR to /workspace (emptyDir) means the entire happy state
directory is ephemeral and always clean on startup.

The rm -f in init-repo.sh is kept as a safety net for the within-run
case but is now a no-op on fresh starts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 10:20:49 -05:00
Chris Farhood 751402be44 fix: remove stale happy daemon lock file before starting
daemon.state.json.lock is left behind when the daemon crashes or is
killed (e.g. pod restart). On next startup happy daemon start sees the
lock and exits with "Failed to start daemon" without further detail.

Remove the lock file unconditionally at startup — if no daemon is
running, the lock is stale by definition.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 10:18:12 -05:00
Chris Farhood 66e0d1f406 Merge pull request #16 from cpfarhood/fix/happy-daemon-no-sudo
fix: remove sudo from happy daemon start
2026-02-20 10:07:24 -05:00
Chris Farhood e89c3040b7 fix: remove sudo from happy daemon start — startapp.sh already runs as app user
sudo -u failed because the app user is not in sudoers. It was unnecessary
anyway: startapp.sh (and init-repo.sh) run as the app user (UID 1000) so
happy daemon start already executes as the correct user with the right HOME.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 10:05:41 -05:00
Chris Farhood 8d7b39f1b5 Merge pull request #15 from cpfarhood/fix/happy-daemon-user
fix: run Happy daemon as app user so it can find credentials
2026-02-20 10:01:06 -05:00
Chris Farhood 32e87254d2 fix: run Happy daemon as app user, not root
When init-repo.sh starts the happy daemon as root, HOME=/root so
Happy and Claude Code can't find credentials stored in /home/user.
The mobile app works when the user manually runs happy from a VSCode
terminal (as the user user) because the right home dir is in scope.

Use sudo -u with -E (preserve environment) so the daemon runs as the
correct user while still inheriting the pod's env vars (HAPPY_SERVER_URL,
HAPPY_WEBAPP_URL, ANTHROPIC_API_KEY, etc.).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 09:59:59 -05:00
Chris Farhood 66ccee1202 Merge pull request #14 from cpfarhood/fix/chrome-in-docker
fix: make Chrome work inside Docker for Claude Max OAuth login
2026-02-20 09:31:59 -05:00
Chris Farhood 1909c2a3aa fix: make Chrome work inside Docker for Claude OAuth browser login
Chrome requires --no-sandbox and --disable-dev-shm-usage when running
inside a Docker container, otherwise it crashes silently and the OAuth
popup never completes.

- Add a /usr/local/bin/google-chrome wrapper that injects these flags
- Install xdg-utils so xdg-open can resolve browser handlers in VNC
- Set BROWSER env var to the wrapper so Claude Code and xdg-open both
  use it when opening the Claude Max login URL

The OAuth callback (to localhost) works fine inside VNC because both
the browser and the Claude Code local auth server share the same
container network namespace.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 09:30:36 -05:00
Chris Farhood d078bb1c44 Merge pull request #12 from cpfarhood/fix/install-claude-code
fix: install Claude Code CLI so Happy Coder can find it
2026-02-20 09:15:36 -05:00
Chris Farhood 56c648187a fix: install Claude Code CLI so Happy Coder can find it
Happy Coder requires the `claude` CLI to be present but it was never
installed in the image. Add @anthropic-ai/claude-code to the npm
global install step alongside happy-coder.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 09:13:39 -05:00
Chris Farhood 8870d60ccc Merge pull request #9 from cpfarhood/fix/app-user-shell
fix: set app user shell to /bin/bash so VSCode terminals work
2026-02-20 07:51:35 -05:00
Chris Farhood d54515244c fix: set app user shell to /bin/bash so VSCode terminals work
baseimage-gui creates the app user (UID 1000) at runtime with
shell=/sbin/nologin and home=/dev/null. VSCode tries to spawn the
user's login shell for terminals, which fails with exit code 1.

Adds a cont-init script that runs as root after baseimage-gui's
adduser step and corrects both the shell and home directory.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-20 07:50:25 -05:00
Chris Farhood 2918cfde25 Merge pull request #7 from cpfarhood/fix/happy-daemon-nonblocking
fix: don't abort startup if happy daemon fails to start
2026-02-20 07:37:10 -05:00
Chris Farhood dd77cf6a48 fix: don't abort startup if happy daemon fails to start
happy daemon start failing should not crash-loop the container.
VSCode should still open regardless.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-20 07:36:21 -05:00
github-actions[bot] 961a0985b6 chore: bump chart version to 0.1.1 [skip ci] 2026-02-20 12:29:12 +00:00
Chris Farhood d3f5e9f185 Merge pull request #6 from cpfarhood/chore/rename-chart-cpfarhood
chore: rename Helm chart from antigravity to cpfarhood
2026-02-20 07:29:01 -05:00
Chris Farhood 9aab08b8e4 Merge pull request #2 from cpfarhood/dependabot/github_actions/docker/build-push-action-6
build(deps): Bump docker/build-push-action from 5 to 6
2026-02-20 07:28:37 -05:00
Chris Farhood 727487053d Merge pull request #1 from cpfarhood/dependabot/github_actions/actions/checkout-6
build(deps): Bump actions/checkout from 4 to 6
2026-02-20 07:28:17 -05:00
Chris Farhood 47a275d667 chore: auto-bump chart patch version on every merge to main
Workflow now increments the patch version in Chart.yaml, commits it
back with [skip ci], then packages and pushes to GHCR so Flux always
picks up a new version on chart changes.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-20 07:27:04 -05:00
Chris Farhood 7788352995 chore: rename Helm chart from antigravity to devcontainer
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-20 07:25:07 -05:00
Chris Farhood 81a7098c21 chore: rename Helm chart from antigravity to cpfarhood
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-20 07:24:25 -05:00
Chris Farhood 3832fd922b Merge pull request #5 from cpfarhood/chore/renovate
chore(deps): replace Dependabot with Renovate
2026-02-20 07:18:10 -05:00
Chris Farhood 4984e5200c chore(deps): replace Dependabot with Renovate
Adds renovate.json modelled after the kubernetes repo config.
Removes .github/dependabot.yml.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-20 07:17:18 -05:00
Chris Farhood c8f51beac6 Merge pull request #4 from cpfarhood/feat/helm-oci-publish
Publish Helm chart to GHCR OCI registry
2026-02-20 07:16:35 -05:00
Chris Farhood ee7a4a0be8 Merge pull request #3 from cpfarhood/fix/happy-daemon-start
Fix Happy Coder startup
2026-02-20 07:16:22 -05:00
Chris Farhood 2472dc5b3f Add GitHub Actions workflow to publish Helm chart to GHCR OCI
Packages chart/ and pushes to oci://ghcr.io/cpfarhood/charts on every
push to main that touches chart/** files.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-20 06:51:40 -05:00
Chris Farhood 23ba5c2e35 Fix Happy Coder startup: use happy daemon start, create home dir
The npm package installs a binary named 'happy', not 'happy-coder'.
Use 'happy daemon start' to run it as a background service.

Also create $HOME on the PVC if it doesn't exist yet, which was
causing git config failures on fresh volumes.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-20 06:33:40 -05:00
Chris Farhood 256a6871e8 Add Helm chart, replace flux/ variable-substitution templates
Introduces chart/ with deployment, service, and PVC templates driven
by Helm values instead of Kustomize variable substitution. Removes
the flux/ directory which is superseded by the chart.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-20 06:14:40 -05:00
Chris Farhood 720a2a982f try change to deployment 2026-02-19 20:49:58 -05:00
Chris Farhood fdfccb17bd catch up 2026-02-19 20:00:37 -05:00
Chris Farhood dfc0a3c1e3 catchup 2026-02-19 19:55:05 -05:00
Chris Farhood 7003d860b7 Move deployment to cpfarhood namespace
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-19 18:20:48 -05:00
Chris Farhood 8eab159e6f Rename user from claude to user, remove HTTPRoute
- Rename container user from 'claude' to 'user' throughout Dockerfile
- Remove httproute.yaml from kustomization (Authentik outpost will manage routing)

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-19 18:08:39 -05:00
Chris Farhood 13f1878ce5 Fix hostname and TLS config for gateway
- Change hostname from antigravity.dev.farh.net to antigravity.farh.net
  to match the gateway's *.farh.net listener
- Set SECURE_CONNECTION=0 since TLS is terminated at the gateway,
  avoiding the redirect loop caused by the app doing its own HTTP->HTTPS redirect

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-19 18:01:52 -05:00
Chris Farhood c01f85ef24 Remove sudo from startup scripts
baseimage-gui already runs startapp.sh as the app user (UID 1000),
so sudo -u is unnecessary and fails since 'app' is not in sudoers.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-19 17:54:35 -05:00
Chris Farhood 3db5961b1c Fix chown errors in startup script
- Remove chown on /home mount root (PVC root not owned by container user)
- Change imagePullPolicy to Always to pick up new images on pod restart

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

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

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-19 17:39:59 -05:00
dependabot[bot] 17c2d04d70 build(deps): Bump docker/build-push-action from 5 to 6
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5 to 6.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v5...v6)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-15 13:34:38 +00:00
dependabot[bot] 636b68d263 build(deps): Bump actions/checkout from 4 to 6
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-15 13:34:35 +00:00
40 changed files with 3891 additions and 1641 deletions
+6
View File
@@ -0,0 +1,6 @@
{
"enabledPlugins": {
"voltagent-dev-exp@voltagent-subagents": true,
"voltagent-lang@voltagent-subagents": true
}
}
+16
View File
@@ -0,0 +1,16 @@
{
"enabledMcpjsonServers": [
"kubernetes",
"flux",
"playwright",
"github",
"pgtuner",
"fetch",
"sequentialthinking"
],
"permissions": {
"allow": [
"Bash(git add .claude/settings.local.json .claude/settings.json && git commit -m \"$\\(cat <<'EOF'\nchore: update Claude Code settings and enable voltagent plugins\n\nAdd fetch and sequentialthinking MCP servers to allowed list, and enable\nvoltagent dev-exp and lang subagent plugins.\n\nCo-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>\nEOF\n\\)\" && git status)"
]
}
}
-1
View File
@@ -19,7 +19,6 @@
- [ ] Built Docker image locally
- [ ] Tested container startup
- [ ] Tested repository cloning
- [ ] Tested Happy Coder integration
- [ ] Tested VNC web interface
## Checklist
-19
View File
@@ -1,19 +0,0 @@
version: 2
updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
labels:
- "dependencies"
- "github-actions"
# Maintain dependencies for Docker
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "weekly"
labels:
- "dependencies"
- "docker"
+93
View File
@@ -0,0 +1,93 @@
# CI/CD Pipeline Guide
## 🚀 Simplified Pipeline - Only 3 Workflows!
### 1️⃣ For Releases → **Unified Release**
Use this for all version releases:
1. Go to [Actions → Unified Release](https://github.com/cpfarhood/devcontainer/actions/workflows/release-unified.yaml)
2. Click "Run workflow"
3. Either:
- Enter specific version (e.g., `0.2.1`), OR
- Choose release type (patch/minor/major) for auto-increment
4. Click "Run workflow"
**This single workflow does EVERYTHING:**
- ✅ Updates chart version
- ✅ Creates git tag
- ✅ Builds Docker image with all proper tags
- ✅ Publishes Helm chart to GitHub Pages (`https://cpfarhood.github.io/devcontainer`)
- ✅ Creates GitHub Release with changelog
### 2️⃣ For Quick Fixes → **Quick Fix Build**
Use this for emergency fixes without version changes:
1. Go to [Actions → Quick Fix Build](https://github.com/cpfarhood/devcontainer/actions/workflows/quick-fix.yaml)
2. Click "Run workflow"
3. Enter tag (default: `latest`)
4. Click "Run workflow"
**Just builds and pushes Docker image** - no version bumps, no releases.
### 3️⃣ Automatic CI → **Build and Push**
Runs automatically on:
- Pushes to `main` (builds and pushes; skipped for release commits via `[skip ci]`)
- Pull requests (builds but doesn't push)
- Manual trigger available
## Workflow Files
| Workflow | File | Purpose | When to Use |
|----------|------|---------|-------------|
| **Unified Release** | `release-unified.yaml` | Full release process | New versions |
| **Quick Fix Build** | `quick-fix.yaml` | Docker build only | Hotfixes |
| **Build and Push** | `build-and-push.yaml` | CI/CD automation | PRs & tags |
## Examples
### Release a new version
```bash
# Via GitHub UI (Recommended):
# Go to Actions → Unified Release → Run workflow
# Via GitHub CLI:
gh workflow run release-unified.yaml -f version=0.2.1
# OR auto-increment:
gh workflow run release-unified.yaml -f release_type=patch
```
### Push a quick fix
```bash
# Via GitHub UI:
# Go to Actions → Quick Fix Build → Run workflow
# Via GitHub CLI:
gh workflow run quick-fix.yaml -f tag=hotfix-1
```
### Check workflow status
```bash
# List all recent runs
gh run list --limit 5
# Watch a specific workflow
gh run watch
```
## Version Strategy
- **Major** (1.0.0): Breaking changes
- **Minor** (0.2.0): New features
- **Patch** (0.2.1): Bug fixes
## What We Fixed
### Before (Nightmare 😱)
- Auto-version-bump with `[skip ci]` prevented Docker builds
- 6+ disconnected workflows
- Manual tag deletion and re-pushing
- Version conflicts everywhere
### After (Simple! 🎉)
- **3 total workflows** (down from 6+)
- **1 button** for complete releases
- Release builds its own Docker image — `[skip ci]` on the version commit prevents duplicate CI builds
- **Clear separation** of concerns
+6 -8
View File
@@ -4,8 +4,6 @@ on:
push:
branches:
- main
tags:
- 'v*'
pull_request:
branches:
- main
@@ -18,14 +16,16 @@ env:
jobs:
build-and-push:
runs-on: ubuntu-latest
if: >-
github.event_name != 'push'
|| !contains(github.event.head_commit.message, '[skip ci]')
permissions:
contents: read
packages: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
@@ -46,19 +46,17 @@ jobs:
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@v5
uses: docker/build-push-action@v6
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: CACHE_BUST=${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64
+56
View File
@@ -0,0 +1,56 @@
name: Quick Fix Build
on:
workflow_dispatch:
inputs:
tag:
description: 'Tag for the image (defaults to latest)'
required: false
default: 'latest'
type: string
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and Push
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.event.inputs.tag }}
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64
- name: Summary
run: |
echo "## ✅ Quick Fix Build Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Images Published:" >> $GITHUB_STEP_SUMMARY
echo "- \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.event.inputs.tag }}\`" >> $GITHUB_STEP_SUMMARY
echo "- \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY
+193
View File
@@ -0,0 +1,193 @@
name: Unified Release
on:
workflow_dispatch:
inputs:
version:
description: 'Explicit version (e.g., 1.2.3). Leave blank to auto-increment.'
required: false
type: string
release_type:
description: 'Release type (used when version is blank)'
required: true
default: 'patch'
type: choice
options:
- patch
- minor
- major
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Set up Helm
uses: azure/setup-helm@v4
- name: Configure Git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Determine Version
id: version
run: |
INPUT_VERSION="${{ github.event.inputs.version }}"
if [ -n "$INPUT_VERSION" ]; then
VERSION="$INPUT_VERSION"
else
# Auto-increment based on release_type
CURRENT=$(grep '^version:' chart/Chart.yaml | awk '{print $2}')
# Strip any pre-release suffix (e.g., 2.0.0-dev -> 2.0.0)
CURRENT=$(echo "$CURRENT" | sed 's/-.*//')
MAJOR=$(echo "$CURRENT" | cut -d. -f1)
MINOR=$(echo "$CURRENT" | cut -d. -f2)
PATCH=$(echo "$CURRENT" | cut -d. -f3)
case "${{ github.event.inputs.release_type }}" in
major) VERSION="$((MAJOR + 1)).0.0" ;;
minor) VERSION="${MAJOR}.$((MINOR + 1)).0" ;;
patch) VERSION="${MAJOR}.${MINOR}.$((PATCH + 1))" ;;
esac
fi
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "tag=v${VERSION}" >> $GITHUB_OUTPUT
echo "Releasing version ${VERSION}"
- name: Update Chart Version
run: |
sed -i "s/^version: .*/version: ${{ steps.version.outputs.version }}/" chart/Chart.yaml
git add chart/Chart.yaml
git diff --quiet --staged || git commit -m "chore(release): ${{ steps.version.outputs.version }} [skip ci]"
- name: Create and Push Tag
run: |
git tag -a "${{ steps.version.outputs.tag }}" -m "Release ${{ steps.version.outputs.tag }}"
git push origin main
git push origin "${{ steps.version.outputs.tag }}"
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and Push Docker Image
uses: docker/build-push-action@v6
with:
context: .
push: true
build-args: CACHE_BUST=${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.tag }}
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }}
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
platforms: linux/amd64
- name: Publish Helm Chart to GitHub Pages
run: |
helm package chart/
CHART_TGZ="devcontainer-${{ steps.version.outputs.version }}.tgz"
# Set up gh-pages in a temporary directory
PAGES_DIR=$(mktemp -d)
if git ls-remote --heads origin gh-pages | grep -q gh-pages; then
# gh-pages exists — shallow clone just that branch
git clone --single-branch --branch gh-pages \
"https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git" \
"$PAGES_DIR"
else
# First time — initialize gh-pages
git init "$PAGES_DIR"
git -C "$PAGES_DIR" checkout --orphan gh-pages
git -C "$PAGES_DIR" remote add origin \
"https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git"
cat > "$PAGES_DIR/index.html" <<'HTMLEOF'
<!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://cpfarhood.github.io/devcontainer</pre>
<p>Install the chart:</p>
<pre>helm install mydev devcontainer/devcontainer --set name=mydev</pre>
</body>
</html>
HTMLEOF
fi
git -C "$PAGES_DIR" config user.name "github-actions[bot]"
git -C "$PAGES_DIR" config user.email "github-actions[bot]@users.noreply.github.com"
# Copy chart package and rebuild index
cp "$CHART_TGZ" "$PAGES_DIR/"
if [ -f "$PAGES_DIR/index.yaml" ]; then
helm repo index "$PAGES_DIR" --url https://cpfarhood.github.io/devcontainer --merge "$PAGES_DIR/index.yaml"
else
helm repo index "$PAGES_DIR" --url https://cpfarhood.github.io/devcontainer
fi
# Commit and push
git -C "$PAGES_DIR" add .
git -C "$PAGES_DIR" commit -m "Publish chart ${{ steps.version.outputs.version }}"
git -C "$PAGES_DIR" push origin gh-pages
- name: Create GitHub Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ steps.version.outputs.version }}
TAG: ${{ steps.version.outputs.tag }}
IMAGE: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.tag }}
run: |
PREV_TAG=$(git describe --tags --abbrev=0 HEAD~1 2>/dev/null || echo "")
if [ -z "$PREV_TAG" ]; then
COMMITS=$(git log --pretty=format:"- %s (%h)" HEAD)
else
COMMITS=$(git log --pretty=format:"- %s (%h)" "${PREV_TAG}..HEAD")
fi
cat > release-notes.md <<NOTESEOF
## Release ${VERSION}
### Changes
${COMMITS}
### Docker Image
\`\`\`bash
docker pull ${IMAGE}
\`\`\`
### Helm Chart
\`\`\`bash
helm repo add devcontainer https://cpfarhood.github.io/devcontainer
helm repo update
helm install mydev devcontainer/devcontainer --version ${VERSION} --set name=mydev
\`\`\`
NOTESEOF
sed -i 's/^ //' release-notes.md
gh release create "${TAG}" \
--title "Release ${TAG}" \
--notes-file release-notes.md
-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@v4
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
```
+23
View File
@@ -0,0 +1,23 @@
{
"mcpServers": {
"github": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/",
"headers": {
"Authorization": "Bearer ${GITHUB_TOKEN}"
}
},
"kubernetes": {
"type": "sse",
"url": "http://localhost:8080/sse"
},
"flux": {
"type": "sse",
"url": "http://localhost:8081/sse"
},
"playwright": {
"type": "sse",
"url": "http://localhost:8086/sse"
}
}
}
+213
View File
@@ -0,0 +1,213 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
The Dev Container is a Docker-based cloud development environment that provides:
- Web-based GUI IDE (VSCode/Antigravity) via VNC on port 5800
- Claude Code, OpenCode, and Crush AI coding agents (terminal-based)
- Built-in web file manager for uploading/downloading files (optional, via `fileManager.enabled`)
- Automatic GitHub repository cloning on startup
- Kubernetes-native deployment with persistent home storage
- MCP (Model Context Protocol) sidecars for AI assistant integrations
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/devcontainer: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_REPOS or GITHUB_REPO (if set)
→ Generate workspace.code-workspace for multi-repo setups
→ Launch VSCode as user `user` in /workspace
```
### Key Files
| File | Purpose |
|------|---------|
| `Dockerfile` | Image definition — installs Chrome, VSCode, Helm, gh CLI, kubeseal, Claude Code, OpenCode, Crush, LSP servers (pyright, typescript-language-server, gopls, clangd, rust-analyzer, lua-language-server, jdtls, kotlin-language-server, intelephense); creates non-root user (UID 1000) |
| `scripts/init-repo.sh` | Configures git credentials, clones GitHub repo(s), generates multi-root workspace file |
| `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 (GitHub Copilot, Kubernetes, Flux, Helm, Fetch, Sequential Thinking, Playwright, pgtuner) |
| `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 |
| `helm-mcp` | `ghcr.io/zekker6/mcp-helm` | v1.3.1 | 8012 | `http://localhost:8012/sse` | Enabled |
| `fetch-mcp` | `mcp/fetch` | latest | 8082 | `http://localhost:8082/sse` | Enabled |
| `sequentialthinking-mcp` | `mcp/sequentialthinking` | latest | 8083 | `http://localhost:8083/sse` | Enabled |
| `homeassistant-mcp` | `ghcr.io/homeassistant-ai/ha-mcp` | stable | 8087 | `http://localhost:8087/sse` | Disabled |
| `pgtuner-mcp` | `dog830228/pgtuner_mcp` | latest | 8085 | `http://localhost:8085/sse` | Disabled |
| `playwright-mcp` | `mcr.microsoft.com/playwright/mcp` | latest | 8086 | `http://localhost:8086/sse` | Enabled |
**Note:**
- GitHub MCP is accessed via the Copilot API (`https://api.githubcopilot.com/mcp/`), not as a sidecar
- 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
- Helm sidecar enables browsing Helm repositories and chart metadata
- Fetch sidecar provides web content fetching capabilities and HTML to markdown conversion
- Sequential thinking sidecar enables structured thinking and problem-solving processes
- Home Assistant sidecar requires `HOMEASSISTANT_URL` and `HOMEASSISTANT_TOKEN` in the env secret
- PostgreSQL tuner sidecar requires `DATABASE_URI` in the env secret (PostgreSQL connection string)
- Playwright sidecar provides browser automation and web testing capabilities
#### Enabling/Disabling MCP Servers
To control MCP sidecars, set the `enabled` flag in your values override:
```yaml
# Disable all MCP sidecars
mcp:
sidecars:
kubernetes:
enabled: false
flux:
enabled: false
helm:
enabled: false
fetch:
enabled: false
sequentialthinking:
enabled: false
homeassistant:
enabled: false
pgtuner:
enabled: false
playwright:
enabled: false
# Or selectively enable/disable
mcp:
sidecars:
kubernetes:
enabled: true # Keep Kubernetes MCP enabled
flux:
enabled: false # Disable Flux MCP
helm:
enabled: true # Enable Helm MCP for chart browsing
fetch:
enabled: true # Enable Fetch MCP for web content fetching
sequentialthinking:
enabled: true # Enable Sequential Thinking MCP for problem-solving
homeassistant:
enabled: true # Enable Home Assistant MCP (requires secrets)
pgtuner:
enabled: true # Enable PostgreSQL tuner MCP (requires DATABASE_URI)
playwright:
enabled: true # Enable Playwright MCP for browser automation
```
When deploying via Helm:
```bash
# Quick start (recommended)
cp chart/values-quickstart.yaml my-values.yaml
# Edit name and githubRepo in my-values.yaml
helm install my-devcontainer ./chart -f my-values.yaml
# Using --set flags
helm install my-devcontainer ./chart \
--set name=mydev \
--set githubRepo=https://github.com/user/repo \
--set mcp.sidecars.kubernetes.enabled=false
# Full customization
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 (at least one):**
- `GITHUB_REPO` — URL of a single repository to clone into `/workspace`
- `GITHUB_REPOS` — Comma-separated list of repository URLs to clone (takes precedence over `GITHUB_REPO`). When multiple repos are cloned, a `workspace.code-workspace` file is generated for multi-root IDE support.
**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)
- `WEB_FILE_MANAGER` — Set to `1` to enable the built-in web file manager (controlled via `fileManager.enabled` in Helm values)
- `WEB_FILE_MANAGER_ALLOWED_PATHS` — Paths accessible by the file manager (default: `/workspace,/config`)
- `WEB_FILE_MANAGER_DENIED_PATHS` — Paths to deny access to (takes precedence over allowed)
### 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-unified.yaml`** — Manual release workflow: bumps chart version, builds Docker image, publishes Helm chart to GitHub Pages (`https://cpfarhood.github.io/devcontainer`), and creates GitHub Release.
- **`dependabot.yml`** — Weekly updates for GitHub Actions and Docker base image.
Image registry: `ghcr.io/cpfarhood/devcontainer`
Helm repo: `https://cpfarhood.github.io/devcontainer`
## Kubernetes Notes
- Deployed via Helm chart (`chart/`), published to GitHub Pages Helm repo, 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`)
+365 -364
View File
@@ -1,436 +1,437 @@
# Deployment Guide
This guide provides step-by-step instructions for deploying the Antigravity Dev Container to Kubernetes.
This guide provides step-by-step instructions for deploying the Antigravity Dev Container using Helm.
## Prerequisites
- Kubernetes cluster with Gateway API support
- 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`)
- Sealed Secrets controller installed (for secret encryption)
- GitHub Container Registry access (images are public)
## Required Configuration Variables
## Quick Start
Before deploying, you need to provide the following configuration:
### 1. Storage Configuration
**Variable:** `storageClassName`
**Location:** `k8s/statefulset.yaml` (line ~117)
**Description:** The ReadWriteMany storage class name in your cluster
**Example values:**
- `ceph-filesystem` (Rook-Ceph)
- `nfs-client` (NFS)
- `efs-sc` (AWS EFS)
- `azurefile` (Azure Files)
- `filestore` (GCP Filestore)
**How to find your storage class:**
```bash
kubectl get storageclass
```
Look for a storage class that supports `ReadWriteMany` access mode.
### 2. GitHub Repository (Required)
**Variable:** `github-repo`
**Location:** `k8s/configmap.yaml` (line ~9)
**Description:** The GitHub repository URL to clone on container startup
**Format:** `https://github.com/username/repository`
**Example:** `https://github.com/cpfarhood/my-project`
### 3. GitHub Token (Optional, for private repos)
**Variable:** `github-token`
**Location:** `k8s/secrets-example.yaml` (sealed secret)
**Description:** GitHub Personal Access Token for cloning private repositories
**Format:** `ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`
**Required:** Only if cloning a private repository
**How to create a GitHub token:**
1. Go to https://github.com/settings/tokens
2. Click "Generate new token (classic)"
3. Select scopes: `repo` (for private repos)
4. Generate and copy the token
### 4. VNC Password (Optional)
**Variable:** `vnc-password`
**Location:** `k8s/secrets-example.yaml` (sealed secret)
**Description:** Password for accessing the VNC web interface
**Format:** Any string (recommend 12+ characters)
**Required:** Optional, but recommended for security
### 5. Gateway Configuration (Required for external access)
**Variables:**
- `parentRefs.name` - Your Gateway resource name
- `parentRefs.namespace` - Namespace where Gateway is deployed
- `hostnames` - Domain name for accessing the container
**Location:** `k8s/httproute.yaml`
**Example:**
```yaml
parentRefs:
- name: cilium-gateway # Your Gateway name
namespace: kube-system # Your Gateway namespace
hostnames:
- "devcontainer.example.com" # Your domain
```
### 6. Namespace (Optional)
**Variable:** `namespace`
**Location:** `k8s/kustomization.yaml` (line ~5)
**Description:** Kubernetes namespace to deploy into
**Default:** `default`
**Example:** `devcontainer`, `development`, `team-workspaces`
### 7. Container Image (Optional)
**Variable:** `image`
**Location:** `k8s/statefulset.yaml` (line ~32)
**Description:** Docker image to use
**Default:** `ghcr.io/cpfarhood/devcontainer:latest`
**Format:** `registry/repository:tag`
### 8. Resource Limits (Optional)
**Variables:**
- `resources.requests.memory` (default: `2Gi`)
- `resources.requests.cpu` (default: `1000m`)
- `resources.limits.memory` (default: `8Gi`)
- `resources.limits.cpu` (default: `4000m`)
**Location:** `k8s/statefulset.yaml` (lines ~98-103)
### 9. Happy Coder Configuration (Optional)
**Variables:**
- `happy-server-url` - Custom Happy server URL
- `happy-webapp-url` - Custom Happy webapp URL
**Location:** `k8s/configmap.yaml` (lines ~12-13, commented out)
**Default:** Uses Happy's default servers
**When to set:** Only if using a self-hosted Happy instance
## Deployment Steps
### Step 1: Clone the Repository
### 1. Clone the Repository
```bash
git clone https://github.com/cpfarhood/devcontainer.git
cd devcontainer
```
### Step 2: Configure Storage Class
### 2. Create Secret (Optional)
Edit `k8s/statefulset.yaml` and find the `volumeClaimTemplates` section (around line 117):
For private repos or VNC password:
```bash
# Find your storage class
kubectl get storageclass
# Edit the file
vi k8s/statefulset.yaml
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-...'
```
Change `storageClassName` to match your cluster:
```yaml
volumeClaimTemplates:
- metadata:
name: userhome
spec:
accessModes: [ "ReadWriteMany" ]
storageClassName: "ceph-filesystem" # ← Change this
resources:
requests:
storage: 10Gi
```
### Step 3: Configure GitHub Repository
Edit `k8s/configmap.yaml`:
### 3. Deploy with Helm
```bash
vi k8s/configmap.yaml
# 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
```
Set your repository URL:
```yaml
data:
github-repo: "https://github.com/yourusername/yourrepo"
```
### Step 4: Configure Gateway (HTTPRoute)
Edit `k8s/httproute.yaml`:
### 4. Access the Container
```bash
# Find your Gateway
kubectl get gateway -A
# Edit the file
vi k8s/httproute.yaml
```
Update with your Gateway details:
```yaml
spec:
parentRefs:
- name: your-gateway-name # ← Change this
namespace: your-gateway-namespace # ← Change this
hostnames:
- "devcontainer.yourdomain.com" # ← Change this
```
### Step 5: Create Secrets
Create the secrets for GitHub token and VNC password:
```bash
# Create the secret
kubectl create secret generic antigravity-secrets \
--from-literal=github-token='ghp_your_token_here' \
--from-literal=vnc-password='your_vnc_password' \
--dry-run=client -o yaml | \
kubeseal --format=yaml > k8s/sealedsecrets.yaml
# Verify the sealed secret was created
cat k8s/sealedsecrets.yaml
```
**If you don't have Sealed Secrets controller:**
Option 1: Install Sealed Secrets
```bash
kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.0/controller.yaml
```
Option 2: Use plain secrets (not recommended for production)
```bash
kubectl create secret generic antigravity-secrets \
--from-literal=github-token='ghp_your_token_here' \
--from-literal=vnc-password='your_vnc_password'
```
### Step 6: Review Configuration (Optional)
Review and adjust optional settings:
**Namespace:**
```bash
vi k8s/kustomization.yaml
# Change line 5: namespace: default
```
**Resource limits:**
```bash
vi k8s/statefulset.yaml
# Adjust lines 98-103 for your needs
```
### Step 7: Deploy to Kubernetes
```bash
# Deploy everything
kubectl apply -k k8s/
# Or if you changed the namespace
kubectl apply -k k8s/ -n your-namespace
```
### Step 8: Verify Deployment
```bash
# Check StatefulSet
kubectl get statefulset antigravity
# Check Pod
kubectl get pods -l app=antigravity
# Check PVC
kubectl get pvc -l app=antigravity
# Check HTTPRoute
kubectl get httproute antigravity
# View logs
kubectl logs antigravity-0
```
### Step 9: Access the Container
**Option A: Via HTTPRoute (external access)**
```bash
# Open in browser
open https://devcontainer.yourdomain.com
```
**Option B: Via Port Forward (local access)**
```bash
# Port forward to localhost
kubectl port-forward statefulset/antigravity 5800:5800
# Open in browser
# Port forward
kubectl port-forward deployment/devcontainer-mydev 5800:5800
open http://localhost:5800
```
## Configuration Summary
## Deployment Options
Here's a quick checklist of all variables you need to set:
### Using Values File
### Required Variables
Create a custom `values.yaml`:
| Variable | File | Line | Example Value |
|----------|------|------|---------------|
| `storageClassName` | `k8s/statefulset.yaml` | ~117 | `ceph-filesystem` |
| `github-repo` | `k8s/configmap.yaml` | ~9 | `https://github.com/user/repo` |
| `parentRefs.name` | `k8s/httproute.yaml` | ~8 | `cilium-gateway` |
| `parentRefs.namespace` | `k8s/httproute.yaml` | ~9 | `kube-system` |
| `hostnames` | `k8s/httproute.yaml` | ~10 | `devcontainer.example.com` |
```yaml
name: mydev
githubRepo: https://github.com/youruser/yourrepo
ide: vscode
ssh: false
### Optional Variables
# Storage
storage:
size: 32Gi
className: ceph-filesystem
| Variable | File | Line | Default | When to Change |
|----------|------|------|---------|----------------|
| `namespace` | `k8s/kustomization.yaml` | ~5 | `default` | If deploying to different namespace |
| `github-token` | Sealed secret | N/A | None | For private repos |
| `vnc-password` | Sealed secret | N/A | None | For VNC security |
| `image` | `k8s/statefulset.yaml` | ~32 | `ghcr.io/cpfarhood/devcontainer:latest` | For specific version or custom build |
| `resources.*` | `k8s/statefulset.yaml` | ~98-103 | 2Gi/8Gi RAM, 1/4 CPU | Based on workload needs |
| `happy-server-url` | `k8s/configmap.yaml` | ~12 | Default Happy server | For self-hosted Happy |
| `happy-webapp-url` | `k8s/configmap.yaml` | ~13 | Default Happy webapp | For self-hosted Happy |
# 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 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
### Pod Not Starting
**Check events:**
```bash
kubectl describe pod antigravity-0
# 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
```
**Common issues:**
- Storage class doesn't support ReadWriteMany
- PVC not binding (check storage class exists)
- Image pull errors (check image name)
### Repository Not Cloning
### Repository not cloning
**Check logs:**
```bash
kubectl logs antigravity-0 | grep -A 10 "Repository Initialization"
# 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
```
**Common issues:**
- Invalid GitHub URL
- Private repo without token
- Token doesn't have correct permissions
### VNC Not Accessible
### HTTPRoute not working
**Check HTTPRoute:**
```bash
kubectl describe httproute antigravity
# Check service
kubectl get svc devcontainer-mydev
kubectl describe svc devcontainer-mydev
# Test with port-forward
kubectl port-forward deployment/devcontainer-mydev 5800:5800
```
**Common issues:**
- Gateway name/namespace incorrect
- Domain not pointing to Gateway
- TLS certificate not issued
### MCP Sidecar Issues
### VNC not accessible
**Check service:**
```bash
kubectl get svc antigravity
kubectl describe svc antigravity
# 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/
```
**Port forward test:**
### Storage Issues
```bash
kubectl port-forward antigravity-0 5800:5800
# Try accessing http://localhost:5800
# 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
```
## Quick Deploy Example
## Best Practices
Complete deployment with all values filled in:
### 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
# 1. Set your values
STORAGE_CLASS="ceph-filesystem"
GITHUB_REPO="https://github.com/myuser/myproject"
GITHUB_TOKEN="ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
VNC_PASSWORD="my-secure-password-123"
GATEWAY_NAME="cilium-gateway"
GATEWAY_NAMESPACE="kube-system"
DOMAIN="devcontainer.example.com"
# User 1
helm install alice-dev ./chart \
--set name=alice-dev \
--set githubRepo=https://github.com/alice/project
# 2. Update storage class
sed -i "s/storageClassName: .*/storageClassName: \"$STORAGE_CLASS\"/" k8s/statefulset.yaml
# 3. Update GitHub repo
sed -i "s|github-repo: .*|github-repo: \"$GITHUB_REPO\"|" k8s/configmap.yaml
# 4. Update Gateway
sed -i "s/- name: gateway/- name: $GATEWAY_NAME/" k8s/httproute.yaml
sed -i "s/namespace: gateway-system/namespace: $GATEWAY_NAMESPACE/" k8s/httproute.yaml
sed -i "s/antigravity.example.com/$DOMAIN/" k8s/httproute.yaml
# 5. Create sealed secret
kubectl create secret generic antigravity-secrets \
--from-literal=github-token="$GITHUB_TOKEN" \
--from-literal=vnc-password="$VNC_PASSWORD" \
--dry-run=client -o yaml | \
kubeseal --format=yaml > k8s/sealedsecrets.yaml
# 6. Deploy
kubectl apply -k k8s/
# 7. Watch deployment
kubectl get pods -l app=antigravity -w
# User 2
helm install bob-dev ./chart \
--set name=bob-dev \
--set githubRepo=https://github.com/bob/project
```
## Updates and Maintenance
### Backup and Recovery
### Updating the Image
The image is automatically built and pushed to ghcr.io on every commit to main.
**To use latest:**
```bash
kubectl set image statefulset/antigravity \
antigravity=ghcr.io/cpfarhood/devcontainer:latest
```
**To use specific version:**
```bash
kubectl set image statefulset/antigravity \
antigravity=ghcr.io/cpfarhood/devcontainer:v1.0.0
```
### Changing Repository
Edit the ConfigMap and restart:
```bash
kubectl edit configmap antigravity-config
# Change github-repo value
kubectl rollout restart statefulset/antigravity
```
### Scaling
The home directory persists on PVC. To backup:
```bash
# Scale to multiple instances (each gets own home PVC)
kubectl scale statefulset antigravity --replicas=3
# 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
- Documentation: https://github.com/cpfarhood/devcontainer
+191 -23
View File
@@ -1,14 +1,17 @@
FROM jlesage/baseimage-gui:ubuntu-22.04-v4
# Bust cache for all layers below (base image pull is still cached)
ARG CACHE_BUST
# Set environment variables
ENV APP_NAME="Antigravity Dev Container" \
ENV APP_NAME="Dev Container" \
KEEP_APP_RUNNING=1 \
DISPLAY_WIDTH=1920 \
DISPLAY_HEIGHT=1080 \
SECURE_CONNECTION=1 \
USER_ID=1000 \
GROUP_ID=1000 \
CLAUDE_USER=claude
CLAUDE_USER=user
# Install system dependencies
RUN apt-get update && apt-get install -y \
@@ -25,52 +28,217 @@ RUN apt-get update && apt-get install -y \
sudo \
&& rm -rf /var/lib/apt/lists/*
# Install Chrome
# 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 && \
apt-get install -y google-chrome-stable xdg-utils && \
rm -rf /var/lib/apt/lists/*
# Install Node.js (LTS version for Happy Coder)
RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - && \
# 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 via NodeSource
ARG NODE_MAJOR=22
RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_MAJOR}.x | bash - && \
apt-get install -y nodejs && \
rm -rf /var/lib/apt/lists/* && \
node --version && npm --version
# Install Claude Code native binary (npm wrapper breaks remote control)
RUN curl -fsSL https://claude.ai/install.sh | bash && \
cp /root/.local/bin/claude /usr/local/bin/claude && \
rm -rf /root/.local/bin/claude && \
claude --version
# Disable Claude Code auto-updater (doesn't work inside Docker)
RUN mkdir -p /etc/skel/.claude && \
echo '{"env":{"DISABLE_AUTOUPDATER":"1"}}' > /etc/skel/.claude/settings.json
# Install OpenCode AI coding agent
RUN OPENCODE_VERSION=$(curl -sL https://api.github.com/repos/opencode-ai/opencode/releases/latest | jq -r '.tag_name') && \
curl -fsSL "https://github.com/opencode-ai/opencode/releases/download/${OPENCODE_VERSION}/opencode-linux-x86_64.tar.gz" | \
tar -xz -C /usr/local/bin opencode && \
chmod +x /usr/local/bin/opencode
# Install Crush AI coding agent (OpenCode successor by Charm)
RUN CRUSH_VERSION=$(curl -sL https://api.github.com/repos/charmbracelet/crush/releases/latest | jq -r '.tag_name' | sed 's/^v//') && \
curl -fsSL "https://github.com/charmbracelet/crush/releases/download/v${CRUSH_VERSION}/crush_${CRUSH_VERSION}_Linux_x86_64.tar.gz" -o /tmp/crush.tar.gz && \
tar -xzf /tmp/crush.tar.gz -C /tmp && \
mv /tmp/crush_${CRUSH_VERSION}_Linux_x86_64/crush /usr/local/bin/crush && \
chmod +x /usr/local/bin/crush && \
rm -rf /tmp/crush*
# Install Helm CLI for Kubernetes chart management
ARG HELM_VERSION=3.17.1
RUN curl -fsSL "https://get.helm.sh/helm-v${HELM_VERSION}-linux-amd64.tar.gz" | \
tar -xz --strip-components=1 -C /usr/local/bin linux-amd64/helm && \
chmod +x /usr/local/bin/helm
# Install OpenTofu (open-source Terraform alternative)
ARG OPENTOFU_VERSION=1.11.5
RUN curl -fsSL "https://github.com/opentofu/opentofu/releases/download/v${OPENTOFU_VERSION}/tofu_${OPENTOFU_VERSION}_linux_amd64.zip" -o /tmp/tofu.zip && \
unzip -o /tmp/tofu.zip -d /usr/local/bin tofu && \
chmod +x /usr/local/bin/tofu && \
rm /tmp/tofu.zip
# Install GitHub CLI (gh) via official APT repo
RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg && \
chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg && \
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" > /etc/apt/sources.list.d/github-cli.list && \
apt-get update && \
apt-get install -y gh && \
rm -rf /var/lib/apt/lists/*
# Install Happy Coder globally
RUN npm install -g happy-coder
# Install kubeseal CLI for Bitnami Sealed Secrets
RUN KUBESEAL_VERSION=$(curl -sL https://api.github.com/repos/bitnami-labs/sealed-secrets/releases/latest | jq -r '.tag_name' | sed 's/^v//') && \
curl -fsSL "https://github.com/bitnami-labs/sealed-secrets/releases/download/v${KUBESEAL_VERSION}/kubeseal-${KUBESEAL_VERSION}-linux-amd64.tar.gz" | \
tar -xz -C /usr/local/bin kubeseal && \
chmod +x /usr/local/bin/kubeseal
# Install Antigravity (Google's Project IDX / Cloud Code alternative)
# Note: Antigravity might be packaged differently - adjust as needed
# For now, we'll use VSCode with Project IDX extensions as a placeholder
RUN wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o /usr/share/keyrings/packages.microsoft.gpg && \
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/packages.microsoft.gpg] https://packages.microsoft.com/repos/code stable main" > /etc/apt/sources.list.d/vscode.list && \
# ── LSP servers for Claude Code language intelligence ──
# npm-based LSP servers: Python (pyright), TypeScript/JavaScript, PHP
RUN npm install -g pyright typescript-language-server typescript intelephense
# Install Go runtime and gopls LSP server
ARG GO_VERSION=1.23.6
RUN curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar -xz -C /usr/local && \
/usr/local/go/bin/go install golang.org/x/tools/gopls@latest && \
mv /root/go/bin/gopls /usr/local/bin/gopls && \
rm -rf /root/go
ENV PATH="/usr/local/go/bin:${PATH}"
# Install clangd LSP server (C/C++)
RUN apt-get update && \
apt-get install -y clangd && \
rm -rf /var/lib/apt/lists/*
# Install rust-analyzer LSP server (Rust) — standalone binary, no full toolchain needed
RUN RUST_ANALYZER_VERSION=$(curl -sL https://api.github.com/repos/rust-lang/rust-analyzer/releases/latest | jq -r '.tag_name') && \
curl -fsSL "https://github.com/rust-lang/rust-analyzer/releases/download/${RUST_ANALYZER_VERSION}/rust-analyzer-x86_64-unknown-linux-gnu.gz" | \
gunzip > /usr/local/bin/rust-analyzer && \
chmod +x /usr/local/bin/rust-analyzer
# Install lua-language-server (Lua)
RUN LUA_LS_VERSION=$(curl -sL https://api.github.com/repos/LuaLS/lua-language-server/releases/latest | jq -r '.tag_name') && \
mkdir -p /opt/lua-language-server && \
curl -fsSL "https://github.com/LuaLS/lua-language-server/releases/download/${LUA_LS_VERSION}/lua-language-server-${LUA_LS_VERSION}-linux-x64.tar.gz" | \
tar -xz -C /opt/lua-language-server && \
ln -s /opt/lua-language-server/bin/lua-language-server /usr/local/bin/lua-language-server
# Install JDK for Java/Kotlin LSP servers
RUN apt-get update && \
apt-get install -y openjdk-17-jdk-headless && \
rm -rf /var/lib/apt/lists/*
# Install kotlin-language-server
RUN KLS_VERSION=$(curl -sL https://api.github.com/repos/fwcd/kotlin-language-server/releases/latest | jq -r '.tag_name') && \
curl -fsSL "https://github.com/fwcd/kotlin-language-server/releases/download/${KLS_VERSION}/server.zip" -o /tmp/kls.zip && \
unzip -o /tmp/kls.zip -d /opt/kotlin-language-server && \
ln -s /opt/kotlin-language-server/server/bin/kotlin-language-server /usr/local/bin/kotlin-language-server && \
rm /tmp/kls.zip
# Install jdtls (Java LSP) — Eclipse JDT Language Server
RUN JDTLS_TARBALL=$(curl -sL https://download.eclipse.org/jdtls/snapshots/latest.txt) && \
mkdir -p /opt/jdtls && \
curl -fsSL "https://download.eclipse.org/jdtls/snapshots/${JDTLS_TARBALL}" | tar -xz -C /opt/jdtls && \
printf '#!/bin/bash\nexec java -Declipse.application=org.eclipse.jdt.ls.core.id1 -Dosgi.bundles.defaultStartLevel=4 -Declipse.product=org.eclipse.jdt.ls.core.product -jar /opt/jdtls/plugins/org.eclipse.equinox.launcher_*.jar -configuration /opt/jdtls/config_linux "$@"\n' > /usr/local/bin/jdtls && \
chmod +x /usr/local/bin/jdtls
# Install VSCode (using Microsoft's current recommended setup)
RUN wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > /tmp/microsoft.gpg && \
install -D -o root -g root -m 644 /tmp/microsoft.gpg /usr/share/keyrings/microsoft.gpg && \
rm -f /tmp/microsoft.gpg && \
printf 'Types: deb\nURIs: https://packages.microsoft.com/repos/code\nSuites: stable\nComponents: main\nArchitectures: amd64\nSigned-By: /usr/share/keyrings/microsoft.gpg\n' \
> /etc/apt/sources.list.d/vscode.sources && \
apt-get update && \
apt-get install -y code && \
rm -rf /var/lib/apt/lists/*
# Create claude user with specific UID/GID
RUN groupadd -g 1000 claude && \
useradd -u 1000 -g 1000 -m -s /bin/bash claude && \
echo "claude ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
# 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 && \
# Clear package cache to force fresh repository data
rm -rf /var/lib/apt/lists/* && \
apt-get update && \
# Show available versions for debugging
apt-cache policy antigravity && \
# Install latest version
apt-get install -y --no-install-recommends antigravity && \
# Display installed version
dpkg -l | grep antigravity && \
rm -rf /var/lib/apt/lists/*
# Pre-configure Antigravity to skip onboarding/setup on first run
RUN mkdir -p /etc/skel/.config/antigravity/User/globalStorage && \
echo '{"antigravityUnifiedStateSync.seenNuxOneTimeMigration": true, "antigravityUnifiedStateSync.browserOnboarding.completed": true, "antigravityUnifiedStateSync.hasOnboardingCompleted": true, "browserOnboarding.hasSeenWelcome": true, "antigravityUnifiedStateSync.browserPreferences.hasAddedLocalhostToAllowlist": true, "antigravityUnifiedStateSync.oauthToken.hasLegacyMigrated": true, "antigravityUnifiedStateSync.auth.tokenSyncEnabled": true, "antigravityUnifiedStateSync.auth.cloudSyncEnabled": true, "theme": "vs-dark"}' \
> /etc/skel/.config/antigravity/User/globalStorage/storage.json && \
echo '{"workbench.startupEditor": "none", "workbench.welcomePage.walkthroughs.openOnInstall": false, "workbench.tips.enabled": false, "extensions.ignoreRecommendations": true, "telemetry.telemetryLevel": "off", "update.mode": "none", "extensions.autoUpdate": false, "extensions.autoCheckUpdates": false, "workbench.enableExperiments": true, "workbench.settings.enableNaturalLanguageSearch": true, "antigravity.onboarding.completed": true, "antigravity.browserOnboarding.completed": true, "antigravity.setup.completed": true, "antigravity.ai.enabled": true, "antigravity.ai.autoComplete.enabled": true, "antigravity.ai.chat.enabled": true, "antigravity.ai.codeActions.enabled": true, "antigravity.ai.explainCode.enabled": true, "antigravity.ai.generateCode.enabled": true, "antigravity.ai.optimizeCode.enabled": true, "antigravity.ai.autoSuggest.enabled": true, "antigravity.telemetry.crashReporter": "on", "antigravity.ai.acceptTerms": true, "antigravity.auth.syncState": true, "antigravity.auth.enableTokenSync": true, "antigravity.ai.enableCloudSync": true, "antigravity.settings.sync": true}' \
> /etc/skel/.config/antigravity/User/settings.json && \
# Validate Antigravity installation
/usr/share/antigravity/antigravity --version || echo "WARNING: Antigravity version check failed"
# 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 claude:claude /workspace
chown -R user:user /workspace
# Copy startup script
# 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 claude user
ENV HOME=/home/claude \
USER=claude
# 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"
RUN set-cont-env APP_NAME "Dev Container"
+49 -31
View File
@@ -2,7 +2,7 @@
# Variables
REGISTRY ?= ghcr.io/cpfarhood
IMAGE_NAME ?= antigravity
IMAGE_NAME ?= devcontainer
IMAGE_TAG ?= latest
FULL_IMAGE = $(REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG)
@@ -26,48 +26,61 @@ run:
-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 \
--name devcontainer \
$(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
@echo "Stopping devcontainer..."
docker stop devcontainer || true
docker rm devcontainer || true
# Clean up local volumes
clean: stop
@echo "Cleaning up..."
rm -rf ./home ./workspace
# Kubernetes deployment
k8s-deploy:
@echo "Deploying to Kubernetes..."
kubectl apply -k k8s/
# Helm deployment
RELEASE_NAME ?= mydev
NAMESPACE ?= default
k8s-delete:
@echo "Deleting from Kubernetes..."
kubectl delete -k k8s/
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)
k8s-logs:
@echo "Showing logs..."
kubectl logs -f antigravity-0
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)"
k8s-shell:
@echo "Opening shell..."
kubectl exec -it antigravity-0 -- bash
helm-logs:
@echo "Showing logs for $(RELEASE_NAME)..."
kubectl logs -f deployment/devcontainer-$(RELEASE_NAME) -n $(NAMESPACE)
k8s-port-forward:
@echo "Port forwarding to localhost:5800..."
kubectl port-forward antigravity-0 5800:5800
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 "Dev Container Makefile"
@echo ""
@echo "Usage: make [target]"
@echo ""
@@ -78,24 +91,29 @@ help:
@echo " stop - Stop running container"
@echo " clean - Clean up containers and volumes"
@echo ""
@echo "Kubernetes Targets:"
@echo " k8s-deploy - Deploy to Kubernetes"
@echo " k8s-delete - Delete from Kubernetes"
@echo " k8s-logs - Show container logs"
@echo " k8s-shell - Open shell in container"
@echo " k8s-port-forward - Port forward to localhost"
@echo "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_NAME - Image name (default: devcontainer)"
@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 "Example:"
@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"
+420 -347
View File
@@ -1,367 +1,440 @@
# Antigravity Dev Container
# Dev Container
![Build and Push](https://github.com/cpfarhood/devcontainer/actions/workflows/build-and-push.yaml/badge.svg)
A containerized development environment with GUI access, featuring:
- **Antigravity** (VSCode/Cloud IDE) via web browser
- **Happy Coder** - AI-powered development assistant
- **Automatic GitHub repo cloning**
- **Persistent user home directory**
- **Secure non-root execution**
## Features
### GUI Access
- Web-based VNC interface (port 5800)
- Full desktop environment in your browser
- Secure connections with optional password protection
### Development Tools
- Antigravity IDE (VSCode-based)
- Happy Coder AI assistant
- Git integration
- Node.js and npm
- Python 3
- Chrome browser
### Security
- Runs as non-root user `claude` (UID 1000, GID 1000)
- Secure VNC connections
- Token-based GitHub authentication
- Isolated workspace
### Persistence
- ReadWriteMany PVC for `/home` (user data persists)
- Workspace mounted at `/workspace`
- Repository cloned on first startup
## Documentation
- **[DEPLOYMENT.md](DEPLOYMENT.md)** - Complete deployment guide with step-by-step instructions
- **[VARIABLES.md](VARIABLES.md)** - Reference for all configuration variables
- **[README.md](README.md)** - This file (overview and quick start)
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)
- **Claude Code**, **OpenCode**, and **Crush** AI coding agents (terminal-based)
- **Built-in web file manager** for uploading/downloading files via the VNC web interface
- **Helm CLI** included for Kubernetes chart development and deployment
- **Automatic GitHub repo cloning** on startup
- **Persistent home directory** via ReadWriteMany PVC
- **Kubernetes-native** Helm chart deployment
## Quick Start
**👉 For detailed deployment instructions, see [DEPLOYMENT.md](DEPLOYMENT.md)**
### 1. Get the Image
The image is automatically built and published to GitHub Container Registry on every push to main.
### Option A: Install from Helm Repo (Recommended)
```bash
# Pull the latest image
docker pull ghcr.io/cpfarhood/devcontainer:latest
# Add the Helm repository
helm repo add devcontainer https://cpfarhood.github.io/devcontainer
helm repo update
# Or pull a specific version
docker pull ghcr.io/cpfarhood/devcontainer:v1.0.0
# Deploy with one command
helm install mydev devcontainer/devcontainer \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo
```
**Building locally (optional):**
### Option B: Install from Source
```bash
# Clone and customize the quickstart template
cp chart/values-quickstart.yaml my-values.yaml
# Edit my-values.yaml to set your name and repository
helm install mydev ./chart -f my-values.yaml
```
### Option C: One-Command from Source
```bash
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo
```
### Option C: Full Configuration
### 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 `mcp.sidecars.homeassistant.enabled: true`) |
| `HOMEASSISTANT_TOKEN` | Home Assistant long-lived access token (required when `mcp.sidecars.homeassistant.enabled: true`) |
| `DATABASE_URI` | PostgreSQL connection string (required when `mcp.sidecars.pgtuner.enabled: true`) |
| `PGTUNER_EXCLUDE_USERIDS` | Comma-separated PostgreSQL user OIDs to exclude from monitoring (optional) |
```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
The Helm chart uses a logical organization with these main sections:
- **Basic Configuration**: name, image, githubRepo
- **Access & Interface**: IDE, SSH, display, user settings
- **Infrastructure**: storage, resources, cluster access
- **Integrations**: MCP sidecars
- **Smart Defaults**: auto-detection and profiles
📖 **Documentation**:
- [USAGE.md](chart/USAGE.md) - Comprehensive examples and scenarios
- [values-quickstart.yaml](chart/values-quickstart.yaml) - Minimal configuration
- [values.schema.json](chart/values.schema.json) - IDE validation support
### Core values
| Value | Default | Description |
|-------|---------|-------------|
| `name` | `""` | Instance name — used in all resource names (`devcontainer-{name}`) |
| `githubRepo` | `""` | Repository to clone into `/workspace` on startup |
| `ide.type` | `vscode` | IDE to launch — `vscode`, `antigravity`, or `none` (see below) |
| `ssh.enabled` | `false` | Also start an OpenSSH server on port 22 (additive, any IDE) |
| `fileManager.enabled` | `false` | Enable the built-in web file manager for upload/download |
| `image.repository` | `ghcr.io/cpfarhood/devcontainer` | Container image |
| `image.tag` | `latest` | Image tag |
### IDE choice
`ide.type` 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.enabled: true` starts OpenSSH on port 22 **in addition to** the IDE. It works with any `ide.type` value:
```bash
# SSH-only (no VNC)
helm install mydev ./chart --set name=mydev --set ide.type=none --set ssh.enabled=true
# VSCode in VNC + SSH access at the same time
helm install mydev ./chart --set name=mydev --set ssh.enabled=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
```
### Web file manager
The base image includes a built-in web file manager for uploading and downloading files through the VNC web interface (port 5800). No additional sidecar is needed.
| Value | Default | Description |
|-------|---------|-------------|
| `fileManager.enabled` | `false` | Enable the web file manager |
| `fileManager.allowedPaths` | `/workspace,/config` | Paths accessible by the file manager (`AUTO`, `ALL`, or comma-separated) |
| `fileManager.deniedPaths` | `""` | Paths to deny (takes precedence over allowed) |
```bash
# Enable the file manager
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set fileManager.enabled=true
```
### 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 |
|---------|---------|---------|
| `mcp.sidecars.kubernetes.enabled` | `true` | Kubernetes API access via MCP |
| `mcp.sidecars.flux.enabled` | `true` | Flux GitOps operations via MCP |
| `mcp.sidecars.homeassistant.enabled` | `false` | Home Assistant smart home control via MCP |
| `mcp.sidecars.pgtuner.enabled` | `false` | PostgreSQL performance tuning and analysis via MCP |
| `mcp.sidecars.playwright.enabled` | `true` | Browser automation and web testing via MCP |
**Notes:**
- GitHub MCP is accessed via the Copilot API (`https://api.githubcopilot.com/mcp/`), not as a sidecar
- 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 `HOMEASSISTANT_URL` and `HOMEASSISTANT_TOKEN` in the env secret
- PostgreSQL tuner sidecar requires `DATABASE_URI` in the env secret (PostgreSQL connection string)
- Playwright sidecar provides browser automation and web testing capabilities
**Disable MCP sidecars:**
```bash
# Disable multiple sidecars
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set mcp.sidecars.kubernetes.enabled=false \
--set mcp.sidecars.flux.enabled=false \
--set mcp.sidecars.playwright.enabled=false
# Or selectively disable
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set mcp.sidecars.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 mcp.sidecars.homeassistant.enabled=true
```
**Enable PostgreSQL Tuner MCP:**
```bash
# Create secret with PostgreSQL connection string
kubectl create secret generic devcontainer-mydev-secrets-env \
--from-literal=GITHUB_TOKEN='ghp_...' \
--from-literal=DATABASE_URI='postgresql://user:password@postgres.example.com:5432/dbname'
# Deploy with PostgreSQL tuner MCP enabled
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set mcp.sidecars.pgtuner.enabled=true
```
**Custom MCP configuration:**
```yaml
# values.yaml override
mcp:
sidecars:
kubernetes:
enabled: true
image:
repository: quay.io/containers/kubernetes_mcp_server
tag: v0.0.57
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: stable
port: 8087
pgtuner:
enabled: true
image:
repository: dog830228/pgtuner_mcp
tag: latest
port: 8085
playwright:
enabled: true
image:
repository: mcr.microsoft.com/playwright/mcp
tag: latest
port: 8086
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "1000m"
```
### Display and resources
| Value | Default | Description |
|-------|---------|-------------|
| `display.width` | `1920` | VNC width (px) |
| `display.height` | `1080` | VNC height (px) |
| `display.secureConnection` | `0` | Set to `1` if TLS is not terminated upstream |
| `user.id` | `1000` | UID for the app user |
| `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}
→ 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 |
|-------|--------|-------------|
| `/config` | ReadWriteMany PVC (`userhome-{name}`) | Survives pod restarts — stores Claude credentials, dotfiles, git config |
| `/workspace` | `emptyDir` | Ephemeral — repo is re-cloned on each pod start |
---
## Troubleshooting
### 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
```
### 2. Configure Secrets
The image is also built and pushed automatically by CI on every push to `main` and on version tags (`v*`).
Edit `k8s/secrets-example.yaml` and create a sealed secret:
```bash
kubectl create secret generic antigravity-secrets \
--from-literal=github-token='ghp_your_token' \
--from-literal=vnc-password='your_password' \
--dry-run=client -o yaml | \
kubeseal --format=yaml > k8s/sealedsecrets.yaml
```
### 3. Configure Repository
Edit `k8s/configmap.yaml`:
```yaml
data:
github-repo: "https://github.com/yourusername/yourrepo"
```
### 4. Deploy to Kubernetes
```bash
kubectl apply -k k8s/
```
### 5. Access the Interface
```bash
# Port forward for local access
kubectl port-forward statefulset/antigravity 5800:5800
# Open in browser
open http://localhost:5800
```
Or configure HTTPRoute (Gateway API) for external access via your domain.
## Environment Variables
### Required
- `GITHUB_REPO` - GitHub repository URL to clone
### Optional
- `GITHUB_TOKEN` - GitHub Personal Access Token (for private repos)
- `VNC_PASSWORD` - Password for VNC access
- `USER_ID` - UID for claude user (default: 1000)
- `GROUP_ID` - GID for claude user (default: 1000)
- `DISPLAY_WIDTH` - VNC display width (default: 1920)
- `DISPLAY_HEIGHT` - VNC display height (default: 1080)
### Happy Coder Configuration (Optional)
- `HAPPY_SERVER_URL` - Custom Happy server URL (default: https://api.cluster-fluster.com)
- `HAPPY_WEBAPP_URL` - Custom Happy webapp URL (default: https://app.happy.engineering)
- `HAPPY_HOME_DIR` - Happy data directory (default: /home/claude/.happy)
- `HAPPY_EXPERIMENTAL` - Enable experimental features (default: true in container)
## Architecture
```
┌─────────────────────────────────────┐
│ Web Browser (Port 5800) │
└──────────────┬──────────────────────┘
┌─────────────────────────────────────┐
│ VNC Web Interface │
│ (jlesage/baseimage-gui) │
└──────────────┬──────────────────────┘
┌─────────────────────────────────────┐
│ Antigravity IDE │
│ (VSCode + Extensions) │
│ Running as user: claude (1000) │
└──────────────┬──────────────────────┘
┌─────────────────────────────────────┐
│ Happy Coder (Background Process) │
│ AI Development Assistant │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Workspace: /workspace/{repo} │
│ Home: /home/claude (RWX PVC) │
└─────────────────────────────────────┘
```
## Startup Flow
1. **Container starts** - baseimage-gui initializes
2. **init-repo.sh runs**:
- Checks for `GITHUB_REPO` environment variable
- Clones repository to `/workspace/{repo-name}` if not exists
- Configures git credentials with `GITHUB_TOKEN`
- Starts Happy Coder in background
3. **startapp.sh runs**:
- Opens Antigravity IDE in the cloned repository
- Happy Coder is already running and accessible
## Happy Coder Integration
Happy Coder runs as a background service and is accessible within the IDE:
```bash
# Check Happy Coder status
ps aux | grep happy-coder
# View logs
cat /tmp/happy-coder.log
# Restart Happy Coder
sudo -u claude bash -c "cd /workspace/your-repo && happy-coder &"
```
## Local Development
### Run with Docker Compose
```yaml
version: '3.8'
services:
antigravity:
build: .
ports:
- "5800:5800"
environment:
- GITHUB_REPO=https://github.com/yourusername/yourrepo
- GITHUB_TOKEN=ghp_your_token
- VNC_PASSWORD=yourpassword
- HAPPY_EXPERIMENTAL=true
volumes:
- ./home:/home
- ./workspace:/workspace
```
```bash
docker-compose up
```
### Run with Docker
```bash
docker run -d \
-p 5800:5800 \
-e GITHUB_REPO="https://github.com/yourusername/yourrepo" \
-e GITHUB_TOKEN="ghp_your_token" \
-e VNC_PASSWORD="yourpassword" \
-e HAPPY_EXPERIMENTAL="true" \
-v $(pwd)/home:/home \
-v $(pwd)/workspace:/workspace \
ghcr.io/cpfarhood/antigravity:latest
```
## Kubernetes Deployment
### With Flux
See the animaniacs cluster configuration for GitOps deployment patterns.
### Standalone
```bash
# Apply manifests
kubectl apply -k k8s/
# Check status
kubectl get statefulset antigravity
kubectl get pods -l app=antigravity
# Access logs
kubectl logs antigravity-0
# Access shell
kubectl exec -it antigravity-0 -- bash
```
## Troubleshooting
### Repository not cloning
```bash
# Check logs
kubectl logs antigravity-0 | grep "Repository Initialization"
# Verify GITHUB_REPO is set
kubectl exec antigravity-0 -- env | grep GITHUB
# Check git credentials
kubectl exec antigravity-0 -- cat /home/claude/.git-credentials
```
### Happy Coder not starting
```bash
# Check Happy Coder logs
kubectl exec antigravity-0 -- cat /tmp/happy-coder.log
# Verify API key
kubectl exec antigravity-0 -- env | grep HAPPY_CODER
# Restart Happy Coder
kubectl exec antigravity-0 -- sudo -u claude bash -c "cd /workspace/repo && happy-coder &"
```
### VNC not accessible
```bash
# Check port forwarding
kubectl port-forward antigravity-0 5800:5800
# Verify service
kubectl get svc antigravity
# Check pod status
kubectl describe pod antigravity-0
```
### Permission issues
```bash
# Check ownership
kubectl exec antigravity-0 -- ls -la /home/claude
kubectl exec antigravity-0 -- ls -la /workspace
# Fix ownership
kubectl exec antigravity-0 -- chown -R claude:claude /home/claude
kubectl exec antigravity-0 -- chown -R claude:claude /workspace
```
## Security Considerations
1. **Secrets Management**: Use SealedSecrets or external secret managers
2. **Network Policies**: Restrict ingress/egress as needed
3. **RBAC**: Limit who can access the namespace
4. **VNC Password**: Always set a strong VNC password
5. **GitHub Token**: Use fine-grained tokens with minimal permissions
6. **Container Security**: Runs as non-root user (claude:1000)
## Storage
### Home Directory (`/home`)
- Mounted from ReadWriteMany PVC (`userhome`)
- Persists user settings, credentials, history
- Survives pod restarts
### Workspace (`/workspace`)
- ephemeral emptyDir (can be changed to PVC)
- Contains cloned repository
- Rebuild on pod restart
To persist workspace:
1. Create a PVC for workspace
2. Update `statefulset.yaml` to use PVC instead of emptyDir
## Customization
### Add More Tools
Edit `Dockerfile`:
```dockerfile
RUN apt-get update && apt-get install -y \
your-package-here \
&& rm -rf /var/lib/apt/lists/*
```
### Change Display Resolution
Set environment variables:
```yaml
env:
- name: DISPLAY_WIDTH
value: "2560"
- name: DISPLAY_HEIGHT
value: "1440"
```
### Auto-clone Multiple Repos
Modify `init-repo.sh` to support `GITHUB_REPOS` (comma-separated):
```bash
IFS=',' read -ra REPOS <<< "$GITHUB_REPOS"
for repo in "${REPOS[@]}"; do
# Clone each repo
done
```
## License
MIT
---
## Credits
- Built on [jlesage/baseimage-gui](https://github.com/jlesage/docker-baseimage-gui)
- Uses [Happy Coder](https://happy.engineering)
- Inspired by Google's Project IDX
- Base image: [jlesage/docker-baseimage-gui](https://github.com/jlesage/docker-baseimage-gui)
- AI assistant: [Claude](https://claude.ai)
+384 -275
View File
@@ -1,305 +1,414 @@
# Configuration Variables Reference
# Helm Chart Values Reference
Quick reference for all configurable variables in this project.
Complete reference for all configurable values in the Antigravity Dev Container Helm chart.
## Required Variables
## Core Configuration
These MUST be configured before deployment:
### Storage Class Name
- **Variable:** `storageClassName`
- **File:** `k8s/statefulset.yaml`
- **Line:** ~117
### name
- **Type:** String
- **Description:** ReadWriteMany storage class available in your cluster
- **Example:** `ceph-filesystem`, `nfs-client`, `efs-sc`
- **How to find:** `kubectl get storageclass`
### GitHub Repository URL
- **Variable:** `github-repo`
- **File:** `k8s/configmap.yaml`
- **Line:** ~9
- **Type:** String (URL)
- **Description:** Repository to clone on container startup
- **Format:** `https://github.com/username/repository`
- **Example:** `https://github.com/cpfarhood/my-project`
### Gateway Name
- **Variable:** `parentRefs[0].name`
- **File:** `k8s/httproute.yaml`
- **Line:** ~8
- **Type:** String
- **Description:** Name of your Gateway resource
- **How to find:** `kubectl get gateway -A`
### Gateway Namespace
- **Variable:** `parentRefs[0].namespace`
- **File:** `k8s/httproute.yaml`
- **Line:** ~9
- **Type:** String
- **Description:** Namespace where Gateway is deployed
- **How to find:** `kubectl get gateway -A`
### Domain Hostname
- **Variable:** `hostnames[0]`
- **File:** `k8s/httproute.yaml`
- **Line:** ~11
- **Type:** String (FQDN)
- **Description:** Domain name for accessing the container
- **Example:** `devcontainer.example.com`
## Optional Variables
### GitHub Token
- **Variable:** `github-token`
- **File:** Sealed Secret
- **Type:** String (GitHub PAT)
- **Description:** Personal Access Token for private repos
- **Required:** Only for private repositories
- **Format:** `ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`
- **Scopes:** `repo`
### VNC Password
- **Variable:** `vnc-password`
- **File:** Sealed Secret
- **Type:** String
- **Description:** Password for VNC web interface
- **Required:** Recommended for security
- **Format:** Any string (12+ characters recommended)
### Namespace
- **Variable:** `namespace`
- **File:** `k8s/kustomization.yaml`
- **Line:** ~5
- **Type:** String
- **Description:** Kubernetes namespace for deployment
- **Default:** `default`
### Container Image
- **Variable:** `image`
- **File:** `k8s/statefulset.yaml`
- **Line:** ~32
- **Type:** String (image reference)
- **Description:** Docker image to deploy
- **Default:** `ghcr.io/cpfarhood/devcontainer:latest`
- **Format:** `registry/repository:tag`
### Memory Request
- **Variable:** `resources.requests.memory`
- **File:** `k8s/statefulset.yaml`
- **Line:** ~99
- **Type:** String (quantity)
- **Description:** Minimum memory to reserve
- **Default:** `2Gi`
- **Format:** `<number>Gi` or `<number>Mi`
### Memory Limit
- **Variable:** `resources.limits.memory`
- **File:** `k8s/statefulset.yaml`
- **Line:** ~102
- **Type:** String (quantity)
- **Description:** Maximum memory allowed
- **Default:** `8Gi`
- **Format:** `<number>Gi` or `<number>Mi`
### CPU Request
- **Variable:** `resources.requests.cpu`
- **File:** `k8s/statefulset.yaml`
- **Line:** ~100
- **Type:** String (quantity)
- **Description:** Minimum CPU to reserve
- **Default:** `1000m` (1 core)
- **Format:** `<number>m` (millicores) or `<number>` (cores)
### CPU Limit
- **Variable:** `resources.limits.cpu`
- **File:** `k8s/statefulset.yaml`
- **Line:** ~103
- **Type:** String (quantity)
- **Description:** Maximum CPU allowed
- **Default:** `4000m` (4 cores)
- **Format:** `<number>m` (millicores) or `<number>` (cores)
### Storage Size
- **Variable:** `storage` (under volumeClaimTemplates)
- **File:** `k8s/statefulset.yaml`
- **Line:** ~120
- **Type:** String (quantity)
- **Description:** Size of home directory PVC
- **Default:** `10Gi`
- **Format:** `<number>Gi` or `<number>Ti`
### Happy Server URL
- **Variable:** `happy-server-url`
- **File:** `k8s/configmap.yaml`
- **Line:** ~12 (commented)
- **Type:** String (URL)
- **Description:** Custom Happy Coder server
- **Default:** `https://api.cluster-fluster.com`
- **When to set:** Self-hosted Happy instance only
### Happy Webapp URL
- **Variable:** `happy-webapp-url`
- **File:** `k8s/configmap.yaml`
- **Line:** ~13 (commented)
- **Type:** String (URL)
- **Description:** Custom Happy Coder webapp
- **Default:** `https://app.happy.engineering`
- **When to set:** Self-hosted Happy instance only
### Display Width
- **Variable:** `DISPLAY_WIDTH`
- **File:** `k8s/statefulset.yaml`
- **Line:** ~56
- **Type:** String (number)
- **Description:** VNC display width in pixels
- **Default:** `1920`
### Display Height
- **Variable:** `DISPLAY_HEIGHT`
- **File:** `k8s/statefulset.yaml`
- **Line:** ~58
- **Type:** String (number)
- **Description:** VNC display height in pixels
- **Default:** `1080`
### User ID
- **Variable:** `USER_ID`
- **File:** `k8s/statefulset.yaml`
- **Line:** ~51
- **Type:** String (number)
- **Description:** UID for claude user
- **Default:** `1000`
### Group ID
- **Variable:** `GROUP_ID`
- **File:** `k8s/statefulset.yaml`
- **Line:** ~53
- **Type:** String (number)
- **Description:** GID for claude user
- **Default:** `1000`
### StatefulSet Replicas
- **Variable:** `replicas`
- **File:** `k8s/statefulset.yaml`
- **Line:** ~21
- **Type:** Integer
- **Description:** Number of container instances
- **Default:** `1`
- **Note:** Each replica gets own home PVC
## Environment Variables (Runtime)
These are set at runtime, not in configuration files:
### GITHUB_REPO
- **Type:** String (URL)
- **Description:** Repository URL (from ConfigMap)
- **Default:** `""`
- **Required:** Yes
- **Source:** ConfigMap `antigravity.github-repo`
- **Description:** Instance name used to generate resource names (`devcontainer-{name}`, `userhome-{name}`)
- **Example:** `mydev`, `alice-dev`, `team-workspace`
### GITHUB_TOKEN
### githubRepo
- **Type:** String
- **Description:** GitHub PAT (from Secret)
- **Required:** No (only for private repos)
- **Source:** Secret `antigravity.github-token`
- **Default:** `""`
- **Required:** Yes
- **Description:** GitHub repository URL to clone into `/workspace`
- **Example:** `https://github.com/username/repository`
### VNC_PASSWORD
### ide
- **Type:** String
- **Description:** VNC password (from Secret)
- **Required:** No
- **Source:** Secret `antigravity.vnc-password`
- **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
### HAPPY_SERVER_URL
- **Type:** String (URL)
- **Description:** Happy server URL (from ConfigMap)
- **Required:** No
- **Source:** ConfigMap `antigravity.happy-server-url`
### 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
### HAPPY_WEBAPP_URL
- **Type:** String (URL)
- **Description:** Happy webapp URL (from ConfigMap)
- **Required:** No
- **Source:** ConfigMap `antigravity.happy-webapp-url`
## Image Configuration
### HAPPY_HOME_DIR
- **Type:** String (path)
- **Description:** Happy data directory
- **Required:** No
- **Default:** `/home/claude/.happy`
- **Source:** Hardcoded in StatefulSet
### image.repository
- **Type:** String
- **Default:** `ghcr.io/cpfarhood/devcontainer`
- **Description:** Container image repository
### HAPPY_EXPERIMENTAL
- **Type:** String (boolean)
- **Description:** Enable Happy experimental features
- **Required:** No
### 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
## 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`
- **Source:** Hardcoded in StatefulSet
- **Description:** Enable Kubernetes MCP server sidecar
## Variable Groups by Use Case
### mcpSidecars.kubernetes.image.repository
- **Type:** String
- **Default:** `quay.io/containers/kubernetes_mcp_server`
- **Description:** Kubernetes MCP server image
### Minimal Deployment
Only these variables are required for basic deployment:
1. `storageClassName`
2. `github-repo`
3. `parentRefs.name`
4. `parentRefs.namespace`
5. `hostnames`
### mcpSidecars.kubernetes.image.tag
- **Type:** String
- **Default:** `latest`
- **Description:** Kubernetes MCP server image tag
### Private Repository Deployment
Add these for private repos:
1. All minimal deployment variables
2. `github-token` (sealed secret)
### mcpSidecars.kubernetes.port
- **Type:** Integer
- **Default:** `8080`
- **Description:** Port for Kubernetes MCP server
### Production Deployment
Recommended for production:
1. All private repository variables
2. `vnc-password` (sealed secret)
3. `resources.requests.*` (adjusted for workload)
4. `resources.limits.*` (adjusted for workload)
5. `namespace` (dedicated namespace)
### mcpSidecars.kubernetes.resources
- **Type:** Object
- **Default:**
```yaml
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "500m"
```
- **Description:** Resource limits for Kubernetes MCP sidecar
### Multi-User Deployment
For multiple users:
1. All production deployment variables
2. `replicas` (set to number of users)
3. Larger `storage` size for home PVCs
### mcpSidecars.flux.enabled
- **Type:** Boolean
- **Default:** `true`
- **Description:** Enable Flux MCP server sidecar
## Quick Copy Templates
### 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
### Minimal Required Variables
```yaml
# k8s/statefulset.yaml
storageClassName: "CHANGE_ME" # Line ~117
# k8s/configmap.yaml
github-repo: "CHANGE_ME" # Line ~9
# k8s/httproute.yaml
parentRefs:
- name: CHANGE_ME # Line ~8
namespace: CHANGE_ME # Line ~9
hostnames:
- "CHANGE_ME" # Line ~11
name: mydev
githubRepo: https://github.com/user/repo
```
### With Secrets
```bash
kubectl create secret generic antigravity-secrets \
--from-literal=github-token='CHANGE_ME' \
--from-literal=vnc-password='CHANGE_ME' \
--dry-run=client -o yaml | \
kubeseal --format=yaml > k8s/sealedsecrets.yaml
```
### Production Configuration
### With Resource Adjustments
```yaml
# k8s/statefulset.yaml (lines ~98-103)
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: "CHANGE_ME" # e.g., 4Gi
cpu: "CHANGE_ME" # e.g., 2000m
memory: "4Gi"
cpu: "2000m"
limits:
memory: "CHANGE_ME" # e.g., 16Gi
cpu: "CHANGE_ME" # e.g., 8000m
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
```
### 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 |
| `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 |
+11
View File
@@ -0,0 +1,11 @@
apiVersion: v2
name: devcontainer
description: Dev Container with AI coding agents and MCP sidecars
type: application
version: 2.6.0
appVersion: "latest"
keywords:
- development
- devcontainer
- vscode
- ai
+381
View File
@@ -0,0 +1,381 @@
# Dev Container Helm Chart Usage Guide
This guide provides common usage patterns and examples for the Dev Container Helm chart.
## Quick Start
### 1. Minimal Installation (Recommended)
Use the quickstart values for the simplest setup:
```bash
# Copy and customize quickstart values
cp values-quickstart.yaml my-values.yaml
# Edit my-values.yaml to set your name and repo:
# name: myproject
# githubRepo: https://github.com/youruser/yourproject
# Install
helm install myproject ./chart -f my-values.yaml
```
### 2. One-Command Installation
```bash
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo
```
## Common Use Cases
### Development Environment
**Scenario**: Standard development with GitHub integration
```yaml
name: dev-environment
githubRepo: https://github.com/company/project
ide:
type: vscode
mcp:
sidecars:
kubernetes:
enabled: true
playwright:
enabled: true
flux:
enabled: false # Disable if not using Flux
```
### Team Workspace
**Scenario**: Shared development environment with more resources
```yaml
name: team-workspace
githubRepo: https://github.com/company/project
resources:
requests:
memory: "4Gi"
cpu: "2000m"
limits:
memory: "16Gi"
cpu: "8000m"
storage:
size: 64Gi
ssh:
enabled: true # Enable SSH access for team
clusterAccess: readwrite # Full cluster access
```
### Kubernetes Admin Environment
**Scenario**: Platform engineering with full cluster access
```yaml
name: k8s-admin
githubRepo: https://github.com/company/k8s-configs
clusterAccess: readwrite
mcp:
sidecars:
kubernetes:
enabled: true
flux:
enabled: true
pgtuner:
enabled: true # Database administration
playwright:
enabled: false # Save resources
```
### AI/ML Development
**Scenario**: AI development with browser automation
```yaml
name: ai-playground
githubRepo: https://github.com/company/ai-project
resources:
requests:
memory: "8Gi" # More memory for ML workloads
cpu: "4000m"
limits:
memory: "32Gi"
cpu: "16000m"
storage:
size: 128Gi # Large datasets
mcp:
sidecars:
playwright:
enabled: true # Web scraping, testing
kubernetes:
enabled: false # Save resources
flux:
enabled: false
```
### Lightweight Environment
**Scenario**: Resource-constrained setup
```yaml
name: lightweight
githubRepo: https://github.com/youruser/small-project
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "1000m"
storage:
size: 8Gi
mcp:
sidecars:
kubernetes:
enabled: false
flux:
enabled: false
playwright:
enabled: false
# Only keep essential sidecars enabled
```
## Secret Configuration
### Basic Secrets
```bash
# GitHub access only
kubectl create secret generic devcontainer-mydev-secrets-env \
--from-literal=GITHUB_TOKEN='ghp_...' \
--from-literal=VNC_PASSWORD='changeme'
```
### Extended Secrets
```bash
# Full feature set
kubectl create secret generic devcontainer-mydev-secrets-env \
--from-literal=GITHUB_TOKEN='ghp_...' \
--from-literal=VNC_PASSWORD='changeme' \
--from-literal=SSH_AUTHORIZED_KEYS='ssh-ed25519 AAAA...' \
--from-literal=HOMEASSISTANT_URL='http://homeassistant.local:8123' \
--from-literal=HOMEASSISTANT_TOKEN='eyJ...' \
--from-literal=DATABASE_URI='postgresql://user:pass@postgres:5432/db'
```
## Storage Configuration
### Different Storage Classes
```yaml
# For different Kubernetes distributions
storage:
className: "" # Auto-detect (recommended)
# className: longhorn # Longhorn
# className: nfs-client # NFS
# className: fast-ssd # Custom fast storage
```
### Storage Sizes by Use Case
```yaml
# Small projects
storage:
size: 8Gi
# Standard development
storage:
size: 32Gi
# Large projects / datasets
storage:
size: 128Gi
# Team environments
storage:
size: 256Gi
```
## Access Patterns
### VNC Only (Default)
```yaml
ide:
type: vscode
# Access via: kubectl port-forward deployment/devcontainer-mydev 5800:5800
```
### SSH Only
```yaml
ide:
type: none
ssh:
enabled: true
# Access via: kubectl port-forward deployment/devcontainer-mydev 2222:22
# ssh -p 2222 user@localhost
```
### Both VNC and SSH
```yaml
ide:
type: vscode
ssh:
enabled: true
# VNC: kubectl port-forward deployment/devcontainer-mydev 5800:5800
# SSH: kubectl port-forward deployment/devcontainer-mydev 2222:22
```
## Resource Profiles
### Small (1-2 developers)
```yaml
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "4Gi"
cpu: "2000m"
```
### Medium (standard development)
```yaml
resources:
requests:
memory: "2Gi"
cpu: "1000m"
limits:
memory: "8Gi"
cpu: "4000m"
```
### Large (intensive workloads)
```yaml
resources:
requests:
memory: "4Gi"
cpu: "2000m"
limits:
memory: "16Gi"
cpu: "8000m"
```
### XLarge (AI/ML, data processing)
```yaml
resources:
requests:
memory: "8Gi"
cpu: "4000m"
limits:
memory: "32Gi"
cpu: "16000m"
```
## MCP Sidecar Combinations
### Minimal (basic development)
```yaml
mcp:
sidecars:
kubernetes:
enabled: false
flux:
enabled: false
playwright:
enabled: true # Keep for web testing
```
### Standard (full-stack development)
```yaml
mcp:
sidecars:
kubernetes:
enabled: true
flux:
enabled: false
playwright:
enabled: true
```
### DevOps/Platform (infrastructure work)
```yaml
mcp:
sidecars:
kubernetes:
enabled: true
flux:
enabled: true
pgtuner:
enabled: true
playwright:
enabled: false
```
### All Features
```yaml
mcp:
sidecars:
kubernetes:
enabled: true
flux:
enabled: true
homeassistant:
enabled: true
pgtuner:
enabled: true
playwright:
enabled: true
```
## Troubleshooting
### Values Validation
Your IDE should automatically validate values.yaml against the schema. If not:
```bash
# Manual validation (if you have a JSON schema validator)
helm template ./chart -f values.yaml > /dev/null
```
### Common Issues
**Resource Limits**: Start with smaller resource requests and increase as needed.
**Storage Class**: Use `className: ""` for auto-detection.
**GitHub Access**: Ensure GITHUB_TOKEN has `repo` scope.
**MCP Sidecars**: Disable unused sidecars to save resources.
### Getting Help
1. Check the main [README.md](../README.md) for detailed documentation
2. Review [values.yaml](values.yaml) for all available options
3. Use [values-quickstart.yaml](values-quickstart.yaml) as a starting point
+31
View File
@@ -0,0 +1,31 @@
Dev Container "{{ .Values.name }}" has been deployed.
{{- if ne (.Values.ide.type | default "vscode") "none" }}
Access the IDE:
kubectl port-forward deployment/{{ include "devcontainer.fullname" . }} 5800:5800 -n {{ .Release.Namespace }}
Then open: http://localhost:5800
{{- end }}
{{- if .Values.ssh.enabled }}
SSH access:
kubectl port-forward deployment/{{ include "devcontainer.fullname" . }} 2222:22 -n {{ .Release.Namespace }}
Then: ssh -p 2222 user@localhost
{{- end }}
Useful commands:
Logs: kubectl logs -f deployment/{{ include "devcontainer.fullname" . }} -n {{ .Release.Namespace }}
Shell: kubectl exec -it deployment/{{ include "devcontainer.fullname" . }} -n {{ .Release.Namespace }} -- bash
{{- if not (lookup "v1" "Secret" .Release.Namespace (include "devcontainer.envSecretName" .)) }}
Optional: Create a secret for GITHUB_TOKEN, VNC_PASSWORD, etc:
kubectl create secret generic {{ include "devcontainer.envSecretName" . }} \
--from-literal=GITHUB_TOKEN=ghp_xxx \
--from-literal=VNC_PASSWORD=changeme \
-n {{ .Release.Namespace }}
{{- end }}
Note: The PVC "{{ include "devcontainer.pvcName" . }}" is protected from deletion on helm uninstall.
To remove it manually: kubectl delete pvc {{ include "devcontainer.pvcName" . }} -n {{ .Release.Namespace }}
+111
View File
@@ -0,0 +1,111 @@
{{/*
Resource name prefix: devcontainer-{name}
*/}}
{{- define "devcontainer.fullname" -}}
{{- if not .Values.name }}
{{- fail "values.name is required and must not be empty" }}
{{- end }}
{{- printf "devcontainer-%s" .Values.name }}
{{- end }}
{{/*
PVC name: userhome-{name}
*/}}
{{- define "devcontainer.pvcName" -}}
{{- printf "userhome-%s" .Values.name }}
{{- end }}
{{/*
Secret name for env vars, default to devcontainer-{name}-secrets-env
*/}}
{{- define "devcontainer.envSecretName" -}}
{{- .Values.envSecretName | default (printf "devcontainer-%s-secrets-env" .Values.name) }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "devcontainer.labels" -}}
app: devcontainer
instance: {{ .Values.name }}
app.kubernetes.io/name: devcontainer
app.kubernetes.io/instance: {{ .Values.name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
{{- end }}
{{/*
Selector labels keep narrow since changing these requires recreating the Deployment
*/}}
{{- define "devcontainer.selectorLabels" -}}
app: devcontainer
instance: {{ .Values.name }}
{{- end }}
{{/*
Smart resource sizing based on enabled features
*/}}
{{- define "devcontainer.smartResources" -}}
{{- $baseMemory := "2Gi" }}
{{- $baseCpu := "1000m" }}
{{- $limitMemory := "8Gi" }}
{{- $limitCpu := "4000m" }}
{{/* Adjust for enabled MCP sidecars */}}
{{- if .Values.mcp.sidecars.playwright.enabled }}
{{- $baseMemory = "3Gi" }}
{{- $limitMemory = "12Gi" }}
{{- end }}
{{/* Adjust for IDE type */}}
{{- if eq .Values.ide.type "antigravity" }}
{{- $baseMemory = "4Gi" }}
{{- $limitMemory = "16Gi" }}
{{- end }}
requests:
memory: {{ .Values.resources.requests.memory | default $baseMemory | quote }}
cpu: {{ .Values.resources.requests.cpu | default $baseCpu | quote }}
limits:
memory: {{ .Values.resources.limits.memory | default $limitMemory | quote }}
cpu: {{ .Values.resources.limits.cpu | default $limitCpu | quote }}
{{- end }}
{{/*
Auto-detect environment type and set smart defaults
*/}}
{{- define "devcontainer.smartDefaults" -}}
{{- $isDev := or (contains "dev" .Values.name) (contains "test" .Values.name) (contains "local" .Values.name) }}
{{- $isProd := or (contains "prod" .Values.name) (contains "production" .Values.name) }}
{{- $isTeam := or (contains "team" .Values.name) (contains "shared" .Values.name) }}
{{/* Development environment - enable more sidecars, smaller resources */}}
{{- if $isDev }}
development: true
{{/* Production environment - conservative defaults, fewer sidecars */}}
{{- else if $isProd }}
production: true
{{/* Team environment - enable SSH, more resources */}}
{{- else if $isTeam }}
team: true
{{- end }}
{{- end }}
{{/*
Smart MCP sidecar selection based on cluster access
*/}}
{{- define "devcontainer.mcpDefaults" -}}
{{- if eq .Values.clusterAccess "none" }}
{{/* No cluster access - disable k8s/flux sidecars */}}
kubernetes:
enabled: false
flux:
enabled: false
{{- else }}
{{/* Has cluster access - enable k8s sidecars */}}
kubernetes:
enabled: true
flux:
enabled: {{ ne .Values.clusterAccess "readonly" }}
{{- end }}
{{- end }}
+310
View File
@@ -0,0 +1,310 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "devcontainer.fullname" . }}
labels:
{{- include "devcontainer.labels" . | nindent 4 }}
spec:
replicas: 1
selector:
matchLabels:
{{- include "devcontainer.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "devcontainer.labels" . | nindent 8 }}
spec:
{{- if ne (.Values.clusterAccess | default "none") "none" }}
serviceAccountName: {{ include "devcontainer.fullname" . }}
{{- end }}
securityContext:
fsGroup: 1000
fsGroupChangePolicy: "OnRootMismatch"
{{- if and .Values.ide.type (eq .Values.ide.type "antigravity") }}
initContainers:
- name: setup-userdata
image: busybox:1.37
command: ['sh', '-c']
args:
- |
echo "Setting up userdata directory..."
mkdir -p /config/userdata
chown 1000:1000 /config/userdata
chmod 755 /config/userdata
echo "Userdata directory setup complete"
volumeMounts:
- name: userhome
mountPath: /config
securityContext:
runAsUser: 0
runAsGroup: 0
{{- end }}
containers:
- name: devcontainer
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
{{- if ne (.Values.ide.type | default "vscode") "none" }}
- containerPort: 5800
name: vnc-web
protocol: TCP
{{- end }}
{{- if .Values.ssh.enabled }}
- containerPort: 22
name: ssh
protocol: TCP
{{- end }}
env:
- name: IDE
value: {{ .Values.ide.type | default "vscode" | quote }}
- name: SSH
value: {{ .Values.ssh.enabled | toString | quote }}
- name: USER_ID
value: {{ .Values.user.id | quote }}
- name: GROUP_ID
value: {{ .Values.user.groupId | quote }}
- name: DISPLAY_WIDTH
value: {{ .Values.display.width | quote }}
- name: DISPLAY_HEIGHT
value: {{ .Values.display.height | quote }}
- name: SECURE_CONNECTION
value: {{ .Values.display.secureConnection | quote }}
{{- if .Values.fileManager.enabled }}
- name: WEB_FILE_MANAGER
value: "1"
- name: WEB_FILE_MANAGER_ALLOWED_PATHS
value: {{ .Values.fileManager.allowedPaths | quote }}
{{- if .Values.fileManager.deniedPaths }}
- name: WEB_FILE_MANAGER_DENIED_PATHS
value: {{ .Values.fileManager.deniedPaths | quote }}
{{- end }}
{{- end }}
{{- if .Values.githubRepo }}
- name: GITHUB_REPO
value: {{ .Values.githubRepo | quote }}
{{- end }}
{{- if .Values.githubRepos }}
- name: GITHUB_REPOS
value: {{ join "," .Values.githubRepos | quote }}
{{- end }}
envFrom:
- secretRef:
name: {{ include "devcontainer.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.type | 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.enabled }}
livenessProbe:
tcpSocket:
port: 22
initialDelaySeconds: 15
periodSeconds: 10
readinessProbe:
tcpSocket:
port: 22
initialDelaySeconds: 5
periodSeconds: 5
{{- end }}
{{- if and .Values.mcp.sidecars.kubernetes.enabled (ne .Values.clusterAccess "none") }}
- name: kubernetes-mcp
image: "{{ .Values.mcp.sidecars.kubernetes.image.repository }}:{{ .Values.mcp.sidecars.kubernetes.image.tag }}"
args:
- --port
- {{ .Values.mcp.sidecars.kubernetes.port | quote }}
ports:
- containerPort: {{ .Values.mcp.sidecars.kubernetes.port }}
name: k8s-mcp
protocol: TCP
livenessProbe:
httpGet:
path: /healthz
port: {{ .Values.mcp.sidecars.kubernetes.port }}
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /healthz
port: {{ .Values.mcp.sidecars.kubernetes.port }}
initialDelaySeconds: 5
periodSeconds: 5
resources:
{{- toYaml .Values.mcp.sidecars.kubernetes.resources | nindent 12 }}
{{- end }}
{{- if and .Values.mcp.sidecars.flux.enabled (ne .Values.clusterAccess "none") }}
- name: flux-mcp
image: "{{ .Values.mcp.sidecars.flux.image.repository }}:{{ .Values.mcp.sidecars.flux.image.tag }}"
args:
- serve
- --transport=sse
- --port={{ .Values.mcp.sidecars.flux.port }}
ports:
- containerPort: {{ .Values.mcp.sidecars.flux.port }}
name: flux-mcp
protocol: TCP
livenessProbe:
tcpSocket:
port: {{ .Values.mcp.sidecars.flux.port }}
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
tcpSocket:
port: {{ .Values.mcp.sidecars.flux.port }}
initialDelaySeconds: 5
periodSeconds: 5
resources:
{{- toYaml .Values.mcp.sidecars.flux.resources | nindent 12 }}
{{- end }}
{{- if .Values.mcp.sidecars.helm.enabled }}
- name: helm-mcp
image: "{{ .Values.mcp.sidecars.helm.image.repository }}:{{ .Values.mcp.sidecars.helm.image.tag }}"
args:
- -mode=sse
ports:
- containerPort: {{ .Values.mcp.sidecars.helm.port }}
name: helm-mcp
protocol: TCP
livenessProbe:
tcpSocket:
port: {{ .Values.mcp.sidecars.helm.port }}
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
tcpSocket:
port: {{ .Values.mcp.sidecars.helm.port }}
initialDelaySeconds: 5
periodSeconds: 5
resources:
{{- toYaml .Values.mcp.sidecars.helm.resources | nindent 12 }}
{{- end }}
{{- if .Values.mcp.sidecars.homeassistant.enabled }}
- name: homeassistant-mcp
image: "{{ .Values.mcp.sidecars.homeassistant.image.repository }}:{{ .Values.mcp.sidecars.homeassistant.image.tag }}"
imagePullPolicy: IfNotPresent
command: ["fastmcp", "run", "--transport", "sse", "--host", "0.0.0.0", "--port", "{{ .Values.mcp.sidecars.homeassistant.port }}"]
ports:
- name: homeassistant
containerPort: {{ .Values.mcp.sidecars.homeassistant.port }}
env:
- name: HOMEASSISTANT_URL
valueFrom:
secretKeyRef:
name: {{ include "devcontainer.envSecretName" . }}
key: HOMEASSISTANT_URL
optional: true
- name: HOMEASSISTANT_TOKEN
valueFrom:
secretKeyRef:
name: {{ include "devcontainer.envSecretName" . }}
key: HOMEASSISTANT_TOKEN
optional: true
livenessProbe:
tcpSocket:
port: {{ .Values.mcp.sidecars.homeassistant.port }}
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
tcpSocket:
port: {{ .Values.mcp.sidecars.homeassistant.port }}
initialDelaySeconds: 5
periodSeconds: 5
resources:
{{- toYaml .Values.mcp.sidecars.homeassistant.resources | nindent 12 }}
{{- end }}
{{- if .Values.mcp.sidecars.pgtuner.enabled }}
- name: pgtuner-mcp
image: "{{ .Values.mcp.sidecars.pgtuner.image.repository }}:{{ .Values.mcp.sidecars.pgtuner.image.tag }}"
imagePullPolicy: Always # pgtuner uses `latest` tag (no versioned releases available)
command: ["python", "-m", "pgtuner_mcp", "--mode", "sse", "--host", "0.0.0.0", "--port", "{{ .Values.mcp.sidecars.pgtuner.port }}"]
ports:
- name: pgtuner
containerPort: {{ .Values.mcp.sidecars.pgtuner.port }}
env:
- name: DATABASE_URI
valueFrom:
secretKeyRef:
name: {{ include "devcontainer.envSecretName" . }}
key: DATABASE_URI
optional: true
- name: PGTUNER_EXCLUDE_USERIDS
valueFrom:
secretKeyRef:
name: {{ include "devcontainer.envSecretName" . }}
key: PGTUNER_EXCLUDE_USERIDS
optional: true
livenessProbe:
tcpSocket:
port: {{ .Values.mcp.sidecars.pgtuner.port }}
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
tcpSocket:
port: {{ .Values.mcp.sidecars.pgtuner.port }}
initialDelaySeconds: 5
periodSeconds: 5
resources:
{{- toYaml .Values.mcp.sidecars.pgtuner.resources | nindent 12 }}
{{- end }}
{{- if .Values.mcp.sidecars.playwright.enabled }}
- name: playwright-mcp
image: "{{ .Values.mcp.sidecars.playwright.image.repository }}:{{ .Values.mcp.sidecars.playwright.image.tag }}"
imagePullPolicy: IfNotPresent
command: ["node"]
args:
- cli.js
- --headless
- --browser
- chromium
- --no-sandbox
- --host
- 0.0.0.0
- --port
- {{ .Values.mcp.sidecars.playwright.port | quote }}
ports:
- name: playwright
containerPort: {{ .Values.mcp.sidecars.playwright.port }}
livenessProbe:
tcpSocket:
port: {{ .Values.mcp.sidecars.playwright.port }}
initialDelaySeconds: 15
periodSeconds: 10
readinessProbe:
tcpSocket:
port: {{ .Values.mcp.sidecars.playwright.port }}
initialDelaySeconds: 10
periodSeconds: 5
resources:
{{- toYaml .Values.mcp.sidecars.playwright.resources | nindent 12 }}
securityContext:
runAsUser: 1000
runAsGroup: 1000
{{- end }}
volumes:
- name: workspace
emptyDir: {}
- name: shm
emptyDir:
medium: Memory
sizeLimit: {{ .Values.shm.sizeLimit }}
- name: userhome
persistentVolumeClaim:
claimName: {{ include "devcontainer.pvcName" . }}
+17
View File
@@ -0,0 +1,17 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ include "devcontainer.pvcName" . }}
annotations:
helm.sh/resource-policy: keep
labels:
{{- include "devcontainer.labels" . | nindent 4 }}
spec:
accessModes:
- ReadWriteMany
{{- if .Values.storage.className }}
storageClassName: {{ .Values.storage.className }}
{{- end }}
resources:
requests:
storage: {{ .Values.storage.size }}
+97
View File
@@ -0,0 +1,97 @@
{{- $access := .Values.clusterAccess | default "none" }}
{{- $name := include "devcontainer.fullname" . }}
{{- $ns := .Release.Namespace }}
{{- $labels := include "devcontainer.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
@@ -0,0 +1,22 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "devcontainer.fullname" . }}
labels:
{{- include "devcontainer.labels" . | nindent 4 }}
spec:
ports:
{{- if ne (.Values.ide.type | default "vscode") "none" }}
- port: 5800
name: vnc-web
protocol: TCP
targetPort: vnc-web
{{- end }}
{{- if .Values.ssh.enabled }}
- port: 22
name: ssh
protocol: TCP
targetPort: ssh
{{- end }}
selector:
{{- include "devcontainer.labels" . | nindent 4 }}
+63
View File
@@ -0,0 +1,63 @@
# =============================================================================
# QUICKSTART VALUES - Just set these 3 essentials!
# =============================================================================
# Instance name (required)
name: mydev
# GitHub repository to clone (required)
githubRepo: https://github.com/youruser/yourrepo
# Multiple repositories (optional, takes precedence over githubRepo)
# githubRepos:
# - https://github.com/youruser/repo1
# - https://github.com/youruser/repo2
# IDE choice (optional - defaults to vscode)
# Options: vscode | antigravity | none
ide:
type: vscode
# =============================================================================
# COMMON CUSTOMIZATIONS (optional)
# =============================================================================
# Enable SSH access
# ssh:
# enabled: true
# Adjust resources for smaller/larger workloads
# resources:
# requests:
# memory: "1Gi" # Smaller
# cpu: "500m"
# limits:
# memory: "4Gi" # Smaller
# cpu: "2000m"
# Different storage size
# storage:
# size: 16Gi # Smaller
# Disable some MCP sidecars to save resources
# mcp:
# sidecars:
# kubernetes:
# enabled: false
# flux:
# enabled: false
# =============================================================================
# USAGE INSTRUCTIONS
# =============================================================================
# 1. Copy this file: cp values-quickstart.yaml my-values.yaml
# 2. Edit the 'name' and 'githubRepo' fields above
# 3. Deploy: helm install mydev ./chart -f my-values.yaml
# 4. Access: kubectl port-forward deployment/devcontainer-mydev 5800:5800
# 5. Open: http://localhost:5800
# For secrets (GitHub token, passwords):
# kubectl create secret generic devcontainer-mydev-secrets-env \
# --from-literal=GITHUB_TOKEN='ghp_...' \
# --from-literal=VNC_PASSWORD='changeme'
+262
View File
@@ -0,0 +1,262 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://github.com/cpfarhood/devcontainer/chart/values.schema.json",
"title": "Dev Container Helm Chart Values Schema",
"description": "Schema for validating values.yaml in the Dev Container Helm chart",
"type": "object",
"additionalProperties": true,
"properties": {
"name": {
"type": "string",
"description": "Instance name used to generate resource names",
"pattern": "^[a-z0-9][a-z0-9-]*[a-z0-9]$",
"minLength": 1,
"maxLength": 63
},
"image": {
"type": "object",
"properties": {
"repository": {
"type": "string",
"description": "Container image repository"
},
"tag": {
"type": "string",
"description": "Container image tag"
},
"pullPolicy": {
"type": "string",
"enum": ["Always", "IfNotPresent", "Never"],
"description": "Image pull policy"
}
},
"required": ["repository", "tag"]
},
"githubRepo": {
"type": "string",
"description": "GitHub repository URL to clone into /workspace"
},
"githubRepos": {
"type": "array",
"items": { "type": "string" },
"description": "Multiple GitHub repository URLs to clone (takes precedence over githubRepo)"
},
"fileManager": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable the built-in web file manager"
},
"allowedPaths": {
"type": "string",
"description": "Paths accessible by the file manager (AUTO, ALL, or comma-separated list)"
},
"deniedPaths": {
"type": "string",
"description": "Paths to deny access to (takes precedence over allowedPaths)"
}
},
"required": ["enabled"]
},
"ide": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["vscode", "antigravity", "none"],
"description": "IDE to launch in the container"
}
},
"required": ["type"]
},
"ssh": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable SSH server on port 22"
}
},
"required": ["enabled"]
},
"display": {
"type": "object",
"properties": {
"width": {
"type": "string",
"pattern": "^[0-9]+$",
"description": "VNC display width in pixels"
},
"height": {
"type": "string",
"pattern": "^[0-9]+$",
"description": "VNC display height in pixels"
},
"secureConnection": {
"type": "string",
"enum": ["0", "1"],
"description": "Enable secure VNC connection"
}
},
"required": ["width", "height", "secureConnection"]
},
"user": {
"type": "object",
"properties": {
"id": {
"type": "string",
"pattern": "^[0-9]+$",
"description": "User ID (UID)"
},
"groupId": {
"type": "string",
"pattern": "^[0-9]+$",
"description": "Group ID (GID)"
}
},
"required": ["id", "groupId"]
},
"storage": {
"type": "object",
"properties": {
"size": {
"type": "string",
"pattern": "^[0-9]+[KMGT]i$",
"description": "Storage size (e.g., 32Gi)"
},
"className": {
"type": "string",
"description": "Storage class name (must support ReadWriteMany)"
}
},
"required": ["size"]
},
"resources": {
"type": "object",
"properties": {
"requests": {
"$ref": "#/$defs/resourceSpec"
},
"limits": {
"$ref": "#/$defs/resourceSpec"
}
},
"required": ["requests", "limits"]
},
"shm": {
"type": "object",
"properties": {
"sizeLimit": {
"type": "string",
"pattern": "^[0-9]+[KMGT]i$",
"description": "Shared memory size limit"
}
},
"required": ["sizeLimit"]
},
"clusterAccess": {
"type": "string",
"enum": ["none", "readonlyns", "readwritens", "readonly", "readwrite"],
"description": "Kubernetes cluster access level"
},
"mcp": {
"type": "object",
"properties": {
"sidecars": {
"type": "object",
"properties": {
"kubernetes": {
"$ref": "#/$defs/mcpSidecar"
},
"flux": {
"$ref": "#/$defs/mcpSidecar"
},
"homeassistant": {
"$ref": "#/$defs/mcpSidecar"
},
"pgtuner": {
"$ref": "#/$defs/mcpSidecar"
},
"helm": {
"$ref": "#/$defs/mcpSidecar"
},
"playwright": {
"$ref": "#/$defs/mcpSidecar"
}
},
"additionalProperties": false
}
},
"required": ["sidecars"]
},
"envSecretName": {
"type": "string",
"description": "Custom environment secret name"
},
"resourceProfile": {
"type": "string",
"enum": ["auto", "small", "medium", "large", "xlarge"],
"description": "Resource profile preset"
}
},
"required": ["name"],
"$defs": {
"resourceSpec": {
"type": "object",
"properties": {
"memory": {
"type": "string",
"pattern": "^[0-9]+[KMGT]i$",
"description": "Memory resource specification"
},
"cpu": {
"type": "string",
"pattern": "^[0-9]+m?$",
"description": "CPU resource specification"
}
},
"required": ["memory", "cpu"]
},
"mcpSidecar": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable this MCP sidecar"
},
"image": {
"type": "object",
"properties": {
"repository": {
"type": "string"
},
"tag": {
"type": "string"
}
},
"required": ["repository", "tag"]
},
"port": {
"type": "integer",
"minimum": 1,
"maximum": 65535,
"description": "Port for the MCP sidecar"
},
"resources": {
"type": "object",
"properties": {
"requests": {
"$ref": "#/$defs/resourceSpec"
},
"limits": {
"$ref": "#/$defs/resourceSpec"
}
},
"required": ["requests", "limits"]
}
},
"required": ["enabled", "image", "port", "resources"]
}
}
}
+202
View File
@@ -0,0 +1,202 @@
# =============================================================================
# BASIC CONFIGURATION
# =============================================================================
# Instance name — used to generate resource names (devcontainer-{name}, userhome-{name})
name: ""
# Container image configuration
image:
repository: ghcr.io/cpfarhood/devcontainer
tag: latest
pullPolicy: Always
# GitHub repository to clone into /workspace
githubRepo: ""
# Multiple GitHub repositories to clone into /workspace (takes precedence over githubRepo)
# Example:
# githubRepos:
# - https://github.com/user/repo1
# - https://github.com/user/repo2
githubRepos: []
# =============================================================================
# ACCESS & INTERFACE
# =============================================================================
# IDE configuration
ide:
# Options: vscode | antigravity | none
type: vscode
# SSH access configuration
ssh:
enabled: false
# Web file manager — built-in upload/download via the VNC web interface (port 5800)
# Uses the base image's WEB_FILE_MANAGER feature (no extra sidecar needed)
fileManager:
enabled: false
# Paths the file manager can access (default: AUTO = mapped volumes)
# Options: AUTO | ALL | comma-separated list of paths
allowedPaths: "/workspace,/config"
# Paths to deny (takes precedence over allowedPaths)
deniedPaths: ""
# VNC display settings
display:
width: "1920"
height: "1080"
secureConnection: "0" # Set to "1" when TLS is not terminated upstream
# User configuration
user:
id: "1000"
groupId: "1000"
# =============================================================================
# INFRASTRUCTURE & RESOURCES
# =============================================================================
# Storage configuration
storage:
size: 32Gi
className: "" # Empty string uses the cluster's default StorageClass (must support ReadWriteMany)
# Resource allocation
resources:
requests:
memory: "2Gi"
cpu: "1000m"
limits:
memory: "8Gi"
cpu: "4000m"
# Shared memory for Electron apps (Chrome, Antigravity)
shm:
sizeLimit: 2Gi
# Kubernetes cluster access via RBAC
# Options: none | readonlyns | readwritens | readonly | readwrite
clusterAccess: none
# =============================================================================
# INTEGRATIONS
# =============================================================================
# MCP (Model Context Protocol) server sidecars
mcp:
sidecars:
# Kubernetes API access
kubernetes:
enabled: true
image:
repository: quay.io/containers/kubernetes_mcp_server
tag: v0.0.57
port: 8080
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "500m"
# Flux GitOps operations
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"
# Helm chart browsing and management
helm:
enabled: false
image:
repository: ghcr.io/zekker6/mcp-helm
tag: v1.3.1
port: 8012
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "500m"
# Home Assistant smart home control
homeassistant:
enabled: false # Requires HOMEASSISTANT_URL and HOMEASSISTANT_TOKEN
image:
repository: ghcr.io/homeassistant-ai/ha-mcp
tag: "6.7.1"
port: 8087
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "500m"
# PostgreSQL performance tuning
pgtuner:
enabled: false # Requires DATABASE_URI in secrets
image:
repository: dog830228/pgtuner_mcp
tag: latest
port: 8085
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "500m"
# Browser automation and web testing
playwright:
enabled: true
image:
repository: mcr.microsoft.com/playwright/mcp
tag: v0.0.68
port: 8086
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "1000m"
# =============================================================================
# SMART DEFAULTS & AUTO-DETECTION
# =============================================================================
# Environment auto-detection based on name patterns
# Automatically adjusts defaults for dev/test/prod/team environments
autoDetect:
environment: true # Auto-detect dev/prod/team from name
storageClass: true # Auto-detect ReadWriteMany storage class
resources: true # Auto-size resources based on enabled features
# Resource profiles (auto-selected based on environment and features)
# Override specific values above to customize
resourceProfile: auto # auto | small | medium | large | xlarge
# =============================================================================
# ADVANCED CONFIGURATION
# =============================================================================
# Custom env secret name (defaults to: devcontainer-{name}-secrets-env)
envSecretName: ""
-13
View File
@@ -1,13 +0,0 @@
---
apiVersion: v1
kind: ConfigMap
metadata:
name: antigravity-config
data:
# GitHub repository to clone on startup
# Example: "https://github.com/username/repository"
github-repo: ""
# Happy Coder configuration (optional)
# happy-server-url: "https://api.cluster-fluster.com"
# happy-webapp-url: "https://app.happy.engineering"
-20
View File
@@ -1,20 +0,0 @@
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: antigravity
spec:
parentRefs:
- name: gateway # Replace with your Gateway name
namespace: gateway-system # Replace with your Gateway namespace
hostnames:
- "antigravity.example.com" # Replace with your domain
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: antigravity
port: 5800
weight: 1
-25
View File
@@ -1,25 +0,0 @@
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: default
resources:
- configmap.yaml
- statefulset.yaml
- httproute.yaml
# Uncomment to create secrets from files
# secretGenerator:
# - name: antigravity-secrets
# literals:
# - github-token=ghp_your_token
# - vnc-password=your_password
commonLabels:
app: antigravity
environment: production
commonAnnotations:
managed-by: kustomize
description: "Antigravity Dev Container with Happy Coder"
-15
View File
@@ -1,15 +0,0 @@
---
# Example secrets - DO NOT commit actual secrets!
# Use SealedSecrets or another secret management solution
apiVersion: v1
kind: Secret
metadata:
name: antigravity-secrets
type: Opaque
stringData:
# GitHub Personal Access Token (for private repos)
github-token: "ghp_your_token_here"
# VNC Password (optional, for secure VNC access)
vnc-password: "your_vnc_password"
-127
View File
@@ -1,127 +0,0 @@
---
apiVersion: v1
kind: Service
metadata:
name: antigravity
labels:
app: antigravity
spec:
ports:
- port: 5800
name: vnc-web
protocol: TCP
clusterIP: None
selector:
app: antigravity
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: antigravity
spec:
serviceName: "antigravity"
replicas: 1
selector:
matchLabels:
app: antigravity
template:
metadata:
labels:
app: antigravity
spec:
securityContext:
fsGroup: 1000
fsGroupChangePolicy: "OnRootMismatch"
containers:
- name: antigravity
image: ghcr.io/cpfarhood/antigravity:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 5800
name: vnc-web
protocol: TCP
volumeMounts:
- name: userhome
mountPath: /home
- name: workspace
mountPath: /workspace
env:
# User/Group IDs for the claude user
- name: USER_ID
value: "1000"
- name: GROUP_ID
value: "1000"
# VNC display settings
- name: DISPLAY_WIDTH
value: "1920"
- name: DISPLAY_HEIGHT
value: "1080"
- name: SECURE_CONNECTION
value: "1"
- name: VNC_PASSWORD
valueFrom:
secretKeyRef:
name: antigravity
key: vnc-password
optional: true
# GitHub configuration
- name: GITHUB_REPO
valueFrom:
configMapKeyRef:
name: antigravity
key: github-repo
optional: true
- name: GITHUB_TOKEN
valueFrom:
secretKeyRef:
name: antigravity
key: github-token
optional: true
# Happy Coder configuration (optional)
- name: HAPPY_SERVER_URL
valueFrom:
configMapKeyRef:
name: antigravity
key: happy-server-url
optional: true
- name: HAPPY_WEBAPP_URL
valueFrom:
configMapKeyRef:
name: antigravity
key: happy-webapp-url
optional: true
- name: HAPPY_HOME_DIR
value: "/home/claude/.happy"
- name: HAPPY_EXPERIMENTAL
value: "true"
resources:
requests:
memory: "2Gi"
cpu: "1000m"
limits:
memory: "8Gi"
cpu: "4000m"
livenessProbe:
httpGet:
path: /
port: 5800
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: 5800
initialDelaySeconds: 10
periodSeconds: 5
volumes:
- name: workspace
emptyDir: {}
volumeClaimTemplates:
- metadata:
name: userhome
spec:
accessModes: [ "ReadWriteMany" ]
storageClassName: "ceph-filesystem"
resources:
requests:
storage: 10Gi
+30
View File
@@ -0,0 +1,30 @@
# 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** (6.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
@@ -0,0 +1,72 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended",
":gitSignOff"
],
"semanticCommits": "enabled",
"dependencyDashboard": true,
"suppressNotifications": [
"prEditedNotification"
],
"rebaseWhen": "conflicted",
"commitMessagePrefix": "chore(deps):",
"commitMessageAction": "update",
"commitMessageTopic": "{{depName}}",
"prConcurrentLimit": 5,
"prHourlyLimit": 2,
"schedule": [
"before 6am on monday"
],
"packageRules": [
{
"description": "GitHub Actions",
"matchManagers": [
"github-actions"
],
"groupName": "github-actions",
"additionalBranchPrefix": "github-actions-",
"semanticCommitScope": "github-actions",
"pinDigests": true
},
{
"description": "Docker base image",
"matchManagers": [
"dockerfile"
],
"groupName": "docker",
"additionalBranchPrefix": "docker-",
"semanticCommitScope": "docker"
},
{
"description": "Automerge patch updates",
"matchUpdateTypes": [
"patch"
],
"automerge": true,
"automergeType": "pr",
"platformAutomerge": true
},
{
"description": "Automerge minor updates for stable packages",
"matchUpdateTypes": [
"minor"
],
"matchCurrentVersion": "!/^0/",
"automerge": true,
"automergeType": "pr",
"platformAutomerge": true
},
{
"description": "Separate major updates - require manual review",
"matchUpdateTypes": [
"major"
],
"automerge": false,
"additionalBranchPrefix": "major-"
}
],
"ignorePaths": [
"**/node_modules/**"
]
}
+45
View File
@@ -0,0 +1,45 @@
#!/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 $!)"
+10
View File
@@ -0,0 +1,10 @@
#!/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.
if id app >/dev/null 2>&1; then
usermod -s /bin/bash app
usermod -d /config/userdata app
else
echo "WARNING: 'app' user not found, skipping usermod" >&2
fi
+138 -57
View File
@@ -1,76 +1,157 @@
#!/bin/bash
# Initialize repository and start Happy Coder
# Initialize repository
set -e
echo "=== Repository Initialization ==="
# Check if GITHUB_REPO is set
if [ -z "$GITHUB_REPO" ]; then
echo "GITHUB_REPO not set, skipping repository clone"
WORKSPACE_DIR="/workspace/default"
mkdir -p "$WORKSPACE_DIR"
# 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"
mkdir -p "$(dirname "$CREDENTIALS_FILE")"
# 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
# Parse repo name from URL
REPO_NAME=$(basename "$GITHUB_REPO" .git)
WORKSPACE_DIR="/workspace/$REPO_NAME"
# 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
echo "Repository: $GITHUB_REPO"
echo "Target directory: $WORKSPACE_DIR"
# Create an empty credentials file with proper permissions
CREDENTIALS_FILE="/config/userdata/.git-credentials"
mkdir -p "$(dirname "$CREDENTIALS_FILE")"
touch "$CREDENTIALS_FILE"
chmod 600 "$CREDENTIALS_FILE"
# Check if repo already exists
if [ -d "$WORKSPACE_DIR/.git" ]; then
echo "Repository already exists, pulling latest changes..."
cd "$WORKSPACE_DIR"
# Configure git to use token if provided
if [ -n "$GITHUB_TOKEN" ]; then
git config credential.helper store
echo "https://oauth2:${GITHUB_TOKEN}@github.com" > /home/claude/.git-credentials
chmod 600 /home/claude/.git-credentials
fi
git pull || echo "Pull failed, continuing anyway..."
else
echo "Cloning repository..."
mkdir -p "$(dirname "$WORKSPACE_DIR")"
# Clone with token if provided
if [ -n "$GITHUB_TOKEN" ]; then
# Replace https://github.com/ with https://oauth2:token@github.com/
CLONE_URL=$(echo "$GITHUB_REPO" | sed "s|https://github.com/|https://oauth2:${GITHUB_TOKEN}@github.com/|")
git clone "$CLONE_URL" "$WORKSPACE_DIR"
# Configure credentials for future use
git config --global credential.helper store
echo "https://oauth2:${GITHUB_TOKEN}@github.com" > /home/claude/.git-credentials
chmod 600 /home/claude/.git-credentials
else
git clone "$GITHUB_REPO" "$WORKSPACE_DIR"
fi
# Create symlink if needed
if [ ! -f "$HOME/.git-credentials" ] && [ "$HOME" != "/config/userdata" ]; then
ln -sf "$CREDENTIALS_FILE" "$HOME/.git-credentials"
fi
fi
# Set ownership
chown -R claude:claude "$WORKSPACE_DIR"
chown -R claude:claude /home/claude
# Build list of repositories to clone
REPOS=()
if [ -n "$GITHUB_REPOS" ]; then
# GITHUB_REPOS is a comma-separated list (takes precedence over GITHUB_REPO)
IFS=',' read -ra RAW_REPOS <<< "$GITHUB_REPOS"
for repo in "${RAW_REPOS[@]}"; do
repo="$(echo "$repo" | xargs)" # trim whitespace
[ -n "$repo" ] && REPOS+=("$repo")
done
elif [ -n "$GITHUB_REPO" ]; then
REPOS+=("$GITHUB_REPO")
fi
# Start Happy Coder in background as claude user
echo "Starting Happy Coder..."
cd "$WORKSPACE_DIR"
if [ ${#REPOS[@]} -eq 0 ]; then
echo "No repositories configured, skipping clone"
WORKSPACE_DIR="/workspace/default"
mkdir -p "$WORKSPACE_DIR"
else
CLONED_DIRS=()
for REPO_URL in "${REPOS[@]}"; do
REPO_NAME=$(basename "$REPO_URL" .git)
REPO_DIR="/workspace/$REPO_NAME"
# Create Happy Coder log file
HAPPY_LOG="/tmp/happy-coder.log"
touch "$HAPPY_LOG"
chown claude:claude "$HAPPY_LOG"
echo "Repository: $REPO_URL"
echo "Target directory: $REPO_DIR"
# Start Happy Coder as claude user
sudo -u claude bash -c "cd '$WORKSPACE_DIR' && happy-coder > '$HAPPY_LOG' 2>&1 &"
if [ -d "$REPO_DIR/.git" ]; then
echo "Repository already exists, pulling latest changes..."
cd "$REPO_DIR"
git pull || echo "Pull failed, continuing anyway..."
else
echo "Cloning repository..."
mkdir -p "$(dirname "$REPO_DIR")"
# Save PID for monitoring
echo $! > /tmp/happy-coder.pid
if [ -n "$GITHUB_TOKEN" ]; then
CLONE_URL=$(echo "$REPO_URL" | sed "s|https://github.com/|https://oauth2:${GITHUB_TOKEN}@github.com/|")
git clone "$CLONE_URL" "$REPO_DIR"
else
git clone "$REPO_URL" "$REPO_DIR"
fi
fi
echo "Happy Coder started (PID: $(cat /tmp/happy-coder.pid))"
echo "Logs available at: $HAPPY_LOG"
CLONED_DIRS+=("$REPO_DIR")
done
if [ ${#CLONED_DIRS[@]} -eq 1 ]; then
# Single repo — open directory directly (same as legacy behavior)
WORKSPACE_DIR="${CLONED_DIRS[0]}"
else
# Multiple repos — generate a multi-root workspace file
WS_FILE="/workspace/workspace.code-workspace"
printf '{\n "folders": [\n' > "$WS_FILE"
for i in "${!CLONED_DIRS[@]}"; do
printf ' {"path": "%s"}' "${CLONED_DIRS[$i]}" >> "$WS_FILE"
if [ "$i" -lt $(( ${#CLONED_DIRS[@]} - 1 )) ]; then
printf ',\n' >> "$WS_FILE"
else
printf '\n' >> "$WS_FILE"
fi
done
printf ' ],\n "settings": {}\n}\n' >> "$WS_FILE"
WORKSPACE_DIR="$WS_FILE"
echo "Generated multi-root workspace: $WS_FILE"
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}"
for dir in "${CLONED_DIRS[@]}"; do
chown -R "$RUN_UID:$RUN_GID" "$dir"
done
if [ -n "$WS_FILE" ] && [ -f "$WS_FILE" ]; then
chown "$RUN_UID:$RUN_GID" "$WS_FILE"
fi
# Ensure default workspace dir ownership if no repos were cloned
if [ ${#REPOS[@]} -eq 0 ]; then
chown -R "$RUN_UID:$RUN_GID" "$WORKSPACE_DIR"
fi
# Ensure home directory exists on the PVC (may be absent on a fresh volume)
mkdir -p "$HOME"
chown "$RUN_UID:$RUN_GID" "$HOME"
# Seed Claude Code settings if missing (disable auto-updater in Docker)
if [ ! -f "$HOME/.claude/settings.json" ]; then
mkdir -p "$HOME/.claude"
echo '{"env":{"DISABLE_AUTOUPDATER":"1"}}' > "$HOME/.claude/settings.json"
chown -R "$RUN_UID:$RUN_GID" "$HOME/.claude"
fi
# Export workspace directory for startapp.sh
echo "$WORKSPACE_DIR" > /tmp/workspace-dir
+28 -6
View File
@@ -2,9 +2,9 @@
# Start application script for baseimage-gui
set -e
echo "=== Starting Antigravity Dev Container ==="
echo "=== Starting Dev Container ==="
# Initialize repository and Happy Coder
# Initialize repository
/usr/local/bin/init-repo
# Get workspace directory
@@ -14,8 +14,30 @@ else
WORKSPACE_DIR="/workspace/default"
fi
echo "Opening Antigravity in: $WORKSPACE_DIR"
IDE="${IDE:-vscode}"
echo "IDE mode: $IDE"
echo "Workspace: $WORKSPACE_DIR"
# Start Antigravity (VSCode) in the workspace directory as claude user
# The baseimage-gui will handle the GUI display
exec sudo -u claude code --new-window --wait "$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
;;
*)
if [ "$IDE" != "vscode" ]; then
echo "WARNING: Unknown IDE value '$IDE', defaulting to VSCode"
fi
echo "Opening VSCode in: $WORKSPACE_DIR"
exec code --new-window --wait "$WORKSPACE_DIR"
;;
esac
+46
View File
@@ -0,0 +1,46 @@
#!/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 ==="