Compare commits

..

82 Commits

Author SHA1 Message Date
github-actions[bot] fcd959ae1f chore(release): 2.5.0 [skip ci] 2026-03-11 11:57:24 +00:00
DevContainer User 149120ff6c feat: add LSP servers for Claude Code language intelligence
Install pyright, typescript-language-server, gopls, clangd,
rust-analyzer, lua-language-server, jdtls, kotlin-language-server,
and intelephense so Claude Code can provide rich language support.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Validated: helm template renders cleanly for both modes.

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

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

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

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

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

This enables immediate testing of serverless features without manual builds.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Also adds resourceProfile to the schema as it was missing.

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

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

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

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

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

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

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

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

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

Closes #43, #44

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

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

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

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

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

Fixes #42

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

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-22 20:43:28 +00:00
25 changed files with 574 additions and 337 deletions
+6
View File
@@ -0,0 +1,6 @@
{
"enabledPlugins": {
"voltagent-dev-exp@voltagent-subagents": true,
"voltagent-lang@voltagent-subagents": true
}
}
+11 -2
View File
@@ -2,6 +2,15 @@
"enabledMcpjsonServers": [ "enabledMcpjsonServers": [
"kubernetes", "kubernetes",
"flux", "flux",
"playwright" "playwright",
] "github",
"pgtuner",
"fetch",
"sequentialthinking"
],
"permissions": {
"allow": [
"Bash(git add .claude/settings.local.json .claude/settings.json && git commit -m \"$\\(cat <<'EOF'\nchore: update Claude Code settings and enable voltagent plugins\n\nAdd fetch and sequentialthinking MCP servers to allowed list, and enable\nvoltagent dev-exp and lang subagent plugins.\n\nCo-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>\nEOF\n\\)\" && git status)"
]
}
} }
-1
View File
@@ -19,7 +19,6 @@
- [ ] Built Docker image locally - [ ] Built Docker image locally
- [ ] Tested container startup - [ ] Tested container startup
- [ ] Tested repository cloning - [ ] Tested repository cloning
- [ ] Tested Happy Coder integration
- [ ] Tested VNC web interface - [ ] Tested VNC web interface
## Checklist ## Checklist
+3 -4
View File
@@ -15,9 +15,8 @@ Use this for all version releases:
- ✅ Updates chart version - ✅ Updates chart version
- ✅ Creates git tag - ✅ Creates git tag
- ✅ Builds Docker image with all proper tags - ✅ Builds Docker image with all proper tags
- ✅ Publishes Helm chart to GHCR - ✅ Publishes Helm chart to GitHub Pages (`https://cpfarhood.github.io/devcontainer`)
- ✅ Creates GitHub Release with changelog - ✅ Creates GitHub Release with changelog
- ✅ No more `[skip ci]` blocking builds!
### 2️⃣ For Quick Fixes → **Quick Fix Build** ### 2️⃣ For Quick Fixes → **Quick Fix Build**
Use this for emergency fixes without version changes: Use this for emergency fixes without version changes:
@@ -30,8 +29,8 @@ Use this for emergency fixes without version changes:
### 3️⃣ Automatic CI → **Build and Push** ### 3️⃣ Automatic CI → **Build and Push**
Runs automatically on: Runs automatically on:
- Pushes to `main` (builds and pushes; skipped for release commits via `[skip ci]`)
- Pull requests (builds but doesn't push) - Pull requests (builds but doesn't push)
- Tags starting with `v*` (builds and pushes)
- Manual trigger available - Manual trigger available
## Workflow Files ## Workflow Files
@@ -90,5 +89,5 @@ gh run watch
### After (Simple! 🎉) ### After (Simple! 🎉)
- **3 total workflows** (down from 6+) - **3 total workflows** (down from 6+)
- **1 button** for complete releases - **1 button** for complete releases
- **No more `[skip ci]`** blocking builds - Release builds its own Docker image — `[skip ci]` on the version commit prevents duplicate CI builds
- **Clear separation** of concerns - **Clear separation** of concerns
+4 -83
View File
@@ -4,8 +4,6 @@ on:
push: push:
branches: branches:
- main - main
tags:
- 'v*'
pull_request: pull_request:
branches: branches:
- main - main
@@ -18,10 +16,12 @@ env:
jobs: jobs:
build-and-push: build-and-push:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: >-
github.event_name != 'push'
|| !contains(github.event.head_commit.message, '[skip ci]')
permissions: permissions:
contents: read contents: read
packages: write packages: write
id-token: write
steps: steps:
- name: Checkout repository - name: Checkout repository
@@ -46,9 +46,6 @@ jobs:
tags: | tags: |
type=ref,event=branch type=ref,event=branch
type=ref,event=pr type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha,prefix=sha- type=sha,prefix=sha-
type=raw,value=latest,enable={{is_default_branch}} type=raw,value=latest,enable={{is_default_branch}}
@@ -59,83 +56,7 @@ jobs:
push: ${{ github.event_name != 'pull_request' }} push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
build-args: CACHE_BUST=${{ github.sha }}
cache-from: type=gha cache-from: type=gha
cache-to: type=gha,mode=max cache-to: type=gha,mode=max
platforms: linux/amd64 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
+2
View File
@@ -16,7 +16,9 @@ env:
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
permissions: permissions:
contents: read
packages: write packages: write
steps: steps:
+85 -51
View File
@@ -4,11 +4,11 @@ on:
workflow_dispatch: workflow_dispatch:
inputs: inputs:
version: version:
description: 'Version to release (e.g., 0.1.25)' description: 'Explicit version (e.g., 1.2.3). Leave blank to auto-increment.'
required: true required: false
type: string type: string
release_type: release_type:
description: 'Release type' description: 'Release type (used when version is blank)'
required: true required: true
default: 'patch' default: 'patch'
type: choice type: choice
@@ -49,37 +49,34 @@ jobs:
- name: Determine Version - name: Determine Version
id: version id: version
run: | run: |
if [ "${{ github.event.inputs.version }}" != "" ]; then INPUT_VERSION="${{ github.event.inputs.version }}"
VERSION="${{ github.event.inputs.version }}" if [ -n "$INPUT_VERSION" ]; then
VERSION="$INPUT_VERSION"
else else
# Auto-determine next version based on release type # Auto-increment based on release_type
CURRENT=$(grep '^version:' chart/Chart.yaml | awk '{print $2}') CURRENT=$(grep '^version:' chart/Chart.yaml | awk '{print $2}')
MAJOR=$(echo $CURRENT | cut -d. -f1) # Strip any pre-release suffix (e.g., 2.0.0-dev -> 2.0.0)
MINOR=$(echo $CURRENT | cut -d. -f2) CURRENT=$(echo "$CURRENT" | sed 's/-.*//')
PATCH=$(echo $CURRENT | cut -d. -f3) 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 case "${{ github.event.inputs.release_type }}" in
major) major) VERSION="$((MAJOR + 1)).0.0" ;;
VERSION="$((MAJOR + 1)).0.0" minor) VERSION="${MAJOR}.$((MINOR + 1)).0" ;;
;; patch) VERSION="${MAJOR}.${MINOR}.$((PATCH + 1))" ;;
minor)
VERSION="${MAJOR}.$((MINOR + 1)).0"
;;
patch)
VERSION="${MAJOR}.${MINOR}.$((PATCH + 1))"
;;
esac esac
fi fi
echo "version=${VERSION}" >> $GITHUB_OUTPUT echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "tag=v${VERSION}" >> $GITHUB_OUTPUT echo "tag=v${VERSION}" >> $GITHUB_OUTPUT
echo "🚀 Releasing version ${VERSION}" echo "Releasing version ${VERSION}"
- name: Update Chart Version - name: Update Chart Version
run: | run: |
sed -i "s/^version: .*/version: ${{ steps.version.outputs.version }}/" chart/Chart.yaml sed -i "s/^version: .*/version: ${{ steps.version.outputs.version }}/" chart/Chart.yaml
git add chart/Chart.yaml git add chart/Chart.yaml
git commit -m "chore: release version ${{ steps.version.outputs.version }}" git diff --quiet --staged || git commit -m "chore(release): ${{ steps.version.outputs.version }} [skip ci]"
- name: Create and Push Tag - name: Create and Push Tag
run: | run: |
@@ -99,61 +96,98 @@ jobs:
with: with:
context: . context: .
push: true push: true
build-args: CACHE_BUST=${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
tags: | tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.tag }} ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.tag }}
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }} ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }}
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64 platforms: linux/amd64
- name: Package Helm Chart - name: Publish Helm Chart to GitHub Pages
run: | run: |
helm registry login ghcr.io \
--username ${{ github.actor }} \
--password ${{ secrets.GITHUB_TOKEN }}
helm package chart/ helm package chart/
helm push devcontainer-${{ steps.version.outputs.version }}.tgz oci://ghcr.io/cpfarhood/charts CHART_TGZ="devcontainer-${{ steps.version.outputs.version }}.tgz"
- name: Generate Release Notes # Set up gh-pages in a temporary directory
id: notes PAGES_DIR=$(mktemp -d)
if git ls-remote --heads origin gh-pages | grep -q gh-pages; then
# gh-pages exists — shallow clone just that branch
git clone --single-branch --branch gh-pages \
"https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git" \
"$PAGES_DIR"
else
# First time — initialize gh-pages
git init "$PAGES_DIR"
git -C "$PAGES_DIR" checkout --orphan gh-pages
git -C "$PAGES_DIR" remote add origin \
"https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git"
cat > "$PAGES_DIR/index.html" <<'HTMLEOF'
<!DOCTYPE html>
<html>
<head><title>Dev Container Helm Chart Repository</title></head>
<body>
<h1>Dev Container Helm Chart Repository</h1>
<p>Add this repository to Helm:</p>
<pre>helm repo add devcontainer https://cpfarhood.github.io/devcontainer</pre>
<p>Install the chart:</p>
<pre>helm install mydev devcontainer/devcontainer --set name=mydev</pre>
</body>
</html>
HTMLEOF
fi
git -C "$PAGES_DIR" config user.name "github-actions[bot]"
git -C "$PAGES_DIR" config user.email "github-actions[bot]@users.noreply.github.com"
# Copy chart package and rebuild index
cp "$CHART_TGZ" "$PAGES_DIR/"
if [ -f "$PAGES_DIR/index.yaml" ]; then
helm repo index "$PAGES_DIR" --url https://cpfarhood.github.io/devcontainer --merge "$PAGES_DIR/index.yaml"
else
helm repo index "$PAGES_DIR" --url https://cpfarhood.github.io/devcontainer
fi
# Commit and push
git -C "$PAGES_DIR" add .
git -C "$PAGES_DIR" commit -m "Publish chart ${{ steps.version.outputs.version }}"
git -C "$PAGES_DIR" push origin gh-pages
- name: Create GitHub Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ steps.version.outputs.version }}
TAG: ${{ steps.version.outputs.tag }}
IMAGE: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.tag }}
run: | run: |
# Get commits since last tag PREV_TAG=$(git describe --tags --abbrev=0 HEAD~1 2>/dev/null || echo "")
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
if [ -z "$PREV_TAG" ]; then if [ -z "$PREV_TAG" ]; then
COMMITS=$(git log --pretty=format:"- %s (%h)" HEAD) COMMITS=$(git log --pretty=format:"- %s (%h)" HEAD)
else else
COMMITS=$(git log --pretty=format:"- %s (%h)" ${PREV_TAG}..HEAD) COMMITS=$(git log --pretty=format:"- %s (%h)" "${PREV_TAG}..HEAD")
fi fi
cat << EOF > release-notes.md cat > release-notes.md <<NOTESEOF
## 🚀 Release ${{ steps.version.outputs.version }} ## Release ${VERSION}
### Changes ### Changes
${COMMITS} ${COMMITS}
### Docker Image ### Docker Image
\`\`\`bash \`\`\`bash
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.tag }} docker pull ${IMAGE}
\`\`\` \`\`\`
### Helm Chart ### Helm Chart
\`\`\`bash \`\`\`bash
helm install devcontainer oci://ghcr.io/cpfarhood/charts/devcontainer --version ${{ steps.version.outputs.version }} helm repo add devcontainer https://cpfarhood.github.io/devcontainer
helm repo update
helm install mydev devcontainer/devcontainer --version ${VERSION} --set name=mydev
\`\`\` \`\`\`
EOF NOTESEOF
sed -i 's/^ //' release-notes.md
echo "notes<<EOF" >> $GITHUB_OUTPUT gh release create "${TAG}" \
cat release-notes.md >> $GITHUB_OUTPUT --title "Release ${TAG}" \
echo "EOF" >> $GITHUB_OUTPUT --notes-file release-notes.md
- 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
+1 -6
View File
@@ -18,11 +18,6 @@
"playwright": { "playwright": {
"type": "sse", "type": "sse",
"url": "http://localhost:8086/sse" "url": "http://localhost:8086/sse"
},
"pgtuner": {
"type": "sse",
"url": "http://localhost:8085/sse"
} }
} }
} }
+35 -11
View File
@@ -6,7 +6,8 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
The Dev Container is a Docker-based cloud development environment that provides: The Dev Container is a Docker-based cloud development environment that provides:
- Web-based GUI IDE (VSCode/Antigravity) via VNC on port 5800 - Web-based GUI IDE (VSCode/Antigravity) via VNC on port 5800
- Claude Code, Happy Coder, OpenCode, and Crush AI coding agents (terminal-based) - Claude Code, OpenCode, and Crush AI coding agents (terminal-based)
- Built-in web file manager for uploading/downloading files (optional, via `fileManager.enabled`)
- Automatic GitHub repository cloning on startup - Automatic GitHub repository cloning on startup
- Kubernetes-native deployment with persistent home storage - Kubernetes-native deployment with persistent home storage
- MCP (Model Context Protocol) sidecars for AI assistant integrations - MCP (Model Context Protocol) sidecars for AI assistant integrations
@@ -60,7 +61,8 @@ Container start
→ scripts/startapp.sh → scripts/startapp.sh
→ scripts/init-repo.sh → scripts/init-repo.sh
→ Configure git user & credentials → Configure git user & credentials
→ Clone GITHUB_REPO (if set) → Clone GITHUB_REPOS or GITHUB_REPO (if set)
→ Generate workspace.code-workspace for multi-repo setups
→ Launch VSCode as user `user` in /workspace → Launch VSCode as user `user` in /workspace
``` ```
@@ -68,8 +70,8 @@ Container start
| File | Purpose | | File | Purpose |
|------|---------| |------|---------|
| `Dockerfile` | Image definition — installs Chrome, Node.js, VSCode, Claude Code, Happy Coder, OpenCode, Crush; creates non-root user (UID 1000) | | `Dockerfile` | Image definition — installs Chrome, VSCode, Helm, gh CLI, kubeseal, Claude Code, OpenCode, Crush, LSP servers (pyright, typescript-language-server, gopls, clangd, rust-analyzer, lua-language-server, jdtls, kotlin-language-server, intelephense); creates non-root user (UID 1000) |
| `scripts/init-repo.sh` | Configures git credentials, clones GitHub repo | | `scripts/init-repo.sh` | Configures git credentials, clones GitHub repo(s), generates multi-root workspace file |
| `scripts/startapp.sh` | Calls init-repo.sh then opens VSCode in the workspace | | `scripts/startapp.sh` | Calls init-repo.sh then opens VSCode in the workspace |
| `chart/` | Helm chart for Kubernetes deployment | | `chart/` | Helm chart for Kubernetes deployment |
| `chart/templates/deployment.yaml` | Deployment spec — main container + MCP sidecar containers | | `chart/templates/deployment.yaml` | Deployment spec — main container + MCP sidecar containers |
@@ -77,7 +79,7 @@ Container start
| `chart/templates/pvc.yaml` | PersistentVolumeClaim for user home | | `chart/templates/pvc.yaml` | PersistentVolumeClaim for user home |
| `chart/templates/service.yaml` | ClusterIP Service (VNC + optional SSH) | | `chart/templates/service.yaml` | ClusterIP Service (VNC + optional SSH) |
| `chart/values.yaml` | Default Helm values | | `chart/values.yaml` | Default Helm values |
| `.mcp.json` | MCP server connection config (GitHub Copilot, Kubernetes, Flux, Playwright, pgtuner) | | `.mcp.json` | MCP server connection config (GitHub Copilot, Kubernetes, Flux, Helm, Fetch, Sequential Thinking, Playwright, pgtuner) |
| `Makefile` | Build/deploy automation | | `Makefile` | Build/deploy automation |
### MCP Sidecars ### MCP Sidecars
@@ -88,6 +90,9 @@ MCP (Model Context Protocol) servers run as sidecar containers in the pod, enabl
|---------|-------|---------|------|----------|---------| |---------|-------|---------|------|----------|---------|
| `kubernetes-mcp` | `quay.io/containers/kubernetes_mcp_server` | v0.0.57 | 8080 | `http://localhost:8080/sse` | Enabled | | `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 | | `flux-mcp` | `ghcr.io/controlplaneio-fluxcd/flux-operator-mcp` | v0.41.1 | 8081 | `http://localhost:8081/sse` | Enabled |
| `helm-mcp` | `ghcr.io/zekker6/mcp-helm` | v1.3.1 | 8012 | `http://localhost:8012/sse` | Enabled |
| `fetch-mcp` | `mcp/fetch` | latest | 8082 | `http://localhost:8082/sse` | Enabled |
| `sequentialthinking-mcp` | `mcp/sequentialthinking` | latest | 8083 | `http://localhost:8083/sse` | Enabled |
| `homeassistant-mcp` | `ghcr.io/homeassistant-ai/ha-mcp` | stable | 8087 | `http://localhost:8087/sse` | Disabled | | `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 | | `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 | | `playwright-mcp` | `mcr.microsoft.com/playwright/mcp` | latest | 8086 | `http://localhost:8086/sse` | Enabled |
@@ -96,6 +101,9 @@ MCP (Model Context Protocol) servers run as sidecar containers in the pod, enabl
- GitHub MCP is accessed via the Copilot API (`https://api.githubcopilot.com/mcp/`), not as a sidecar - 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 require `clusterAccess` != `none` to be deployed (they need RBAC permissions)
- Kubernetes and Flux sidecars inherit the pod's ServiceAccount RBAC permissions - Kubernetes and Flux sidecars inherit the pod's ServiceAccount RBAC permissions
- Helm sidecar enables browsing Helm repositories and chart metadata
- Fetch sidecar provides web content fetching capabilities and HTML to markdown conversion
- Sequential thinking sidecar enables structured thinking and problem-solving processes
- Home Assistant sidecar requires `HOMEASSISTANT_URL` and `HOMEASSISTANT_TOKEN` in the env secret - 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) - PostgreSQL tuner sidecar requires `DATABASE_URI` in the env secret (PostgreSQL connection string)
- Playwright sidecar provides browser automation and web testing capabilities - Playwright sidecar provides browser automation and web testing capabilities
@@ -112,6 +120,12 @@ mcp:
enabled: false enabled: false
flux: flux:
enabled: false enabled: false
helm:
enabled: false
fetch:
enabled: false
sequentialthinking:
enabled: false
homeassistant: homeassistant:
enabled: false enabled: false
pgtuner: pgtuner:
@@ -126,6 +140,12 @@ mcp:
enabled: true # Keep Kubernetes MCP enabled enabled: true # Keep Kubernetes MCP enabled
flux: flux:
enabled: false # Disable Flux MCP enabled: false # Disable Flux MCP
helm:
enabled: true # Enable Helm MCP for chart browsing
fetch:
enabled: true # Enable Fetch MCP for web content fetching
sequentialthinking:
enabled: true # Enable Sequential Thinking MCP for problem-solving
homeassistant: homeassistant:
enabled: true # Enable Home Assistant MCP (requires secrets) enabled: true # Enable Home Assistant MCP (requires secrets)
pgtuner: pgtuner:
@@ -158,8 +178,9 @@ helm install my-devcontainer ./chart -f custom-values.yaml
### Environment Variables ### Environment Variables
**Required:** **Required (at least one):**
- `GITHUB_REPO` — URL of repository to clone into `/workspace` - `GITHUB_REPO` — URL of a single repository to clone into `/workspace`
- `GITHUB_REPOS` — Comma-separated list of repository URLs to clone (takes precedence over `GITHUB_REPO`). When multiple repos are cloned, a `workspace.code-workspace` file is generated for multi-root IDE support.
**Optional:** **Optional:**
- `GITHUB_TOKEN` — PAT for private repo access (automatically configures git credentials) - `GITHUB_TOKEN` — PAT for private repo access (automatically configures git credentials)
@@ -169,19 +190,22 @@ helm install my-devcontainer ./chart -f custom-values.yaml
- `VNC_PASSWORD` — VNC web interface password - `VNC_PASSWORD` — VNC web interface password
- `DISPLAY_WIDTH` / `DISPLAY_HEIGHT` — VNC resolution - `DISPLAY_WIDTH` / `DISPLAY_HEIGHT` — VNC resolution
- `USER_ID` / `GROUP_ID` — Override UID/GID (default 1000) - `USER_ID` / `GROUP_ID` — Override UID/GID (default 1000)
- `HAPPY_SERVER_URL` / `HAPPY_WEBAPP_URL` — Custom Happy Coder endpoints - `WEB_FILE_MANAGER` — Set to `1` to enable the built-in web file manager (controlled via `fileManager.enabled` in Helm values)
- `HAPPY_HOME_DIR` / `HAPPY_EXPERIMENTAL` - `WEB_FILE_MANAGER_ALLOWED_PATHS` — Paths accessible by the file manager (default: `/workspace,/config`)
- `WEB_FILE_MANAGER_DENIED_PATHS` — Paths to deny access to (takes precedence over allowed)
### CI/CD ### 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. - **`build-and-push.yaml`** — Builds and pushes to GHCR on every push to `main`, version tags (`v*`), and PRs. Tags: `latest` (main), semver, branch name, commit SHA.
- **`release-unified.yaml`** — Manual release workflow: bumps chart version, builds Docker image, publishes Helm chart to GitHub Pages (`https://cpfarhood.github.io/devcontainer`), and creates GitHub Release.
- **`dependabot.yml`** — Weekly updates for GitHub Actions and Docker base image. - **`dependabot.yml`** — Weekly updates for GitHub Actions and Docker base image.
Image registry: `ghcr.io/cpfarhood/devcontainer` Image registry: `ghcr.io/cpfarhood/devcontainer`
Helm repo: `https://cpfarhood.github.io/devcontainer`
## Kubernetes Notes ## Kubernetes Notes
- Deployed via Helm chart (`chart/`), published as OCI artifact to GHCR, reconciled by Flux - Deployed via Helm chart (`chart/`), published to GitHub Pages Helm repo, reconciled by Flux
- Storage class is `ceph-filesystem` by default — change via `storage.className` in values - Storage class is `ceph-filesystem` by default — change via `storage.className` in values
- Resource limits: 14 CPU, 28Gi memory - Resource limits: 14 CPU, 28Gi memory
- Health checks (liveness/readiness probes) on port 5800 - Health checks (liveness/readiness probes) on port 5800
-12
View File
@@ -225,18 +225,6 @@ spec:
## Advanced Configurations ## 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 ### Custom Display Resolution
```bash ```bash
+123 -12
View File
@@ -1,5 +1,8 @@
FROM jlesage/baseimage-gui:ubuntu-22.04-v4 FROM jlesage/baseimage-gui:ubuntu-22.04-v4
# Bust cache for all layers below (base image pull is still cached)
ARG CACHE_BUST
# Set environment variables # Set environment variables
ENV APP_NAME="Dev Container" \ ENV APP_NAME="Dev Container" \
KEEP_APP_RUNNING=1 \ KEEP_APP_RUNNING=1 \
@@ -56,13 +59,22 @@ exec /usr/bin/google-chrome-stable \\\n\
"$@"\n' > /usr/local/bin/google-chrome && \ "$@"\n' > /usr/local/bin/google-chrome && \
chmod +x /usr/local/bin/google-chrome chmod +x /usr/local/bin/google-chrome
# Install Node.js (LTS version for Happy Coder) # Install Node.js LTS via NodeSource
RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - && \ ARG NODE_MAJOR=22
RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_MAJOR}.x | bash - && \
apt-get install -y nodejs && \ apt-get install -y nodejs && \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/* && \
node --version && npm --version
# Install Happy Coder and Claude Code globally # Install Claude Code native binary (npm wrapper breaks remote control)
RUN npm install -g happy-coder @anthropic-ai/claude-code RUN curl -fsSL https://claude.ai/install.sh | bash && \
cp /root/.local/bin/claude /usr/local/bin/claude && \
rm -rf /root/.local/bin/claude && \
claude --version
# Disable Claude Code auto-updater (doesn't work inside Docker)
RUN mkdir -p /etc/skel/.claude && \
echo '{"env":{"DISABLE_AUTOUPDATER":"1"}}' > /etc/skel/.claude/settings.json
# Install OpenCode AI coding agent # Install OpenCode AI coding agent
RUN OPENCODE_VERSION=$(curl -sL https://api.github.com/repos/opencode-ai/opencode/releases/latest | jq -r '.tag_name') && \ RUN OPENCODE_VERSION=$(curl -sL https://api.github.com/repos/opencode-ai/opencode/releases/latest | jq -r '.tag_name') && \
@@ -72,13 +84,96 @@ RUN OPENCODE_VERSION=$(curl -sL https://api.github.com/repos/opencode-ai/opencod
# Install Crush AI coding agent (OpenCode successor by Charm) # 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//') && \ 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" | \ curl -fsSL "https://github.com/charmbracelet/crush/releases/download/v${CRUSH_VERSION}/crush_${CRUSH_VERSION}_Linux_x86_64.tar.gz" -o /tmp/crush.tar.gz && \
tar -xz --strip-components=1 -C /usr/local/bin "crush_${CRUSH_VERSION}_Linux_x86_64/crush" && \ tar -xzf /tmp/crush.tar.gz -C /tmp && \
chmod +x /usr/local/bin/crush mv /tmp/crush_${CRUSH_VERSION}_Linux_x86_64/crush /usr/local/bin/crush && \
chmod +x /usr/local/bin/crush && \
rm -rf /tmp/crush*
# Install VSCode # Install Helm CLI for Kubernetes chart management
RUN wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o /usr/share/keyrings/packages.microsoft.gpg && \ ARG HELM_VERSION=3.17.1
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 && \ RUN curl -fsSL "https://get.helm.sh/helm-v${HELM_VERSION}-linux-amd64.tar.gz" | \
tar -xz --strip-components=1 -C /usr/local/bin linux-amd64/helm && \
chmod +x /usr/local/bin/helm
# Install OpenTofu (open-source Terraform alternative)
ARG OPENTOFU_VERSION=1.11.5
RUN curl -fsSL "https://github.com/opentofu/opentofu/releases/download/v${OPENTOFU_VERSION}/tofu_${OPENTOFU_VERSION}_linux_amd64.zip" -o /tmp/tofu.zip && \
unzip -o /tmp/tofu.zip -d /usr/local/bin tofu && \
chmod +x /usr/local/bin/tofu && \
rm /tmp/tofu.zip
# Install GitHub CLI (gh) via official APT repo
RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg && \
chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg && \
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" > /etc/apt/sources.list.d/github-cli.list && \
apt-get update && \
apt-get install -y gh && \
rm -rf /var/lib/apt/lists/*
# Install kubeseal CLI for Bitnami Sealed Secrets
RUN KUBESEAL_VERSION=$(curl -sL https://api.github.com/repos/bitnami-labs/sealed-secrets/releases/latest | jq -r '.tag_name' | sed 's/^v//') && \
curl -fsSL "https://github.com/bitnami-labs/sealed-secrets/releases/download/v${KUBESEAL_VERSION}/kubeseal-${KUBESEAL_VERSION}-linux-amd64.tar.gz" | \
tar -xz -C /usr/local/bin kubeseal && \
chmod +x /usr/local/bin/kubeseal
# ── LSP servers for Claude Code language intelligence ──
# npm-based LSP servers: Python (pyright), TypeScript/JavaScript, PHP
RUN npm install -g pyright typescript-language-server typescript intelephense
# Install Go runtime and gopls LSP server
ARG GO_VERSION=1.23.6
RUN curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar -xz -C /usr/local && \
/usr/local/go/bin/go install golang.org/x/tools/gopls@latest && \
mv /root/go/bin/gopls /usr/local/bin/gopls && \
rm -rf /root/go
ENV PATH="/usr/local/go/bin:${PATH}"
# Install clangd LSP server (C/C++)
RUN apt-get update && \
apt-get install -y clangd && \
rm -rf /var/lib/apt/lists/*
# Install rust-analyzer LSP server (Rust) — standalone binary, no full toolchain needed
RUN RUST_ANALYZER_VERSION=$(curl -sL https://api.github.com/repos/rust-lang/rust-analyzer/releases/latest | jq -r '.tag_name') && \
curl -fsSL "https://github.com/rust-lang/rust-analyzer/releases/download/${RUST_ANALYZER_VERSION}/rust-analyzer-x86_64-unknown-linux-gnu.gz" | \
gunzip > /usr/local/bin/rust-analyzer && \
chmod +x /usr/local/bin/rust-analyzer
# Install lua-language-server (Lua)
RUN LUA_LS_VERSION=$(curl -sL https://api.github.com/repos/LuaLS/lua-language-server/releases/latest | jq -r '.tag_name') && \
mkdir -p /opt/lua-language-server && \
curl -fsSL "https://github.com/LuaLS/lua-language-server/releases/download/${LUA_LS_VERSION}/lua-language-server-${LUA_LS_VERSION}-linux-x64.tar.gz" | \
tar -xz -C /opt/lua-language-server && \
ln -s /opt/lua-language-server/bin/lua-language-server /usr/local/bin/lua-language-server
# Install JDK for Java/Kotlin LSP servers
RUN apt-get update && \
apt-get install -y openjdk-17-jdk-headless && \
rm -rf /var/lib/apt/lists/*
# Install kotlin-language-server
RUN KLS_VERSION=$(curl -sL https://api.github.com/repos/fwcd/kotlin-language-server/releases/latest | jq -r '.tag_name') && \
curl -fsSL "https://github.com/fwcd/kotlin-language-server/releases/download/${KLS_VERSION}/server.zip" -o /tmp/kls.zip && \
unzip -o /tmp/kls.zip -d /opt/kotlin-language-server && \
ln -s /opt/kotlin-language-server/server/bin/kotlin-language-server /usr/local/bin/kotlin-language-server && \
rm /tmp/kls.zip
# Install jdtls (Java LSP) — Eclipse JDT Language Server
RUN JDTLS_VERSION=$(curl -sL https://api.github.com/repos/eclipse-jdtls/eclipse.jdt.ls/releases/latest | jq -r '.tag_name' | sed 's/^v//') && \
JDTLS_URL=$(curl -sL https://api.github.com/repos/eclipse-jdtls/eclipse.jdt.ls/releases/latest | jq -r '.assets[] | select(.name | endswith(".tar.gz")) | .browser_download_url' | head -1) && \
mkdir -p /opt/jdtls && \
curl -fsSL "$JDTLS_URL" | tar -xz -C /opt/jdtls && \
printf '#!/bin/bash\nexec java -Declipse.application=org.eclipse.jdt.ls.core.id1 -Dosgi.bundles.defaultStartLevel=4 -Declipse.product=org.eclipse.jdt.ls.core.product -jar /opt/jdtls/plugins/org.eclipse.equinox.launcher_*.jar -configuration /opt/jdtls/config_linux "$@"\n' > /usr/local/bin/jdtls && \
chmod +x /usr/local/bin/jdtls
# Install VSCode (using Microsoft's current recommended setup)
RUN wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > /tmp/microsoft.gpg && \
install -D -o root -g root -m 644 /tmp/microsoft.gpg /usr/share/keyrings/microsoft.gpg && \
rm -f /tmp/microsoft.gpg && \
printf 'Types: deb\nURIs: https://packages.microsoft.com/repos/code\nSuites: stable\nComponents: main\nArchitectures: amd64\nSigned-By: /usr/share/keyrings/microsoft.gpg\n' \
> /etc/apt/sources.list.d/vscode.sources && \
apt-get update && \ apt-get update && \
apt-get install -y code && \ apt-get install -y code && \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
@@ -89,10 +184,26 @@ RUN mkdir -p /etc/apt/keyrings && \
gpg --dearmor --yes -o /etc/apt/keyrings/antigravity-repo-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" \ 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 && \ > /etc/apt/sources.list.d/antigravity.list && \
# Clear package cache to force fresh repository data
rm -rf /var/lib/apt/lists/* && \
apt-get update && \ apt-get update && \
apt-get install -y antigravity && \ # Show available versions for debugging
apt-cache policy antigravity && \
# Install latest version
apt-get install -y --no-install-recommends antigravity && \
# Display installed version
dpkg -l | grep antigravity && \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
# Pre-configure Antigravity to skip onboarding/setup on first run
RUN mkdir -p /etc/skel/.config/antigravity/User/globalStorage && \
echo '{"antigravityUnifiedStateSync.seenNuxOneTimeMigration": true, "antigravityUnifiedStateSync.browserOnboarding.completed": true, "antigravityUnifiedStateSync.hasOnboardingCompleted": true, "browserOnboarding.hasSeenWelcome": true, "antigravityUnifiedStateSync.browserPreferences.hasAddedLocalhostToAllowlist": true, "antigravityUnifiedStateSync.oauthToken.hasLegacyMigrated": true, "antigravityUnifiedStateSync.auth.tokenSyncEnabled": true, "antigravityUnifiedStateSync.auth.cloudSyncEnabled": true, "theme": "vs-dark"}' \
> /etc/skel/.config/antigravity/User/globalStorage/storage.json && \
echo '{"workbench.startupEditor": "none", "workbench.welcomePage.walkthroughs.openOnInstall": false, "workbench.tips.enabled": false, "extensions.ignoreRecommendations": true, "telemetry.telemetryLevel": "off", "update.mode": "none", "extensions.autoUpdate": false, "extensions.autoCheckUpdates": false, "workbench.enableExperiments": true, "workbench.settings.enableNaturalLanguageSearch": true, "antigravity.onboarding.completed": true, "antigravity.browserOnboarding.completed": true, "antigravity.setup.completed": true, "antigravity.ai.enabled": true, "antigravity.ai.autoComplete.enabled": true, "antigravity.ai.chat.enabled": true, "antigravity.ai.codeActions.enabled": true, "antigravity.ai.explainCode.enabled": true, "antigravity.ai.generateCode.enabled": true, "antigravity.ai.optimizeCode.enabled": true, "antigravity.ai.autoSuggest.enabled": true, "antigravity.telemetry.crashReporter": "on", "antigravity.ai.acceptTerms": true, "antigravity.auth.syncState": true, "antigravity.auth.enableTokenSync": true, "antigravity.ai.enableCloudSync": true, "antigravity.settings.sync": true}' \
> /etc/skel/.config/antigravity/User/settings.json && \
# Validate Antigravity installation
/usr/share/antigravity/antigravity --version || echo "WARNING: Antigravity version check failed"
# Install OpenSSH server (for SSH IDE mode) # Install OpenSSH server (for SSH IDE mode)
RUN apt-get update && \ RUN apt-get update && \
apt-get install -y openssh-server && \ apt-get install -y openssh-server && \
+7 -8
View File
@@ -2,7 +2,7 @@
# Variables # Variables
REGISTRY ?= ghcr.io/cpfarhood REGISTRY ?= ghcr.io/cpfarhood
IMAGE_NAME ?= antigravity IMAGE_NAME ?= devcontainer
IMAGE_TAG ?= latest IMAGE_TAG ?= latest
FULL_IMAGE = $(REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG) FULL_IMAGE = $(REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG)
@@ -26,18 +26,17 @@ run:
-e GITHUB_REPO="${GITHUB_REPO}" \ -e GITHUB_REPO="${GITHUB_REPO}" \
-e GITHUB_TOKEN="${GITHUB_TOKEN}" \ -e GITHUB_TOKEN="${GITHUB_TOKEN}" \
-e VNC_PASSWORD="${VNC_PASSWORD}" \ -e VNC_PASSWORD="${VNC_PASSWORD}" \
-e HAPPY_EXPERIMENTAL="true" \
-v $(PWD)/home:/home \ -v $(PWD)/home:/home \
-v $(PWD)/workspace:/workspace \ -v $(PWD)/workspace:/workspace \
--name antigravity \ --name devcontainer \
$(FULL_IMAGE) $(FULL_IMAGE)
@echo "Access at http://localhost:5800" @echo "Access at http://localhost:5800"
# Stop the running container # Stop the running container
stop: stop:
@echo "Stopping antigravity container..." @echo "Stopping devcontainer..."
docker stop antigravity || true docker stop devcontainer || true
docker rm antigravity || true docker rm devcontainer || true
# Clean up local volumes # Clean up local volumes
clean: stop clean: stop
@@ -81,7 +80,7 @@ helm-port-forward:
# Show help # Show help
help: help:
@echo "Antigravity Dev Container Makefile" @echo "Dev Container Makefile"
@echo "" @echo ""
@echo "Usage: make [target]" @echo "Usage: make [target]"
@echo "" @echo ""
@@ -101,7 +100,7 @@ help:
@echo "" @echo ""
@echo "Variables:" @echo "Variables:"
@echo " REGISTRY - Docker registry (default: ghcr.io/cpfarhood)" @echo " REGISTRY - Docker registry (default: ghcr.io/cpfarhood)"
@echo " IMAGE_NAME - Image name (default: antigravity)" @echo " IMAGE_NAME - Image name (default: devcontainer)"
@echo " IMAGE_TAG - Image tag (default: latest)" @echo " IMAGE_TAG - Image tag (default: latest)"
@echo " RELEASE_NAME - Helm release name (default: mydev)" @echo " RELEASE_NAME - Helm release name (default: mydev)"
@echo " NAMESPACE - Kubernetes namespace (default: default)" @echo " NAMESPACE - Kubernetes namespace (default: default)"
+37 -38
View File
@@ -5,30 +5,39 @@
A containerized cloud development environment with web-based GUI access, featuring: A containerized cloud development environment with web-based GUI access, featuring:
- **VSCode or Google Antigravity** via browser-based VNC (port 5800) - **VSCode or Google Antigravity** via browser-based VNC (port 5800)
- **SSH access** option (OpenSSH on port 22, additive with any IDE) - **SSH access** option (OpenSSH on port 22, additive with any IDE)
- **Claude Code**, **Happy Coder**, **OpenCode**, and **Crush** AI coding agents (terminal-based) - **Claude Code**, **OpenCode**, and **Crush** AI coding agents (terminal-based)
- **Built-in web file manager** for uploading/downloading files via the VNC web interface
- **Helm CLI** included for Kubernetes chart development and deployment
- **Automatic GitHub repo cloning** on startup - **Automatic GitHub repo cloning** on startup
- **Persistent home directory** via ReadWriteMany PVC - **Persistent home directory** via ReadWriteMany PVC
- **Kubernetes-native** Helm chart deployment - **Kubernetes-native** Helm chart deployment
## Quick Start ## Quick Start
### Option A: Quickstart (Recommended) ### Option A: Install from Helm Repo (Recommended)
For 80% of users, use the simplified quickstart values:
```bash ```bash
# Copy and customize the quickstart template # Add the Helm repository
helm repo add devcontainer https://cpfarhood.github.io/devcontainer
helm repo update
# Deploy with one command
helm install mydev devcontainer/devcontainer \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo
```
### Option B: Install from Source
```bash
# Clone and customize the quickstart template
cp chart/values-quickstart.yaml my-values.yaml cp chart/values-quickstart.yaml my-values.yaml
# Edit my-values.yaml to set your name and repository
# 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 helm install mydev ./chart -f my-values.yaml
``` ```
### Option B: One-Command Deploy ### Option C: One-Command from Source
```bash ```bash
helm install mydev ./chart \ helm install mydev ./chart \
@@ -105,7 +114,7 @@ The Helm chart uses a logical organization with these main sections:
- **Basic Configuration**: name, image, githubRepo - **Basic Configuration**: name, image, githubRepo
- **Access & Interface**: IDE, SSH, display, user settings - **Access & Interface**: IDE, SSH, display, user settings
- **Infrastructure**: storage, resources, cluster access - **Infrastructure**: storage, resources, cluster access
- **Integrations**: Happy Coder, MCP sidecars - **Integrations**: MCP sidecars
- **Smart Defaults**: auto-detection and profiles - **Smart Defaults**: auto-detection and profiles
📖 **Documentation**: 📖 **Documentation**:
@@ -121,6 +130,7 @@ The Helm chart uses a logical organization with these main sections:
| `githubRepo` | `""` | Repository to clone into `/workspace` on startup | | `githubRepo` | `""` | Repository to clone into `/workspace` on startup |
| `ide.type` | `vscode` | IDE to launch — `vscode`, `antigravity`, or `none` (see below) | | `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) | | `ssh.enabled` | `false` | Also start an OpenSSH server on port 22 (additive, any IDE) |
| `fileManager.enabled` | `false` | Enable the built-in web file manager for upload/download |
| `image.repository` | `ghcr.io/cpfarhood/devcontainer` | Container image | | `image.repository` | `ghcr.io/cpfarhood/devcontainer` | Container image |
| `image.tag` | `latest` | Image tag | | `image.tag` | `latest` | Image tag |
@@ -161,14 +171,23 @@ kubectl port-forward deployment/devcontainer-mydev 2222:22
ssh -p 2222 user@localhost ssh -p 2222 user@localhost
``` ```
### Happy Coder ### Web file manager
The base image includes a built-in web file manager for uploading and downloading files through the VNC web interface (port 5800). No additional sidecar is needed.
| Value | Default | Description | | Value | Default | Description |
|-------|---------|-------------| |-------|---------|-------------|
| `happy.serverUrl` | `https://happy.farh.net` | Happy Coder server endpoint | | `fileManager.enabled` | `false` | Enable the web file manager |
| `happy.webappUrl` | `https://happy-coder.farh.net` | Happy Coder webapp URL | | `fileManager.allowedPaths` | `/workspace,/config` | Paths accessible by the file manager (`AUTO`, `ALL`, or comma-separated) |
| `happy.homeDir` | `/config/userdata/.happy` | Happy runtime state directory (persists on the home PVC) | | `fileManager.deniedPaths` | `""` | Paths to deny (takes precedence over allowed) |
| `happy.experimental` | `true` | Enable experimental Happy features |
```bash
# Enable the file manager
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set fileManager.enabled=true
```
### Kubernetes cluster access ### Kubernetes cluster access
@@ -349,30 +368,10 @@ Container start
| `/config` | ReadWriteMany PVC (`userhome-{name}`) | Survives pod restarts — stores Claude credentials, dotfiles, git config | | `/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 | | `/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 ## 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 ### Claude not authenticated
Browser-based OAuth login is the primary method (works inside VNC via the Chrome wrapper). If you prefer API key auth: Browser-based OAuth login is the primary method (works inside VNC via the Chrome wrapper). If you prefer API key auth:
@@ -438,4 +437,4 @@ The image is also built and pushed automatically by CI on every push to `main` a
## Credits ## Credits
- Base image: [jlesage/docker-baseimage-gui](https://github.com/jlesage/docker-baseimage-gui) - Base image: [jlesage/docker-baseimage-gui](https://github.com/jlesage/docker-baseimage-gui)
- AI assistant: [Happy Coder](https://happy.engineering) + [Claude](https://claude.ai) - AI assistant: [Claude](https://claude.ai)
-30
View File
@@ -52,30 +52,6 @@ Complete reference for all configurable values in the Antigravity Dev Container
- **Options:** `Always`, `IfNotPresent`, `Never` - **Options:** `Always`, `IfNotPresent`, `Never`
- **Description:** Image pull policy - **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 Configuration
### display.width ### display.width
@@ -339,8 +315,6 @@ storage:
clusterAccess: readonly clusterAccess: readonly
happyServerUrl: https://happy.internal.company.com
happyWebappUrl: https://happy-app.internal.company.com
``` ```
### Smart Home Development Configuration ### Smart Home Development Configuration
@@ -431,10 +405,6 @@ These environment variables are set in the container based on chart values:
| `VNC_PASSWORD` | Secret: `vnc-password` | VNC access password | | `VNC_PASSWORD` | Secret: `vnc-password` | VNC access password |
| `ANTHROPIC_API_KEY` | Secret: `anthropic-api-key` | Claude API key | | `ANTHROPIC_API_KEY` | Secret: `anthropic-api-key` | Claude API key |
| `SSH_AUTHORIZED_KEYS` | Secret: `ssh-authorized-keys` | SSH public keys | | `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_WIDTH` | `display.width` | VNC width |
| `DISPLAY_HEIGHT` | `display.height` | VNC height | | `DISPLAY_HEIGHT` | `display.height` | VNC height |
| `SECURE_CONNECTION` | `secureConnection` | TLS termination | | `SECURE_CONNECTION` | `secureConnection` | TLS termination |
+6 -1
View File
@@ -2,5 +2,10 @@ apiVersion: v2
name: devcontainer name: devcontainer
description: Dev Container with AI coding agents and MCP sidecars description: Dev Container with AI coding agents and MCP sidecars
type: application type: application
version: 0.4.2 version: 2.5.0
appVersion: "latest" appVersion: "latest"
keywords:
- development
- devcontainer
- vscode
- ai
+31
View File
@@ -0,0 +1,31 @@
Dev Container "{{ .Values.name }}" has been deployed.
{{- if ne (.Values.ide.type | default "vscode") "none" }}
Access the IDE:
kubectl port-forward deployment/{{ include "devcontainer.fullname" . }} 5800:5800 -n {{ .Release.Namespace }}
Then open: http://localhost:5800
{{- end }}
{{- if .Values.ssh.enabled }}
SSH access:
kubectl port-forward deployment/{{ include "devcontainer.fullname" . }} 2222:22 -n {{ .Release.Namespace }}
Then: ssh -p 2222 user@localhost
{{- end }}
Useful commands:
Logs: kubectl logs -f deployment/{{ include "devcontainer.fullname" . }} -n {{ .Release.Namespace }}
Shell: kubectl exec -it deployment/{{ include "devcontainer.fullname" . }} -n {{ .Release.Namespace }} -- bash
{{- if not (lookup "v1" "Secret" .Release.Namespace (include "devcontainer.envSecretName" .)) }}
Optional: Create a secret for GITHUB_TOKEN, VNC_PASSWORD, etc:
kubectl create secret generic {{ include "devcontainer.envSecretName" . }} \
--from-literal=GITHUB_TOKEN=ghp_xxx \
--from-literal=VNC_PASSWORD=changeme \
-n {{ .Release.Namespace }}
{{- end }}
Note: The PVC "{{ include "devcontainer.pvcName" . }}" is protected from deletion on helm uninstall.
To remove it manually: kubectl delete pvc {{ include "devcontainer.pvcName" . }} -n {{ .Release.Namespace }}
+15
View File
@@ -2,6 +2,9 @@
Resource name prefix: devcontainer-{name} Resource name prefix: devcontainer-{name}
*/}} */}}
{{- define "devcontainer.fullname" -}} {{- define "devcontainer.fullname" -}}
{{- if not .Values.name }}
{{- fail "values.name is required and must not be empty" }}
{{- end }}
{{- printf "devcontainer-%s" .Values.name }} {{- printf "devcontainer-%s" .Values.name }}
{{- end }} {{- end }}
@@ -25,6 +28,18 @@ Common labels
{{- define "devcontainer.labels" -}} {{- define "devcontainer.labels" -}}
app: devcontainer app: devcontainer
instance: {{ .Values.name }} instance: {{ .Values.name }}
app.kubernetes.io/name: devcontainer
app.kubernetes.io/instance: {{ .Values.name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
{{- end }}
{{/*
Selector labels keep narrow since changing these requires recreating the Deployment
*/}}
{{- define "devcontainer.selectorLabels" -}}
app: devcontainer
instance: {{ .Values.name }}
{{- end }} {{- end }}
{{/* {{/*
+45 -13
View File
@@ -8,7 +8,7 @@ spec:
replicas: 1 replicas: 1
selector: selector:
matchLabels: matchLabels:
{{- include "devcontainer.labels" . | nindent 6 }} {{- include "devcontainer.selectorLabels" . | nindent 6 }}
template: template:
metadata: metadata:
labels: labels:
@@ -23,7 +23,7 @@ spec:
{{- if and .Values.ide.type (eq .Values.ide.type "antigravity") }} {{- if and .Values.ide.type (eq .Values.ide.type "antigravity") }}
initContainers: initContainers:
- name: setup-userdata - name: setup-userdata
image: busybox:latest image: busybox:1.37
command: ['sh', '-c'] command: ['sh', '-c']
args: args:
- | - |
@@ -69,16 +69,24 @@ spec:
value: {{ .Values.display.height | quote }} value: {{ .Values.display.height | quote }}
- name: SECURE_CONNECTION - name: SECURE_CONNECTION
value: {{ .Values.display.secureConnection | quote }} value: {{ .Values.display.secureConnection | quote }}
- name: HAPPY_HOME_DIR {{- if .Values.fileManager.enabled }}
value: {{ .Values.happy.homeDir | quote }} - name: WEB_FILE_MANAGER
- name: HAPPY_EXPERIMENTAL value: "1"
value: {{ .Values.happy.experimental | quote }} - name: WEB_FILE_MANAGER_ALLOWED_PATHS
- name: HAPPY_SERVER_URL value: {{ .Values.fileManager.allowedPaths | quote }}
value: {{ .Values.happy.serverUrl | quote }} {{- if .Values.fileManager.deniedPaths }}
- name: HAPPY_WEBAPP_URL - name: WEB_FILE_MANAGER_DENIED_PATHS
value: {{ .Values.happy.webappUrl | quote }} value: {{ .Values.fileManager.deniedPaths | quote }}
{{- end }}
{{- end }}
{{- if .Values.githubRepo }}
- name: GITHUB_REPO - name: GITHUB_REPO
value: {{ .Values.githubRepo | quote }} value: {{ .Values.githubRepo | quote }}
{{- end }}
{{- if .Values.githubRepos }}
- name: GITHUB_REPOS
value: {{ join "," .Values.githubRepos | quote }}
{{- end }}
envFrom: envFrom:
- secretRef: - secretRef:
name: {{ include "devcontainer.envSecretName" . }} name: {{ include "devcontainer.envSecretName" . }}
@@ -166,10 +174,32 @@ spec:
resources: resources:
{{- toYaml .Values.mcp.sidecars.flux.resources | nindent 12 }} {{- toYaml .Values.mcp.sidecars.flux.resources | nindent 12 }}
{{- end }} {{- end }}
{{- if .Values.mcp.sidecars.helm.enabled }}
- name: helm-mcp
image: "{{ .Values.mcp.sidecars.helm.image.repository }}:{{ .Values.mcp.sidecars.helm.image.tag }}"
args:
- -mode=sse
ports:
- containerPort: {{ .Values.mcp.sidecars.helm.port }}
name: helm-mcp
protocol: TCP
livenessProbe:
tcpSocket:
port: {{ .Values.mcp.sidecars.helm.port }}
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
tcpSocket:
port: {{ .Values.mcp.sidecars.helm.port }}
initialDelaySeconds: 5
periodSeconds: 5
resources:
{{- toYaml .Values.mcp.sidecars.helm.resources | nindent 12 }}
{{- end }}
{{- if .Values.mcp.sidecars.homeassistant.enabled }} {{- if .Values.mcp.sidecars.homeassistant.enabled }}
- name: homeassistant-mcp - name: homeassistant-mcp
image: "{{ .Values.mcp.sidecars.homeassistant.image.repository }}:{{ .Values.mcp.sidecars.homeassistant.image.tag }}" image: "{{ .Values.mcp.sidecars.homeassistant.image.repository }}:{{ .Values.mcp.sidecars.homeassistant.image.tag }}"
imagePullPolicy: Always imagePullPolicy: IfNotPresent
command: ["fastmcp", "run", "--transport", "sse", "--host", "0.0.0.0", "--port", "{{ .Values.mcp.sidecars.homeassistant.port }}"] command: ["fastmcp", "run", "--transport", "sse", "--host", "0.0.0.0", "--port", "{{ .Values.mcp.sidecars.homeassistant.port }}"]
ports: ports:
- name: homeassistant - name: homeassistant
@@ -203,7 +233,7 @@ spec:
{{- if .Values.mcp.sidecars.pgtuner.enabled }} {{- if .Values.mcp.sidecars.pgtuner.enabled }}
- name: pgtuner-mcp - name: pgtuner-mcp
image: "{{ .Values.mcp.sidecars.pgtuner.image.repository }}:{{ .Values.mcp.sidecars.pgtuner.image.tag }}" image: "{{ .Values.mcp.sidecars.pgtuner.image.repository }}:{{ .Values.mcp.sidecars.pgtuner.image.tag }}"
imagePullPolicy: Always imagePullPolicy: Always # pgtuner uses `latest` tag (no versioned releases available)
command: ["python", "-m", "pgtuner_mcp", "--mode", "sse", "--host", "0.0.0.0", "--port", "{{ .Values.mcp.sidecars.pgtuner.port }}"] command: ["python", "-m", "pgtuner_mcp", "--mode", "sse", "--host", "0.0.0.0", "--port", "{{ .Values.mcp.sidecars.pgtuner.port }}"]
ports: ports:
- name: pgtuner - name: pgtuner
@@ -237,7 +267,7 @@ spec:
{{- if .Values.mcp.sidecars.playwright.enabled }} {{- if .Values.mcp.sidecars.playwright.enabled }}
- name: playwright-mcp - name: playwright-mcp
image: "{{ .Values.mcp.sidecars.playwright.image.repository }}:{{ .Values.mcp.sidecars.playwright.image.tag }}" image: "{{ .Values.mcp.sidecars.playwright.image.repository }}:{{ .Values.mcp.sidecars.playwright.image.tag }}"
imagePullPolicy: Always imagePullPolicy: IfNotPresent
command: ["node"] command: ["node"]
args: args:
- cli.js - cli.js
@@ -245,6 +275,8 @@ spec:
- --browser - --browser
- chromium - chromium
- --no-sandbox - --no-sandbox
- --host
- 0.0.0.0
- --port - --port
- {{ .Values.mcp.sidecars.playwright.port | quote }} - {{ .Values.mcp.sidecars.playwright.port | quote }}
ports: ports:
+4
View File
@@ -2,12 +2,16 @@ apiVersion: v1
kind: PersistentVolumeClaim kind: PersistentVolumeClaim
metadata: metadata:
name: {{ include "devcontainer.pvcName" . }} name: {{ include "devcontainer.pvcName" . }}
annotations:
helm.sh/resource-policy: keep
labels: labels:
{{- include "devcontainer.labels" . | nindent 4 }} {{- include "devcontainer.labels" . | nindent 4 }}
spec: spec:
accessModes: accessModes:
- ReadWriteMany - ReadWriteMany
{{- if .Values.storage.className }}
storageClassName: {{ .Values.storage.className }} storageClassName: {{ .Values.storage.className }}
{{- end }}
resources: resources:
requests: requests:
storage: {{ .Values.storage.size }} storage: {{ .Values.storage.size }}
+5
View File
@@ -8,6 +8,11 @@ name: mydev
# GitHub repository to clone (required) # GitHub repository to clone (required)
githubRepo: https://github.com/youruser/yourrepo githubRepo: https://github.com/youruser/yourrepo
# Multiple repositories (optional, takes precedence over githubRepo)
# githubRepos:
# - https://github.com/youruser/repo1
# - https://github.com/youruser/repo2
# IDE choice (optional - defaults to vscode) # IDE choice (optional - defaults to vscode)
# Options: vscode | antigravity | none # Options: vscode | antigravity | none
ide: ide:
+35 -29
View File
@@ -4,6 +4,7 @@
"title": "Dev Container Helm Chart Values Schema", "title": "Dev Container Helm Chart Values Schema",
"description": "Schema for validating values.yaml in the Dev Container Helm chart", "description": "Schema for validating values.yaml in the Dev Container Helm chart",
"type": "object", "type": "object",
"additionalProperties": true,
"properties": { "properties": {
"name": { "name": {
"type": "string", "type": "string",
@@ -33,8 +34,30 @@
}, },
"githubRepo": { "githubRepo": {
"type": "string", "type": "string",
"description": "GitHub repository URL to clone", "description": "GitHub repository URL to clone into /workspace"
"pattern": "^https://github\\.com/.+/.+$" },
"githubRepos": {
"type": "array",
"items": { "type": "string" },
"description": "Multiple GitHub repository URLs to clone (takes precedence over githubRepo)"
},
"fileManager": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable the built-in web file manager"
},
"allowedPaths": {
"type": "string",
"description": "Paths accessible by the file manager (AUTO, ALL, or comma-separated list)"
},
"deniedPaths": {
"type": "string",
"description": "Paths to deny access to (takes precedence over allowedPaths)"
}
},
"required": ["enabled"]
}, },
"ide": { "ide": {
"type": "object", "type": "object",
@@ -107,7 +130,7 @@
"description": "Storage class name (must support ReadWriteMany)" "description": "Storage class name (must support ReadWriteMany)"
} }
}, },
"required": ["size", "className"] "required": ["size"]
}, },
"resources": { "resources": {
"type": "object", "type": "object",
@@ -137,31 +160,6 @@
"enum": ["none", "readonlyns", "readwritens", "readonly", "readwrite"], "enum": ["none", "readonlyns", "readwritens", "readonly", "readwrite"],
"description": "Kubernetes cluster access level" "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": { "mcp": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -180,6 +178,9 @@
"pgtuner": { "pgtuner": {
"$ref": "#/$defs/mcpSidecar" "$ref": "#/$defs/mcpSidecar"
}, },
"helm": {
"$ref": "#/$defs/mcpSidecar"
},
"playwright": { "playwright": {
"$ref": "#/$defs/mcpSidecar" "$ref": "#/$defs/mcpSidecar"
} }
@@ -192,6 +193,11 @@
"envSecretName": { "envSecretName": {
"type": "string", "type": "string",
"description": "Custom environment secret name" "description": "Custom environment secret name"
},
"resourceProfile": {
"type": "string",
"enum": ["auto", "small", "medium", "large", "xlarge"],
"description": "Resource profile preset"
} }
}, },
"required": ["name"], "required": ["name"],
@@ -253,4 +259,4 @@
"required": ["enabled", "image", "port", "resources"] "required": ["enabled", "image", "port", "resources"]
} }
} }
} }
+36 -10
View File
@@ -14,6 +14,13 @@ image:
# GitHub repository to clone into /workspace # GitHub repository to clone into /workspace
githubRepo: "" githubRepo: ""
# Multiple GitHub repositories to clone into /workspace (takes precedence over githubRepo)
# Example:
# githubRepos:
# - https://github.com/user/repo1
# - https://github.com/user/repo2
githubRepos: []
# ============================================================================= # =============================================================================
# ACCESS & INTERFACE # ACCESS & INTERFACE
# ============================================================================= # =============================================================================
@@ -27,6 +34,16 @@ ide:
ssh: ssh:
enabled: false enabled: false
# Web file manager — built-in upload/download via the VNC web interface (port 5800)
# Uses the base image's WEB_FILE_MANAGER feature (no extra sidecar needed)
fileManager:
enabled: false
# Paths the file manager can access (default: AUTO = mapped volumes)
# Options: AUTO | ALL | comma-separated list of paths
allowedPaths: "/workspace,/config"
# Paths to deny (takes precedence over allowedPaths)
deniedPaths: ""
# VNC display settings # VNC display settings
display: display:
width: "1920" width: "1920"
@@ -45,7 +62,7 @@ user:
# Storage configuration # Storage configuration
storage: storage:
size: 32Gi size: 32Gi
className: ceph-filesystem className: "" # Empty string uses the cluster's default StorageClass (must support ReadWriteMany)
# Resource allocation # Resource allocation
resources: resources:
@@ -68,13 +85,6 @@ clusterAccess: none
# INTEGRATIONS # 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 (Model Context Protocol) server sidecars
mcp: mcp:
sidecars: sidecars:
@@ -108,12 +118,28 @@ mcp:
memory: "256Mi" memory: "256Mi"
cpu: "500m" cpu: "500m"
# Helm chart browsing and management
helm:
enabled: false
image:
repository: ghcr.io/zekker6/mcp-helm
tag: v1.3.1
port: 8012
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "500m"
# Home Assistant smart home control # Home Assistant smart home control
homeassistant: homeassistant:
enabled: false # Requires HOMEASSISTANT_URL and HOMEASSISTANT_TOKEN enabled: false # Requires HOMEASSISTANT_URL and HOMEASSISTANT_TOKEN
image: image:
repository: ghcr.io/homeassistant-ai/ha-mcp repository: ghcr.io/homeassistant-ai/ha-mcp
tag: stable tag: "6.7.1"
port: 8087 port: 8087
resources: resources:
requests: requests:
@@ -143,7 +169,7 @@ mcp:
enabled: true enabled: true
image: image:
repository: mcr.microsoft.com/playwright/mcp repository: mcr.microsoft.com/playwright/mcp
tag: latest tag: v0.0.68
port: 8086 port: 8086
resources: resources:
requests: requests:
+6 -2
View File
@@ -2,5 +2,9 @@
# Fix the app user (UID 1000) created by baseimage-gui at runtime. # Fix the app user (UID 1000) created by baseimage-gui at runtime.
# baseimage-gui sets shell=/sbin/nologin and home=/dev/null, which # baseimage-gui sets shell=/sbin/nologin and home=/dev/null, which
# prevents VSCode from opening terminals. # prevents VSCode from opening terminals.
usermod -s /bin/bash app if id app >/dev/null 2>&1; then
usermod -d /config/userdata app usermod -s /bin/bash app
usermod -d /config/userdata app
else
echo "WARNING: 'app' user not found, skipping usermod" >&2
fi
+74 -24
View File
@@ -22,6 +22,7 @@ if [ -n "$GITHUB_TOKEN" ]; then
# Create or update the credentials file # Create or update the credentials file
CREDENTIALS_FILE="/config/userdata/.git-credentials" CREDENTIALS_FILE="/config/userdata/.git-credentials"
mkdir -p "$(dirname "$CREDENTIALS_FILE")"
# Support multiple git hosting providers # Support multiple git hosting providers
# GitHub supports both oauth2 and token as username # GitHub supports both oauth2 and token as username
@@ -51,6 +52,7 @@ else
# Create an empty credentials file with proper permissions # Create an empty credentials file with proper permissions
CREDENTIALS_FILE="/config/userdata/.git-credentials" CREDENTIALS_FILE="/config/userdata/.git-credentials"
mkdir -p "$(dirname "$CREDENTIALS_FILE")"
touch "$CREDENTIALS_FILE" touch "$CREDENTIALS_FILE"
chmod 600 "$CREDENTIALS_FILE" chmod 600 "$CREDENTIALS_FILE"
@@ -60,48 +62,96 @@ else
fi fi
fi fi
# Check if GITHUB_REPO is set # Build list of repositories to clone
if [ -z "$GITHUB_REPO" ]; then REPOS=()
echo "GITHUB_REPO not set, skipping repository clone" if [ -n "$GITHUB_REPOS" ]; then
# GITHUB_REPOS is a comma-separated list (takes precedence over GITHUB_REPO)
IFS=',' read -ra RAW_REPOS <<< "$GITHUB_REPOS"
for repo in "${RAW_REPOS[@]}"; do
repo="$(echo "$repo" | xargs)" # trim whitespace
[ -n "$repo" ] && REPOS+=("$repo")
done
elif [ -n "$GITHUB_REPO" ]; then
REPOS+=("$GITHUB_REPO")
fi
if [ ${#REPOS[@]} -eq 0 ]; then
echo "No repositories configured, skipping clone"
WORKSPACE_DIR="/workspace/default" WORKSPACE_DIR="/workspace/default"
mkdir -p "$WORKSPACE_DIR" mkdir -p "$WORKSPACE_DIR"
else else
# Parse repo name from URL CLONED_DIRS=()
REPO_NAME=$(basename "$GITHUB_REPO" .git) for REPO_URL in "${REPOS[@]}"; do
WORKSPACE_DIR="/workspace/$REPO_NAME" REPO_NAME=$(basename "$REPO_URL" .git)
REPO_DIR="/workspace/$REPO_NAME"
echo "Repository: $GITHUB_REPO" echo "Repository: $REPO_URL"
echo "Target directory: $WORKSPACE_DIR" echo "Target directory: $REPO_DIR"
# Check if repo already exists if [ -d "$REPO_DIR/.git" ]; then
if [ -d "$WORKSPACE_DIR/.git" ]; then echo "Repository already exists, pulling latest changes..."
echo "Repository already exists, pulling latest changes..." cd "$REPO_DIR"
cd "$WORKSPACE_DIR" git pull || echo "Pull failed, continuing anyway..."
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 else
git clone "$GITHUB_REPO" "$WORKSPACE_DIR" echo "Cloning repository..."
mkdir -p "$(dirname "$REPO_DIR")"
if [ -n "$GITHUB_TOKEN" ]; then
CLONE_URL=$(echo "$REPO_URL" | sed "s|https://github.com/|https://oauth2:${GITHUB_TOKEN}@github.com/|")
git clone "$CLONE_URL" "$REPO_DIR"
else
git clone "$REPO_URL" "$REPO_DIR"
fi
fi fi
CLONED_DIRS+=("$REPO_DIR")
done
if [ ${#CLONED_DIRS[@]} -eq 1 ]; then
# Single repo — open directory directly (same as legacy behavior)
WORKSPACE_DIR="${CLONED_DIRS[0]}"
else
# Multiple repos — generate a multi-root workspace file
WS_FILE="/workspace/workspace.code-workspace"
printf '{\n "folders": [\n' > "$WS_FILE"
for i in "${!CLONED_DIRS[@]}"; do
printf ' {"path": "%s"}' "${CLONED_DIRS[$i]}" >> "$WS_FILE"
if [ "$i" -lt $(( ${#CLONED_DIRS[@]} - 1 )) ]; then
printf ',\n' >> "$WS_FILE"
else
printf '\n' >> "$WS_FILE"
fi
done
printf ' ],\n "settings": {}\n}\n' >> "$WS_FILE"
WORKSPACE_DIR="$WS_FILE"
echo "Generated multi-root workspace: $WS_FILE"
fi fi
fi fi
# Set ownership using numeric IDs (username may not exist yet in baseimage-gui) # Set ownership using numeric IDs (username may not exist yet in baseimage-gui)
RUN_UID="${USER_ID:-1000}" RUN_UID="${USER_ID:-1000}"
RUN_GID="${GROUP_ID:-1000}" RUN_GID="${GROUP_ID:-1000}"
chown -R "$RUN_UID:$RUN_GID" "$WORKSPACE_DIR" for dir in "${CLONED_DIRS[@]}"; do
chown -R "$RUN_UID:$RUN_GID" "$dir"
done
if [ -n "$WS_FILE" ] && [ -f "$WS_FILE" ]; then
chown "$RUN_UID:$RUN_GID" "$WS_FILE"
fi
# Ensure default workspace dir ownership if no repos were cloned
if [ ${#REPOS[@]} -eq 0 ]; then
chown -R "$RUN_UID:$RUN_GID" "$WORKSPACE_DIR"
fi
# Ensure home directory exists on the PVC (may be absent on a fresh volume) # Ensure home directory exists on the PVC (may be absent on a fresh volume)
mkdir -p "$HOME" mkdir -p "$HOME"
chown "$RUN_UID:$RUN_GID" "$HOME" chown "$RUN_UID:$RUN_GID" "$HOME"
# Seed Claude Code settings if missing (disable auto-updater in Docker)
if [ ! -f "$HOME/.claude/settings.json" ]; then
mkdir -p "$HOME/.claude"
echo '{"env":{"DISABLE_AUTOUPDATER":"1"}}' > "$HOME/.claude/settings.json"
chown -R "$RUN_UID:$RUN_GID" "$HOME/.claude"
fi
# Export workspace directory for startapp.sh # Export workspace directory for startapp.sh
echo "$WORKSPACE_DIR" > /tmp/workspace-dir echo "$WORKSPACE_DIR" > /tmp/workspace-dir
+3
View File
@@ -34,6 +34,9 @@ case "$IDE" in
exec sleep infinity exec sleep infinity
;; ;;
*) *)
if [ "$IDE" != "vscode" ]; then
echo "WARNING: Unknown IDE value '$IDE', defaulting to VSCode"
fi
echo "Opening VSCode in: $WORKSPACE_DIR" echo "Opening VSCode in: $WORKSPACE_DIR"
exec code --new-window --wait "$WORKSPACE_DIR" exec code --new-window --wait "$WORKSPACE_DIR"
;; ;;