Compare commits

..

125 Commits

Author SHA1 Message Date
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
Chris Farhood dd12262c83 Fix Happy Coder npm package name
Changed from incorrect `@happy-sdk/happy-coder` to correct `happy-coder`
package name. The scoped package doesn't exist on npm, causing Docker
build failures.

This fixes the v1.0.0 release Docker build.

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-15 08:45:32 -05:00
Chris Farhood 8f0fee838e Fix invalid Docker tag format in GitHub Actions workflow
Changed SHA tag prefix from `{{branch}}-` to `sha-` to fix build
failures when creating releases from tags. The `{{branch}}` variable
is empty when building from tags (e.g., v1.0.0), which resulted in
invalid Docker tags like `:-7bdde95` instead of `sha-7bdde95`.

This fixes the v1.0.0 release build failure.

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-15 08:42:50 -05:00
Chris Farhood 7bdde95cb4 docs: Add release process and changelog
Add comprehensive release management documentation and changelog.

New files:
- .github/workflows/tag-release.md: Complete release process guide
  - Semantic versioning guidelines
  - 3 methods for creating releases (CLI, git, web UI)
  - Version bump guidelines (major/minor/patch)
  - Pre-release support (alpha/beta/rc)
  - Release checklist
  - Docker image tagging strategy
  - Example workflows

- CHANGELOG.md: Changelog following Keep a Changelog format
  - Unreleased section with all current features
  - Template for future releases
  - Adheres to Semantic Versioning

Release Process:
1. Create annotated git tag (e.g., v1.0.0)
2. Push tag to GitHub
3. GitHub Actions automatically:
   - Builds Docker image
   - Pushes with multiple tags (v1.0.0, 1.0, 1, latest)
   - Creates GitHub release with notes

Ready for v1.0.0 release!

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-15 08:39:17 -05:00
Chris Farhood f79be89e4e docs: Add comprehensive deployment and variables reference
Add detailed documentation to enable autonomous deployment by other AI agents.

New files:
- DEPLOYMENT.md: Complete step-by-step deployment guide
  - Prerequisites checklist
  - Required vs optional variables clearly marked
  - All configuration variables documented with examples
  - Troubleshooting section
  - Quick deploy example with all values
  - Configuration summary table

- VARIABLES.md: Complete variables reference
  - All variables listed with file, line, type, description
  - Required variables clearly marked
  - Optional variables with defaults
  - Variables grouped by use case
  - Quick copy templates
  - Environment variables reference

Updated README.md:
- Add links to new documentation files
- Clear pointer to DEPLOYMENT.md for deployment

These docs enable another Claude Code instance to:
1. Identify all required configuration variables
2. Know where to find/set each variable
3. Understand what values are valid
4. Deploy successfully without human intervention

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-15 08:36:45 -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
Chris Farhood 46b2077f99 feat: Add GitHub Actions for automated Docker image builds
Add comprehensive CI/CD pipeline for building and publishing Docker images
to GitHub Container Registry (ghcr.io).

Components:
- build-and-push.yaml: Main workflow for building and pushing images
  - Triggers on push to main, tags, PRs, and manual dispatch
  - Multi-tag strategy (latest, semver, sha, branch)
  - Docker buildx with layer caching
  - Automatic GHCR authentication
  - Platform: linux/amd64

- release.yaml: Automated GitHub releases on version tags
  - Auto-generates release notes from commits
  - Creates GitHub release with Docker pull command

- dependabot.yml: Automated dependency updates
  - Weekly updates for GitHub Actions
  - Weekly updates for Docker base images

- PULL_REQUEST_TEMPLATE.md: PR template with testing checklist

Updated README:
- Add build status badge
- Update image pull instructions
- Reference automated builds

Image tagging strategy:
- main branch → :latest, :main, :main-{sha}
- v1.2.3 tag → :v1.2.3, :1.2, :1, :latest
- PR → :pr-{number}

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-15 08:34:06 -05:00
Chris Farhood 667845b9a2 fix: Remove fake HAPPY_CODER_API_KEY, use actual Happy Coder env vars
Happy Coder does not require an API key. It uses these environment variables:
- HAPPY_SERVER_URL (optional custom server)
- HAPPY_WEBAPP_URL (optional custom webapp)
- HAPPY_HOME_DIR (data directory)
- HAPPY_EXPERIMENTAL (enable experimental features)

Changes:
- Remove HAPPY_CODER_API_KEY from all files
- Add proper Happy Coder environment variables to StatefulSet
- Add Happy Coder config options to ConfigMap
- Update README with correct Happy Coder configuration
- Update Makefile and docker-compose examples
- Update secrets-example.yaml

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-15 08:32:57 -05:00
Chris Farhood 2c80117602 fix: Replace Ingress with HTTPRoute (Gateway API)
Switch from Ingress to HTTPRoute for Gateway API compatibility.

Changes:
- Remove k8s/ingress.yaml
- Add k8s/httproute.yaml with Gateway API configuration
- Update kustomization.yaml to reference httproute.yaml
- Update README.md references from Ingress to HTTPRoute

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-15 08:23:56 -05:00
Chris Farhood c7a7e8bd12 Initial commit: Antigravity dev container with Happy Coder
Add containerized GUI development environment featuring:
- Antigravity IDE (VSCode) accessible via web browser
- Happy Coder AI assistant integration
- Automatic GitHub repository cloning on startup
- Persistent user home directory (ReadWriteMany PVC)
- Secure non-root execution as user claude (UID 1000)

Components:
- Dockerfile based on jlesage/baseimage-gui
- Startup scripts for repo initialization and app launch
- Kubernetes manifests (StatefulSet, ConfigMap, Secrets)
- Makefile for build and deployment automation
- Comprehensive documentation

Features:
- Web-based VNC interface (port 5800)
- GitHub token authentication for private repos
- Happy Coder runs as background service in workspace
- CephFS storage for persistent home directory
- Configurable display resolution and security

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

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-15 08:23:08 -05:00
51 changed files with 4173 additions and 297 deletions
+7
View File
@@ -0,0 +1,7 @@
{
"enabledMcpjsonServers": [
"kubernetes",
"flux",
"playwright"
]
}
+24
View File
@@ -0,0 +1,24 @@
.git
.gitignore
.dockerignore
*.md
README.md
LICENSE
Makefile
docker-compose.yml
# Kubernetes files
k8s/
# Local development
home/
workspace/
*.log
# IDE
.vscode/
.idea/
# OS
.DS_Store
Thumbs.db
+36
View File
@@ -0,0 +1,36 @@
## Description
<!-- Describe your changes in detail -->
## Type of Change
<!-- Mark with an `x` all that apply -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation update
- [ ] CI/CD update
## Testing
<!-- Describe the tests you ran to verify your changes -->
- [ ] Built Docker image locally
- [ ] Tested container startup
- [ ] Tested repository cloning
- [ ] Tested Happy Coder integration
- [ ] Tested VNC web interface
## Checklist
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have tested that the Docker image builds successfully
## Screenshots (if applicable)
<!-- Add screenshots to help explain your changes -->
+94
View File
@@ -0,0 +1,94 @@
# 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 GHCR
- ✅ Creates GitHub Release with changelog
- ✅ No more `[skip ci]` blocking builds!
### 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:
- Pull requests (builds but doesn't push)
- Tags starting with `v*` (builds and pushes)
- 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
- **No more `[skip ci]`** blocking builds
- **Clear separation** of concerns
+141
View File
@@ -0,0 +1,141 @@
name: Build and Push Docker Image
on:
push:
branches:
- main
tags:
- 'v*'
pull_request:
branches:
- main
workflow_dispatch:
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha,prefix=sha-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64
release:
if: startsWith(github.ref, 'refs/tags/v')
needs: build-and-push
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Set up Helm
uses: azure/setup-helm@v4
- name: Extract version from tag
id: version
run: |
TAG=${GITHUB_REF#refs/tags/}
VERSION=${TAG#v}
echo "tag=${TAG}" >> $GITHUB_OUTPUT
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "🚀 Creating release for ${TAG}"
- name: Package and Push Helm Chart
run: |
helm registry login ghcr.io \
--username ${{ github.actor }} \
--password ${{ secrets.GITHUB_TOKEN }}
helm package chart/
helm push devcontainer-${{ steps.version.outputs.version }}.tgz oci://ghcr.io/cpfarhood/charts
- name: Generate Release Notes
id: notes
run: |
# Get commits since last tag
PREV_TAG=$(git describe --tags --abbrev=0 ${{ steps.version.outputs.tag }}^ 2>/dev/null || echo "")
if [ -z "$PREV_TAG" ]; then
COMMITS=$(git log --pretty=format:"- %s (%h)" ${{ steps.version.outputs.tag }})
else
COMMITS=$(git log --pretty=format:"- %s (%h)" ${PREV_TAG}..${{ steps.version.outputs.tag }})
fi
cat << EOF > release-notes.md
## 🚀 Release ${{ steps.version.outputs.version }}
### Changes
${COMMITS}
### Docker Image
\`\`\`bash
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.tag }}
\`\`\`
### Helm Chart
\`\`\`bash
helm install devcontainer oci://ghcr.io/cpfarhood/charts/devcontainer --version ${{ steps.version.outputs.version }}
\`\`\`
EOF
echo "notes<<EOF" >> $GITHUB_OUTPUT
cat release-notes.md >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Create GitHub Release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.version.outputs.tag }}
release_name: Release ${{ steps.version.outputs.tag }}
body: ${{ steps.notes.outputs.notes }}
draft: false
prerelease: false
+54
View File
@@ -0,0 +1,54 @@
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
permissions:
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
+159
View File
@@ -0,0 +1,159 @@
name: Unified Release
on:
workflow_dispatch:
inputs:
version:
description: 'Version to release (e.g., 0.1.25)'
required: true
type: string
release_type:
description: 'Release type'
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: |
if [ "${{ github.event.inputs.version }}" != "" ]; then
VERSION="${{ github.event.inputs.version }}"
else
# Auto-determine next version based on release type
CURRENT=$(grep '^version:' chart/Chart.yaml | awk '{print $2}')
MAJOR=$(echo $CURRENT | cut -d. -f1)
MINOR=$(echo $CURRENT | cut -d. -f2)
PATCH=$(echo $CURRENT | cut -d. -f3)
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 commit -m "chore: release version ${{ steps.version.outputs.version }}"
- 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
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
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64
- name: Package Helm Chart
run: |
helm registry login ghcr.io \
--username ${{ github.actor }} \
--password ${{ secrets.GITHUB_TOKEN }}
helm package chart/
helm push devcontainer-${{ steps.version.outputs.version }}.tgz oci://ghcr.io/cpfarhood/charts
- name: Generate Release Notes
id: notes
run: |
# Get commits since last tag
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 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 << EOF > release-notes.md
## 🚀 Release ${{ steps.version.outputs.version }}
### Changes
${COMMITS}
### Docker Image
\`\`\`bash
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.tag }}
\`\`\`
### Helm Chart
\`\`\`bash
helm install devcontainer oci://ghcr.io/cpfarhood/charts/devcontainer --version ${{ steps.version.outputs.version }}
\`\`\`
EOF
echo "notes<<EOF" >> $GITHUB_OUTPUT
cat release-notes.md >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Create GitHub Release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.version.outputs.tag }}
release_name: Release ${{ steps.version.outputs.tag }}
body: ${{ steps.notes.outputs.notes }}
draft: false
prerelease: false
+24
View File
@@ -0,0 +1,24 @@
# Secrets
*.env
.env.local
secrets.yaml
k8s/sealedsecrets.yaml
# Local volumes
home/
workspace/
# IDE
.vscode/
.idea/
# OS
.DS_Store
Thumbs.db
# Logs
*.log
# Build artifacts
*.tar
*.tar.gz
+28
View File
@@ -0,0 +1,28 @@
{
"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"
},
"pgtuner": {
"type": "sse",
"url": "http://localhost:8085/sse"
}
}
}
+98
View File
@@ -0,0 +1,98 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- Initial project setup
- Antigravity IDE (VSCode) with web-based VNC access
- Happy Coder AI assistant integration
- Automatic GitHub repository cloning on startup
- Persistent home directory with ReadWriteMany PVC support
- Secure non-root execution (claude user, UID 1000, GID 1000)
- Support for private repositories via GitHub token
- HTTPRoute (Gateway API) support
- VNC password protection
- Multi-platform Docker image builds
- GitHub Actions CI/CD pipeline
- Automated releases on version tags
- Comprehensive deployment documentation (DEPLOYMENT.md)
- Complete variables reference (VARIABLES.md)
### Container Features
- Base: jlesage/baseimage-gui:ubuntu-22.04-v4
- Antigravity IDE (VSCode)
- Happy Coder npm package
- Chrome browser
- Node.js (LTS)
- Python 3
- Git
### Kubernetes Resources
- StatefulSet with volumeClaimTemplates
- ReadWriteMany PVC for /home directory
- ConfigMap for configuration
- Sealed Secrets support
- HTTPRoute for external access
- Service (headless)
### Configuration Options
- GitHub repository URL (required)
- GitHub token (optional, for private repos)
- VNC password (optional)
- Happy Coder server URL (optional)
- Happy Coder webapp URL (optional)
- Display resolution (configurable)
- Resource limits (configurable)
- Storage size (configurable)
### Documentation
- README.md with quick start guide
- DEPLOYMENT.md with step-by-step instructions
- VARIABLES.md with complete variable reference
- Release process documentation
- Pull request template
- Dependabot configuration
## Version History
No releases yet. See [Unreleased] section above for planned v1.0.0 features.
---
## Release Template
Use this template for future releases:
```markdown
## [1.0.0] - YYYY-MM-DD
### Added
- New features
- New configuration options
### Changed
- Changes to existing features
- Updated dependencies
### Deprecated
- Features that will be removed in future versions
### Removed
- Removed features
- Breaking changes
### Fixed
- Bug fixes
- Security patches
### Security
- Security improvements
- Vulnerability fixes
```
[Unreleased]: https://github.com/cpfarhood/devcontainer/compare/v1.0.0...HEAD
+189
View File
@@ -0,0 +1,189 @@
# 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, Happy Coder, OpenCode, and Crush AI coding agents (terminal-based)
- 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_REPO (if set)
→ Launch VSCode as user `user` in /workspace
```
### Key Files
| File | Purpose |
|------|---------|
| `Dockerfile` | Image definition — installs Chrome, Node.js, VSCode, Claude Code, Happy Coder, OpenCode, Crush; creates non-root user (UID 1000) |
| `scripts/init-repo.sh` | Configures git credentials, clones GitHub repo |
| `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, 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 |
| `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
- 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
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
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:**
- `GITHUB_REPO` — URL of repository to clone into `/workspace`
**Optional:**
- `GITHUB_TOKEN` — PAT for private repo access (automatically configures git credentials)
- `GIT_USER_NAME` — Git user name for commits (default: "DevContainer User")
- `GIT_USER_EMAIL` — Git user email for commits (default: "devcontainer@example.com")
- `GITLAB_HOST` — GitLab hostname if using GitLab with same token
- `VNC_PASSWORD` — VNC web interface password
- `DISPLAY_WIDTH` / `DISPLAY_HEIGHT` — VNC resolution
- `USER_ID` / `GROUP_ID` — Override UID/GID (default 1000)
- `HAPPY_SERVER_URL` / `HAPPY_WEBAPP_URL` — Custom Happy Coder endpoints
- `HAPPY_HOME_DIR` / `HAPPY_EXPERIMENTAL`
### CI/CD
- **`build-and-push.yaml`** — Builds and pushes to GHCR on every push to `main`, version tags (`v*`), and PRs. For version tags, also creates GitHub Release with Helm chart after Docker build completes. Tags: `latest` (main), semver, branch name, commit SHA.
- **`dependabot.yml`** — Weekly updates for GitHub Actions and Docker base image.
Image registry: `ghcr.io/cpfarhood/devcontainer`
## Kubernetes Notes
- Deployed via Helm chart (`chart/`), published as OCI artifact to GHCR, reconciled by Flux
- Storage class is `ceph-filesystem` by default — change via `storage.className` in values
- Resource limits: 14 CPU, 28Gi memory
- Health checks (liveness/readiness probes) on port 5800
- Secrets: optional env Secret (`devcontainer-{name}-secrets-env`) for `GITHUB_TOKEN`, `VNC_PASSWORD`, etc.
- RBAC: controlled by `clusterAccess` value (`none`, `readonlyns`, `readwritens`, `readonly`, `readwrite`)
+449
View File
@@ -0,0 +1,449 @@
# Deployment Guide
This guide provides step-by-step instructions for deploying the Antigravity Dev Container using Helm.
## Prerequisites
- Kubernetes cluster (1.19+)
- `kubectl` configured to access your cluster
- `helm` CLI installed (3.0+)
- ReadWriteMany storage class available (e.g., `ceph-filesystem`, `nfs-client`, `efs-sc`)
- GitHub Container Registry access (images are public)
## Quick Start
### 1. Clone the Repository
```bash
git clone https://github.com/cpfarhood/devcontainer.git
cd devcontainer
```
### 2. Create Secret (Optional)
For private repos or VNC password:
```bash
kubectl create secret generic devcontainer-mydev-secrets-env \
--from-literal=GITHUB_TOKEN='ghp_...' \
--from-literal=VNC_PASSWORD='changeme' \
--from-literal=ANTHROPIC_API_KEY='sk-ant-...'
```
### 3. Deploy with Helm
```bash
# Basic deployment
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo
# With custom storage class
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set storage.className=nfs-client
# With cluster access for kubectl
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set clusterAccess=readwritens
```
### 4. Access the Container
```bash
# Port forward
kubectl port-forward deployment/devcontainer-mydev 5800:5800
open http://localhost:5800
```
## Deployment Options
### Using Values File
Create a custom `values.yaml`:
```yaml
name: mydev
githubRepo: https://github.com/youruser/yourrepo
ide: vscode
ssh: false
# Storage
storage:
size: 32Gi
className: ceph-filesystem
# Resources
resources:
requests:
memory: "4Gi"
cpu: "2000m"
limits:
memory: "16Gi"
cpu: "8000m"
# Kubernetes access
clusterAccess: readwritens
# MCP sidecars
mcpSidecars:
kubernetes:
enabled: true
flux:
enabled: false
```
Deploy:
```bash
helm install mydev ./chart -f values.yaml
```
### SSH Access Setup
Enable SSH and add your public key:
```bash
# Create secret with SSH key
kubectl create secret generic devcontainer-mydev-secrets-env \
--from-literal=SSH_AUTHORIZED_KEYS='ssh-ed25519 AAAA...'
# Deploy with SSH enabled
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set ssh=true
# Connect via SSH
kubectl port-forward deployment/devcontainer-mydev 2222:22
ssh -p 2222 user@localhost
```
### MCP Sidecar Configuration
Control MCP servers for AI-assisted operations.
**Important:** Kubernetes and Flux MCP sidecars are only deployed when:
1. They are enabled in values (`mcpSidecars.<name>.enabled: true`)
2. AND `clusterAccess` is not `none` (they need RBAC permissions to function)
```bash
# Disable all MCP sidecars
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set mcpSidecars.kubernetes.enabled=false \
--set mcpSidecars.flux.enabled=false \
--set mcpSidecars.homeassistant.enabled=false
# Enable only Kubernetes MCP
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set mcpSidecars.kubernetes.enabled=true \
--set mcpSidecars.flux.enabled=false
# Enable Home Assistant MCP (requires credentials)
kubectl create secret generic devcontainer-mydev-secrets-env \
--from-literal=homeassistant-url='http://homeassistant.local:8123' \
--from-literal=homeassistant-token='your_long_lived_token'
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set mcpSidecars.homeassistant.enabled=true
```
### Cluster Access Levels
Configure Kubernetes RBAC permissions:
| Value | Scope | Permissions | Use Case |
|-------|-------|-------------|----------|
| `none` | No access | None | Default, isolated development |
| `readonlyns` | Namespace | Read-only | View resources in namespace |
| `readwritens` | Namespace | Full access | Deploy apps in namespace |
| `readonly` | Cluster-wide | Read-only | View all cluster resources |
| `readwrite` | Cluster-wide | Full access | Cluster administration |
```bash
# Example: Full access within namespace
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set clusterAccess=readwritens
```
## Ingress Configuration
### Using Gateway API HTTPRoute
Create an HTTPRoute for external access:
```yaml
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: devcontainer-mydev
spec:
parentRefs:
- name: your-gateway
namespace: your-gateway-namespace
hostnames:
- devcontainer.example.com
rules:
- backendRefs:
- name: devcontainer-mydev
port: 5800
```
### Using Traditional Ingress
Create an Ingress resource:
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: devcontainer-mydev
spec:
rules:
- host: devcontainer.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: devcontainer-mydev
port:
number: 5800
```
## Advanced Configurations
### Custom Happy Coder Endpoints
For self-hosted Happy instances:
```bash
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set happyServerUrl=https://your-happy-server.com \
--set happyWebappUrl=https://your-happy-webapp.com
```
### Custom Display Resolution
```bash
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set display.width=2560 \
--set display.height=1440
```
### Different IDE Options
```bash
# Use Google Antigravity
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set ide=antigravity
# SSH-only mode (no GUI)
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set ide=none \
--set ssh=true
```
## Helm Operations
### List Deployments
```bash
helm list
```
### Upgrade Deployment
```bash
# Change values
helm upgrade mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/newrepo
# Upgrade with new chart version
git pull
helm upgrade mydev ./chart
```
### Uninstall
```bash
helm uninstall mydev
# Note: PVC persists by default
kubectl delete pvc userhome-mydev
```
### Rollback
```bash
# View history
helm history mydev
# Rollback to previous version
helm rollback mydev
# Rollback to specific revision
helm rollback mydev 3
```
## Troubleshooting
### Pod Not Starting
```bash
# Check pod status
kubectl get pods -l app.kubernetes.io/instance=mydev
# Describe pod for events
kubectl describe pod -l app.kubernetes.io/instance=mydev
# Check logs
kubectl logs deployment/devcontainer-mydev
```
### Repository Not Cloning
```bash
# Check init logs
kubectl logs deployment/devcontainer-mydev | grep "Repository Initialization"
# Verify secret exists
kubectl get secret devcontainer-mydev-secrets-env
# Check environment
kubectl exec deployment/devcontainer-mydev -- env | grep GITHUB
```
### VNC Not Accessible
```bash
# Check service
kubectl get svc devcontainer-mydev
kubectl describe svc devcontainer-mydev
# Test with port-forward
kubectl port-forward deployment/devcontainer-mydev 5800:5800
```
### MCP Sidecar Issues
```bash
# Check all containers
kubectl get pod -l app.kubernetes.io/instance=mydev -o jsonpath='{.items[0].spec.containers[*].name}'
# Check MCP container logs
kubectl logs deployment/devcontainer-mydev -c kubernetes-mcp
kubectl logs deployment/devcontainer-mydev -c flux-mcp
kubectl logs deployment/devcontainer-mydev -c homeassistant-mcp
# Verify RBAC permissions (for Kubernetes/Flux MCP)
kubectl auth can-i --list --as system:serviceaccount:default:devcontainer-mydev
# Check Home Assistant MCP credentials
kubectl get secret devcontainer-mydev-secrets-env -o jsonpath='{.data.homeassistant-url}' | base64 -d
# Verify the URL is accessible from the pod
kubectl exec deployment/devcontainer-mydev -- curl -s http://homeassistant.local:8123/api/
```
### Storage Issues
```bash
# Check PVC
kubectl get pvc userhome-mydev
kubectl describe pvc userhome-mydev
# Check available storage classes
kubectl get storageclass
# Verify ReadWriteMany support
kubectl get storageclass <class-name> -o yaml | grep -i accessmodes
```
## Best Practices
### Production Deployment
1. **Use specific image tags** instead of `latest`:
```bash
helm install mydev ./chart --set image.tag=v1.0.0
```
2. **Set resource limits** appropriately:
```yaml
resources:
requests:
memory: "4Gi"
cpu: "2000m"
limits:
memory: "8Gi"
cpu: "4000m"
```
3. **Enable VNC password**:
```bash
kubectl create secret generic devcontainer-mydev-secrets-env \
--from-literal=VNC_PASSWORD='strong-password-here'
```
4. **Use dedicated namespace**:
```bash
kubectl create namespace dev-environments
helm install mydev ./chart -n dev-environments
```
5. **Configure appropriate cluster access**:
- Use `readonlyns` or `readwritens` for namespace-scoped work
- Avoid `readwrite` cluster-wide access unless necessary
### Multi-User Deployment
For teams, create separate deployments per user:
```bash
# User 1
helm install alice-dev ./chart \
--set name=alice-dev \
--set githubRepo=https://github.com/alice/project
# User 2
helm install bob-dev ./chart \
--set name=bob-dev \
--set githubRepo=https://github.com/bob/project
```
### Backup and Recovery
The home directory persists on PVC. To backup:
```bash
# Create backup pod
kubectl run backup --image=busybox --restart=Never --rm -i --tty \
-- tar czf - -C /home . | gzip > home-backup.tar.gz
```
## Support
For issues or questions:
- GitHub Issues: https://github.com/cpfarhood/devcontainer/issues
- Documentation: https://github.com/cpfarhood/devcontainer
+134
View File
@@ -0,0 +1,134 @@
FROM jlesage/baseimage-gui:ubuntu-22.04-v4
# Set environment variables
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=user
# Install system dependencies
RUN apt-get update && apt-get install -y \
curl \
wget \
gnupg \
ca-certificates \
git \
build-essential \
python3 \
python3-pip \
jq \
unzip \
sudo \
&& rm -rf /var/lib/apt/lists/*
# Install Chrome and xdg-utils (needed for xdg-open to work in VNC)
RUN wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | gpg --dearmor -o /usr/share/keyrings/google-chrome-keyring.gpg && \
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/google-chrome-keyring.gpg] http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list && \
apt-get update && \
apt-get install -y google-chrome-stable xdg-utils && \
rm -rf /var/lib/apt/lists/*
# Chrome wrapper: adds flags required for running inside a Docker container.
# xdg-open (used by Claude Code on Linux) respects $BROWSER, so pointing it
# here ensures the OAuth popup works without manual --no-sandbox invocations.
# Cleans up crash lock files and suppresses the crash-restore bubble so that
# sessions/cookies survive unclean pod shutdowns (SIGKILL).
RUN printf '#!/bin/bash\n\
CHROME_DIR="/config/userdata/.config/google-chrome"\n\
mkdir -p "$CHROME_DIR"\n\
# Remove stale lock files left by unclean container shutdown\n\
rm -f "$CHROME_DIR/SingletonLock" "$CHROME_DIR/SingletonSocket" "$CHROME_DIR/SingletonCookie"\n\
# Mark the previous session as clean so Chrome does not clear cookies\n\
PREFS="$CHROME_DIR/Default/Preferences"\n\
if [ -f "$PREFS" ]; then\n\
sed -i '\''s/"exit_type":"Crashed"/"exit_type":"Normal"/g; s/"exited_cleanly":false/"exited_cleanly":true/g'\'' "$PREFS"\n\
fi\n\
exec /usr/bin/google-chrome-stable \\\n\
--no-sandbox \\\n\
--disable-dev-shm-usage \\\n\
--disable-gpu \\\n\
--disable-session-crashed-bubble \\\n\
--user-data-dir="$CHROME_DIR" \\\n\
"$@"\n' > /usr/local/bin/google-chrome && \
chmod +x /usr/local/bin/google-chrome
# Install Node.js (LTS version for Happy Coder)
RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - && \
apt-get install -y nodejs && \
rm -rf /var/lib/apt/lists/*
# Install Happy Coder and Claude Code globally
RUN npm install -g happy-coder @anthropic-ai/claude-code
# Install 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" | \
tar -xz --strip-components=1 -C /usr/local/bin "crush_${CRUSH_VERSION}_Linux_x86_64/crush" && \
chmod +x /usr/local/bin/crush
# Install VSCode
RUN wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o /usr/share/keyrings/packages.microsoft.gpg && \
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/packages.microsoft.gpg] https://packages.microsoft.com/repos/code stable main" > /etc/apt/sources.list.d/vscode.list && \
apt-get update && \
apt-get install -y code && \
rm -rf /var/lib/apt/lists/*
# Install Google Antigravity IDE
RUN mkdir -p /etc/apt/keyrings && \
curl -fsSL https://us-central1-apt.pkg.dev/doc/repo-signing-key.gpg | \
gpg --dearmor --yes -o /etc/apt/keyrings/antigravity-repo-key.gpg && \
echo "deb [signed-by=/etc/apt/keyrings/antigravity-repo-key.gpg] https://us-central1-apt.pkg.dev/projects/antigravity-auto-updater-dev/ antigravity-debian main" \
> /etc/apt/sources.list.d/antigravity.list && \
apt-get update && \
apt-get install -y antigravity && \
rm -rf /var/lib/apt/lists/*
# Install OpenSSH server (for SSH IDE mode)
RUN apt-get update && \
apt-get install -y openssh-server && \
rm -rf /var/lib/apt/lists/* && \
mkdir -p /var/run/sshd && \
sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/' /etc/ssh/sshd_config && \
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config && \
sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config && \
echo "PermitRootLogin no" >> /etc/ssh/sshd_config
# Create user user with specific UID/GID
RUN groupadd -g 1000 user && \
useradd -u 1000 -g 1000 -m -s /bin/bash user && \
echo "user ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
# Create workspace directory
RUN mkdir -p /workspace && \
chown -R user:user /workspace
# Copy startup scripts
COPY --chmod=755 scripts/startapp.sh /startapp.sh
COPY --chmod=755 scripts/init-repo.sh /usr/local/bin/init-repo
# Fix app user shell after baseimage-gui creates it at runtime
COPY --chmod=755 scripts/cont-init-user.sh /etc/cont-init.d/20-fix-user-shell.sh
COPY --chmod=755 scripts/cont-init-sshd.sh /etc/cont-init.d/25-start-sshd.sh
# Set working directory
WORKDIR /workspace
# Configure container to run as user user
ENV HOME=/config/userdata \
USER=user \
BROWSER=/usr/local/bin/google-chrome
# Expose VNC port (baseimage-gui default)
EXPOSE 5800
# Set app name for baseimage-gui
RUN set-cont-env APP_NAME "Dev Container"
+120
View File
@@ -0,0 +1,120 @@
.PHONY: build push run stop clean help
# Variables
REGISTRY ?= ghcr.io/cpfarhood
IMAGE_NAME ?= antigravity
IMAGE_TAG ?= latest
FULL_IMAGE = $(REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG)
.DEFAULT_GOAL := help
# Build the Docker image
build:
@echo "Building $(FULL_IMAGE)..."
docker build -t $(FULL_IMAGE) .
# Push the image to registry
push: build
@echo "Pushing $(FULL_IMAGE)..."
docker push $(FULL_IMAGE)
# Run locally with Docker
run:
@echo "Running $(FULL_IMAGE) locally..."
docker run -d \
-p 5800:5800 \
-e GITHUB_REPO="${GITHUB_REPO}" \
-e GITHUB_TOKEN="${GITHUB_TOKEN}" \
-e VNC_PASSWORD="${VNC_PASSWORD}" \
-e HAPPY_EXPERIMENTAL="true" \
-v $(PWD)/home:/home \
-v $(PWD)/workspace:/workspace \
--name antigravity \
$(FULL_IMAGE)
@echo "Access at http://localhost:5800"
# Stop the running container
stop:
@echo "Stopping antigravity container..."
docker stop antigravity || true
docker rm antigravity || true
# Clean up local volumes
clean: stop
@echo "Cleaning up..."
rm -rf ./home ./workspace
# Helm deployment
RELEASE_NAME ?= mydev
NAMESPACE ?= default
helm-deploy:
@echo "Deploying with Helm (release: $(RELEASE_NAME))..."
@if [ -z "$(GITHUB_REPO)" ]; then \
echo "ERROR: GITHUB_REPO environment variable is required"; \
echo "Usage: GITHUB_REPO=https://github.com/user/repo make helm-deploy"; \
exit 1; \
fi
helm upgrade --install $(RELEASE_NAME) ./chart \
--namespace $(NAMESPACE) \
--set name=$(RELEASE_NAME) \
--set githubRepo="$(GITHUB_REPO)" \
--set image.repository=$(REGISTRY)/$(IMAGE_NAME) \
--set image.tag=$(IMAGE_TAG)
helm-delete:
@echo "Deleting Helm release $(RELEASE_NAME)..."
helm uninstall $(RELEASE_NAME) --namespace $(NAMESPACE)
@echo "Note: PVC persists. To delete: kubectl delete pvc userhome-$(RELEASE_NAME) -n $(NAMESPACE)"
helm-logs:
@echo "Showing logs for $(RELEASE_NAME)..."
kubectl logs -f deployment/devcontainer-$(RELEASE_NAME) -n $(NAMESPACE)
helm-shell:
@echo "Opening shell in $(RELEASE_NAME)..."
kubectl exec -it deployment/devcontainer-$(RELEASE_NAME) -n $(NAMESPACE) -- bash
helm-port-forward:
@echo "Port forwarding $(RELEASE_NAME) to localhost:5800..."
kubectl port-forward deployment/devcontainer-$(RELEASE_NAME) 5800:5800 -n $(NAMESPACE)
# Show help
help:
@echo "Antigravity Dev Container Makefile"
@echo ""
@echo "Usage: make [target]"
@echo ""
@echo "Docker Targets:"
@echo " build - Build the Docker image"
@echo " push - Push image to registry"
@echo " run - Run container locally (requires env vars)"
@echo " stop - Stop running container"
@echo " clean - Clean up containers and volumes"
@echo ""
@echo "Helm/Kubernetes Targets:"
@echo " helm-deploy - Deploy with Helm chart (requires GITHUB_REPO)"
@echo " helm-delete - Delete Helm release"
@echo " helm-logs - Show container logs"
@echo " helm-shell - Open shell in container"
@echo " helm-port-forward - Port forward to localhost"
@echo ""
@echo "Variables:"
@echo " REGISTRY - Docker registry (default: ghcr.io/cpfarhood)"
@echo " IMAGE_NAME - Image name (default: antigravity)"
@echo " IMAGE_TAG - Image tag (default: latest)"
@echo " RELEASE_NAME - Helm release name (default: mydev)"
@echo " NAMESPACE - Kubernetes namespace (default: default)"
@echo " GITHUB_REPO - GitHub repository URL (required for helm-deploy)"
@echo ""
@echo "Environment Variables for 'make run':"
@echo " GITHUB_REPO - GitHub repository URL"
@echo " GITHUB_TOKEN - GitHub token (optional)"
@echo " VNC_PASSWORD - VNC password (optional)"
@echo ""
@echo "Examples:"
@echo " make build"
@echo " make push REGISTRY=ghcr.io/myuser IMAGE_TAG=v1.0"
@echo " GITHUB_REPO=https://github.com/user/repo make run"
@echo " GITHUB_REPO=https://github.com/user/repo make helm-deploy"
@echo " RELEASE_NAME=alice-dev GITHUB_REPO=https://github.com/alice/project make helm-deploy"
+441
View File
@@ -0,0 +1,441 @@
# Dev Container
![Build and Push](https://github.com/cpfarhood/devcontainer/actions/workflows/build-and-push.yaml/badge.svg)
A containerized cloud development environment with web-based GUI access, featuring:
- **VSCode or Google Antigravity** via browser-based VNC (port 5800)
- **SSH access** option (OpenSSH on port 22, additive with any IDE)
- **Claude Code**, **Happy Coder**, **OpenCode**, and **Crush** AI coding agents (terminal-based)
- **Automatic GitHub repo cloning** on startup
- **Persistent home directory** via ReadWriteMany PVC
- **Kubernetes-native** Helm chart deployment
## Quick Start
### Option A: Quickstart (Recommended)
For 80% of users, use the simplified quickstart values:
```bash
# Copy and customize the quickstart template
cp chart/values-quickstart.yaml my-values.yaml
# Edit my-values.yaml to set your name and repository:
# name: mydev
# githubRepo: https://github.com/youruser/yourrepo
# Deploy with minimal configuration
helm install mydev ./chart -f my-values.yaml
```
### Option B: One-Command Deploy
```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**: Happy Coder, 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) |
| `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
```
### Happy Coder
| Value | Default | Description |
|-------|---------|-------------|
| `happy.serverUrl` | `https://happy.farh.net` | Happy Coder server endpoint |
| `happy.webappUrl` | `https://happy-coder.farh.net` | Happy Coder webapp URL |
| `happy.homeDir` | `/config/userdata/.happy` | Happy runtime state directory (persists on the home PVC) |
| `happy.experimental` | `true` | Enable experimental Happy features |
### Kubernetes cluster access
The `clusterAccess` value provisions a ServiceAccount, Role/ClusterRole, and binding so the devcontainer pod can interact with the Kubernetes API. The default is `none` — no RBAC resources are created.
| Value | Scope | Verbs |
|-------|-------|-------|
| `none` (default) | — | no access |
| `readonlyns` | release namespace | `get`, `list`, `watch` |
| `readwritens` | release namespace | `*` |
| `readonly` | cluster-wide | `get`, `list`, `watch` |
| `readwrite` | cluster-wide | `*` |
```bash
# Give the pod read-only access to its own namespace
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set clusterAccess=readonlyns
```
With any non-`none` value, a `ServiceAccount` named `devcontainer-{name}` is created and set as the pod's `serviceAccountName`, so `kubectl` and any in-cluster API calls use it automatically.
### MCP Sidecars
The devcontainer includes MCP (Model Context Protocol) servers as sidecar containers that enable AI assistants to interact with various services:
| Sidecar | Default | Purpose |
|---------|---------|---------|
| `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 |
Happy Coder's runtime state (`HAPPY_HOME_DIR`) is kept in `/config/userdata/.happy` on the persistent home PVC, so auth credentials and settings survive pod restarts when manually started.
---
## Troubleshooting
### Happy Coder (manual startup)
Happy daemon is not started automatically. Launch it manually when needed:
```bash
# Start Happy Coder daemon manually
happy daemon start
# Check daemon status
happy daemon status
# View daemon logs
ls ~/.happy/logs/
# Stop daemon if needed
happy daemon stop
```
### Claude not authenticated
Browser-based OAuth login is the primary method (works inside VNC via the Chrome wrapper). If you prefer API key auth:
```bash
kubectl patch secret devcontainer-mydev-secrets-env \
--type='json' \
-p='[{"op":"add","path":"/data/ANTHROPIC_API_KEY","value":"'$(echo -n "sk-ant-..." | base64)'"}]'
```
Then restart the pod to pick up the new env var.
### VNC not loading
```bash
kubectl port-forward deployment/devcontainer-mydev 5800:5800
kubectl logs deployment/devcontainer-mydev
kubectl describe pod -l app.kubernetes.io/instance=mydev
```
### Pod not picking up new image after upgrade
The chart uses `image.tag: latest`. Kubernetes won't restart the pod on a Helm upgrade unless the Deployment spec changes. Force a restart manually:
```bash
kubectl rollout restart deployment/devcontainer-mydev
```
### Repository not cloning
```bash
kubectl logs deployment/devcontainer-mydev | grep "Repository Initialization"
kubectl exec deployment/devcontainer-mydev -- env | grep GITHUB
```
---
## Local Docker run
```bash
docker run -d \
-p 5800:5800 \
-e GITHUB_REPO="https://github.com/youruser/yourrepo" \
-e GITHUB_TOKEN="ghp_..." \
-e VNC_PASSWORD="changeme" \
-v $(pwd)/home:/home \
ghcr.io/cpfarhood/devcontainer:latest
```
---
## Building
```bash
docker build -t ghcr.io/cpfarhood/devcontainer:latest .
docker push ghcr.io/cpfarhood/devcontainer:latest
```
The image is also built and pushed automatically by CI on every push to `main` and on version tags (`v*`).
---
## Credits
- Base image: [jlesage/docker-baseimage-gui](https://github.com/jlesage/docker-baseimage-gui)
- AI assistant: [Happy Coder](https://happy.engineering) + [Claude](https://claude.ai)
+444
View File
@@ -0,0 +1,444 @@
# Helm Chart Values Reference
Complete reference for all configurable values in the Antigravity Dev Container Helm chart.
## Core Configuration
### name
- **Type:** String
- **Default:** `""`
- **Required:** Yes
- **Description:** Instance name used to generate resource names (`devcontainer-{name}`, `userhome-{name}`)
- **Example:** `mydev`, `alice-dev`, `team-workspace`
### githubRepo
- **Type:** String
- **Default:** `""`
- **Required:** Yes
- **Description:** GitHub repository URL to clone into `/workspace`
- **Example:** `https://github.com/username/repository`
### ide
- **Type:** String
- **Default:** `vscode`
- **Options:** `vscode`, `antigravity`, `none`
- **Description:** IDE to launch inside the container
- `vscode` — VSCode via VNC browser UI on port 5800
- `antigravity` — Google Antigravity (VSCode fork) via VNC on port 5800
- `none` — No IDE; useful when `ssh: true` is the sole access method
### ssh
- **Type:** Boolean
- **Default:** `false`
- **Description:** Start an OpenSSH server on port 22 in addition to the IDE
- **Note:** Requires `SSH_AUTHORIZED_KEYS` in env secret for key-based login
## Image Configuration
### image.repository
- **Type:** String
- **Default:** `ghcr.io/cpfarhood/devcontainer`
- **Description:** Container image repository
### image.tag
- **Type:** String
- **Default:** `latest`
- **Description:** Container image tag
- **Best Practice:** Use specific version tags for production
### image.pullPolicy
- **Type:** String
- **Default:** `Always`
- **Options:** `Always`, `IfNotPresent`, `Never`
- **Description:** Image pull policy
## Happy Coder Configuration
### happyServerUrl
- **Type:** String
- **Default:** `https://happy.farh.net`
- **Description:** Happy Coder server endpoint
- **When to Change:** Self-hosted Happy instance
### happyWebappUrl
- **Type:** String
- **Default:** `https://happy-coder.farh.net`
- **Description:** Happy Coder webapp URL
- **When to Change:** Self-hosted Happy instance
### happyHomeDir
- **Type:** String
- **Default:** `/config/userdata/.happy`
- **Description:** Happy runtime state directory (persists on PVC)
### happyExperimental
- **Type:** String
- **Default:** `"true"`
- **Description:** Enable experimental Happy features
## Display Configuration
### display.width
- **Type:** String
- **Default:** `"1920"`
- **Description:** VNC display width in pixels
### display.height
- **Type:** String
- **Default:** `"1080"`
- **Description:** VNC display height in pixels
### secureConnection
- **Type:** String
- **Default:** `"0"`
- **Options:** `"0"`, `"1"`
- **Description:** Set to `"0"` when TLS is terminated at the gateway layer
## User Configuration
### userId
- **Type:** String
- **Default:** `"1000"`
- **Description:** UID for the app user
### groupId
- **Type:** String
- **Default:** `"1000"`
- **Description:** GID for the app user
## Storage Configuration
### storage.size
- **Type:** String
- **Default:** `32Gi`
- **Description:** Size of the persistent home directory
- **Format:** Kubernetes quantity (e.g., `10Gi`, `100Gi`, `1Ti`)
### storage.className
- **Type:** String
- **Default:** `ceph-filesystem`
- **Description:** StorageClass name (must support ReadWriteMany)
- **Examples:** `ceph-filesystem`, `nfs-client`, `efs-sc`, `azurefile`
### shm.sizeLimit
- **Type:** String
- **Default:** `2Gi`
- **Description:** `/dev/shm` size (memory-backed emptyDir for Electron apps)
## Resource Limits
### resources.requests.memory
- **Type:** String
- **Default:** `2Gi`
- **Description:** Minimum memory to reserve
- **Format:** Kubernetes quantity
### resources.requests.cpu
- **Type:** String
- **Default:** `1000m`
- **Description:** Minimum CPU to reserve
- **Format:** Millicores (`1000m` = 1 CPU core)
### resources.limits.memory
- **Type:** String
- **Default:** `8Gi`
- **Description:** Maximum memory allowed
- **Format:** Kubernetes quantity
### resources.limits.cpu
- **Type:** String
- **Default:** `4000m`
- **Description:** Maximum CPU allowed
- **Format:** Millicores (`4000m` = 4 CPU cores)
## Kubernetes Access
### clusterAccess
- **Type:** String
- **Default:** `none`
- **Options:**
- `none` — No cluster access
- `readonlyns` — Read-only access to release namespace
- `readwritens` — Full access to release namespace
- `readonly` — Read-only access cluster-wide
- `readwrite` — Full access cluster-wide
- **Description:** RBAC permissions for the pod's ServiceAccount
## Secrets
### envSecretName
- **Type:** String
- **Default:** `""` (auto-generates as `devcontainer-{name}-secrets-env`)
- **Description:** Name of existing Secret containing environment variables
- **Keys Recognized:**
- `GITHUB_TOKEN` — PAT for private repo access
- `VNC_PASSWORD` — Password for VNC web UI
- `ANTHROPIC_API_KEY` — API key for Claude
- `SSH_AUTHORIZED_KEYS` — Public keys for SSH access
- `homeassistant-url` — Home Assistant base URL (e.g., http://homeassistant.local:8123)
- `homeassistant-token` — Home Assistant long-lived access token
## MCP Sidecars
### mcpSidecars.kubernetes.enabled
- **Type:** Boolean
- **Default:** `true`
- **Description:** Enable Kubernetes MCP server sidecar
### mcpSidecars.kubernetes.image.repository
- **Type:** String
- **Default:** `quay.io/containers/kubernetes_mcp_server`
- **Description:** Kubernetes MCP server image
### mcpSidecars.kubernetes.image.tag
- **Type:** String
- **Default:** `latest`
- **Description:** Kubernetes MCP server image tag
### mcpSidecars.kubernetes.port
- **Type:** Integer
- **Default:** `8080`
- **Description:** Port for Kubernetes MCP server
### mcpSidecars.kubernetes.resources
- **Type:** Object
- **Default:**
```yaml
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "500m"
```
- **Description:** Resource limits for Kubernetes MCP sidecar
### mcpSidecars.flux.enabled
- **Type:** Boolean
- **Default:** `true`
- **Description:** Enable Flux MCP server sidecar
### mcpSidecars.flux.image.repository
- **Type:** String
- **Default:** `ghcr.io/controlplaneio-fluxcd/flux-operator-mcp`
- **Description:** Flux MCP server image
### mcpSidecars.flux.image.tag
- **Type:** String
- **Default:** `v0.41.1`
- **Description:** Flux MCP server image tag
### mcpSidecars.flux.port
- **Type:** Integer
- **Default:** `8081`
- **Description:** Port for Flux MCP server
### mcpSidecars.flux.resources
- **Type:** Object
- **Default:**
```yaml
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "500m"
```
- **Description:** Resource limits for Flux MCP sidecar
### mcpSidecars.homeassistant.enabled
- **Type:** Boolean
- **Default:** `false`
- **Description:** Enable Home Assistant MCP server sidecar
- **Note:** Requires `homeassistant-url` and `homeassistant-token` in env secret
### mcpSidecars.homeassistant.image.repository
- **Type:** String
- **Default:** `ghcr.io/homeassistant-ai/ha-mcp`
- **Description:** Home Assistant MCP server image
### mcpSidecars.homeassistant.image.tag
- **Type:** String
- **Default:** `stable`
- **Description:** Home Assistant MCP server image tag
- **Options:** `stable` (recommended), `latest` (dev builds), `v{version}` (specific version)
### mcpSidecars.homeassistant.port
- **Type:** Integer
- **Default:** `8087`
- **Description:** Port for Home Assistant MCP server (SSE mode)
### mcpSidecars.homeassistant.resources
- **Type:** Object
- **Default:**
```yaml
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "500m"
```
- **Description:** Resource limits for Home Assistant MCP sidecar
## Usage Examples
### Minimal Configuration
```yaml
name: mydev
githubRepo: https://github.com/user/repo
```
### Production Configuration
```yaml
name: prod-workspace
githubRepo: https://github.com/company/application
ide: vscode
ssh: true
image:
tag: v1.0.0
storage:
size: 100Gi
className: ceph-filesystem
resources:
requests:
memory: "4Gi"
cpu: "2000m"
limits:
memory: "16Gi"
cpu: "8000m"
clusterAccess: readwritens
mcpSidecars:
kubernetes:
enabled: true
flux:
enabled: false
```
### Development Team Configuration
```yaml
name: team-dev
githubRepo: https://github.com/team/project
ide: antigravity
display:
width: "2560"
height: "1440"
storage:
size: 50Gi
className: nfs-client
clusterAccess: readonly
happyServerUrl: https://happy.internal.company.com
happyWebappUrl: https://happy-app.internal.company.com
```
### Smart Home Development Configuration
```yaml
name: smarthome-dev
githubRepo: https://github.com/user/home-automation
ide: vscode
clusterAccess: readwritens
mcpSidecars:
kubernetes:
enabled: true
flux:
enabled: false
homeassistant:
enabled: true
image:
tag: stable
# Requires secrets:
# homeassistant-url: http://homeassistant.local:8123
# homeassistant-token: <long-lived-access-token>
```
## Helm CLI Examples
### Using --set Flags
```bash
# Basic deployment
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/user/repo
# With multiple values
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/user/repo \
--set ide=antigravity \
--set storage.size=50Gi \
--set clusterAccess=readwritens \
--set mcpSidecars.flux.enabled=false
```
### Using Values File
Create `custom-values.yaml`:
```yaml
name: mydev
githubRepo: https://github.com/user/repo
storage:
size: 50Gi
clusterAccess: readwritens
```
Deploy:
```bash
helm install mydev ./chart -f custom-values.yaml
```
### Combining Methods
```bash
helm install mydev ./chart \
-f base-values.yaml \
-f prod-values.yaml \
--set githubRepo=https://github.com/user/repo \
--set image.tag=v2.0.0
```
## Value Precedence
Values are applied in order of precedence (highest to lowest):
1. `--set` flags on command line
2. `-f` values files (later files override earlier)
3. `chart/values.yaml` defaults
## Environment Variables
These environment variables are set in the container based on chart values:
| Environment Variable | Source Value | Description |
|---------------------|--------------|-------------|
| `GITHUB_REPO` | `githubRepo` | Repository to clone |
| `GITHUB_TOKEN` | Secret: `github-token` | PAT for private repos |
| `VNC_PASSWORD` | Secret: `vnc-password` | VNC access password |
| `ANTHROPIC_API_KEY` | Secret: `anthropic-api-key` | Claude API key |
| `SSH_AUTHORIZED_KEYS` | Secret: `ssh-authorized-keys` | SSH public keys |
| `HAPPY_SERVER_URL` | `happyServerUrl` | Happy server endpoint |
| `HAPPY_WEBAPP_URL` | `happyWebappUrl` | Happy webapp URL |
| `HAPPY_HOME_DIR` | `happyHomeDir` | Happy data directory |
| `HAPPY_EXPERIMENTAL` | `happyExperimental` | Experimental features |
| `DISPLAY_WIDTH` | `display.width` | VNC width |
| `DISPLAY_HEIGHT` | `display.height` | VNC height |
| `SECURE_CONNECTION` | `secureConnection` | TLS termination |
| `USER_ID` | `userId` | App user UID |
| `GROUP_ID` | `groupId` | App user GID |
| `IDE` | `ide` | IDE to launch |
| `SSH` | `ssh` | SSH server enabled |
+6
View File
@@ -0,0 +1,6 @@
apiVersion: v2
name: devcontainer
description: Dev Container with AI coding agents and MCP sidecars
type: application
version: 0.4.2
appVersion: "latest"
+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
+96
View File
@@ -0,0 +1,96 @@
{{/*
Resource name prefix: devcontainer-{name}
*/}}
{{- define "devcontainer.fullname" -}}
{{- 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 }}
{{- 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 }}
+278
View File
@@ -0,0 +1,278 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "devcontainer.fullname" . }}
labels:
{{- include "devcontainer.labels" . | nindent 4 }}
spec:
replicas: 1
selector:
matchLabels:
{{- include "devcontainer.labels" . | 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:latest
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 }}
- name: HAPPY_HOME_DIR
value: {{ .Values.happy.homeDir | quote }}
- name: HAPPY_EXPERIMENTAL
value: {{ .Values.happy.experimental | quote }}
- name: HAPPY_SERVER_URL
value: {{ .Values.happy.serverUrl | quote }}
- name: HAPPY_WEBAPP_URL
value: {{ .Values.happy.webappUrl | quote }}
- name: GITHUB_REPO
value: {{ .Values.githubRepo | quote }}
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.homeassistant.enabled }}
- name: homeassistant-mcp
image: "{{ .Values.mcp.sidecars.homeassistant.image.repository }}:{{ .Values.mcp.sidecars.homeassistant.image.tag }}"
imagePullPolicy: Always
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
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: Always
command: ["node"]
args:
- cli.js
- --headless
- --browser
- chromium
- --no-sandbox
- --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" . }}
+13
View File
@@ -0,0 +1,13 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ include "devcontainer.pvcName" . }}
labels:
{{- include "devcontainer.labels" . | nindent 4 }}
spec:
accessModes:
- ReadWriteMany
storageClassName: {{ .Values.storage.className }}
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 }}
+58
View File
@@ -0,0 +1,58 @@
# =============================================================================
# QUICKSTART VALUES - Just set these 3 essentials!
# =============================================================================
# Instance name (required)
name: mydev
# GitHub repository to clone (required)
githubRepo: https://github.com/youruser/yourrepo
# 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'
+256
View File
@@ -0,0 +1,256 @@
{
"$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",
"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",
"pattern": "^https://github\\.com/.+/.+$"
},
"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", "className"]
},
"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"
},
"happy": {
"type": "object",
"properties": {
"serverUrl": {
"type": "string",
"format": "uri",
"description": "Happy Coder server URL"
},
"webappUrl": {
"type": "string",
"format": "uri",
"description": "Happy Coder webapp URL"
},
"homeDir": {
"type": "string",
"description": "Happy Coder home directory"
},
"experimental": {
"type": "string",
"enum": ["true", "false"],
"description": "Enable experimental Happy features"
}
},
"required": ["serverUrl", "webappUrl", "homeDir", "experimental"]
},
"mcp": {
"type": "object",
"properties": {
"sidecars": {
"type": "object",
"properties": {
"kubernetes": {
"$ref": "#/$defs/mcpSidecar"
},
"flux": {
"$ref": "#/$defs/mcpSidecar"
},
"homeassistant": {
"$ref": "#/$defs/mcpSidecar"
},
"pgtuner": {
"$ref": "#/$defs/mcpSidecar"
},
"playwright": {
"$ref": "#/$defs/mcpSidecar"
}
},
"additionalProperties": false
}
},
"required": ["sidecars"]
},
"envSecretName": {
"type": "string",
"description": "Custom environment secret name"
}
},
"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"]
}
}
}
+176
View File
@@ -0,0 +1,176 @@
# =============================================================================
# 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: ""
# =============================================================================
# ACCESS & INTERFACE
# =============================================================================
# IDE configuration
ide:
# Options: vscode | antigravity | none
type: vscode
# SSH access configuration
ssh:
enabled: false
# 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: ceph-filesystem
# 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
# =============================================================================
# Happy Coder AI assistant configuration
happy:
serverUrl: "https://happy.farh.net"
webappUrl: "https://happy-coder.farh.net"
homeDir: "/config/userdata/.happy"
experimental: "true"
# 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"
# Home Assistant smart home control
homeassistant:
enabled: false # Requires HOMEASSISTANT_URL and HOMEASSISTANT_TOKEN
image:
repository: ghcr.io/homeassistant-ai/ha-mcp
tag: stable
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: latest
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: ""
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
-11
View File
@@ -1,11 +0,0 @@
<!DOCTYPE html>
<html>
<head><title>Dev Container Helm Chart Repository</title></head>
<body>
<h1>Dev Container Helm Chart Repository</h1>
<p>Add this repository to Helm:</p>
<pre>helm repo add devcontainer https://farhoodliquor.github.io/devcontainer</pre>
<p>Install the chart:</p>
<pre>helm install mydev devcontainer/devcontainer --set name=mydev</pre>
</body>
</html>
-286
View File
@@ -1,286 +0,0 @@
apiVersion: v1
entries:
devcontainer:
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.634530101Z"
description: Dev Container with AI coding agents and MCP sidecars
digest: 0c46b5d7a889cc21c28b8b088eb6c0c785a3f9324a4b39d14813c4eefe3f8e7a
keywords:
- development
- devcontainer
- vscode
- ai
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.6.0.tgz
version: 2.6.0
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.633861353Z"
description: Dev Container with AI coding agents and MCP sidecars
digest: 010294a1a9b4bb6a92c685d819a0cd34dd03bc111da715d0807799168d0aba66
keywords:
- development
- devcontainer
- vscode
- ai
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.4.0.tgz
version: 2.4.0
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.633145718Z"
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
and dynamic deployment modes
digest: a650e9c7a8feb961232aee4048d3bf0ff1d04c55100f51a7db138e1a0f8b524e
keywords:
- development
- devcontainer
- vscode
- ai
- knative
- serverless
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.3.0.tgz
version: 2.3.0
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.631873087Z"
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
and dynamic deployment modes
digest: f5c440846e7672239a6f7b14a393888988ef627d896bc967bfc018130d65921d
keywords:
- development
- devcontainer
- vscode
- ai
- knative
- serverless
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.2.5.tgz
version: 2.2.5
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.6304485Z"
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
and dynamic deployment modes
digest: 2bea7dc5c198a5b4dab0b74f0a75089210c7ba49b56176ba2af205b7dac3fe23
keywords:
- development
- devcontainer
- vscode
- ai
- knative
- serverless
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.2.4.tgz
version: 2.2.4
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.62965028Z"
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
and dynamic deployment modes
digest: afde89b22d7e4a5dfd4c918a06258d9f27f1b17493a70dba98d1ae544280505a
keywords:
- development
- devcontainer
- vscode
- ai
- knative
- serverless
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.2.3.tgz
version: 2.2.3
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.628801186Z"
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
and dynamic deployment modes
digest: 5b3b994b74da01579156021fcfb718c61989def7c16cafadb36e7ddc90cbeea7
keywords:
- development
- devcontainer
- vscode
- ai
- knative
- serverless
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.2.2.tgz
version: 2.2.2
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.627970857Z"
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
and dynamic deployment modes
digest: 8db383b24252edd37998c56bbba76793d1b6eeb37365a6894a713eef6af81210
keywords:
- development
- devcontainer
- vscode
- ai
- knative
- serverless
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.2.1.tgz
version: 2.2.1
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.627169642Z"
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
and dynamic deployment modes
digest: a1ea207bc96a35cc545d12fa8aca00452792de54e3fae74993260cd69afee0fa
keywords:
- development
- devcontainer
- vscode
- ai
- knative
- serverless
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.2.0.tgz
version: 2.2.0
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.626328122Z"
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
and dynamic deployment modes
digest: beac1182a39f158fb9aa1f3308b4b030bf378d612f2aa860f792fad62aa30321
keywords:
- development
- devcontainer
- vscode
- ai
- knative
- serverless
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.1.1.tgz
version: 2.1.1
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.625121312Z"
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
and dynamic deployment modes
digest: efb6cbd932a7ac082853d305e970db72e0086543a6963baabef16ebd2e8498f8
keywords:
- development
- devcontainer
- vscode
- ai
- knative
- serverless
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.1.0.tgz
version: 2.1.0
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.623735055Z"
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
and dynamic deployment modes
digest: 46f25124d9045802d0e50d34209d15a7fa15e1ef1c9d3f0e93ac4bb39b7c9b17
keywords:
- development
- devcontainer
- vscode
- ai
- knative
- serverless
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.0.5.tgz
version: 2.0.5
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.622870733Z"
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
and dynamic deployment modes
digest: db82381ffe831b07ce7777e8e6e05455a8eaeccfcd0afcd87825433a96cb2d65
keywords:
- development
- devcontainer
- vscode
- ai
- knative
- serverless
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.0.4.tgz
version: 2.0.4
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.622004867Z"
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
and dynamic deployment modes
digest: 872919ea64531b35dfa7f956d8a6e4130a1c7f0f80c50141b6f2d1cddd49682e
keywords:
- development
- devcontainer
- vscode
- ai
- knative
- serverless
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.0.3.tgz
version: 2.0.3
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.621180038Z"
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
and dynamic deployment modes
digest: 7cbb5379f8b41bc938a29b44c563757358c25fd843c75fb6d2f1a2b9365c0cf1
keywords:
- development
- devcontainer
- vscode
- ai
- knative
- serverless
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.0.1-dev.tgz
version: 2.0.1-dev
- apiVersion: v2
appVersion: latest
created: "2026-03-11T12:15:44.620303012Z"
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
and dynamic deployment modes
digest: 5736c92626e2c3edb8e81ddba5be581360dbf3901ccc607db8bad238ea831cd0
keywords:
- development
- devcontainer
- vscode
- ai
- knative
- serverless
name: devcontainer
type: application
urls:
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.0.0-dev.tgz
version: 2.0.0-dev
generated: "2026-03-11T12:15:44.619306973Z"
+30
View File
@@ -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 $!)"
+6
View File
@@ -0,0 +1,6 @@
#!/bin/sh
# Fix the app user (UID 1000) created by baseimage-gui at runtime.
# baseimage-gui sets shell=/sbin/nologin and home=/dev/null, which
# prevents VSCode from opening terminals.
usermod -s /bin/bash app
usermod -d /config/userdata app
+109
View File
@@ -0,0 +1,109 @@
#!/bin/bash
# Initialize repository
set -e
echo "=== Repository Initialization ==="
# Set up basic git configuration
echo "Configuring git user settings..."
# Use environment variables if provided, otherwise use defaults
GIT_USER_NAME="${GIT_USER_NAME:-DevContainer User}"
GIT_USER_EMAIL="${GIT_USER_EMAIL:-devcontainer@example.com}"
git config --global user.name "$GIT_USER_NAME"
git config --global user.email "$GIT_USER_EMAIL"
# Set up git credentials early if GITHUB_TOKEN is provided
# This ensures all git operations have proper authentication
if [ -n "$GITHUB_TOKEN" ]; then
echo "Setting up git credentials..."
# Configure git to use credential store globally
git config --global credential.helper store
# Create or update the credentials file
CREDENTIALS_FILE="/config/userdata/.git-credentials"
# Support multiple git hosting providers
# GitHub supports both oauth2 and token as username
echo "https://oauth2:${GITHUB_TOKEN}@github.com" > "$CREDENTIALS_FILE"
echo "https://${GITHUB_TOKEN}:x-oauth-basic@github.com" >> "$CREDENTIALS_FILE"
echo "https://token:${GITHUB_TOKEN}@github.com" >> "$CREDENTIALS_FILE"
# GitLab format (if same token works)
if [ -n "$GITLAB_HOST" ]; then
echo "https://oauth2:${GITHUB_TOKEN}@${GITLAB_HOST}" >> "$CREDENTIALS_FILE"
fi
chmod 600 "$CREDENTIALS_FILE"
# Also create a symlink in the home directory if it doesn't exist
# This handles cases where git might look in different locations
if [ ! -f "$HOME/.git-credentials" ] && [ "$HOME" != "/config/userdata" ]; then
ln -sf "$CREDENTIALS_FILE" "$HOME/.git-credentials"
fi
echo "Git credentials configured"
else
# Even without a token, ensure git has a proper credential helper configured
# This prevents errors when credentials are added later
echo "No GITHUB_TOKEN provided, configuring basic git settings..."
git config --global credential.helper store
# Create an empty credentials file with proper permissions
CREDENTIALS_FILE="/config/userdata/.git-credentials"
touch "$CREDENTIALS_FILE"
chmod 600 "$CREDENTIALS_FILE"
# Create symlink if needed
if [ ! -f "$HOME/.git-credentials" ] && [ "$HOME" != "/config/userdata" ]; then
ln -sf "$CREDENTIALS_FILE" "$HOME/.git-credentials"
fi
fi
# Check if GITHUB_REPO is set
if [ -z "$GITHUB_REPO" ]; then
echo "GITHUB_REPO not set, skipping repository clone"
WORKSPACE_DIR="/workspace/default"
mkdir -p "$WORKSPACE_DIR"
else
# Parse repo name from URL
REPO_NAME=$(basename "$GITHUB_REPO" .git)
WORKSPACE_DIR="/workspace/$REPO_NAME"
echo "Repository: $GITHUB_REPO"
echo "Target directory: $WORKSPACE_DIR"
# Check if repo already exists
if [ -d "$WORKSPACE_DIR/.git" ]; then
echo "Repository already exists, pulling latest changes..."
cd "$WORKSPACE_DIR"
git pull || echo "Pull failed, continuing anyway..."
else
echo "Cloning repository..."
mkdir -p "$(dirname "$WORKSPACE_DIR")"
# Clone with token if provided
if [ -n "$GITHUB_TOKEN" ]; then
# Replace https://github.com/ with https://oauth2:token@github.com/
CLONE_URL=$(echo "$GITHUB_REPO" | sed "s|https://github.com/|https://oauth2:${GITHUB_TOKEN}@github.com/|")
git clone "$CLONE_URL" "$WORKSPACE_DIR"
else
git clone "$GITHUB_REPO" "$WORKSPACE_DIR"
fi
fi
fi
# Set ownership using numeric IDs (username may not exist yet in baseimage-gui)
RUN_UID="${USER_ID:-1000}"
RUN_GID="${GROUP_ID:-1000}"
chown -R "$RUN_UID:$RUN_GID" "$WORKSPACE_DIR"
# Ensure home directory exists on the PVC (may be absent on a fresh volume)
mkdir -p "$HOME"
chown "$RUN_UID:$RUN_GID" "$HOME"
# Export workspace directory for startapp.sh
echo "$WORKSPACE_DIR" > /tmp/workspace-dir
echo "=== Initialization Complete ==="
+40
View File
@@ -0,0 +1,40 @@
#!/bin/bash
# Start application script for baseimage-gui
set -e
echo "=== Starting Dev Container ==="
# Initialize repository
/usr/local/bin/init-repo
# Get workspace directory
if [ -f /tmp/workspace-dir ]; then
WORKSPACE_DIR=$(cat /tmp/workspace-dir)
else
WORKSPACE_DIR="/workspace/default"
fi
IDE="${IDE:-vscode}"
echo "IDE mode: $IDE"
echo "Workspace: $WORKSPACE_DIR"
case "$IDE" in
antigravity)
echo "Opening Google Antigravity in: $WORKSPACE_DIR"
# --no-sandbox is required for Electron apps in Docker (no kernel sandbox available).
# Explicit --user-data-dir and --extensions-dir pin config to the home PVC so
# settings and the setup wizard state survive pod restarts.
exec antigravity --no-sandbox \
--user-data-dir "$HOME/.config/antigravity" \
--extensions-dir "$HOME/.antigravity/extensions" \
--new-window --wait "$WORKSPACE_DIR"
;;
none)
echo "IDE=none: no IDE launched, keeping container alive."
exec sleep infinity
;;
*)
echo "Opening VSCode in: $WORKSPACE_DIR"
exec code --new-window --wait "$WORKSPACE_DIR"
;;
esac
+46
View File
@@ -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 ==="