Compare commits

..

30 Commits

Author SHA1 Message Date
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
20 changed files with 111 additions and 240 deletions
-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 -51
View File
@@ -4,7 +4,6 @@ on:
push: push:
branches: branches:
- main - main
- 'feature/serverless-*' # Build development images for serverless features
pull_request: pull_request:
branches: branches:
- main - main
@@ -17,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
@@ -47,8 +48,6 @@ jobs:
type=ref,event=pr type=ref,event=pr
type=sha,prefix=sha- type=sha,prefix=sha-
type=raw,value=latest,enable={{is_default_branch}} type=raw,value=latest,enable={{is_default_branch}}
# Development tags for serverless features
type=raw,value=2.0.0-dev,enable=${{ github.ref == 'refs/heads/feature/serverless-2.0.0' }}
- name: Build and push Docker image - name: Build and push Docker image
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
@@ -57,53 +56,6 @@ 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: |
TOOLS_CACHEBUST=${{ github.run_id }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64
# Build routing proxy image for serverless features
build-routing-proxy:
runs-on: ubuntu-latest
# Only build routing proxy for serverless feature branches
if: github.ref == 'refs/heads/feature/serverless-2.0.0' && github.event_name != 'pull_request'
permissions:
contents: read
packages: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata for routing proxy
id: meta-proxy
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/cpfarhood/devcontainer-routing-proxy
tags: |
type=raw,value=latest
type=raw,value=2.0.0-dev
type=sha,prefix=sha-
- name: Build and push routing proxy image
uses: docker/build-push-action@v6
with:
context: ./serverless/routing-proxy
push: true
tags: ${{ steps.meta-proxy.outputs.tags }}
labels: ${{ steps.meta-proxy.outputs.labels }}
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
+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:
+12 -11
View File
@@ -100,8 +100,6 @@ jobs:
${{ 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
build-args: |
TOOLS_CACHEBUST=${{ github.run_id }}
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
@@ -158,34 +156,37 @@ jobs:
- name: Create GitHub Release - name: Create GitHub Release
env: env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 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: |
# Build release notes 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 > release-notes.md <<EOF 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 repo add devcontainer https://cpfarhood.github.io/devcontainer helm repo add devcontainer https://cpfarhood.github.io/devcontainer
helm repo update helm repo update
helm install mydev devcontainer/devcontainer --version ${{ steps.version.outputs.version }} --set name=mydev helm install mydev devcontainer/devcontainer --version ${VERSION} --set name=mydev
\`\`\` \`\`\`
EOF NOTESEOF
sed -i 's/^ //' release-notes.md
gh release create "${{ steps.version.outputs.tag }}" \ gh release create "${TAG}" \
--title "Release ${{ steps.version.outputs.tag }}" \ --title "Release ${TAG}" \
--notes-file release-notes.md --notes-file release-notes.md
+3 -4
View File
@@ -19,10 +19,9 @@
"type": "sse", "type": "sse",
"url": "http://localhost:8086/sse" "url": "http://localhost:8086/sse"
}, },
"pgtuner": { "helm": {
"type": "sse", "type": "sse",
"url": "http://localhost:8085/sse" "url": "http://localhost:8012/sse"
} }
} }
} }
+9 -5
View File
@@ -6,7 +6,7 @@ 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`) - 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
@@ -69,7 +69,7 @@ Container start
| File | Purpose | | File | Purpose |
|------|---------| |------|---------|
| `Dockerfile` | Image definition — installs Chrome, Node.js, VSCode, Helm, 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; 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 |
| `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 |
@@ -78,7 +78,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, Fetch, Sequential Thinking, 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
@@ -89,6 +89,7 @@ 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 | | `fetch-mcp` | `mcp/fetch` | latest | 8082 | `http://localhost:8082/sse` | Enabled |
| `sequentialthinking-mcp` | `mcp/sequentialthinking` | latest | 8083 | `http://localhost:8083/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 |
@@ -99,6 +100,7 @@ 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 - Fetch sidecar provides web content fetching capabilities and HTML to markdown conversion
- Sequential thinking sidecar enables structured thinking and problem-solving processes - 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
@@ -117,6 +119,8 @@ mcp:
enabled: false enabled: false
flux: flux:
enabled: false enabled: false
helm:
enabled: false
fetch: fetch:
enabled: false enabled: false
sequentialthinking: sequentialthinking:
@@ -135,6 +139,8 @@ 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: fetch:
enabled: true # Enable Fetch MCP for web content fetching enabled: true # Enable Fetch MCP for web content fetching
sequentialthinking: sequentialthinking:
@@ -182,8 +188,6 @@ 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
- `HAPPY_HOME_DIR` / `HAPPY_EXPERIMENTAL`
- `WEB_FILE_MANAGER` — Set to `1` to enable the built-in web file manager (controlled via `fileManager.enabled` in Helm values) - `WEB_FILE_MANAGER` — Set to `1` to enable the built-in web file manager (controlled via `fileManager.enabled` in Helm values)
- `WEB_FILE_MANAGER_ALLOWED_PATHS` — Paths accessible by the file manager (default: `/workspace,/config`) - `WEB_FILE_MANAGER_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) - `WEB_FILE_MANAGER_DENIED_PATHS` — Paths to deny access to (takes precedence over allowed)
-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
+28 -21
View File
@@ -56,26 +56,16 @@ 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 (required by Happy Coder) # Install Claude Code native binary (npm wrapper breaks remote control)
RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - && \ RUN curl -fsSL https://claude.ai/install.sh | bash && \
apt-get install -y nodejs && \ cp /root/.local/bin/claude /usr/local/bin/claude && \
rm -rf /var/lib/apt/lists/* rm -rf /root/.local/bin/claude && \
# Install Happy Coder globally via npm
RUN npm install -g happy-coder
# Cache-bust: tools below fetch "latest" at build time — a changing ARG
# forces Docker to re-run these layers instead of serving stale cache.
ARG TOOLS_CACHEBUST=0
# Install Claude Code via native installer (no Node.js dependency)
RUN CLAUDE_VERSION=$(curl -fsSL https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases/latest) && \
echo "Installing Claude Code ${CLAUDE_VERSION}" && \
curl -fsSL "https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases/${CLAUDE_VERSION}/linux-x64/claude" \
-o /usr/local/bin/claude && \
chmod +x /usr/local/bin/claude && \
claude --version 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') && \
curl -fsSL "https://github.com/opencode-ai/opencode/releases/download/${OPENCODE_VERSION}/opencode-linux-x86_64.tar.gz" | \ curl -fsSL "https://github.com/opencode-ai/opencode/releases/download/${OPENCODE_VERSION}/opencode-linux-x86_64.tar.gz" | \
@@ -96,9 +86,26 @@ 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 && \ tar -xz --strip-components=1 -C /usr/local/bin linux-amd64/helm && \
chmod +x /usr/local/bin/helm chmod +x /usr/local/bin/helm
# Install VSCode # Install GitHub CLI (gh) via official APT repo
RUN wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o /usr/share/keyrings/packages.microsoft.gpg && \ RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg && \
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/packages.microsoft.gpg] https://packages.microsoft.com/repos/code stable main" > /etc/apt/sources.list.d/vscode.list && \ 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
# 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/*
-1
View File
@@ -26,7 +26,6 @@ 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 devcontainer \ --name devcontainer \
+3 -32
View File
@@ -5,7 +5,7 @@
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 - **Built-in web file manager** for uploading/downloading files via the VNC web interface
- **Helm CLI** included for Kubernetes chart development and deployment - **Helm CLI** included for Kubernetes chart development and deployment
- **Automatic GitHub repo cloning** on startup - **Automatic GitHub repo cloning** on startup
@@ -114,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**:
@@ -189,15 +189,6 @@ helm install mydev ./chart \
--set fileManager.enabled=true --set fileManager.enabled=true
``` ```
### Happy Coder
| Value | Default | Description |
|-------|---------|-------------|
| `happy.serverUrl` | `https://happy.farh.net` | Happy Coder server endpoint |
| `happy.webappUrl` | `https://happy-coder.farh.net` | Happy Coder webapp URL |
| `happy.homeDir` | `/config/userdata/.happy` | Happy runtime state directory (persists on the home PVC) |
| `happy.experimental` | `true` | Enable experimental Happy features |
### Kubernetes cluster access ### Kubernetes cluster access
The `clusterAccess` value provisions a ServiceAccount, Role/ClusterRole, and binding so the devcontainer pod can interact with the Kubernetes API. The default is `none` — no RBAC resources are created. The `clusterAccess` value provisions a ServiceAccount, Role/ClusterRole, and binding so the devcontainer pod can interact with the Kubernetes API. The default is `none` — no RBAC resources are created.
@@ -377,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:
@@ -466,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 |
+1 -1
View File
@@ -2,7 +2,7 @@ apiVersion: v2
name: devcontainer name: devcontainer
description: Dev Container with AI coding agents and MCP sidecars - supports persistent and dynamic deployment modes description: Dev Container with AI coding agents and MCP sidecars - supports persistent and dynamic deployment modes
type: application type: application
version: 2.0.2 version: 2.2.2
appVersion: "latest" appVersion: "latest"
keywords: keywords:
- development - development
+24 -8
View File
@@ -80,16 +80,10 @@ spec:
value: {{ .Values.fileManager.deniedPaths | quote }} value: {{ .Values.fileManager.deniedPaths | quote }}
{{- end }} {{- end }}
{{- end }} {{- end }}
- name: HAPPY_HOME_DIR {{- if .Values.githubRepo }}
value: {{ .Values.happy.homeDir | quote }}
- name: HAPPY_EXPERIMENTAL
value: {{ .Values.happy.experimental | quote }}
- name: HAPPY_SERVER_URL
value: {{ .Values.happy.serverUrl | quote }}
- name: HAPPY_WEBAPP_URL
value: {{ .Values.happy.webappUrl | quote }}
- name: GITHUB_REPO - name: GITHUB_REPO
value: {{ .Values.githubRepo | quote }} value: {{ .Values.githubRepo | quote }}
{{- end }}
envFrom: envFrom:
- secretRef: - secretRef:
name: {{ include "devcontainer.envSecretName" . }} name: {{ include "devcontainer.envSecretName" . }}
@@ -177,6 +171,28 @@ 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 }}"
-13
View File
@@ -59,19 +59,6 @@ spec:
value: "1" value: "1"
- name: WEB_FILE_MANAGER_ALLOWED_PATHS - name: WEB_FILE_MANAGER_ALLOWED_PATHS
value: "/workspace,/tmp" # No persistent /config in dynamic mode value: "/workspace,/tmp" # No persistent /config in dynamic mode
# Happy Coder (ephemeral in dynamic mode)
- name: HAPPY_HOME_DIR
value: "/tmp/.happy"
- name: HAPPY_EXPERIMENTAL
value: {{ .Values.happy.experimental | quote }}
{{- if .Values.happy.serverUrl }}
- name: HAPPY_SERVER_URL
value: {{ .Values.happy.serverUrl | quote }}
{{- end }}
{{- if .Values.happy.webappUrl }}
- name: HAPPY_WEBAPP_URL
value: {{ .Values.happy.webappUrl | quote }}
{{- end }}
# Secret environment variables # Secret environment variables
envFrom: envFrom:
- secretRef: - secretRef:
-7
View File
@@ -100,13 +100,6 @@ user:
shm: shm:
sizeLimit: 2Gi sizeLimit: 2Gi
# Happy Coder (ephemeral in dynamic mode)
happy:
serverUrl: ""
webappUrl: ""
homeDir: "/tmp/.happy" # Ephemeral location in dynamic mode
experimental: "true"
# MCP sidecars are not supported in dynamic mode (Knative limitation) # MCP sidecars are not supported in dynamic mode (Knative limitation)
mcp: mcp:
sidecars: sidecars:
+3 -23
View File
@@ -229,29 +229,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",
"description": "Happy Coder server URL"
},
"webappUrl": {
"type": "string",
"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": ["homeDir", "experimental"]
},
"mcp": { "mcp": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -270,6 +247,9 @@
"pgtuner": { "pgtuner": {
"$ref": "#/$defs/mcpSidecar" "$ref": "#/$defs/mcpSidecar"
}, },
"helm": {
"$ref": "#/$defs/mcpSidecar"
},
"playwright": { "playwright": {
"$ref": "#/$defs/mcpSidecar" "$ref": "#/$defs/mcpSidecar"
} }
+17 -10
View File
@@ -13,7 +13,7 @@ deploymentMode: persistent # persistent | dynamic
# Container image configuration # Container image configuration
image: image:
repository: ghcr.io/cpfarhood/devcontainer repository: ghcr.io/cpfarhood/devcontainer
tag: 2.0.0-dev tag: latest
pullPolicy: Always pullPolicy: Always
# GitHub repository to clone into /workspace (ignored in dynamic mode - uses URL routing) # GitHub repository to clone into /workspace (ignored in dynamic mode - uses URL routing)
@@ -83,13 +83,6 @@ clusterAccess: none
# INTEGRATIONS # INTEGRATIONS
# ============================================================================= # =============================================================================
# Happy Coder AI assistant configuration
happy:
serverUrl: ""
webappUrl: ""
homeDir: "/config/userdata/.happy"
experimental: "true"
# MCP (Model Context Protocol) server sidecars # MCP (Model Context Protocol) server sidecars
mcp: mcp:
sidecars: sidecars:
@@ -124,13 +117,27 @@ mcp:
cpu: "500m" cpu: "500m"
# Helm chart browsing and management
helm:
enabled: true
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: v6.7.1 tag: "6.7.1"
port: 8087 port: 8087
resources: resources:
requests: requests:
@@ -217,7 +224,7 @@ dynamic:
replicas: 2 # High availability replicas: 2 # High availability
image: image:
repository: ghcr.io/cpfarhood/devcontainer-routing-proxy repository: ghcr.io/cpfarhood/devcontainer-routing-proxy
tag: 2.0.0-dev tag: latest
pullPolicy: Always pullPolicy: Always
resources: resources:
+6
View File
@@ -104,6 +104,12 @@ chown -R "$RUN_UID:$RUN_GID" "$WORKSPACE_DIR"
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
-5
View File
@@ -155,11 +155,6 @@ spec:
value: "1" value: "1"
- name: WEB_FILE_MANAGER_ALLOWED_PATHS - name: WEB_FILE_MANAGER_ALLOWED_PATHS
value: "/workspace,/config" value: "/workspace,/config"
# Happy Coder config (ephemeral in serverless mode)
- name: HAPPY_HOME_DIR
value: "/tmp/.happy"
- name: HAPPY_EXPERIMENTAL
value: "true"
# Use secrets for sensitive data # Use secrets for sensitive data
envFrom: envFrom:
- secretRef: - secretRef:
-5
View File
@@ -49,11 +49,6 @@ spec:
value: "1" value: "1"
- name: WEB_FILE_MANAGER_ALLOWED_PATHS - name: WEB_FILE_MANAGER_ALLOWED_PATHS
value: "/workspace,/config" value: "/workspace,/config"
# Happy Coder config
- name: HAPPY_HOME_DIR
value: "/config/userdata/.happy"
- name: HAPPY_EXPERIMENTAL
value: "true"
# Use secrets for sensitive data # Use secrets for sensitive data
envFrom: envFrom:
- secretRef: - secretRef: