Compare commits
139 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ea71f71c74 | |||
| f6eceb4d94 | |||
| 84bf7841c3 | |||
| c823a30c2a | |||
| 27af9dc9c4 | |||
| 0944dcec1c | |||
| 60a2689658 | |||
| 53bc4b68a6 | |||
| d526a445fd | |||
| f56b3efb66 | |||
| a778d32b3b | |||
| b48fce97d5 | |||
| 47af7acc5e | |||
| da45415cfe | |||
| 897555b1dc | |||
| df1f4d9b50 | |||
| 2f5a8d65d5 | |||
| 0d8fe1ec64 | |||
| 00638d372c | |||
| 31ec139a8a | |||
| 71c6ca70cc | |||
| b9c30b8e4d | |||
| 794de6d0e5 | |||
| fbcd9c1f72 | |||
| 3be59e56eb | |||
| be9479ef75 | |||
| 065a6534e3 | |||
| d8d83ffa47 | |||
| 9535886945 | |||
| 44f30ec03f | |||
| 76391a8ed0 | |||
| ac1e5074b1 | |||
| 2a63f227f1 | |||
| 5da23def5b | |||
| 5532eee8cd | |||
| d32e453f93 | |||
| f95e8877e8 | |||
| 46267b6e26 | |||
| c4cbd67399 | |||
| a7799dbb16 | |||
| 45b8e5e95e | |||
| a0b409239e | |||
| eacf41302c | |||
| cbdee590bf | |||
| 5570b2c617 | |||
| c3f8421d60 | |||
| 21d8fc73e6 | |||
| 0a63894f6d | |||
| a50a1815e0 | |||
| 131dad8611 | |||
| 581d0737e4 | |||
| 68110d911f | |||
| 427f7a710c | |||
| 745a0cdf59 | |||
| 115907cdc8 | |||
| 0c4f93c077 | |||
| a83d79bc10 | |||
| 2258df4ae3 | |||
| d4b069cbdc | |||
| db7e422b96 | |||
| d5bbf21578 | |||
| 1c3398b178 | |||
| 4e67c48a4c | |||
| df3413f54e | |||
| 6a35f38a8c | |||
| 431b9079ee | |||
| 00d88b16b5 | |||
| c10dd718e1 | |||
| b6bf4b6640 | |||
| c42b47bb56 | |||
| 288c1a4103 | |||
| 2caa8a790f | |||
| 7a6a515b53 | |||
| 4f126a938b | |||
| 4af38a5d2e | |||
| 90350a2090 | |||
| 5b8e6a290b | |||
| e860499757 | |||
| e90a2fe553 | |||
| 897f1409b5 | |||
| 32d4fe4944 | |||
| e8c263a045 | |||
| 927c9f1051 | |||
| 298a1ce6ec | |||
| f33c7e1ae8 | |||
| b0d4b98bb4 | |||
| b5820cfc7f | |||
| bace308394 | |||
| 9c964e7069 | |||
| d7210fb4e5 | |||
| 7a96f5156c | |||
| 8df46d6b6f | |||
| 5d8b1369c3 | |||
| 751402be44 | |||
| 66e0d1f406 | |||
| e89c3040b7 | |||
| 8d7b39f1b5 | |||
| 32e87254d2 | |||
| 66ccee1202 | |||
| 1909c2a3aa | |||
| d078bb1c44 | |||
| 56c648187a | |||
| 8870d60ccc | |||
| d54515244c | |||
| 2918cfde25 | |||
| dd77cf6a48 | |||
| 961a0985b6 | |||
| d3f5e9f185 | |||
| 9aab08b8e4 | |||
| 727487053d | |||
| 47a275d667 | |||
| 7788352995 | |||
| 81a7098c21 | |||
| 3832fd922b | |||
| 4984e5200c | |||
| c8f51beac6 | |||
| ee7a4a0be8 | |||
| 2472dc5b3f | |||
| 23ba5c2e35 | |||
| 256a6871e8 | |||
| 720a2a982f | |||
| fdfccb17bd | |||
| dfc0a3c1e3 | |||
| 7003d860b7 | |||
| 8eab159e6f | |||
| 13f1878ce5 | |||
| c01f85ef24 | |||
| 3db5961b1c | |||
| 3794ec60d7 | |||
| dd12262c83 | |||
| 8f0fee838e | |||
| 7bdde95cb4 | |||
| f79be89e4e | |||
| 17c2d04d70 | |||
| 636b68d263 | |||
| 46b2077f99 | |||
| 667845b9a2 | |||
| 2c80117602 | |||
| c7a7e8bd12 |
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"enabledMcpjsonServers": [
|
||||
"kubernetes",
|
||||
"flux",
|
||||
"playwright",
|
||||
"github",
|
||||
"pgtuner"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
.git
|
||||
.gitignore
|
||||
.dockerignore
|
||||
*.md
|
||||
README.md
|
||||
LICENSE
|
||||
Makefile
|
||||
docker-compose.yml
|
||||
|
||||
# Kubernetes files
|
||||
k8s/
|
||||
|
||||
# Local development
|
||||
home/
|
||||
workspace/
|
||||
*.log
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
@@ -0,0 +1,36 @@
|
||||
## Description
|
||||
|
||||
<!-- Describe your changes in detail -->
|
||||
|
||||
## Type of Change
|
||||
|
||||
<!-- Mark with an `x` all that apply -->
|
||||
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- [ ] Documentation update
|
||||
- [ ] CI/CD update
|
||||
|
||||
## Testing
|
||||
|
||||
<!-- Describe the tests you ran to verify your changes -->
|
||||
|
||||
- [ ] Built Docker image locally
|
||||
- [ ] Tested container startup
|
||||
- [ ] Tested repository cloning
|
||||
- [ ] Tested Happy Coder integration
|
||||
- [ ] Tested VNC web interface
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] My code follows the style guidelines of this project
|
||||
- [ ] I have performed a self-review of my own code
|
||||
- [ ] I have commented my code, particularly in hard-to-understand areas
|
||||
- [ ] I have made corresponding changes to the documentation
|
||||
- [ ] My changes generate no new warnings
|
||||
- [ ] I have tested that the Docker image builds successfully
|
||||
|
||||
## Screenshots (if applicable)
|
||||
|
||||
<!-- Add screenshots to help explain your changes -->
|
||||
@@ -0,0 +1,94 @@
|
||||
# CI/CD Pipeline Guide
|
||||
|
||||
## 🚀 Simplified Pipeline - Only 3 Workflows!
|
||||
|
||||
### 1️⃣ For Releases → **Unified Release**
|
||||
Use this for all version releases:
|
||||
1. Go to [Actions → Unified Release](https://github.com/cpfarhood/devcontainer/actions/workflows/release-unified.yaml)
|
||||
2. Click "Run workflow"
|
||||
3. Either:
|
||||
- Enter specific version (e.g., `0.2.1`), OR
|
||||
- Choose release type (patch/minor/major) for auto-increment
|
||||
4. Click "Run workflow"
|
||||
|
||||
**This single workflow does EVERYTHING:**
|
||||
- ✅ Updates chart version
|
||||
- ✅ Creates git tag
|
||||
- ✅ Builds Docker image with all proper tags
|
||||
- ✅ Publishes Helm chart to GHCR
|
||||
- ✅ Creates GitHub Release with changelog
|
||||
- ✅ No more `[skip ci]` blocking builds!
|
||||
|
||||
### 2️⃣ For Quick Fixes → **Quick Fix Build**
|
||||
Use this for emergency fixes without version changes:
|
||||
1. Go to [Actions → Quick Fix Build](https://github.com/cpfarhood/devcontainer/actions/workflows/quick-fix.yaml)
|
||||
2. Click "Run workflow"
|
||||
3. Enter tag (default: `latest`)
|
||||
4. Click "Run workflow"
|
||||
|
||||
**Just builds and pushes Docker image** - no version bumps, no releases.
|
||||
|
||||
### 3️⃣ Automatic CI → **Build and Push**
|
||||
Runs automatically on:
|
||||
- Pull requests (builds but doesn't push)
|
||||
- Tags starting with `v*` (builds and pushes)
|
||||
- Manual trigger available
|
||||
|
||||
## Workflow Files
|
||||
|
||||
| Workflow | File | Purpose | When to Use |
|
||||
|----------|------|---------|-------------|
|
||||
| **Unified Release** | `release-unified.yaml` | Full release process | New versions |
|
||||
| **Quick Fix Build** | `quick-fix.yaml` | Docker build only | Hotfixes |
|
||||
| **Build and Push** | `build-and-push.yaml` | CI/CD automation | PRs & tags |
|
||||
|
||||
## Examples
|
||||
|
||||
### Release a new version
|
||||
```bash
|
||||
# Via GitHub UI (Recommended):
|
||||
# Go to Actions → Unified Release → Run workflow
|
||||
|
||||
# Via GitHub CLI:
|
||||
gh workflow run release-unified.yaml -f version=0.2.1
|
||||
# OR auto-increment:
|
||||
gh workflow run release-unified.yaml -f release_type=patch
|
||||
```
|
||||
|
||||
### Push a quick fix
|
||||
```bash
|
||||
# Via GitHub UI:
|
||||
# Go to Actions → Quick Fix Build → Run workflow
|
||||
|
||||
# Via GitHub CLI:
|
||||
gh workflow run quick-fix.yaml -f tag=hotfix-1
|
||||
```
|
||||
|
||||
### Check workflow status
|
||||
```bash
|
||||
# List all recent runs
|
||||
gh run list --limit 5
|
||||
|
||||
# Watch a specific workflow
|
||||
gh run watch
|
||||
```
|
||||
|
||||
## Version Strategy
|
||||
|
||||
- **Major** (1.0.0): Breaking changes
|
||||
- **Minor** (0.2.0): New features
|
||||
- **Patch** (0.2.1): Bug fixes
|
||||
|
||||
## What We Fixed
|
||||
|
||||
### Before (Nightmare 😱)
|
||||
- Auto-version-bump with `[skip ci]` prevented Docker builds
|
||||
- 6+ disconnected workflows
|
||||
- Manual tag deletion and re-pushing
|
||||
- Version conflicts everywhere
|
||||
|
||||
### After (Simple! 🎉)
|
||||
- **3 total workflows** (down from 6+)
|
||||
- **1 button** for complete releases
|
||||
- **No more `[skip ci]`** blocking builds
|
||||
- **Clear separation** of concerns
|
||||
@@ -0,0 +1,62 @@
|
||||
name: Build and Push Docker Image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
# Skip builds triggered by release-unified.yaml commits (github-actions[bot])
|
||||
# to prevent racing with the release workflow's own Docker build
|
||||
if: github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request' || github.actor != 'github-actions[bot]'
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to GitHub Container Registry
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata (tags, labels)
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=sha,prefix=sha-
|
||||
type=raw,value=latest,enable={{is_default_branch}}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
platforms: linux/amd64
|
||||
@@ -0,0 +1,54 @@
|
||||
name: Quick Fix Build
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Tag for the image (defaults to latest)'
|
||||
required: false
|
||||
default: 'latest'
|
||||
type: string
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and Push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.event.inputs.tag }}
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
platforms: linux/amd64
|
||||
|
||||
- name: Summary
|
||||
run: |
|
||||
echo "## ✅ Quick Fix Build Complete" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "### Images Published:" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.event.inputs.tag }}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY
|
||||
@@ -0,0 +1,159 @@
|
||||
name: Unified Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version to release (e.g., 0.1.25)'
|
||||
required: true
|
||||
type: string
|
||||
release_type:
|
||||
description: 'Release type'
|
||||
required: true
|
||||
default: 'patch'
|
||||
type: choice
|
||||
options:
|
||||
- patch
|
||||
- minor
|
||||
- major
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Set up Helm
|
||||
uses: azure/setup-helm@v4
|
||||
|
||||
- name: Configure Git
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
- name: Determine Version
|
||||
id: version
|
||||
run: |
|
||||
if [ "${{ github.event.inputs.version }}" != "" ]; then
|
||||
VERSION="${{ github.event.inputs.version }}"
|
||||
else
|
||||
# Auto-determine next version based on release type
|
||||
CURRENT=$(grep '^version:' chart/Chart.yaml | awk '{print $2}')
|
||||
MAJOR=$(echo $CURRENT | cut -d. -f1)
|
||||
MINOR=$(echo $CURRENT | cut -d. -f2)
|
||||
PATCH=$(echo $CURRENT | cut -d. -f3)
|
||||
|
||||
case "${{ github.event.inputs.release_type }}" in
|
||||
major)
|
||||
VERSION="$((MAJOR + 1)).0.0"
|
||||
;;
|
||||
minor)
|
||||
VERSION="${MAJOR}.$((MINOR + 1)).0"
|
||||
;;
|
||||
patch)
|
||||
VERSION="${MAJOR}.${MINOR}.$((PATCH + 1))"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
echo "version=${VERSION}" >> $GITHUB_OUTPUT
|
||||
echo "tag=v${VERSION}" >> $GITHUB_OUTPUT
|
||||
echo "🚀 Releasing version ${VERSION}"
|
||||
|
||||
- name: Update Chart Version
|
||||
run: |
|
||||
sed -i "s/^version: .*/version: ${{ steps.version.outputs.version }}/" chart/Chart.yaml
|
||||
git add chart/Chart.yaml
|
||||
git commit -m "chore: release version ${{ steps.version.outputs.version }}"
|
||||
|
||||
- name: Create and Push Tag
|
||||
run: |
|
||||
git tag -a "${{ steps.version.outputs.tag }}" -m "Release ${{ steps.version.outputs.tag }}"
|
||||
git push origin main
|
||||
git push origin "${{ steps.version.outputs.tag }}"
|
||||
|
||||
- name: Log in to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and Push Docker Image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.tag }}
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }}
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
platforms: linux/amd64
|
||||
|
||||
- name: Package Helm Chart
|
||||
run: |
|
||||
helm registry login ghcr.io \
|
||||
--username ${{ github.actor }} \
|
||||
--password ${{ secrets.GITHUB_TOKEN }}
|
||||
helm package chart/
|
||||
helm push devcontainer-${{ steps.version.outputs.version }}.tgz oci://ghcr.io/cpfarhood/charts
|
||||
|
||||
- name: Generate Release Notes
|
||||
id: notes
|
||||
run: |
|
||||
# Get commits since last tag
|
||||
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
|
||||
if [ -z "$PREV_TAG" ]; then
|
||||
COMMITS=$(git log --pretty=format:"- %s (%h)" HEAD)
|
||||
else
|
||||
COMMITS=$(git log --pretty=format:"- %s (%h)" ${PREV_TAG}..HEAD)
|
||||
fi
|
||||
|
||||
cat << EOF > release-notes.md
|
||||
## 🚀 Release ${{ steps.version.outputs.version }}
|
||||
|
||||
### Changes
|
||||
${COMMITS}
|
||||
|
||||
### Docker Image
|
||||
\`\`\`bash
|
||||
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.tag }}
|
||||
\`\`\`
|
||||
|
||||
### Helm Chart
|
||||
\`\`\`bash
|
||||
helm install devcontainer oci://ghcr.io/cpfarhood/charts/devcontainer --version ${{ steps.version.outputs.version }}
|
||||
\`\`\`
|
||||
EOF
|
||||
|
||||
echo "notes<<EOF" >> $GITHUB_OUTPUT
|
||||
cat release-notes.md >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create GitHub Release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ steps.version.outputs.tag }}
|
||||
release_name: Release ${{ steps.version.outputs.tag }}
|
||||
body: ${{ steps.notes.outputs.notes }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
# Secrets
|
||||
*.env
|
||||
.env.local
|
||||
secrets.yaml
|
||||
k8s/sealedsecrets.yaml
|
||||
|
||||
# Local volumes
|
||||
home/
|
||||
workspace/
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
|
||||
# Build artifacts
|
||||
*.tar
|
||||
*.tar.gz
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"github": {
|
||||
"type": "http",
|
||||
"url": "https://api.githubcopilot.com/mcp/",
|
||||
"headers": {
|
||||
"Authorization": "Bearer ${GITHUB_TOKEN}"
|
||||
}
|
||||
},
|
||||
"kubernetes": {
|
||||
"type": "sse",
|
||||
"url": "http://localhost:8080/sse"
|
||||
},
|
||||
"flux": {
|
||||
"type": "sse",
|
||||
"url": "http://localhost:8081/sse"
|
||||
},
|
||||
"playwright": {
|
||||
"type": "sse",
|
||||
"url": "http://localhost:8086/sse"
|
||||
},
|
||||
"pgtuner": {
|
||||
"type": "sse",
|
||||
"url": "http://localhost:8085/sse"
|
||||
},
|
||||
"fetch": {
|
||||
"type": "sse",
|
||||
"url": "http://localhost:8082/sse"
|
||||
},
|
||||
"sequentialthinking": {
|
||||
"type": "sse",
|
||||
"url": "http://localhost:8083/sse"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- Initial project setup
|
||||
- Antigravity IDE (VSCode) with web-based VNC access
|
||||
- Happy Coder AI assistant integration
|
||||
- Automatic GitHub repository cloning on startup
|
||||
- Persistent home directory with ReadWriteMany PVC support
|
||||
- Secure non-root execution (claude user, UID 1000, GID 1000)
|
||||
- Support for private repositories via GitHub token
|
||||
- HTTPRoute (Gateway API) support
|
||||
- VNC password protection
|
||||
- Multi-platform Docker image builds
|
||||
- GitHub Actions CI/CD pipeline
|
||||
- Automated releases on version tags
|
||||
- Comprehensive deployment documentation (DEPLOYMENT.md)
|
||||
- Complete variables reference (VARIABLES.md)
|
||||
|
||||
### Container Features
|
||||
- Base: jlesage/baseimage-gui:ubuntu-22.04-v4
|
||||
- Antigravity IDE (VSCode)
|
||||
- Happy Coder npm package
|
||||
- Chrome browser
|
||||
- Node.js (LTS)
|
||||
- Python 3
|
||||
- Git
|
||||
|
||||
### Kubernetes Resources
|
||||
- StatefulSet with volumeClaimTemplates
|
||||
- ReadWriteMany PVC for /home directory
|
||||
- ConfigMap for configuration
|
||||
- Sealed Secrets support
|
||||
- HTTPRoute for external access
|
||||
- Service (headless)
|
||||
|
||||
### Configuration Options
|
||||
- GitHub repository URL (required)
|
||||
- GitHub token (optional, for private repos)
|
||||
- VNC password (optional)
|
||||
- Happy Coder server URL (optional)
|
||||
- Happy Coder webapp URL (optional)
|
||||
- Display resolution (configurable)
|
||||
- Resource limits (configurable)
|
||||
- Storage size (configurable)
|
||||
|
||||
### Documentation
|
||||
- README.md with quick start guide
|
||||
- DEPLOYMENT.md with step-by-step instructions
|
||||
- VARIABLES.md with complete variable reference
|
||||
- Release process documentation
|
||||
- Pull request template
|
||||
- Dependabot configuration
|
||||
|
||||
## Version History
|
||||
|
||||
No releases yet. See [Unreleased] section above for planned v1.0.0 features.
|
||||
|
||||
---
|
||||
|
||||
## Release Template
|
||||
|
||||
Use this template for future releases:
|
||||
|
||||
```markdown
|
||||
## [1.0.0] - YYYY-MM-DD
|
||||
|
||||
### Added
|
||||
- New features
|
||||
- New configuration options
|
||||
|
||||
### Changed
|
||||
- Changes to existing features
|
||||
- Updated dependencies
|
||||
|
||||
### Deprecated
|
||||
- Features that will be removed in future versions
|
||||
|
||||
### Removed
|
||||
- Removed features
|
||||
- Breaking changes
|
||||
|
||||
### Fixed
|
||||
- Bug fixes
|
||||
- Security patches
|
||||
|
||||
### Security
|
||||
- Security improvements
|
||||
- Vulnerability fixes
|
||||
```
|
||||
|
||||
[Unreleased]: https://github.com/cpfarhood/devcontainer/compare/v1.0.0...HEAD
|
||||
@@ -0,0 +1,201 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
The Dev Container is a Docker-based cloud development environment that provides:
|
||||
- Web-based GUI IDE (VSCode/Antigravity) via VNC on port 5800
|
||||
- Claude Code, Happy Coder, OpenCode, and Crush AI coding agents (terminal-based)
|
||||
- Automatic GitHub repository cloning on startup
|
||||
- Kubernetes-native deployment with persistent home storage
|
||||
- MCP (Model Context Protocol) sidecars for AI assistant integrations
|
||||
|
||||
The stack is primarily **Bash scripts + YAML** — there is no Node.js package, compiled language, or test framework.
|
||||
|
||||
## Common Commands
|
||||
|
||||
### Building
|
||||
|
||||
```bash
|
||||
make build # Build Docker image
|
||||
make build REGISTRY=ghcr.io/myuser IMAGE_TAG=v1.0 # Custom registry/tag
|
||||
docker build -t ghcr.io/cpfarhood/devcontainer:latest . # Direct build
|
||||
```
|
||||
|
||||
### Running Locally
|
||||
|
||||
```bash
|
||||
GITHUB_REPO="https://github.com/user/repo" make run # Run with Docker
|
||||
make stop # Stop container
|
||||
make clean # Remove volumes
|
||||
```
|
||||
|
||||
### Kubernetes Deployment
|
||||
|
||||
```bash
|
||||
GITHUB_REPO="https://github.com/user/repo" make helm-deploy # Deploy with Helm
|
||||
make helm-delete # Tear down Helm release
|
||||
make helm-port-forward # Forward port 5800 to localhost
|
||||
make helm-logs # Stream container logs
|
||||
make helm-shell # Open interactive shell in pod
|
||||
|
||||
# Or use Helm directly
|
||||
helm install mydev ./chart --set name=mydev --set githubRepo=https://github.com/user/repo
|
||||
```
|
||||
|
||||
### Other Useful Targets
|
||||
|
||||
```bash
|
||||
make help # List all Makefile targets with descriptions
|
||||
make push # Push image to registry (build first)
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
### Startup Flow
|
||||
|
||||
```
|
||||
Container start
|
||||
→ scripts/startapp.sh
|
||||
→ scripts/init-repo.sh
|
||||
→ Configure git user & credentials
|
||||
→ Clone GITHUB_REPO (if set)
|
||||
→ Launch VSCode as user `user` in /workspace
|
||||
```
|
||||
|
||||
### Key Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `Dockerfile` | Image definition — installs Chrome, Node.js, VSCode, Claude Code, Happy Coder, OpenCode, Crush; creates non-root user (UID 1000) |
|
||||
| `scripts/init-repo.sh` | Configures git credentials, clones GitHub repo |
|
||||
| `scripts/startapp.sh` | Calls init-repo.sh then opens VSCode in the workspace |
|
||||
| `chart/` | Helm chart for Kubernetes deployment |
|
||||
| `chart/templates/deployment.yaml` | Deployment spec — main container + MCP sidecar containers |
|
||||
| `chart/templates/rbac.yaml` | ServiceAccount, Role/ClusterRole based on `clusterAccess` value |
|
||||
| `chart/templates/pvc.yaml` | PersistentVolumeClaim for user home |
|
||||
| `chart/templates/service.yaml` | ClusterIP Service (VNC + optional SSH) |
|
||||
| `chart/values.yaml` | Default Helm values |
|
||||
| `.mcp.json` | MCP server connection config (GitHub Copilot, Kubernetes, Flux, Fetch, Sequential Thinking, Playwright, pgtuner) |
|
||||
| `Makefile` | Build/deploy automation |
|
||||
|
||||
### MCP Sidecars
|
||||
|
||||
MCP (Model Context Protocol) servers run as sidecar containers in the pod, enabling AI assistants to interact with various services:
|
||||
|
||||
| Sidecar | Image | Version | Port | Endpoint | Default |
|
||||
|---------|-------|---------|------|----------|---------|
|
||||
| `kubernetes-mcp` | `quay.io/containers/kubernetes_mcp_server` | v0.0.57 | 8080 | `http://localhost:8080/sse` | Enabled |
|
||||
| `flux-mcp` | `ghcr.io/controlplaneio-fluxcd/flux-operator-mcp` | v0.41.1 | 8081 | `http://localhost:8081/sse` | Enabled |
|
||||
| `fetch-mcp` | `mcp/fetch` | latest | 8082 | `http://localhost:8082/sse` | Enabled |
|
||||
| `sequentialthinking-mcp` | `mcp/sequentialthinking` | latest | 8083 | `http://localhost:8083/sse` | Enabled |
|
||||
| `homeassistant-mcp` | `ghcr.io/homeassistant-ai/ha-mcp` | stable | 8087 | `http://localhost:8087/sse` | Disabled |
|
||||
| `pgtuner-mcp` | `dog830228/pgtuner_mcp` | latest | 8085 | `http://localhost:8085/sse` | Disabled |
|
||||
| `playwright-mcp` | `mcr.microsoft.com/playwright/mcp` | latest | 8086 | `http://localhost:8086/sse` | Enabled |
|
||||
|
||||
**Note:**
|
||||
- GitHub MCP is accessed via the Copilot API (`https://api.githubcopilot.com/mcp/`), not as a sidecar
|
||||
- Kubernetes and Flux sidecars require `clusterAccess` != `none` to be deployed (they need RBAC permissions)
|
||||
- Kubernetes and Flux sidecars inherit the pod's ServiceAccount RBAC permissions
|
||||
- Fetch sidecar provides web content fetching capabilities and HTML to markdown conversion
|
||||
- Sequential thinking sidecar enables structured thinking and problem-solving processes
|
||||
- Home Assistant sidecar requires `HOMEASSISTANT_URL` and `HOMEASSISTANT_TOKEN` in the env secret
|
||||
- PostgreSQL tuner sidecar requires `DATABASE_URI` in the env secret (PostgreSQL connection string)
|
||||
- Playwright sidecar provides browser automation and web testing capabilities
|
||||
|
||||
#### Enabling/Disabling MCP Servers
|
||||
|
||||
To control MCP sidecars, set the `enabled` flag in your values override:
|
||||
|
||||
```yaml
|
||||
# Disable all MCP sidecars
|
||||
mcp:
|
||||
sidecars:
|
||||
kubernetes:
|
||||
enabled: false
|
||||
flux:
|
||||
enabled: false
|
||||
fetch:
|
||||
enabled: false
|
||||
sequentialthinking:
|
||||
enabled: false
|
||||
homeassistant:
|
||||
enabled: false
|
||||
pgtuner:
|
||||
enabled: false
|
||||
playwright:
|
||||
enabled: false
|
||||
|
||||
# Or selectively enable/disable
|
||||
mcp:
|
||||
sidecars:
|
||||
kubernetes:
|
||||
enabled: true # Keep Kubernetes MCP enabled
|
||||
flux:
|
||||
enabled: false # Disable Flux MCP
|
||||
fetch:
|
||||
enabled: true # Enable Fetch MCP for web content fetching
|
||||
sequentialthinking:
|
||||
enabled: true # Enable Sequential Thinking MCP for problem-solving
|
||||
homeassistant:
|
||||
enabled: true # Enable Home Assistant MCP (requires secrets)
|
||||
pgtuner:
|
||||
enabled: true # Enable PostgreSQL tuner MCP (requires DATABASE_URI)
|
||||
playwright:
|
||||
enabled: true # Enable Playwright MCP for browser automation
|
||||
```
|
||||
|
||||
When deploying via Helm:
|
||||
```bash
|
||||
# Quick start (recommended)
|
||||
cp chart/values-quickstart.yaml my-values.yaml
|
||||
# Edit name and githubRepo in my-values.yaml
|
||||
helm install my-devcontainer ./chart -f my-values.yaml
|
||||
|
||||
# Using --set flags
|
||||
helm install my-devcontainer ./chart \
|
||||
--set name=mydev \
|
||||
--set githubRepo=https://github.com/user/repo \
|
||||
--set mcp.sidecars.kubernetes.enabled=false
|
||||
|
||||
# Full customization
|
||||
helm install my-devcontainer ./chart -f custom-values.yaml
|
||||
```
|
||||
|
||||
### Storage Model
|
||||
|
||||
- `/config` — ReadWriteMany PVC (persists across pod restarts, holds user config/dotfiles)
|
||||
- `/workspace` — emptyDir by default (ephemeral; can be changed to PVC)
|
||||
|
||||
### Environment Variables
|
||||
|
||||
**Required:**
|
||||
- `GITHUB_REPO` — URL of repository to clone into `/workspace`
|
||||
|
||||
**Optional:**
|
||||
- `GITHUB_TOKEN` — PAT for private repo access (automatically configures git credentials)
|
||||
- `GIT_USER_NAME` — Git user name for commits (default: "DevContainer User")
|
||||
- `GIT_USER_EMAIL` — Git user email for commits (default: "devcontainer@example.com")
|
||||
- `GITLAB_HOST` — GitLab hostname if using GitLab with same token
|
||||
- `VNC_PASSWORD` — VNC web interface password
|
||||
- `DISPLAY_WIDTH` / `DISPLAY_HEIGHT` — VNC resolution
|
||||
- `USER_ID` / `GROUP_ID` — Override UID/GID (default 1000)
|
||||
- `HAPPY_SERVER_URL` / `HAPPY_WEBAPP_URL` — Custom Happy Coder endpoints
|
||||
- `HAPPY_HOME_DIR` / `HAPPY_EXPERIMENTAL`
|
||||
|
||||
### CI/CD
|
||||
|
||||
- **`build-and-push.yaml`** — Builds and pushes to GHCR on every push to `main`, version tags (`v*`), and PRs. For version tags, also creates GitHub Release with Helm chart after Docker build completes. Tags: `latest` (main), semver, branch name, commit SHA.
|
||||
- **`dependabot.yml`** — Weekly updates for GitHub Actions and Docker base image.
|
||||
|
||||
Image registry: `ghcr.io/cpfarhood/devcontainer`
|
||||
|
||||
## Kubernetes Notes
|
||||
|
||||
- Deployed via Helm chart (`chart/`), published as OCI artifact to GHCR, reconciled by Flux
|
||||
- Storage class is `ceph-filesystem` by default — change via `storage.className` in values
|
||||
- Resource limits: 1–4 CPU, 2–8Gi memory
|
||||
- Health checks (liveness/readiness probes) on port 5800
|
||||
- Secrets: optional env Secret (`devcontainer-{name}-secrets-env`) for `GITHUB_TOKEN`, `VNC_PASSWORD`, etc.
|
||||
- RBAC: controlled by `clusterAccess` value (`none`, `readonlyns`, `readwritens`, `readonly`, `readwrite`)
|
||||
+449
@@ -0,0 +1,449 @@
|
||||
# Deployment Guide
|
||||
|
||||
This guide provides step-by-step instructions for deploying the Antigravity Dev Container using Helm.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Kubernetes cluster (1.19+)
|
||||
- `kubectl` configured to access your cluster
|
||||
- `helm` CLI installed (3.0+)
|
||||
- ReadWriteMany storage class available (e.g., `ceph-filesystem`, `nfs-client`, `efs-sc`)
|
||||
- GitHub Container Registry access (images are public)
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Clone the Repository
|
||||
|
||||
```bash
|
||||
git clone https://github.com/cpfarhood/devcontainer.git
|
||||
cd devcontainer
|
||||
```
|
||||
|
||||
### 2. Create Secret (Optional)
|
||||
|
||||
For private repos or VNC password:
|
||||
|
||||
```bash
|
||||
kubectl create secret generic devcontainer-mydev-secrets-env \
|
||||
--from-literal=GITHUB_TOKEN='ghp_...' \
|
||||
--from-literal=VNC_PASSWORD='changeme' \
|
||||
--from-literal=ANTHROPIC_API_KEY='sk-ant-...'
|
||||
```
|
||||
|
||||
### 3. Deploy with Helm
|
||||
|
||||
```bash
|
||||
# Basic deployment
|
||||
helm install mydev ./chart \
|
||||
--set name=mydev \
|
||||
--set githubRepo=https://github.com/youruser/yourrepo
|
||||
|
||||
# With custom storage class
|
||||
helm install mydev ./chart \
|
||||
--set name=mydev \
|
||||
--set githubRepo=https://github.com/youruser/yourrepo \
|
||||
--set storage.className=nfs-client
|
||||
|
||||
# With cluster access for kubectl
|
||||
helm install mydev ./chart \
|
||||
--set name=mydev \
|
||||
--set githubRepo=https://github.com/youruser/yourrepo \
|
||||
--set clusterAccess=readwritens
|
||||
```
|
||||
|
||||
### 4. Access the Container
|
||||
|
||||
```bash
|
||||
# Port forward
|
||||
kubectl port-forward deployment/devcontainer-mydev 5800:5800
|
||||
open http://localhost:5800
|
||||
```
|
||||
|
||||
## Deployment Options
|
||||
|
||||
### Using Values File
|
||||
|
||||
Create a custom `values.yaml`:
|
||||
|
||||
```yaml
|
||||
name: mydev
|
||||
githubRepo: https://github.com/youruser/yourrepo
|
||||
ide: vscode
|
||||
ssh: false
|
||||
|
||||
# Storage
|
||||
storage:
|
||||
size: 32Gi
|
||||
className: ceph-filesystem
|
||||
|
||||
# Resources
|
||||
resources:
|
||||
requests:
|
||||
memory: "4Gi"
|
||||
cpu: "2000m"
|
||||
limits:
|
||||
memory: "16Gi"
|
||||
cpu: "8000m"
|
||||
|
||||
# Kubernetes access
|
||||
clusterAccess: readwritens
|
||||
|
||||
# MCP sidecars
|
||||
mcpSidecars:
|
||||
kubernetes:
|
||||
enabled: true
|
||||
flux:
|
||||
enabled: false
|
||||
```
|
||||
|
||||
Deploy:
|
||||
|
||||
```bash
|
||||
helm install mydev ./chart -f values.yaml
|
||||
```
|
||||
|
||||
### SSH Access Setup
|
||||
|
||||
Enable SSH and add your public key:
|
||||
|
||||
```bash
|
||||
# Create secret with SSH key
|
||||
kubectl create secret generic devcontainer-mydev-secrets-env \
|
||||
--from-literal=SSH_AUTHORIZED_KEYS='ssh-ed25519 AAAA...'
|
||||
|
||||
# Deploy with SSH enabled
|
||||
helm install mydev ./chart \
|
||||
--set name=mydev \
|
||||
--set githubRepo=https://github.com/youruser/yourrepo \
|
||||
--set ssh=true
|
||||
|
||||
# Connect via SSH
|
||||
kubectl port-forward deployment/devcontainer-mydev 2222:22
|
||||
ssh -p 2222 user@localhost
|
||||
```
|
||||
|
||||
### MCP Sidecar Configuration
|
||||
|
||||
Control MCP servers for AI-assisted operations.
|
||||
|
||||
**Important:** Kubernetes and Flux MCP sidecars are only deployed when:
|
||||
1. They are enabled in values (`mcpSidecars.<name>.enabled: true`)
|
||||
2. AND `clusterAccess` is not `none` (they need RBAC permissions to function)
|
||||
|
||||
```bash
|
||||
# Disable all MCP sidecars
|
||||
helm install mydev ./chart \
|
||||
--set name=mydev \
|
||||
--set githubRepo=https://github.com/youruser/yourrepo \
|
||||
--set mcpSidecars.kubernetes.enabled=false \
|
||||
--set mcpSidecars.flux.enabled=false \
|
||||
--set mcpSidecars.homeassistant.enabled=false
|
||||
|
||||
# Enable only Kubernetes MCP
|
||||
helm install mydev ./chart \
|
||||
--set name=mydev \
|
||||
--set githubRepo=https://github.com/youruser/yourrepo \
|
||||
--set mcpSidecars.kubernetes.enabled=true \
|
||||
--set mcpSidecars.flux.enabled=false
|
||||
|
||||
# Enable Home Assistant MCP (requires credentials)
|
||||
kubectl create secret generic devcontainer-mydev-secrets-env \
|
||||
--from-literal=homeassistant-url='http://homeassistant.local:8123' \
|
||||
--from-literal=homeassistant-token='your_long_lived_token'
|
||||
|
||||
helm install mydev ./chart \
|
||||
--set name=mydev \
|
||||
--set githubRepo=https://github.com/youruser/yourrepo \
|
||||
--set mcpSidecars.homeassistant.enabled=true
|
||||
```
|
||||
|
||||
### Cluster Access Levels
|
||||
|
||||
Configure Kubernetes RBAC permissions:
|
||||
|
||||
| Value | Scope | Permissions | Use Case |
|
||||
|-------|-------|-------------|----------|
|
||||
| `none` | No access | None | Default, isolated development |
|
||||
| `readonlyns` | Namespace | Read-only | View resources in namespace |
|
||||
| `readwritens` | Namespace | Full access | Deploy apps in namespace |
|
||||
| `readonly` | Cluster-wide | Read-only | View all cluster resources |
|
||||
| `readwrite` | Cluster-wide | Full access | Cluster administration |
|
||||
|
||||
```bash
|
||||
# Example: Full access within namespace
|
||||
helm install mydev ./chart \
|
||||
--set name=mydev \
|
||||
--set githubRepo=https://github.com/youruser/yourrepo \
|
||||
--set clusterAccess=readwritens
|
||||
```
|
||||
|
||||
## Ingress Configuration
|
||||
|
||||
### Using Gateway API HTTPRoute
|
||||
|
||||
Create an HTTPRoute for external access:
|
||||
|
||||
```yaml
|
||||
apiVersion: gateway.networking.k8s.io/v1beta1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: devcontainer-mydev
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: your-gateway
|
||||
namespace: your-gateway-namespace
|
||||
hostnames:
|
||||
- devcontainer.example.com
|
||||
rules:
|
||||
- backendRefs:
|
||||
- name: devcontainer-mydev
|
||||
port: 5800
|
||||
```
|
||||
|
||||
### Using Traditional Ingress
|
||||
|
||||
Create an Ingress resource:
|
||||
|
||||
```yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: devcontainer-mydev
|
||||
spec:
|
||||
rules:
|
||||
- host: devcontainer.example.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: devcontainer-mydev
|
||||
port:
|
||||
number: 5800
|
||||
```
|
||||
|
||||
## Advanced Configurations
|
||||
|
||||
### Custom Happy Coder Endpoints
|
||||
|
||||
For self-hosted Happy instances:
|
||||
|
||||
```bash
|
||||
helm install mydev ./chart \
|
||||
--set name=mydev \
|
||||
--set githubRepo=https://github.com/youruser/yourrepo \
|
||||
--set happyServerUrl=https://your-happy-server.com \
|
||||
--set happyWebappUrl=https://your-happy-webapp.com
|
||||
```
|
||||
|
||||
### Custom Display Resolution
|
||||
|
||||
```bash
|
||||
helm install mydev ./chart \
|
||||
--set name=mydev \
|
||||
--set githubRepo=https://github.com/youruser/yourrepo \
|
||||
--set display.width=2560 \
|
||||
--set display.height=1440
|
||||
```
|
||||
|
||||
### Different IDE Options
|
||||
|
||||
```bash
|
||||
# Use Google Antigravity
|
||||
helm install mydev ./chart \
|
||||
--set name=mydev \
|
||||
--set githubRepo=https://github.com/youruser/yourrepo \
|
||||
--set ide=antigravity
|
||||
|
||||
# SSH-only mode (no GUI)
|
||||
helm install mydev ./chart \
|
||||
--set name=mydev \
|
||||
--set githubRepo=https://github.com/youruser/yourrepo \
|
||||
--set ide=none \
|
||||
--set ssh=true
|
||||
```
|
||||
|
||||
## Helm Operations
|
||||
|
||||
### List Deployments
|
||||
|
||||
```bash
|
||||
helm list
|
||||
```
|
||||
|
||||
### Upgrade Deployment
|
||||
|
||||
```bash
|
||||
# Change values
|
||||
helm upgrade mydev ./chart \
|
||||
--set name=mydev \
|
||||
--set githubRepo=https://github.com/youruser/newrepo
|
||||
|
||||
# Upgrade with new chart version
|
||||
git pull
|
||||
helm upgrade mydev ./chart
|
||||
```
|
||||
|
||||
### Uninstall
|
||||
|
||||
```bash
|
||||
helm uninstall mydev
|
||||
|
||||
# Note: PVC persists by default
|
||||
kubectl delete pvc userhome-mydev
|
||||
```
|
||||
|
||||
### Rollback
|
||||
|
||||
```bash
|
||||
# View history
|
||||
helm history mydev
|
||||
|
||||
# Rollback to previous version
|
||||
helm rollback mydev
|
||||
|
||||
# Rollback to specific revision
|
||||
helm rollback mydev 3
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Pod Not Starting
|
||||
|
||||
```bash
|
||||
# Check pod status
|
||||
kubectl get pods -l app.kubernetes.io/instance=mydev
|
||||
|
||||
# Describe pod for events
|
||||
kubectl describe pod -l app.kubernetes.io/instance=mydev
|
||||
|
||||
# Check logs
|
||||
kubectl logs deployment/devcontainer-mydev
|
||||
```
|
||||
|
||||
### Repository Not Cloning
|
||||
|
||||
```bash
|
||||
# Check init logs
|
||||
kubectl logs deployment/devcontainer-mydev | grep "Repository Initialization"
|
||||
|
||||
# Verify secret exists
|
||||
kubectl get secret devcontainer-mydev-secrets-env
|
||||
|
||||
# Check environment
|
||||
kubectl exec deployment/devcontainer-mydev -- env | grep GITHUB
|
||||
```
|
||||
|
||||
### VNC Not Accessible
|
||||
|
||||
```bash
|
||||
# Check service
|
||||
kubectl get svc devcontainer-mydev
|
||||
kubectl describe svc devcontainer-mydev
|
||||
|
||||
# Test with port-forward
|
||||
kubectl port-forward deployment/devcontainer-mydev 5800:5800
|
||||
```
|
||||
|
||||
### MCP Sidecar Issues
|
||||
|
||||
```bash
|
||||
# Check all containers
|
||||
kubectl get pod -l app.kubernetes.io/instance=mydev -o jsonpath='{.items[0].spec.containers[*].name}'
|
||||
|
||||
# Check MCP container logs
|
||||
kubectl logs deployment/devcontainer-mydev -c kubernetes-mcp
|
||||
kubectl logs deployment/devcontainer-mydev -c flux-mcp
|
||||
kubectl logs deployment/devcontainer-mydev -c homeassistant-mcp
|
||||
|
||||
# Verify RBAC permissions (for Kubernetes/Flux MCP)
|
||||
kubectl auth can-i --list --as system:serviceaccount:default:devcontainer-mydev
|
||||
|
||||
# Check Home Assistant MCP credentials
|
||||
kubectl get secret devcontainer-mydev-secrets-env -o jsonpath='{.data.homeassistant-url}' | base64 -d
|
||||
# Verify the URL is accessible from the pod
|
||||
kubectl exec deployment/devcontainer-mydev -- curl -s http://homeassistant.local:8123/api/
|
||||
```
|
||||
|
||||
### Storage Issues
|
||||
|
||||
```bash
|
||||
# Check PVC
|
||||
kubectl get pvc userhome-mydev
|
||||
kubectl describe pvc userhome-mydev
|
||||
|
||||
# Check available storage classes
|
||||
kubectl get storageclass
|
||||
|
||||
# Verify ReadWriteMany support
|
||||
kubectl get storageclass <class-name> -o yaml | grep -i accessmodes
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Production Deployment
|
||||
|
||||
1. **Use specific image tags** instead of `latest`:
|
||||
```bash
|
||||
helm install mydev ./chart --set image.tag=v1.0.0
|
||||
```
|
||||
|
||||
2. **Set resource limits** appropriately:
|
||||
```yaml
|
||||
resources:
|
||||
requests:
|
||||
memory: "4Gi"
|
||||
cpu: "2000m"
|
||||
limits:
|
||||
memory: "8Gi"
|
||||
cpu: "4000m"
|
||||
```
|
||||
|
||||
3. **Enable VNC password**:
|
||||
```bash
|
||||
kubectl create secret generic devcontainer-mydev-secrets-env \
|
||||
--from-literal=VNC_PASSWORD='strong-password-here'
|
||||
```
|
||||
|
||||
4. **Use dedicated namespace**:
|
||||
```bash
|
||||
kubectl create namespace dev-environments
|
||||
helm install mydev ./chart -n dev-environments
|
||||
```
|
||||
|
||||
5. **Configure appropriate cluster access**:
|
||||
- Use `readonlyns` or `readwritens` for namespace-scoped work
|
||||
- Avoid `readwrite` cluster-wide access unless necessary
|
||||
|
||||
### Multi-User Deployment
|
||||
|
||||
For teams, create separate deployments per user:
|
||||
|
||||
```bash
|
||||
# User 1
|
||||
helm install alice-dev ./chart \
|
||||
--set name=alice-dev \
|
||||
--set githubRepo=https://github.com/alice/project
|
||||
|
||||
# User 2
|
||||
helm install bob-dev ./chart \
|
||||
--set name=bob-dev \
|
||||
--set githubRepo=https://github.com/bob/project
|
||||
```
|
||||
|
||||
### Backup and Recovery
|
||||
|
||||
The home directory persists on PVC. To backup:
|
||||
|
||||
```bash
|
||||
# Create backup pod
|
||||
kubectl run backup --image=busybox --restart=Never --rm -i --tty \
|
||||
-- tar czf - -C /home . | gzip > home-backup.tar.gz
|
||||
```
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions:
|
||||
- GitHub Issues: https://github.com/cpfarhood/devcontainer/issues
|
||||
- Documentation: https://github.com/cpfarhood/devcontainer
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
FROM jlesage/baseimage-gui:ubuntu-22.04-v4
|
||||
|
||||
# Set environment variables
|
||||
ENV APP_NAME="Dev Container" \
|
||||
KEEP_APP_RUNNING=1 \
|
||||
DISPLAY_WIDTH=1920 \
|
||||
DISPLAY_HEIGHT=1080 \
|
||||
SECURE_CONNECTION=1 \
|
||||
USER_ID=1000 \
|
||||
GROUP_ID=1000 \
|
||||
CLAUDE_USER=user
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
curl \
|
||||
wget \
|
||||
gnupg \
|
||||
ca-certificates \
|
||||
git \
|
||||
build-essential \
|
||||
python3 \
|
||||
python3-pip \
|
||||
jq \
|
||||
unzip \
|
||||
sudo \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Chrome and xdg-utils (needed for xdg-open to work in VNC)
|
||||
RUN wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | gpg --dearmor -o /usr/share/keyrings/google-chrome-keyring.gpg && \
|
||||
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/google-chrome-keyring.gpg] http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list && \
|
||||
apt-get update && \
|
||||
apt-get install -y google-chrome-stable xdg-utils && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Chrome wrapper: adds flags required for running inside a Docker container.
|
||||
# xdg-open (used by Claude Code on Linux) respects $BROWSER, so pointing it
|
||||
# here ensures the OAuth popup works without manual --no-sandbox invocations.
|
||||
# Cleans up crash lock files and suppresses the crash-restore bubble so that
|
||||
# sessions/cookies survive unclean pod shutdowns (SIGKILL).
|
||||
RUN printf '#!/bin/bash\n\
|
||||
CHROME_DIR="/config/userdata/.config/google-chrome"\n\
|
||||
mkdir -p "$CHROME_DIR"\n\
|
||||
# Remove stale lock files left by unclean container shutdown\n\
|
||||
rm -f "$CHROME_DIR/SingletonLock" "$CHROME_DIR/SingletonSocket" "$CHROME_DIR/SingletonCookie"\n\
|
||||
# Mark the previous session as clean so Chrome does not clear cookies\n\
|
||||
PREFS="$CHROME_DIR/Default/Preferences"\n\
|
||||
if [ -f "$PREFS" ]; then\n\
|
||||
sed -i '\''s/"exit_type":"Crashed"/"exit_type":"Normal"/g; s/"exited_cleanly":false/"exited_cleanly":true/g'\'' "$PREFS"\n\
|
||||
fi\n\
|
||||
exec /usr/bin/google-chrome-stable \\\n\
|
||||
--no-sandbox \\\n\
|
||||
--disable-dev-shm-usage \\\n\
|
||||
--disable-gpu \\\n\
|
||||
--disable-session-crashed-bubble \\\n\
|
||||
--user-data-dir="$CHROME_DIR" \\\n\
|
||||
"$@"\n' > /usr/local/bin/google-chrome && \
|
||||
chmod +x /usr/local/bin/google-chrome
|
||||
|
||||
# Install Node.js (LTS version for Happy Coder)
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - && \
|
||||
apt-get install -y nodejs && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Happy Coder and Claude Code globally
|
||||
RUN npm install -g happy-coder @anthropic-ai/claude-code
|
||||
|
||||
# Install OpenCode AI coding agent
|
||||
RUN OPENCODE_VERSION=$(curl -sL https://api.github.com/repos/opencode-ai/opencode/releases/latest | jq -r '.tag_name') && \
|
||||
curl -fsSL "https://github.com/opencode-ai/opencode/releases/download/${OPENCODE_VERSION}/opencode-linux-x86_64.tar.gz" | \
|
||||
tar -xz -C /usr/local/bin opencode && \
|
||||
chmod +x /usr/local/bin/opencode
|
||||
|
||||
# Install Crush AI coding agent (OpenCode successor by Charm)
|
||||
RUN CRUSH_VERSION=$(curl -sL https://api.github.com/repos/charmbracelet/crush/releases/latest | jq -r '.tag_name' | sed 's/^v//') && \
|
||||
curl -fsSL "https://github.com/charmbracelet/crush/releases/download/v${CRUSH_VERSION}/crush_${CRUSH_VERSION}_Linux_x86_64.tar.gz" | \
|
||||
tar -xz --strip-components=1 -C /usr/local/bin "crush_${CRUSH_VERSION}_Linux_x86_64/crush" && \
|
||||
chmod +x /usr/local/bin/crush
|
||||
|
||||
# Install VSCode
|
||||
RUN wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o /usr/share/keyrings/packages.microsoft.gpg && \
|
||||
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/packages.microsoft.gpg] https://packages.microsoft.com/repos/code stable main" > /etc/apt/sources.list.d/vscode.list && \
|
||||
apt-get update && \
|
||||
apt-get install -y code && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Google Antigravity IDE
|
||||
RUN mkdir -p /etc/apt/keyrings && \
|
||||
curl -fsSL https://us-central1-apt.pkg.dev/doc/repo-signing-key.gpg | \
|
||||
gpg --dearmor --yes -o /etc/apt/keyrings/antigravity-repo-key.gpg && \
|
||||
echo "deb [signed-by=/etc/apt/keyrings/antigravity-repo-key.gpg] https://us-central1-apt.pkg.dev/projects/antigravity-auto-updater-dev/ antigravity-debian main" \
|
||||
> /etc/apt/sources.list.d/antigravity.list && \
|
||||
# Clear package cache to force fresh repository data
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
apt-get update && \
|
||||
# Show available versions for debugging
|
||||
apt-cache policy antigravity && \
|
||||
# Install latest version
|
||||
apt-get install -y --no-install-recommends antigravity && \
|
||||
# Display installed version
|
||||
dpkg -l | grep antigravity && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Pre-configure Antigravity to skip onboarding/setup on first run
|
||||
RUN mkdir -p /etc/skel/.config/antigravity/User/globalStorage && \
|
||||
echo '{"antigravityUnifiedStateSync.seenNuxOneTimeMigration": true, "antigravityUnifiedStateSync.browserOnboarding.completed": true, "antigravityUnifiedStateSync.hasOnboardingCompleted": true, "browserOnboarding.hasSeenWelcome": true, "antigravityUnifiedStateSync.browserPreferences.hasAddedLocalhostToAllowlist": true, "antigravityUnifiedStateSync.oauthToken.hasLegacyMigrated": true, "antigravityUnifiedStateSync.auth.tokenSyncEnabled": true, "antigravityUnifiedStateSync.auth.cloudSyncEnabled": true, "theme": "vs-dark"}' \
|
||||
> /etc/skel/.config/antigravity/User/globalStorage/storage.json && \
|
||||
echo '{"workbench.startupEditor": "none", "workbench.welcomePage.walkthroughs.openOnInstall": false, "workbench.tips.enabled": false, "extensions.ignoreRecommendations": true, "telemetry.telemetryLevel": "off", "update.mode": "none", "extensions.autoUpdate": false, "extensions.autoCheckUpdates": false, "workbench.enableExperiments": true, "workbench.settings.enableNaturalLanguageSearch": true, "antigravity.onboarding.completed": true, "antigravity.browserOnboarding.completed": true, "antigravity.setup.completed": true, "antigravity.ai.enabled": true, "antigravity.ai.autoComplete.enabled": true, "antigravity.ai.chat.enabled": true, "antigravity.ai.codeActions.enabled": true, "antigravity.ai.explainCode.enabled": true, "antigravity.ai.generateCode.enabled": true, "antigravity.ai.optimizeCode.enabled": true, "antigravity.ai.autoSuggest.enabled": true, "antigravity.telemetry.crashReporter": "on", "antigravity.ai.acceptTerms": true, "antigravity.auth.syncState": true, "antigravity.auth.enableTokenSync": true, "antigravity.ai.enableCloudSync": true, "antigravity.settings.sync": true}' \
|
||||
> /etc/skel/.config/antigravity/User/settings.json && \
|
||||
# Validate Antigravity installation
|
||||
/usr/share/antigravity/antigravity --version || echo "WARNING: Antigravity version check failed"
|
||||
|
||||
# Install OpenSSH server (for SSH IDE mode)
|
||||
RUN apt-get update && \
|
||||
apt-get install -y openssh-server && \
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
mkdir -p /var/run/sshd && \
|
||||
sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/' /etc/ssh/sshd_config && \
|
||||
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config && \
|
||||
sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config && \
|
||||
echo "PermitRootLogin no" >> /etc/ssh/sshd_config
|
||||
|
||||
# Create user user with specific UID/GID
|
||||
RUN groupadd -g 1000 user && \
|
||||
useradd -u 1000 -g 1000 -m -s /bin/bash user && \
|
||||
echo "user ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
|
||||
|
||||
# Create workspace directory
|
||||
RUN mkdir -p /workspace && \
|
||||
chown -R user:user /workspace
|
||||
|
||||
# Copy startup scripts
|
||||
COPY --chmod=755 scripts/startapp.sh /startapp.sh
|
||||
COPY --chmod=755 scripts/init-repo.sh /usr/local/bin/init-repo
|
||||
# Fix app user shell after baseimage-gui creates it at runtime
|
||||
COPY --chmod=755 scripts/cont-init-user.sh /etc/cont-init.d/20-fix-user-shell.sh
|
||||
COPY --chmod=755 scripts/cont-init-sshd.sh /etc/cont-init.d/25-start-sshd.sh
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /workspace
|
||||
|
||||
# Configure container to run as user user
|
||||
ENV HOME=/config/userdata \
|
||||
USER=user \
|
||||
BROWSER=/usr/local/bin/google-chrome
|
||||
|
||||
# Expose VNC port (baseimage-gui default)
|
||||
EXPOSE 5800
|
||||
|
||||
# Set app name for baseimage-gui
|
||||
RUN set-cont-env APP_NAME "Dev Container"
|
||||
@@ -0,0 +1,120 @@
|
||||
.PHONY: build push run stop clean help
|
||||
|
||||
# Variables
|
||||
REGISTRY ?= ghcr.io/cpfarhood
|
||||
IMAGE_NAME ?= antigravity
|
||||
IMAGE_TAG ?= latest
|
||||
FULL_IMAGE = $(REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG)
|
||||
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
# Build the Docker image
|
||||
build:
|
||||
@echo "Building $(FULL_IMAGE)..."
|
||||
docker build -t $(FULL_IMAGE) .
|
||||
|
||||
# Push the image to registry
|
||||
push: build
|
||||
@echo "Pushing $(FULL_IMAGE)..."
|
||||
docker push $(FULL_IMAGE)
|
||||
|
||||
# Run locally with Docker
|
||||
run:
|
||||
@echo "Running $(FULL_IMAGE) locally..."
|
||||
docker run -d \
|
||||
-p 5800:5800 \
|
||||
-e GITHUB_REPO="${GITHUB_REPO}" \
|
||||
-e GITHUB_TOKEN="${GITHUB_TOKEN}" \
|
||||
-e VNC_PASSWORD="${VNC_PASSWORD}" \
|
||||
-e HAPPY_EXPERIMENTAL="true" \
|
||||
-v $(PWD)/home:/home \
|
||||
-v $(PWD)/workspace:/workspace \
|
||||
--name antigravity \
|
||||
$(FULL_IMAGE)
|
||||
@echo "Access at http://localhost:5800"
|
||||
|
||||
# Stop the running container
|
||||
stop:
|
||||
@echo "Stopping antigravity container..."
|
||||
docker stop antigravity || true
|
||||
docker rm antigravity || true
|
||||
|
||||
# Clean up local volumes
|
||||
clean: stop
|
||||
@echo "Cleaning up..."
|
||||
rm -rf ./home ./workspace
|
||||
|
||||
# Helm deployment
|
||||
RELEASE_NAME ?= mydev
|
||||
NAMESPACE ?= default
|
||||
|
||||
helm-deploy:
|
||||
@echo "Deploying with Helm (release: $(RELEASE_NAME))..."
|
||||
@if [ -z "$(GITHUB_REPO)" ]; then \
|
||||
echo "ERROR: GITHUB_REPO environment variable is required"; \
|
||||
echo "Usage: GITHUB_REPO=https://github.com/user/repo make helm-deploy"; \
|
||||
exit 1; \
|
||||
fi
|
||||
helm upgrade --install $(RELEASE_NAME) ./chart \
|
||||
--namespace $(NAMESPACE) \
|
||||
--set name=$(RELEASE_NAME) \
|
||||
--set githubRepo="$(GITHUB_REPO)" \
|
||||
--set image.repository=$(REGISTRY)/$(IMAGE_NAME) \
|
||||
--set image.tag=$(IMAGE_TAG)
|
||||
|
||||
helm-delete:
|
||||
@echo "Deleting Helm release $(RELEASE_NAME)..."
|
||||
helm uninstall $(RELEASE_NAME) --namespace $(NAMESPACE)
|
||||
@echo "Note: PVC persists. To delete: kubectl delete pvc userhome-$(RELEASE_NAME) -n $(NAMESPACE)"
|
||||
|
||||
helm-logs:
|
||||
@echo "Showing logs for $(RELEASE_NAME)..."
|
||||
kubectl logs -f deployment/devcontainer-$(RELEASE_NAME) -n $(NAMESPACE)
|
||||
|
||||
helm-shell:
|
||||
@echo "Opening shell in $(RELEASE_NAME)..."
|
||||
kubectl exec -it deployment/devcontainer-$(RELEASE_NAME) -n $(NAMESPACE) -- bash
|
||||
|
||||
helm-port-forward:
|
||||
@echo "Port forwarding $(RELEASE_NAME) to localhost:5800..."
|
||||
kubectl port-forward deployment/devcontainer-$(RELEASE_NAME) 5800:5800 -n $(NAMESPACE)
|
||||
|
||||
# Show help
|
||||
help:
|
||||
@echo "Antigravity Dev Container Makefile"
|
||||
@echo ""
|
||||
@echo "Usage: make [target]"
|
||||
@echo ""
|
||||
@echo "Docker Targets:"
|
||||
@echo " build - Build the Docker image"
|
||||
@echo " push - Push image to registry"
|
||||
@echo " run - Run container locally (requires env vars)"
|
||||
@echo " stop - Stop running container"
|
||||
@echo " clean - Clean up containers and volumes"
|
||||
@echo ""
|
||||
@echo "Helm/Kubernetes Targets:"
|
||||
@echo " helm-deploy - Deploy with Helm chart (requires GITHUB_REPO)"
|
||||
@echo " helm-delete - Delete Helm release"
|
||||
@echo " helm-logs - Show container logs"
|
||||
@echo " helm-shell - Open shell in container"
|
||||
@echo " helm-port-forward - Port forward to localhost"
|
||||
@echo ""
|
||||
@echo "Variables:"
|
||||
@echo " REGISTRY - Docker registry (default: ghcr.io/cpfarhood)"
|
||||
@echo " IMAGE_NAME - Image name (default: antigravity)"
|
||||
@echo " IMAGE_TAG - Image tag (default: latest)"
|
||||
@echo " RELEASE_NAME - Helm release name (default: mydev)"
|
||||
@echo " NAMESPACE - Kubernetes namespace (default: default)"
|
||||
@echo " GITHUB_REPO - GitHub repository URL (required for helm-deploy)"
|
||||
@echo ""
|
||||
@echo "Environment Variables for 'make run':"
|
||||
@echo " GITHUB_REPO - GitHub repository URL"
|
||||
@echo " GITHUB_TOKEN - GitHub token (optional)"
|
||||
@echo " VNC_PASSWORD - VNC password (optional)"
|
||||
@echo ""
|
||||
@echo "Examples:"
|
||||
@echo " make build"
|
||||
@echo " make push REGISTRY=ghcr.io/myuser IMAGE_TAG=v1.0"
|
||||
@echo " GITHUB_REPO=https://github.com/user/repo make run"
|
||||
@echo " GITHUB_REPO=https://github.com/user/repo make helm-deploy"
|
||||
@echo " RELEASE_NAME=alice-dev GITHUB_REPO=https://github.com/alice/project make helm-deploy"
|
||||
@@ -0,0 +1,441 @@
|
||||
# Dev Container
|
||||
|
||||

|
||||
|
||||
A containerized cloud development environment with web-based GUI access, featuring:
|
||||
- **VSCode or Google Antigravity** via browser-based VNC (port 5800)
|
||||
- **SSH access** option (OpenSSH on port 22, additive with any IDE)
|
||||
- **Claude Code**, **Happy Coder**, **OpenCode**, and **Crush** AI coding agents (terminal-based)
|
||||
- **Automatic GitHub repo cloning** on startup
|
||||
- **Persistent home directory** via ReadWriteMany PVC
|
||||
- **Kubernetes-native** Helm chart deployment
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Option A: Quickstart (Recommended)
|
||||
|
||||
For 80% of users, use the simplified quickstart values:
|
||||
|
||||
```bash
|
||||
# Copy and customize the quickstart template
|
||||
cp chart/values-quickstart.yaml my-values.yaml
|
||||
|
||||
# Edit my-values.yaml to set your name and repository:
|
||||
# name: mydev
|
||||
# githubRepo: https://github.com/youruser/yourrepo
|
||||
|
||||
# Deploy with minimal configuration
|
||||
helm install mydev ./chart -f my-values.yaml
|
||||
```
|
||||
|
||||
### Option B: One-Command Deploy
|
||||
|
||||
```bash
|
||||
helm install mydev ./chart \
|
||||
--set name=mydev \
|
||||
--set githubRepo=https://github.com/youruser/yourrepo
|
||||
```
|
||||
|
||||
### Option C: Full Configuration
|
||||
|
||||
### 1. Create a secret
|
||||
|
||||
The secret is picked up automatically via `envFrom`. Keys recognised:
|
||||
|
||||
| Key | Purpose |
|
||||
|-----|---------|
|
||||
| `GITHUB_TOKEN` | PAT for private repo access (`repo` scope) |
|
||||
| `VNC_PASSWORD` | Password for the VNC web UI |
|
||||
| `ANTHROPIC_API_KEY` | API key — alternative to browser-based Claude login |
|
||||
| `SSH_AUTHORIZED_KEYS` | Public key(s) for SSH access (required when `ssh: true`) |
|
||||
| `HOMEASSISTANT_URL` | Home Assistant URL (required when `mcp.sidecars.homeassistant.enabled: true`) |
|
||||
| `HOMEASSISTANT_TOKEN` | Home Assistant long-lived access token (required when `mcp.sidecars.homeassistant.enabled: true`) |
|
||||
| `DATABASE_URI` | PostgreSQL connection string (required when `mcp.sidecars.pgtuner.enabled: true`) |
|
||||
| `PGTUNER_EXCLUDE_USERIDS` | Comma-separated PostgreSQL user OIDs to exclude from monitoring (optional) |
|
||||
|
||||
```bash
|
||||
kubectl create secret generic devcontainer-mydev-secrets-env \
|
||||
--from-literal=GITHUB_TOKEN='ghp_...' \
|
||||
--from-literal=VNC_PASSWORD='changeme'
|
||||
```
|
||||
|
||||
Or use SealedSecrets:
|
||||
|
||||
```bash
|
||||
kubectl create secret generic devcontainer-mydev-secrets-env \
|
||||
--from-literal=GITHUB_TOKEN='ghp_...' \
|
||||
--from-literal=VNC_PASSWORD='changeme' \
|
||||
--dry-run=client -o yaml | \
|
||||
kubeseal --format=yaml | kubectl apply -f -
|
||||
```
|
||||
|
||||
### 2. Deploy with Helm
|
||||
|
||||
```bash
|
||||
helm install mydev ./chart \
|
||||
--set name=mydev \
|
||||
--set githubRepo=https://github.com/youruser/yourrepo
|
||||
```
|
||||
|
||||
### 3. Access
|
||||
|
||||
```bash
|
||||
# Local port-forward
|
||||
kubectl port-forward deployment/devcontainer-mydev 5800:5800
|
||||
open http://localhost:5800
|
||||
```
|
||||
|
||||
Or configure an ingress / Gateway API HTTPRoute pointing at port 5800.
|
||||
|
||||
### 4. Authenticate Claude
|
||||
|
||||
On first launch, open a terminal in the VSCode GUI and run:
|
||||
|
||||
```bash
|
||||
claude
|
||||
```
|
||||
|
||||
A Chrome browser window will open inside VNC for the Claude Max OAuth login. Credentials are stored on the home PVC and persist across pod restarts.
|
||||
|
||||
---
|
||||
|
||||
## Helm Chart Reference
|
||||
|
||||
The Helm chart uses a logical organization with these main sections:
|
||||
- **Basic Configuration**: name, image, githubRepo
|
||||
- **Access & Interface**: IDE, SSH, display, user settings
|
||||
- **Infrastructure**: storage, resources, cluster access
|
||||
- **Integrations**: Happy Coder, MCP sidecars
|
||||
- **Smart Defaults**: auto-detection and profiles
|
||||
|
||||
📖 **Documentation**:
|
||||
- [USAGE.md](chart/USAGE.md) - Comprehensive examples and scenarios
|
||||
- [values-quickstart.yaml](chart/values-quickstart.yaml) - Minimal configuration
|
||||
- [values.schema.json](chart/values.schema.json) - IDE validation support
|
||||
|
||||
### Core values
|
||||
|
||||
| Value | Default | Description |
|
||||
|-------|---------|-------------|
|
||||
| `name` | `""` | Instance name — used in all resource names (`devcontainer-{name}`) |
|
||||
| `githubRepo` | `""` | Repository to clone into `/workspace` on startup |
|
||||
| `ide.type` | `vscode` | IDE to launch — `vscode`, `antigravity`, or `none` (see below) |
|
||||
| `ssh.enabled` | `false` | Also start an OpenSSH server on port 22 (additive, any IDE) |
|
||||
| `image.repository` | `ghcr.io/cpfarhood/devcontainer` | Container image |
|
||||
| `image.tag` | `latest` | Image tag |
|
||||
|
||||
### IDE choice
|
||||
|
||||
`ide.type` controls what GUI is launched in the VNC session:
|
||||
|
||||
| Value | Port | Description |
|
||||
|-------|------|-------------|
|
||||
| `vscode` (default) | 5800 (VNC) | VSCode desktop via browser-based VNC |
|
||||
| `antigravity` | 5800 (VNC) | Google Antigravity (VSCode fork with AI) via VNC |
|
||||
| `none` | — | No IDE; container stays alive (useful when `ssh: true`) |
|
||||
|
||||
### SSH access
|
||||
|
||||
`ssh.enabled: true` starts OpenSSH on port 22 **in addition to** the IDE. It works with any `ide.type` value:
|
||||
|
||||
```bash
|
||||
# SSH-only (no VNC)
|
||||
helm install mydev ./chart --set name=mydev --set ide.type=none --set ssh.enabled=true
|
||||
|
||||
# VSCode in VNC + SSH access at the same time
|
||||
helm install mydev ./chart --set name=mydev --set ssh.enabled=true
|
||||
```
|
||||
|
||||
Add your public key to the env secret:
|
||||
|
||||
```bash
|
||||
kubectl create secret generic devcontainer-mydev-secrets-env \
|
||||
--from-literal=GITHUB_TOKEN='ghp_...' \
|
||||
--from-literal=SSH_AUTHORIZED_KEYS='ssh-ed25519 AAAA...'
|
||||
```
|
||||
|
||||
Then connect:
|
||||
|
||||
```bash
|
||||
kubectl port-forward deployment/devcontainer-mydev 2222:22
|
||||
ssh -p 2222 user@localhost
|
||||
```
|
||||
|
||||
### Happy Coder
|
||||
|
||||
| Value | Default | Description |
|
||||
|-------|---------|-------------|
|
||||
| `happy.serverUrl` | `https://happy.farh.net` | Happy Coder server endpoint |
|
||||
| `happy.webappUrl` | `https://happy-coder.farh.net` | Happy Coder webapp URL |
|
||||
| `happy.homeDir` | `/config/userdata/.happy` | Happy runtime state directory (persists on the home PVC) |
|
||||
| `happy.experimental` | `true` | Enable experimental Happy features |
|
||||
|
||||
### Kubernetes cluster access
|
||||
|
||||
The `clusterAccess` value provisions a ServiceAccount, Role/ClusterRole, and binding so the devcontainer pod can interact with the Kubernetes API. The default is `none` — no RBAC resources are created.
|
||||
|
||||
| Value | Scope | Verbs |
|
||||
|-------|-------|-------|
|
||||
| `none` (default) | — | no access |
|
||||
| `readonlyns` | release namespace | `get`, `list`, `watch` |
|
||||
| `readwritens` | release namespace | `*` |
|
||||
| `readonly` | cluster-wide | `get`, `list`, `watch` |
|
||||
| `readwrite` | cluster-wide | `*` |
|
||||
|
||||
```bash
|
||||
# Give the pod read-only access to its own namespace
|
||||
helm install mydev ./chart \
|
||||
--set name=mydev \
|
||||
--set githubRepo=https://github.com/youruser/yourrepo \
|
||||
--set clusterAccess=readonlyns
|
||||
```
|
||||
|
||||
With any non-`none` value, a `ServiceAccount` named `devcontainer-{name}` is created and set as the pod's `serviceAccountName`, so `kubectl` and any in-cluster API calls use it automatically.
|
||||
|
||||
### MCP Sidecars
|
||||
|
||||
The devcontainer includes MCP (Model Context Protocol) servers as sidecar containers that enable AI assistants to interact with various services:
|
||||
|
||||
| Sidecar | Default | Purpose |
|
||||
|---------|---------|---------|
|
||||
| `mcp.sidecars.kubernetes.enabled` | `true` | Kubernetes API access via MCP |
|
||||
| `mcp.sidecars.flux.enabled` | `true` | Flux GitOps operations via MCP |
|
||||
| `mcp.sidecars.homeassistant.enabled` | `false` | Home Assistant smart home control via MCP |
|
||||
| `mcp.sidecars.pgtuner.enabled` | `false` | PostgreSQL performance tuning and analysis via MCP |
|
||||
| `mcp.sidecars.playwright.enabled` | `true` | Browser automation and web testing via MCP |
|
||||
|
||||
**Notes:**
|
||||
- GitHub MCP is accessed via the Copilot API (`https://api.githubcopilot.com/mcp/`), not as a sidecar
|
||||
- Kubernetes and Flux sidecars require `clusterAccess` != `none` to be deployed (automatically disabled when no cluster access)
|
||||
- Kubernetes and Flux sidecars inherit the pod's ServiceAccount RBAC permissions (controlled by `clusterAccess`)
|
||||
- Home Assistant sidecar requires `HOMEASSISTANT_URL` and `HOMEASSISTANT_TOKEN` in the env secret
|
||||
- PostgreSQL tuner sidecar requires `DATABASE_URI` in the env secret (PostgreSQL connection string)
|
||||
- Playwright sidecar provides browser automation and web testing capabilities
|
||||
|
||||
**Disable MCP sidecars:**
|
||||
```bash
|
||||
# Disable multiple sidecars
|
||||
helm install mydev ./chart \
|
||||
--set name=mydev \
|
||||
--set githubRepo=https://github.com/youruser/yourrepo \
|
||||
--set mcp.sidecars.kubernetes.enabled=false \
|
||||
--set mcp.sidecars.flux.enabled=false \
|
||||
--set mcp.sidecars.playwright.enabled=false
|
||||
|
||||
# Or selectively disable
|
||||
helm install mydev ./chart \
|
||||
--set name=mydev \
|
||||
--set githubRepo=https://github.com/youruser/yourrepo \
|
||||
--set mcp.sidecars.flux.enabled=false # Disable only Flux MCP
|
||||
```
|
||||
|
||||
**Enable Home Assistant MCP:**
|
||||
```bash
|
||||
# Create secret with Home Assistant credentials
|
||||
kubectl create secret generic devcontainer-mydev-secrets-env \
|
||||
--from-literal=GITHUB_TOKEN='ghp_...' \
|
||||
--from-literal=HOMEASSISTANT_URL='http://homeassistant.local:8123' \
|
||||
--from-literal=HOMEASSISTANT_TOKEN='your_long_lived_access_token'
|
||||
|
||||
# Deploy with Home Assistant MCP enabled
|
||||
helm install mydev ./chart \
|
||||
--set name=mydev \
|
||||
--set githubRepo=https://github.com/youruser/yourrepo \
|
||||
--set mcp.sidecars.homeassistant.enabled=true
|
||||
```
|
||||
|
||||
**Enable PostgreSQL Tuner MCP:**
|
||||
```bash
|
||||
# Create secret with PostgreSQL connection string
|
||||
kubectl create secret generic devcontainer-mydev-secrets-env \
|
||||
--from-literal=GITHUB_TOKEN='ghp_...' \
|
||||
--from-literal=DATABASE_URI='postgresql://user:password@postgres.example.com:5432/dbname'
|
||||
|
||||
# Deploy with PostgreSQL tuner MCP enabled
|
||||
helm install mydev ./chart \
|
||||
--set name=mydev \
|
||||
--set githubRepo=https://github.com/youruser/yourrepo \
|
||||
--set mcp.sidecars.pgtuner.enabled=true
|
||||
```
|
||||
|
||||
**Custom MCP configuration:**
|
||||
```yaml
|
||||
# values.yaml override
|
||||
mcp:
|
||||
sidecars:
|
||||
kubernetes:
|
||||
enabled: true
|
||||
image:
|
||||
repository: quay.io/containers/kubernetes_mcp_server
|
||||
tag: v0.0.57
|
||||
port: 8080
|
||||
resources:
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "50m"
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
cpu: "500m"
|
||||
flux:
|
||||
enabled: false # Disabled in this example
|
||||
homeassistant:
|
||||
enabled: true
|
||||
image:
|
||||
repository: ghcr.io/homeassistant-ai/ha-mcp
|
||||
tag: stable
|
||||
port: 8087
|
||||
pgtuner:
|
||||
enabled: true
|
||||
image:
|
||||
repository: dog830228/pgtuner_mcp
|
||||
tag: latest
|
||||
port: 8085
|
||||
playwright:
|
||||
enabled: true
|
||||
image:
|
||||
repository: mcr.microsoft.com/playwright/mcp
|
||||
tag: latest
|
||||
port: 8086
|
||||
resources:
|
||||
requests:
|
||||
memory: "128Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "512Mi"
|
||||
cpu: "1000m"
|
||||
```
|
||||
|
||||
### Display and resources
|
||||
|
||||
| Value | Default | Description |
|
||||
|-------|---------|-------------|
|
||||
| `display.width` | `1920` | VNC width (px) |
|
||||
| `display.height` | `1080` | VNC height (px) |
|
||||
| `display.secureConnection` | `0` | Set to `1` if TLS is not terminated upstream |
|
||||
| `user.id` | `1000` | UID for the app user |
|
||||
| `user.groupId` | `1000` | GID for the app user |
|
||||
| `storage.size` | `32Gi` | Home PVC size |
|
||||
| `storage.className` | `ceph-filesystem` | StorageClass (must be ReadWriteMany) |
|
||||
| `shm.sizeLimit` | `2Gi` | `/dev/shm` size (memory-backed; used by Electron apps) |
|
||||
| `resources.requests.memory` | `2Gi` | |
|
||||
| `resources.requests.cpu` | `1000m` | |
|
||||
| `resources.limits.memory` | `8Gi` | |
|
||||
| `resources.limits.cpu` | `4000m` | |
|
||||
| `envSecretName` | `devcontainer-{name}-secrets-env` | Override the secret name |
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
### Startup flow
|
||||
|
||||
```
|
||||
Container start
|
||||
→ cont-init.d/20-fix-user-shell.sh — fix shell/home on baseimage-gui app user
|
||||
→ cont-init.d/25-start-sshd.sh — start sshd if SSH=true
|
||||
→ /startapp.sh (runs as app user, UID 1000)
|
||||
→ init-repo.sh
|
||||
→ clone / pull GITHUB_REPO into /workspace/{repo}
|
||||
→ IDE=vscode: code --new-window --wait /workspace/{repo}
|
||||
IDE=antigravity: antigravity --no-sandbox --user-data-dir ~/.config/antigravity ... /workspace/{repo}
|
||||
IDE=none: sleep infinity
|
||||
(SSH=true: sshd also running as root on port 22; host keys persisted on PVC)
|
||||
```
|
||||
|
||||
### Storage
|
||||
|
||||
| Mount | Source | Persistence |
|
||||
|-------|--------|-------------|
|
||||
| `/config` | ReadWriteMany PVC (`userhome-{name}`) | Survives pod restarts — stores Claude credentials, dotfiles, git config |
|
||||
| `/workspace` | `emptyDir` | Ephemeral — repo is re-cloned on each pod start |
|
||||
|
||||
Happy Coder's runtime state (`HAPPY_HOME_DIR`) is kept in `/config/userdata/.happy` on the persistent home PVC, so auth credentials and settings survive pod restarts when manually started.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Happy Coder (manual startup)
|
||||
|
||||
Happy daemon is not started automatically. Launch it manually when needed:
|
||||
|
||||
```bash
|
||||
# Start Happy Coder daemon manually
|
||||
happy daemon start
|
||||
|
||||
# Check daemon status
|
||||
happy daemon status
|
||||
|
||||
# View daemon logs
|
||||
ls ~/.happy/logs/
|
||||
|
||||
# Stop daemon if needed
|
||||
happy daemon stop
|
||||
```
|
||||
|
||||
### Claude not authenticated
|
||||
|
||||
Browser-based OAuth login is the primary method (works inside VNC via the Chrome wrapper). If you prefer API key auth:
|
||||
|
||||
```bash
|
||||
kubectl patch secret devcontainer-mydev-secrets-env \
|
||||
--type='json' \
|
||||
-p='[{"op":"add","path":"/data/ANTHROPIC_API_KEY","value":"'$(echo -n "sk-ant-..." | base64)'"}]'
|
||||
```
|
||||
|
||||
Then restart the pod to pick up the new env var.
|
||||
|
||||
### VNC not loading
|
||||
|
||||
```bash
|
||||
kubectl port-forward deployment/devcontainer-mydev 5800:5800
|
||||
kubectl logs deployment/devcontainer-mydev
|
||||
kubectl describe pod -l app.kubernetes.io/instance=mydev
|
||||
```
|
||||
|
||||
### Pod not picking up new image after upgrade
|
||||
|
||||
The chart uses `image.tag: latest`. Kubernetes won't restart the pod on a Helm upgrade unless the Deployment spec changes. Force a restart manually:
|
||||
|
||||
```bash
|
||||
kubectl rollout restart deployment/devcontainer-mydev
|
||||
```
|
||||
|
||||
### Repository not cloning
|
||||
|
||||
```bash
|
||||
kubectl logs deployment/devcontainer-mydev | grep "Repository Initialization"
|
||||
kubectl exec deployment/devcontainer-mydev -- env | grep GITHUB
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Local Docker run
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
-p 5800:5800 \
|
||||
-e GITHUB_REPO="https://github.com/youruser/yourrepo" \
|
||||
-e GITHUB_TOKEN="ghp_..." \
|
||||
-e VNC_PASSWORD="changeme" \
|
||||
-v $(pwd)/home:/home \
|
||||
ghcr.io/cpfarhood/devcontainer:latest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Building
|
||||
|
||||
```bash
|
||||
docker build -t ghcr.io/cpfarhood/devcontainer:latest .
|
||||
docker push ghcr.io/cpfarhood/devcontainer:latest
|
||||
```
|
||||
|
||||
The image is also built and pushed automatically by CI on every push to `main` and on version tags (`v*`).
|
||||
|
||||
---
|
||||
|
||||
## Credits
|
||||
|
||||
- Base image: [jlesage/docker-baseimage-gui](https://github.com/jlesage/docker-baseimage-gui)
|
||||
- AI assistant: [Happy Coder](https://happy.engineering) + [Claude](https://claude.ai)
|
||||
+444
@@ -0,0 +1,444 @@
|
||||
# Helm Chart Values Reference
|
||||
|
||||
Complete reference for all configurable values in the Antigravity Dev Container Helm chart.
|
||||
|
||||
## Core Configuration
|
||||
|
||||
### name
|
||||
- **Type:** String
|
||||
- **Default:** `""`
|
||||
- **Required:** Yes
|
||||
- **Description:** Instance name used to generate resource names (`devcontainer-{name}`, `userhome-{name}`)
|
||||
- **Example:** `mydev`, `alice-dev`, `team-workspace`
|
||||
|
||||
### githubRepo
|
||||
- **Type:** String
|
||||
- **Default:** `""`
|
||||
- **Required:** Yes
|
||||
- **Description:** GitHub repository URL to clone into `/workspace`
|
||||
- **Example:** `https://github.com/username/repository`
|
||||
|
||||
### ide
|
||||
- **Type:** String
|
||||
- **Default:** `vscode`
|
||||
- **Options:** `vscode`, `antigravity`, `none`
|
||||
- **Description:** IDE to launch inside the container
|
||||
- `vscode` — VSCode via VNC browser UI on port 5800
|
||||
- `antigravity` — Google Antigravity (VSCode fork) via VNC on port 5800
|
||||
- `none` — No IDE; useful when `ssh: true` is the sole access method
|
||||
|
||||
### ssh
|
||||
- **Type:** Boolean
|
||||
- **Default:** `false`
|
||||
- **Description:** Start an OpenSSH server on port 22 in addition to the IDE
|
||||
- **Note:** Requires `SSH_AUTHORIZED_KEYS` in env secret for key-based login
|
||||
|
||||
## Image Configuration
|
||||
|
||||
### image.repository
|
||||
- **Type:** String
|
||||
- **Default:** `ghcr.io/cpfarhood/devcontainer`
|
||||
- **Description:** Container image repository
|
||||
|
||||
### image.tag
|
||||
- **Type:** String
|
||||
- **Default:** `latest`
|
||||
- **Description:** Container image tag
|
||||
- **Best Practice:** Use specific version tags for production
|
||||
|
||||
### image.pullPolicy
|
||||
- **Type:** String
|
||||
- **Default:** `Always`
|
||||
- **Options:** `Always`, `IfNotPresent`, `Never`
|
||||
- **Description:** Image pull policy
|
||||
|
||||
## Happy Coder Configuration
|
||||
|
||||
### happyServerUrl
|
||||
- **Type:** String
|
||||
- **Default:** `https://happy.farh.net`
|
||||
- **Description:** Happy Coder server endpoint
|
||||
- **When to Change:** Self-hosted Happy instance
|
||||
|
||||
### happyWebappUrl
|
||||
- **Type:** String
|
||||
- **Default:** `https://happy-coder.farh.net`
|
||||
- **Description:** Happy Coder webapp URL
|
||||
- **When to Change:** Self-hosted Happy instance
|
||||
|
||||
### happyHomeDir
|
||||
- **Type:** String
|
||||
- **Default:** `/config/userdata/.happy`
|
||||
- **Description:** Happy runtime state directory (persists on PVC)
|
||||
|
||||
### happyExperimental
|
||||
- **Type:** String
|
||||
- **Default:** `"true"`
|
||||
- **Description:** Enable experimental Happy features
|
||||
|
||||
## Display Configuration
|
||||
|
||||
### display.width
|
||||
- **Type:** String
|
||||
- **Default:** `"1920"`
|
||||
- **Description:** VNC display width in pixels
|
||||
|
||||
### display.height
|
||||
- **Type:** String
|
||||
- **Default:** `"1080"`
|
||||
- **Description:** VNC display height in pixels
|
||||
|
||||
### secureConnection
|
||||
- **Type:** String
|
||||
- **Default:** `"0"`
|
||||
- **Options:** `"0"`, `"1"`
|
||||
- **Description:** Set to `"0"` when TLS is terminated at the gateway layer
|
||||
|
||||
## User Configuration
|
||||
|
||||
### userId
|
||||
- **Type:** String
|
||||
- **Default:** `"1000"`
|
||||
- **Description:** UID for the app user
|
||||
|
||||
### groupId
|
||||
- **Type:** String
|
||||
- **Default:** `"1000"`
|
||||
- **Description:** GID for the app user
|
||||
|
||||
## Storage Configuration
|
||||
|
||||
### storage.size
|
||||
- **Type:** String
|
||||
- **Default:** `32Gi`
|
||||
- **Description:** Size of the persistent home directory
|
||||
- **Format:** Kubernetes quantity (e.g., `10Gi`, `100Gi`, `1Ti`)
|
||||
|
||||
### storage.className
|
||||
- **Type:** String
|
||||
- **Default:** `ceph-filesystem`
|
||||
- **Description:** StorageClass name (must support ReadWriteMany)
|
||||
- **Examples:** `ceph-filesystem`, `nfs-client`, `efs-sc`, `azurefile`
|
||||
|
||||
### shm.sizeLimit
|
||||
- **Type:** String
|
||||
- **Default:** `2Gi`
|
||||
- **Description:** `/dev/shm` size (memory-backed emptyDir for Electron apps)
|
||||
|
||||
## Resource Limits
|
||||
|
||||
### resources.requests.memory
|
||||
- **Type:** String
|
||||
- **Default:** `2Gi`
|
||||
- **Description:** Minimum memory to reserve
|
||||
- **Format:** Kubernetes quantity
|
||||
|
||||
### resources.requests.cpu
|
||||
- **Type:** String
|
||||
- **Default:** `1000m`
|
||||
- **Description:** Minimum CPU to reserve
|
||||
- **Format:** Millicores (`1000m` = 1 CPU core)
|
||||
|
||||
### resources.limits.memory
|
||||
- **Type:** String
|
||||
- **Default:** `8Gi`
|
||||
- **Description:** Maximum memory allowed
|
||||
- **Format:** Kubernetes quantity
|
||||
|
||||
### resources.limits.cpu
|
||||
- **Type:** String
|
||||
- **Default:** `4000m`
|
||||
- **Description:** Maximum CPU allowed
|
||||
- **Format:** Millicores (`4000m` = 4 CPU cores)
|
||||
|
||||
## Kubernetes Access
|
||||
|
||||
### clusterAccess
|
||||
- **Type:** String
|
||||
- **Default:** `none`
|
||||
- **Options:**
|
||||
- `none` — No cluster access
|
||||
- `readonlyns` — Read-only access to release namespace
|
||||
- `readwritens` — Full access to release namespace
|
||||
- `readonly` — Read-only access cluster-wide
|
||||
- `readwrite` — Full access cluster-wide
|
||||
- **Description:** RBAC permissions for the pod's ServiceAccount
|
||||
|
||||
## Secrets
|
||||
|
||||
### envSecretName
|
||||
- **Type:** String
|
||||
- **Default:** `""` (auto-generates as `devcontainer-{name}-secrets-env`)
|
||||
- **Description:** Name of existing Secret containing environment variables
|
||||
- **Keys Recognized:**
|
||||
- `GITHUB_TOKEN` — PAT for private repo access
|
||||
- `VNC_PASSWORD` — Password for VNC web UI
|
||||
- `ANTHROPIC_API_KEY` — API key for Claude
|
||||
- `SSH_AUTHORIZED_KEYS` — Public keys for SSH access
|
||||
- `homeassistant-url` — Home Assistant base URL (e.g., http://homeassistant.local:8123)
|
||||
- `homeassistant-token` — Home Assistant long-lived access token
|
||||
|
||||
## MCP Sidecars
|
||||
|
||||
### mcpSidecars.kubernetes.enabled
|
||||
- **Type:** Boolean
|
||||
- **Default:** `true`
|
||||
- **Description:** Enable Kubernetes MCP server sidecar
|
||||
|
||||
### mcpSidecars.kubernetes.image.repository
|
||||
- **Type:** String
|
||||
- **Default:** `quay.io/containers/kubernetes_mcp_server`
|
||||
- **Description:** Kubernetes MCP server image
|
||||
|
||||
### mcpSidecars.kubernetes.image.tag
|
||||
- **Type:** String
|
||||
- **Default:** `latest`
|
||||
- **Description:** Kubernetes MCP server image tag
|
||||
|
||||
### mcpSidecars.kubernetes.port
|
||||
- **Type:** Integer
|
||||
- **Default:** `8080`
|
||||
- **Description:** Port for Kubernetes MCP server
|
||||
|
||||
### mcpSidecars.kubernetes.resources
|
||||
- **Type:** Object
|
||||
- **Default:**
|
||||
```yaml
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "50m"
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
cpu: "500m"
|
||||
```
|
||||
- **Description:** Resource limits for Kubernetes MCP sidecar
|
||||
|
||||
### mcpSidecars.flux.enabled
|
||||
- **Type:** Boolean
|
||||
- **Default:** `true`
|
||||
- **Description:** Enable Flux MCP server sidecar
|
||||
|
||||
### mcpSidecars.flux.image.repository
|
||||
- **Type:** String
|
||||
- **Default:** `ghcr.io/controlplaneio-fluxcd/flux-operator-mcp`
|
||||
- **Description:** Flux MCP server image
|
||||
|
||||
### mcpSidecars.flux.image.tag
|
||||
- **Type:** String
|
||||
- **Default:** `v0.41.1`
|
||||
- **Description:** Flux MCP server image tag
|
||||
|
||||
### mcpSidecars.flux.port
|
||||
- **Type:** Integer
|
||||
- **Default:** `8081`
|
||||
- **Description:** Port for Flux MCP server
|
||||
|
||||
### mcpSidecars.flux.resources
|
||||
- **Type:** Object
|
||||
- **Default:**
|
||||
```yaml
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "50m"
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
cpu: "500m"
|
||||
```
|
||||
- **Description:** Resource limits for Flux MCP sidecar
|
||||
|
||||
### mcpSidecars.homeassistant.enabled
|
||||
- **Type:** Boolean
|
||||
- **Default:** `false`
|
||||
- **Description:** Enable Home Assistant MCP server sidecar
|
||||
- **Note:** Requires `homeassistant-url` and `homeassistant-token` in env secret
|
||||
|
||||
### mcpSidecars.homeassistant.image.repository
|
||||
- **Type:** String
|
||||
- **Default:** `ghcr.io/homeassistant-ai/ha-mcp`
|
||||
- **Description:** Home Assistant MCP server image
|
||||
|
||||
### mcpSidecars.homeassistant.image.tag
|
||||
- **Type:** String
|
||||
- **Default:** `stable`
|
||||
- **Description:** Home Assistant MCP server image tag
|
||||
- **Options:** `stable` (recommended), `latest` (dev builds), `v{version}` (specific version)
|
||||
|
||||
### mcpSidecars.homeassistant.port
|
||||
- **Type:** Integer
|
||||
- **Default:** `8087`
|
||||
- **Description:** Port for Home Assistant MCP server (SSE mode)
|
||||
|
||||
### mcpSidecars.homeassistant.resources
|
||||
- **Type:** Object
|
||||
- **Default:**
|
||||
```yaml
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "50m"
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
cpu: "500m"
|
||||
```
|
||||
- **Description:** Resource limits for Home Assistant MCP sidecar
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Minimal Configuration
|
||||
|
||||
```yaml
|
||||
name: mydev
|
||||
githubRepo: https://github.com/user/repo
|
||||
```
|
||||
|
||||
### Production Configuration
|
||||
|
||||
```yaml
|
||||
name: prod-workspace
|
||||
githubRepo: https://github.com/company/application
|
||||
ide: vscode
|
||||
ssh: true
|
||||
|
||||
image:
|
||||
tag: v1.0.0
|
||||
|
||||
storage:
|
||||
size: 100Gi
|
||||
className: ceph-filesystem
|
||||
|
||||
resources:
|
||||
requests:
|
||||
memory: "4Gi"
|
||||
cpu: "2000m"
|
||||
limits:
|
||||
memory: "16Gi"
|
||||
cpu: "8000m"
|
||||
|
||||
clusterAccess: readwritens
|
||||
|
||||
mcpSidecars:
|
||||
kubernetes:
|
||||
enabled: true
|
||||
flux:
|
||||
enabled: false
|
||||
```
|
||||
|
||||
### Development Team Configuration
|
||||
|
||||
```yaml
|
||||
name: team-dev
|
||||
githubRepo: https://github.com/team/project
|
||||
ide: antigravity
|
||||
|
||||
display:
|
||||
width: "2560"
|
||||
height: "1440"
|
||||
|
||||
storage:
|
||||
size: 50Gi
|
||||
className: nfs-client
|
||||
|
||||
clusterAccess: readonly
|
||||
|
||||
happyServerUrl: https://happy.internal.company.com
|
||||
happyWebappUrl: https://happy-app.internal.company.com
|
||||
```
|
||||
|
||||
### Smart Home Development Configuration
|
||||
|
||||
```yaml
|
||||
name: smarthome-dev
|
||||
githubRepo: https://github.com/user/home-automation
|
||||
ide: vscode
|
||||
|
||||
clusterAccess: readwritens
|
||||
|
||||
mcpSidecars:
|
||||
kubernetes:
|
||||
enabled: true
|
||||
flux:
|
||||
enabled: false
|
||||
homeassistant:
|
||||
enabled: true
|
||||
image:
|
||||
tag: stable
|
||||
|
||||
# Requires secrets:
|
||||
# homeassistant-url: http://homeassistant.local:8123
|
||||
# homeassistant-token: <long-lived-access-token>
|
||||
```
|
||||
|
||||
## Helm CLI Examples
|
||||
|
||||
### Using --set Flags
|
||||
|
||||
```bash
|
||||
# Basic deployment
|
||||
helm install mydev ./chart \
|
||||
--set name=mydev \
|
||||
--set githubRepo=https://github.com/user/repo
|
||||
|
||||
# With multiple values
|
||||
helm install mydev ./chart \
|
||||
--set name=mydev \
|
||||
--set githubRepo=https://github.com/user/repo \
|
||||
--set ide=antigravity \
|
||||
--set storage.size=50Gi \
|
||||
--set clusterAccess=readwritens \
|
||||
--set mcpSidecars.flux.enabled=false
|
||||
```
|
||||
|
||||
### Using Values File
|
||||
|
||||
Create `custom-values.yaml`:
|
||||
```yaml
|
||||
name: mydev
|
||||
githubRepo: https://github.com/user/repo
|
||||
storage:
|
||||
size: 50Gi
|
||||
clusterAccess: readwritens
|
||||
```
|
||||
|
||||
Deploy:
|
||||
```bash
|
||||
helm install mydev ./chart -f custom-values.yaml
|
||||
```
|
||||
|
||||
### Combining Methods
|
||||
|
||||
```bash
|
||||
helm install mydev ./chart \
|
||||
-f base-values.yaml \
|
||||
-f prod-values.yaml \
|
||||
--set githubRepo=https://github.com/user/repo \
|
||||
--set image.tag=v2.0.0
|
||||
```
|
||||
|
||||
## Value Precedence
|
||||
|
||||
Values are applied in order of precedence (highest to lowest):
|
||||
1. `--set` flags on command line
|
||||
2. `-f` values files (later files override earlier)
|
||||
3. `chart/values.yaml` defaults
|
||||
|
||||
## Environment Variables
|
||||
|
||||
These environment variables are set in the container based on chart values:
|
||||
|
||||
| Environment Variable | Source Value | Description |
|
||||
|---------------------|--------------|-------------|
|
||||
| `GITHUB_REPO` | `githubRepo` | Repository to clone |
|
||||
| `GITHUB_TOKEN` | Secret: `github-token` | PAT for private repos |
|
||||
| `VNC_PASSWORD` | Secret: `vnc-password` | VNC access password |
|
||||
| `ANTHROPIC_API_KEY` | Secret: `anthropic-api-key` | Claude API key |
|
||||
| `SSH_AUTHORIZED_KEYS` | Secret: `ssh-authorized-keys` | SSH public keys |
|
||||
| `HAPPY_SERVER_URL` | `happyServerUrl` | Happy server endpoint |
|
||||
| `HAPPY_WEBAPP_URL` | `happyWebappUrl` | Happy webapp URL |
|
||||
| `HAPPY_HOME_DIR` | `happyHomeDir` | Happy data directory |
|
||||
| `HAPPY_EXPERIMENTAL` | `happyExperimental` | Experimental features |
|
||||
| `DISPLAY_WIDTH` | `display.width` | VNC width |
|
||||
| `DISPLAY_HEIGHT` | `display.height` | VNC height |
|
||||
| `SECURE_CONNECTION` | `secureConnection` | TLS termination |
|
||||
| `USER_ID` | `userId` | App user UID |
|
||||
| `GROUP_ID` | `groupId` | App user GID |
|
||||
| `IDE` | `ide` | IDE to launch |
|
||||
| `SSH` | `ssh` | SSH server enabled |
|
||||
@@ -0,0 +1,6 @@
|
||||
apiVersion: v2
|
||||
name: devcontainer
|
||||
description: Dev Container with AI coding agents and MCP sidecars
|
||||
type: application
|
||||
version: 0.4.9
|
||||
appVersion: "latest"
|
||||
+381
@@ -0,0 +1,381 @@
|
||||
# Dev Container Helm Chart Usage Guide
|
||||
|
||||
This guide provides common usage patterns and examples for the Dev Container Helm chart.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Minimal Installation (Recommended)
|
||||
|
||||
Use the quickstart values for the simplest setup:
|
||||
|
||||
```bash
|
||||
# Copy and customize quickstart values
|
||||
cp values-quickstart.yaml my-values.yaml
|
||||
|
||||
# Edit my-values.yaml to set your name and repo:
|
||||
# name: myproject
|
||||
# githubRepo: https://github.com/youruser/yourproject
|
||||
|
||||
# Install
|
||||
helm install myproject ./chart -f my-values.yaml
|
||||
```
|
||||
|
||||
### 2. One-Command Installation
|
||||
|
||||
```bash
|
||||
helm install mydev ./chart \
|
||||
--set name=mydev \
|
||||
--set githubRepo=https://github.com/youruser/yourrepo
|
||||
```
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### Development Environment
|
||||
|
||||
**Scenario**: Standard development with GitHub integration
|
||||
|
||||
```yaml
|
||||
name: dev-environment
|
||||
githubRepo: https://github.com/company/project
|
||||
|
||||
ide:
|
||||
type: vscode
|
||||
|
||||
mcp:
|
||||
sidecars:
|
||||
kubernetes:
|
||||
enabled: true
|
||||
playwright:
|
||||
enabled: true
|
||||
flux:
|
||||
enabled: false # Disable if not using Flux
|
||||
```
|
||||
|
||||
### Team Workspace
|
||||
|
||||
**Scenario**: Shared development environment with more resources
|
||||
|
||||
```yaml
|
||||
name: team-workspace
|
||||
githubRepo: https://github.com/company/project
|
||||
|
||||
resources:
|
||||
requests:
|
||||
memory: "4Gi"
|
||||
cpu: "2000m"
|
||||
limits:
|
||||
memory: "16Gi"
|
||||
cpu: "8000m"
|
||||
|
||||
storage:
|
||||
size: 64Gi
|
||||
|
||||
ssh:
|
||||
enabled: true # Enable SSH access for team
|
||||
|
||||
clusterAccess: readwrite # Full cluster access
|
||||
```
|
||||
|
||||
### Kubernetes Admin Environment
|
||||
|
||||
**Scenario**: Platform engineering with full cluster access
|
||||
|
||||
```yaml
|
||||
name: k8s-admin
|
||||
githubRepo: https://github.com/company/k8s-configs
|
||||
|
||||
clusterAccess: readwrite
|
||||
|
||||
mcp:
|
||||
sidecars:
|
||||
kubernetes:
|
||||
enabled: true
|
||||
flux:
|
||||
enabled: true
|
||||
pgtuner:
|
||||
enabled: true # Database administration
|
||||
playwright:
|
||||
enabled: false # Save resources
|
||||
```
|
||||
|
||||
### AI/ML Development
|
||||
|
||||
**Scenario**: AI development with browser automation
|
||||
|
||||
```yaml
|
||||
name: ai-playground
|
||||
githubRepo: https://github.com/company/ai-project
|
||||
|
||||
resources:
|
||||
requests:
|
||||
memory: "8Gi" # More memory for ML workloads
|
||||
cpu: "4000m"
|
||||
limits:
|
||||
memory: "32Gi"
|
||||
cpu: "16000m"
|
||||
|
||||
storage:
|
||||
size: 128Gi # Large datasets
|
||||
|
||||
mcp:
|
||||
sidecars:
|
||||
playwright:
|
||||
enabled: true # Web scraping, testing
|
||||
kubernetes:
|
||||
enabled: false # Save resources
|
||||
flux:
|
||||
enabled: false
|
||||
```
|
||||
|
||||
### Lightweight Environment
|
||||
|
||||
**Scenario**: Resource-constrained setup
|
||||
|
||||
```yaml
|
||||
name: lightweight
|
||||
githubRepo: https://github.com/youruser/small-project
|
||||
|
||||
resources:
|
||||
requests:
|
||||
memory: "1Gi"
|
||||
cpu: "500m"
|
||||
limits:
|
||||
memory: "2Gi"
|
||||
cpu: "1000m"
|
||||
|
||||
storage:
|
||||
size: 8Gi
|
||||
|
||||
mcp:
|
||||
sidecars:
|
||||
kubernetes:
|
||||
enabled: false
|
||||
flux:
|
||||
enabled: false
|
||||
playwright:
|
||||
enabled: false
|
||||
# Only keep essential sidecars enabled
|
||||
```
|
||||
|
||||
## Secret Configuration
|
||||
|
||||
### Basic Secrets
|
||||
|
||||
```bash
|
||||
# GitHub access only
|
||||
kubectl create secret generic devcontainer-mydev-secrets-env \
|
||||
--from-literal=GITHUB_TOKEN='ghp_...' \
|
||||
--from-literal=VNC_PASSWORD='changeme'
|
||||
```
|
||||
|
||||
### Extended Secrets
|
||||
|
||||
```bash
|
||||
# Full feature set
|
||||
kubectl create secret generic devcontainer-mydev-secrets-env \
|
||||
--from-literal=GITHUB_TOKEN='ghp_...' \
|
||||
--from-literal=VNC_PASSWORD='changeme' \
|
||||
--from-literal=SSH_AUTHORIZED_KEYS='ssh-ed25519 AAAA...' \
|
||||
--from-literal=HOMEASSISTANT_URL='http://homeassistant.local:8123' \
|
||||
--from-literal=HOMEASSISTANT_TOKEN='eyJ...' \
|
||||
--from-literal=DATABASE_URI='postgresql://user:pass@postgres:5432/db'
|
||||
```
|
||||
|
||||
## Storage Configuration
|
||||
|
||||
### Different Storage Classes
|
||||
|
||||
```yaml
|
||||
# For different Kubernetes distributions
|
||||
storage:
|
||||
className: "" # Auto-detect (recommended)
|
||||
# className: longhorn # Longhorn
|
||||
# className: nfs-client # NFS
|
||||
# className: fast-ssd # Custom fast storage
|
||||
```
|
||||
|
||||
### Storage Sizes by Use Case
|
||||
|
||||
```yaml
|
||||
# Small projects
|
||||
storage:
|
||||
size: 8Gi
|
||||
|
||||
# Standard development
|
||||
storage:
|
||||
size: 32Gi
|
||||
|
||||
# Large projects / datasets
|
||||
storage:
|
||||
size: 128Gi
|
||||
|
||||
# Team environments
|
||||
storage:
|
||||
size: 256Gi
|
||||
```
|
||||
|
||||
## Access Patterns
|
||||
|
||||
### VNC Only (Default)
|
||||
|
||||
```yaml
|
||||
ide:
|
||||
type: vscode
|
||||
|
||||
# Access via: kubectl port-forward deployment/devcontainer-mydev 5800:5800
|
||||
```
|
||||
|
||||
### SSH Only
|
||||
|
||||
```yaml
|
||||
ide:
|
||||
type: none
|
||||
|
||||
ssh:
|
||||
enabled: true
|
||||
|
||||
# Access via: kubectl port-forward deployment/devcontainer-mydev 2222:22
|
||||
# ssh -p 2222 user@localhost
|
||||
```
|
||||
|
||||
### Both VNC and SSH
|
||||
|
||||
```yaml
|
||||
ide:
|
||||
type: vscode
|
||||
|
||||
ssh:
|
||||
enabled: true
|
||||
|
||||
# VNC: kubectl port-forward deployment/devcontainer-mydev 5800:5800
|
||||
# SSH: kubectl port-forward deployment/devcontainer-mydev 2222:22
|
||||
```
|
||||
|
||||
## Resource Profiles
|
||||
|
||||
### Small (1-2 developers)
|
||||
```yaml
|
||||
resources:
|
||||
requests:
|
||||
memory: "1Gi"
|
||||
cpu: "500m"
|
||||
limits:
|
||||
memory: "4Gi"
|
||||
cpu: "2000m"
|
||||
```
|
||||
|
||||
### Medium (standard development)
|
||||
```yaml
|
||||
resources:
|
||||
requests:
|
||||
memory: "2Gi"
|
||||
cpu: "1000m"
|
||||
limits:
|
||||
memory: "8Gi"
|
||||
cpu: "4000m"
|
||||
```
|
||||
|
||||
### Large (intensive workloads)
|
||||
```yaml
|
||||
resources:
|
||||
requests:
|
||||
memory: "4Gi"
|
||||
cpu: "2000m"
|
||||
limits:
|
||||
memory: "16Gi"
|
||||
cpu: "8000m"
|
||||
```
|
||||
|
||||
### XLarge (AI/ML, data processing)
|
||||
```yaml
|
||||
resources:
|
||||
requests:
|
||||
memory: "8Gi"
|
||||
cpu: "4000m"
|
||||
limits:
|
||||
memory: "32Gi"
|
||||
cpu: "16000m"
|
||||
```
|
||||
|
||||
## MCP Sidecar Combinations
|
||||
|
||||
### Minimal (basic development)
|
||||
```yaml
|
||||
mcp:
|
||||
sidecars:
|
||||
kubernetes:
|
||||
enabled: false
|
||||
flux:
|
||||
enabled: false
|
||||
playwright:
|
||||
enabled: true # Keep for web testing
|
||||
```
|
||||
|
||||
### Standard (full-stack development)
|
||||
```yaml
|
||||
mcp:
|
||||
sidecars:
|
||||
kubernetes:
|
||||
enabled: true
|
||||
flux:
|
||||
enabled: false
|
||||
playwright:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
### DevOps/Platform (infrastructure work)
|
||||
```yaml
|
||||
mcp:
|
||||
sidecars:
|
||||
kubernetes:
|
||||
enabled: true
|
||||
flux:
|
||||
enabled: true
|
||||
pgtuner:
|
||||
enabled: true
|
||||
playwright:
|
||||
enabled: false
|
||||
```
|
||||
|
||||
### All Features
|
||||
```yaml
|
||||
mcp:
|
||||
sidecars:
|
||||
kubernetes:
|
||||
enabled: true
|
||||
flux:
|
||||
enabled: true
|
||||
homeassistant:
|
||||
enabled: true
|
||||
pgtuner:
|
||||
enabled: true
|
||||
playwright:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Values Validation
|
||||
|
||||
Your IDE should automatically validate values.yaml against the schema. If not:
|
||||
|
||||
```bash
|
||||
# Manual validation (if you have a JSON schema validator)
|
||||
helm template ./chart -f values.yaml > /dev/null
|
||||
```
|
||||
|
||||
### Common Issues
|
||||
|
||||
**Resource Limits**: Start with smaller resource requests and increase as needed.
|
||||
|
||||
**Storage Class**: Use `className: ""` for auto-detection.
|
||||
|
||||
**GitHub Access**: Ensure GITHUB_TOKEN has `repo` scope.
|
||||
|
||||
**MCP Sidecars**: Disable unused sidecars to save resources.
|
||||
|
||||
### Getting Help
|
||||
|
||||
1. Check the main [README.md](../README.md) for detailed documentation
|
||||
2. Review [values.yaml](values.yaml) for all available options
|
||||
3. Use [values-quickstart.yaml](values-quickstart.yaml) as a starting point
|
||||
@@ -0,0 +1,96 @@
|
||||
{{/*
|
||||
Resource name prefix: devcontainer-{name}
|
||||
*/}}
|
||||
{{- define "devcontainer.fullname" -}}
|
||||
{{- printf "devcontainer-%s" .Values.name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
PVC name: userhome-{name}
|
||||
*/}}
|
||||
{{- define "devcontainer.pvcName" -}}
|
||||
{{- printf "userhome-%s" .Values.name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Secret name for env vars, default to devcontainer-{name}-secrets-env
|
||||
*/}}
|
||||
{{- define "devcontainer.envSecretName" -}}
|
||||
{{- .Values.envSecretName | default (printf "devcontainer-%s-secrets-env" .Values.name) }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "devcontainer.labels" -}}
|
||||
app: devcontainer
|
||||
instance: {{ .Values.name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Smart resource sizing based on enabled features
|
||||
*/}}
|
||||
{{- define "devcontainer.smartResources" -}}
|
||||
{{- $baseMemory := "2Gi" }}
|
||||
{{- $baseCpu := "1000m" }}
|
||||
{{- $limitMemory := "8Gi" }}
|
||||
{{- $limitCpu := "4000m" }}
|
||||
|
||||
{{/* Adjust for enabled MCP sidecars */}}
|
||||
{{- if .Values.mcp.sidecars.playwright.enabled }}
|
||||
{{- $baseMemory = "3Gi" }}
|
||||
{{- $limitMemory = "12Gi" }}
|
||||
{{- end }}
|
||||
|
||||
{{/* Adjust for IDE type */}}
|
||||
{{- if eq .Values.ide.type "antigravity" }}
|
||||
{{- $baseMemory = "4Gi" }}
|
||||
{{- $limitMemory = "16Gi" }}
|
||||
{{- end }}
|
||||
|
||||
requests:
|
||||
memory: {{ .Values.resources.requests.memory | default $baseMemory | quote }}
|
||||
cpu: {{ .Values.resources.requests.cpu | default $baseCpu | quote }}
|
||||
limits:
|
||||
memory: {{ .Values.resources.limits.memory | default $limitMemory | quote }}
|
||||
cpu: {{ .Values.resources.limits.cpu | default $limitCpu | quote }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Auto-detect environment type and set smart defaults
|
||||
*/}}
|
||||
{{- define "devcontainer.smartDefaults" -}}
|
||||
{{- $isDev := or (contains "dev" .Values.name) (contains "test" .Values.name) (contains "local" .Values.name) }}
|
||||
{{- $isProd := or (contains "prod" .Values.name) (contains "production" .Values.name) }}
|
||||
{{- $isTeam := or (contains "team" .Values.name) (contains "shared" .Values.name) }}
|
||||
|
||||
{{/* Development environment - enable more sidecars, smaller resources */}}
|
||||
{{- if $isDev }}
|
||||
development: true
|
||||
{{/* Production environment - conservative defaults, fewer sidecars */}}
|
||||
{{- else if $isProd }}
|
||||
production: true
|
||||
{{/* Team environment - enable SSH, more resources */}}
|
||||
{{- else if $isTeam }}
|
||||
team: true
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Smart MCP sidecar selection based on cluster access
|
||||
*/}}
|
||||
{{- define "devcontainer.mcpDefaults" -}}
|
||||
{{- if eq .Values.clusterAccess "none" }}
|
||||
{{/* No cluster access - disable k8s/flux sidecars */}}
|
||||
kubernetes:
|
||||
enabled: false
|
||||
flux:
|
||||
enabled: false
|
||||
{{- else }}
|
||||
{{/* Has cluster access - enable k8s sidecars */}}
|
||||
kubernetes:
|
||||
enabled: true
|
||||
flux:
|
||||
enabled: {{ ne .Values.clusterAccess "readonly" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,322 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "devcontainer.fullname" . }}
|
||||
labels:
|
||||
{{- include "devcontainer.labels" . | nindent 4 }}
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "devcontainer.labels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "devcontainer.labels" . | nindent 8 }}
|
||||
spec:
|
||||
{{- if ne (.Values.clusterAccess | default "none") "none" }}
|
||||
serviceAccountName: {{ include "devcontainer.fullname" . }}
|
||||
{{- end }}
|
||||
securityContext:
|
||||
fsGroup: 1000
|
||||
fsGroupChangePolicy: "OnRootMismatch"
|
||||
{{- if and .Values.ide.type (eq .Values.ide.type "antigravity") }}
|
||||
initContainers:
|
||||
- name: setup-userdata
|
||||
image: busybox:latest
|
||||
command: ['sh', '-c']
|
||||
args:
|
||||
- |
|
||||
echo "Setting up userdata directory..."
|
||||
mkdir -p /config/userdata
|
||||
chown 1000:1000 /config/userdata
|
||||
chmod 755 /config/userdata
|
||||
echo "Userdata directory setup complete"
|
||||
volumeMounts:
|
||||
- name: userhome
|
||||
mountPath: /config
|
||||
securityContext:
|
||||
runAsUser: 0
|
||||
runAsGroup: 0
|
||||
{{- end }}
|
||||
containers:
|
||||
- name: devcontainer
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
ports:
|
||||
{{- if ne (.Values.ide.type | default "vscode") "none" }}
|
||||
- containerPort: 5800
|
||||
name: vnc-web
|
||||
protocol: TCP
|
||||
{{- end }}
|
||||
{{- if .Values.ssh.enabled }}
|
||||
- containerPort: 22
|
||||
name: ssh
|
||||
protocol: TCP
|
||||
{{- end }}
|
||||
env:
|
||||
- name: IDE
|
||||
value: {{ .Values.ide.type | default "vscode" | quote }}
|
||||
- name: SSH
|
||||
value: {{ .Values.ssh.enabled | toString | quote }}
|
||||
- name: USER_ID
|
||||
value: {{ .Values.user.id | quote }}
|
||||
- name: GROUP_ID
|
||||
value: {{ .Values.user.groupId | quote }}
|
||||
- name: DISPLAY_WIDTH
|
||||
value: {{ .Values.display.width | quote }}
|
||||
- name: DISPLAY_HEIGHT
|
||||
value: {{ .Values.display.height | quote }}
|
||||
- name: SECURE_CONNECTION
|
||||
value: {{ .Values.display.secureConnection | quote }}
|
||||
- name: HAPPY_HOME_DIR
|
||||
value: {{ .Values.happy.homeDir | quote }}
|
||||
- name: HAPPY_EXPERIMENTAL
|
||||
value: {{ .Values.happy.experimental | quote }}
|
||||
- name: HAPPY_SERVER_URL
|
||||
value: {{ .Values.happy.serverUrl | quote }}
|
||||
- name: HAPPY_WEBAPP_URL
|
||||
value: {{ .Values.happy.webappUrl | quote }}
|
||||
- name: GITHUB_REPO
|
||||
value: {{ .Values.githubRepo | quote }}
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: {{ include "devcontainer.envSecretName" . }}
|
||||
optional: true
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
- name: userhome
|
||||
mountPath: /config
|
||||
- name: workspace
|
||||
mountPath: /workspace
|
||||
- name: shm
|
||||
mountPath: /dev/shm
|
||||
{{- if ne (.Values.ide.type | default "vscode") "none" }}
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 5800
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 5800
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
{{- else if .Values.ssh.enabled }}
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: 22
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 22
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
{{- end }}
|
||||
{{- if and .Values.mcp.sidecars.kubernetes.enabled (ne .Values.clusterAccess "none") }}
|
||||
- name: kubernetes-mcp
|
||||
image: "{{ .Values.mcp.sidecars.kubernetes.image.repository }}:{{ .Values.mcp.sidecars.kubernetes.image.tag }}"
|
||||
args:
|
||||
- --port
|
||||
- {{ .Values.mcp.sidecars.kubernetes.port | quote }}
|
||||
ports:
|
||||
- containerPort: {{ .Values.mcp.sidecars.kubernetes.port }}
|
||||
name: k8s-mcp
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: {{ .Values.mcp.sidecars.kubernetes.port }}
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: {{ .Values.mcp.sidecars.kubernetes.port }}
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
resources:
|
||||
{{- toYaml .Values.mcp.sidecars.kubernetes.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if and .Values.mcp.sidecars.flux.enabled (ne .Values.clusterAccess "none") }}
|
||||
- name: flux-mcp
|
||||
image: "{{ .Values.mcp.sidecars.flux.image.repository }}:{{ .Values.mcp.sidecars.flux.image.tag }}"
|
||||
args:
|
||||
- serve
|
||||
- --transport=sse
|
||||
- --port={{ .Values.mcp.sidecars.flux.port }}
|
||||
ports:
|
||||
- containerPort: {{ .Values.mcp.sidecars.flux.port }}
|
||||
name: flux-mcp
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: {{ .Values.mcp.sidecars.flux.port }}
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: {{ .Values.mcp.sidecars.flux.port }}
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
resources:
|
||||
{{- toYaml .Values.mcp.sidecars.flux.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.mcp.sidecars.fetch.enabled }}
|
||||
- name: fetch-mcp
|
||||
image: "{{ .Values.mcp.sidecars.fetch.image.repository }}:{{ .Values.mcp.sidecars.fetch.image.tag }}"
|
||||
imagePullPolicy: Always
|
||||
command: ["fastmcp", "run", "--transport", "sse", "--host", "0.0.0.0", "--port", "{{ .Values.mcp.sidecars.fetch.port }}"]
|
||||
ports:
|
||||
- name: fetch
|
||||
containerPort: {{ .Values.mcp.sidecars.fetch.port }}
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: {{ .Values.mcp.sidecars.fetch.port }}
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: {{ .Values.mcp.sidecars.fetch.port }}
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
resources:
|
||||
{{- toYaml .Values.mcp.sidecars.fetch.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.mcp.sidecars.sequentialthinking.enabled }}
|
||||
- name: sequentialthinking-mcp
|
||||
image: "{{ .Values.mcp.sidecars.sequentialthinking.image.repository }}:{{ .Values.mcp.sidecars.sequentialthinking.image.tag }}"
|
||||
imagePullPolicy: Always
|
||||
command: ["fastmcp", "run", "--transport", "sse", "--host", "0.0.0.0", "--port", "{{ .Values.mcp.sidecars.sequentialthinking.port }}"]
|
||||
ports:
|
||||
- name: seqthinking
|
||||
containerPort: {{ .Values.mcp.sidecars.sequentialthinking.port }}
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: {{ .Values.mcp.sidecars.sequentialthinking.port }}
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: {{ .Values.mcp.sidecars.sequentialthinking.port }}
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
resources:
|
||||
{{- toYaml .Values.mcp.sidecars.sequentialthinking.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.mcp.sidecars.homeassistant.enabled }}
|
||||
- name: homeassistant-mcp
|
||||
image: "{{ .Values.mcp.sidecars.homeassistant.image.repository }}:{{ .Values.mcp.sidecars.homeassistant.image.tag }}"
|
||||
imagePullPolicy: Always
|
||||
command: ["fastmcp", "run", "--transport", "sse", "--host", "0.0.0.0", "--port", "{{ .Values.mcp.sidecars.homeassistant.port }}"]
|
||||
ports:
|
||||
- name: homeassistant
|
||||
containerPort: {{ .Values.mcp.sidecars.homeassistant.port }}
|
||||
env:
|
||||
- name: HOMEASSISTANT_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "devcontainer.envSecretName" . }}
|
||||
key: HOMEASSISTANT_URL
|
||||
optional: true
|
||||
- name: HOMEASSISTANT_TOKEN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "devcontainer.envSecretName" . }}
|
||||
key: HOMEASSISTANT_TOKEN
|
||||
optional: true
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: {{ .Values.mcp.sidecars.homeassistant.port }}
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: {{ .Values.mcp.sidecars.homeassistant.port }}
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
resources:
|
||||
{{- toYaml .Values.mcp.sidecars.homeassistant.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.mcp.sidecars.pgtuner.enabled }}
|
||||
- name: pgtuner-mcp
|
||||
image: "{{ .Values.mcp.sidecars.pgtuner.image.repository }}:{{ .Values.mcp.sidecars.pgtuner.image.tag }}"
|
||||
imagePullPolicy: Always
|
||||
command: ["python", "-m", "pgtuner_mcp", "--mode", "sse", "--host", "0.0.0.0", "--port", "{{ .Values.mcp.sidecars.pgtuner.port }}"]
|
||||
ports:
|
||||
- name: pgtuner
|
||||
containerPort: {{ .Values.mcp.sidecars.pgtuner.port }}
|
||||
env:
|
||||
- name: DATABASE_URI
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "devcontainer.envSecretName" . }}
|
||||
key: DATABASE_URI
|
||||
optional: true
|
||||
- name: PGTUNER_EXCLUDE_USERIDS
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "devcontainer.envSecretName" . }}
|
||||
key: PGTUNER_EXCLUDE_USERIDS
|
||||
optional: true
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: {{ .Values.mcp.sidecars.pgtuner.port }}
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: {{ .Values.mcp.sidecars.pgtuner.port }}
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
resources:
|
||||
{{- toYaml .Values.mcp.sidecars.pgtuner.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.mcp.sidecars.playwright.enabled }}
|
||||
- name: playwright-mcp
|
||||
image: "{{ .Values.mcp.sidecars.playwright.image.repository }}:{{ .Values.mcp.sidecars.playwright.image.tag }}"
|
||||
imagePullPolicy: Always
|
||||
command: ["node"]
|
||||
args:
|
||||
- cli.js
|
||||
- --headless
|
||||
- --browser
|
||||
- chromium
|
||||
- --no-sandbox
|
||||
- --host
|
||||
- 0.0.0.0
|
||||
- --port
|
||||
- {{ .Values.mcp.sidecars.playwright.port | quote }}
|
||||
ports:
|
||||
- name: playwright
|
||||
containerPort: {{ .Values.mcp.sidecars.playwright.port }}
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: {{ .Values.mcp.sidecars.playwright.port }}
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: {{ .Values.mcp.sidecars.playwright.port }}
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
resources:
|
||||
{{- toYaml .Values.mcp.sidecars.playwright.resources | nindent 12 }}
|
||||
securityContext:
|
||||
runAsUser: 1000
|
||||
runAsGroup: 1000
|
||||
{{- end }}
|
||||
volumes:
|
||||
- name: workspace
|
||||
emptyDir: {}
|
||||
- name: shm
|
||||
emptyDir:
|
||||
medium: Memory
|
||||
sizeLimit: {{ .Values.shm.sizeLimit }}
|
||||
- name: userhome
|
||||
persistentVolumeClaim:
|
||||
claimName: {{ include "devcontainer.pvcName" . }}
|
||||
@@ -0,0 +1,13 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: {{ include "devcontainer.pvcName" . }}
|
||||
labels:
|
||||
{{- include "devcontainer.labels" . | nindent 4 }}
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
storageClassName: {{ .Values.storage.className }}
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.storage.size }}
|
||||
@@ -0,0 +1,97 @@
|
||||
{{- $access := .Values.clusterAccess | default "none" }}
|
||||
{{- $name := include "devcontainer.fullname" . }}
|
||||
{{- $ns := .Release.Namespace }}
|
||||
{{- $labels := include "devcontainer.labels" . }}
|
||||
|
||||
{{- if ne $access "none" }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ $name }}
|
||||
namespace: {{ $ns }}
|
||||
labels:
|
||||
{{- $labels | nindent 4 }}
|
||||
|
||||
{{- if or (eq $access "readonlyns") (eq $access "readwritens") }}
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: {{ $name }}
|
||||
namespace: {{ $ns }}
|
||||
labels:
|
||||
{{- $labels | nindent 4 }}
|
||||
rules:
|
||||
- apiGroups: ["*"]
|
||||
resources: ["*"]
|
||||
verbs:
|
||||
{{- if eq $access "readonlyns" }}
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
{{- else }}
|
||||
- "*"
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: {{ $name }}
|
||||
namespace: {{ $ns }}
|
||||
labels:
|
||||
{{- $labels | nindent 4 }}
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ $name }}
|
||||
namespace: {{ $ns }}
|
||||
roleRef:
|
||||
kind: Role
|
||||
name: {{ $name }}
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
{{- end }}
|
||||
|
||||
{{- if or (eq $access "readonly") (eq $access "readwrite") }}
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: {{ $name }}
|
||||
labels:
|
||||
{{- $labels | nindent 4 }}
|
||||
rules:
|
||||
- apiGroups: ["*"]
|
||||
resources: ["*"]
|
||||
verbs:
|
||||
{{- if eq $access "readonly" }}
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
{{- else }}
|
||||
- "*"
|
||||
{{- end }}
|
||||
- nonResourceURLs: ["*"]
|
||||
verbs:
|
||||
{{- if eq $access "readonly" }}
|
||||
- get
|
||||
{{- else }}
|
||||
- "*"
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: {{ $name }}
|
||||
labels:
|
||||
{{- $labels | nindent 4 }}
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ $name }}
|
||||
namespace: {{ $ns }}
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: {{ $name }}
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
{{- end }}
|
||||
|
||||
{{- end }}
|
||||
@@ -0,0 +1,22 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "devcontainer.fullname" . }}
|
||||
labels:
|
||||
{{- include "devcontainer.labels" . | nindent 4 }}
|
||||
spec:
|
||||
ports:
|
||||
{{- if ne (.Values.ide.type | default "vscode") "none" }}
|
||||
- port: 5800
|
||||
name: vnc-web
|
||||
protocol: TCP
|
||||
targetPort: vnc-web
|
||||
{{- end }}
|
||||
{{- if .Values.ssh.enabled }}
|
||||
- port: 22
|
||||
name: ssh
|
||||
protocol: TCP
|
||||
targetPort: ssh
|
||||
{{- end }}
|
||||
selector:
|
||||
{{- include "devcontainer.labels" . | nindent 4 }}
|
||||
@@ -0,0 +1,58 @@
|
||||
# =============================================================================
|
||||
# QUICKSTART VALUES - Just set these 3 essentials!
|
||||
# =============================================================================
|
||||
|
||||
# Instance name (required)
|
||||
name: mydev
|
||||
|
||||
# GitHub repository to clone (required)
|
||||
githubRepo: https://github.com/youruser/yourrepo
|
||||
|
||||
# IDE choice (optional - defaults to vscode)
|
||||
# Options: vscode | antigravity | none
|
||||
ide:
|
||||
type: vscode
|
||||
|
||||
# =============================================================================
|
||||
# COMMON CUSTOMIZATIONS (optional)
|
||||
# =============================================================================
|
||||
|
||||
# Enable SSH access
|
||||
# ssh:
|
||||
# enabled: true
|
||||
|
||||
# Adjust resources for smaller/larger workloads
|
||||
# resources:
|
||||
# requests:
|
||||
# memory: "1Gi" # Smaller
|
||||
# cpu: "500m"
|
||||
# limits:
|
||||
# memory: "4Gi" # Smaller
|
||||
# cpu: "2000m"
|
||||
|
||||
# Different storage size
|
||||
# storage:
|
||||
# size: 16Gi # Smaller
|
||||
|
||||
# Disable some MCP sidecars to save resources
|
||||
# mcp:
|
||||
# sidecars:
|
||||
# kubernetes:
|
||||
# enabled: false
|
||||
# flux:
|
||||
# enabled: false
|
||||
|
||||
# =============================================================================
|
||||
# USAGE INSTRUCTIONS
|
||||
# =============================================================================
|
||||
|
||||
# 1. Copy this file: cp values-quickstart.yaml my-values.yaml
|
||||
# 2. Edit the 'name' and 'githubRepo' fields above
|
||||
# 3. Deploy: helm install mydev ./chart -f my-values.yaml
|
||||
# 4. Access: kubectl port-forward deployment/devcontainer-mydev 5800:5800
|
||||
# 5. Open: http://localhost:5800
|
||||
|
||||
# For secrets (GitHub token, passwords):
|
||||
# kubectl create secret generic devcontainer-mydev-secrets-env \
|
||||
# --from-literal=GITHUB_TOKEN='ghp_...' \
|
||||
# --from-literal=VNC_PASSWORD='changeme'
|
||||
@@ -0,0 +1,267 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"$id": "https://github.com/cpfarhood/devcontainer/chart/values.schema.json",
|
||||
"title": "Dev Container Helm Chart Values Schema",
|
||||
"description": "Schema for validating values.yaml in the Dev Container Helm chart",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Instance name used to generate resource names",
|
||||
"pattern": "^[a-z0-9][a-z0-9-]*[a-z0-9]$",
|
||||
"minLength": 1,
|
||||
"maxLength": 63
|
||||
},
|
||||
"image": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"repository": {
|
||||
"type": "string",
|
||||
"description": "Container image repository"
|
||||
},
|
||||
"tag": {
|
||||
"type": "string",
|
||||
"description": "Container image tag"
|
||||
},
|
||||
"pullPolicy": {
|
||||
"type": "string",
|
||||
"enum": ["Always", "IfNotPresent", "Never"],
|
||||
"description": "Image pull policy"
|
||||
}
|
||||
},
|
||||
"required": ["repository", "tag"]
|
||||
},
|
||||
"githubRepo": {
|
||||
"type": "string",
|
||||
"description": "GitHub repository URL to clone",
|
||||
"pattern": "^https://github\\.com/.+/.+$"
|
||||
},
|
||||
"ide": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["vscode", "antigravity", "none"],
|
||||
"description": "IDE to launch in the container"
|
||||
}
|
||||
},
|
||||
"required": ["type"]
|
||||
},
|
||||
"ssh": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Enable SSH server on port 22"
|
||||
}
|
||||
},
|
||||
"required": ["enabled"]
|
||||
},
|
||||
"display": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"width": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+$",
|
||||
"description": "VNC display width in pixels"
|
||||
},
|
||||
"height": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+$",
|
||||
"description": "VNC display height in pixels"
|
||||
},
|
||||
"secureConnection": {
|
||||
"type": "string",
|
||||
"enum": ["0", "1"],
|
||||
"description": "Enable secure VNC connection"
|
||||
}
|
||||
},
|
||||
"required": ["width", "height", "secureConnection"]
|
||||
},
|
||||
"user": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+$",
|
||||
"description": "User ID (UID)"
|
||||
},
|
||||
"groupId": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+$",
|
||||
"description": "Group ID (GID)"
|
||||
}
|
||||
},
|
||||
"required": ["id", "groupId"]
|
||||
},
|
||||
"storage": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"size": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+[KMGT]i$",
|
||||
"description": "Storage size (e.g., 32Gi)"
|
||||
},
|
||||
"className": {
|
||||
"type": "string",
|
||||
"description": "Storage class name (must support ReadWriteMany)"
|
||||
}
|
||||
},
|
||||
"required": ["size", "className"]
|
||||
},
|
||||
"resources": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"requests": {
|
||||
"$ref": "#/$defs/resourceSpec"
|
||||
},
|
||||
"limits": {
|
||||
"$ref": "#/$defs/resourceSpec"
|
||||
}
|
||||
},
|
||||
"required": ["requests", "limits"]
|
||||
},
|
||||
"shm": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sizeLimit": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+[KMGT]i$",
|
||||
"description": "Shared memory size limit"
|
||||
}
|
||||
},
|
||||
"required": ["sizeLimit"]
|
||||
},
|
||||
"clusterAccess": {
|
||||
"type": "string",
|
||||
"enum": ["none", "readonlyns", "readwritens", "readonly", "readwrite"],
|
||||
"description": "Kubernetes cluster access level"
|
||||
},
|
||||
"happy": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"serverUrl": {
|
||||
"type": "string",
|
||||
"format": "uri",
|
||||
"description": "Happy Coder server URL"
|
||||
},
|
||||
"webappUrl": {
|
||||
"type": "string",
|
||||
"format": "uri",
|
||||
"description": "Happy Coder webapp URL"
|
||||
},
|
||||
"homeDir": {
|
||||
"type": "string",
|
||||
"description": "Happy Coder home directory"
|
||||
},
|
||||
"experimental": {
|
||||
"type": "string",
|
||||
"enum": ["true", "false"],
|
||||
"description": "Enable experimental Happy features"
|
||||
}
|
||||
},
|
||||
"required": ["serverUrl", "webappUrl", "homeDir", "experimental"]
|
||||
},
|
||||
"mcp": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sidecars": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kubernetes": {
|
||||
"$ref": "#/$defs/mcpSidecar"
|
||||
},
|
||||
"flux": {
|
||||
"$ref": "#/$defs/mcpSidecar"
|
||||
},
|
||||
"homeassistant": {
|
||||
"$ref": "#/$defs/mcpSidecar"
|
||||
},
|
||||
"pgtuner": {
|
||||
"$ref": "#/$defs/mcpSidecar"
|
||||
},
|
||||
"playwright": {
|
||||
"$ref": "#/$defs/mcpSidecar"
|
||||
},
|
||||
"fetch": {
|
||||
"$ref": "#/$defs/mcpSidecar"
|
||||
},
|
||||
"sequentialthinking": {
|
||||
"$ref": "#/$defs/mcpSidecar"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": ["sidecars"]
|
||||
},
|
||||
"envSecretName": {
|
||||
"type": "string",
|
||||
"description": "Custom environment secret name"
|
||||
},
|
||||
"resourceProfile": {
|
||||
"type": "string",
|
||||
"enum": ["auto", "small", "medium", "large", "xlarge"],
|
||||
"description": "Resource profile preset"
|
||||
}
|
||||
},
|
||||
"required": ["name"],
|
||||
"$defs": {
|
||||
"resourceSpec": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"memory": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+[KMGT]i$",
|
||||
"description": "Memory resource specification"
|
||||
},
|
||||
"cpu": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+m?$",
|
||||
"description": "CPU resource specification"
|
||||
}
|
||||
},
|
||||
"required": ["memory", "cpu"]
|
||||
},
|
||||
"mcpSidecar": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Enable this MCP sidecar"
|
||||
},
|
||||
"image": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"repository": {
|
||||
"type": "string"
|
||||
},
|
||||
"tag": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["repository", "tag"]
|
||||
},
|
||||
"port": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 65535,
|
||||
"description": "Port for the MCP sidecar"
|
||||
},
|
||||
"resources": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"requests": {
|
||||
"$ref": "#/$defs/resourceSpec"
|
||||
},
|
||||
"limits": {
|
||||
"$ref": "#/$defs/resourceSpec"
|
||||
}
|
||||
},
|
||||
"required": ["requests", "limits"]
|
||||
}
|
||||
},
|
||||
"required": ["enabled", "image", "port", "resources"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
# =============================================================================
|
||||
# BASIC CONFIGURATION
|
||||
# =============================================================================
|
||||
|
||||
# Instance name — used to generate resource names (devcontainer-{name}, userhome-{name})
|
||||
name: ""
|
||||
|
||||
# Container image configuration
|
||||
image:
|
||||
repository: ghcr.io/cpfarhood/devcontainer
|
||||
tag: latest
|
||||
pullPolicy: Always
|
||||
|
||||
# GitHub repository to clone into /workspace
|
||||
githubRepo: ""
|
||||
|
||||
# =============================================================================
|
||||
# ACCESS & INTERFACE
|
||||
# =============================================================================
|
||||
|
||||
# IDE configuration
|
||||
ide:
|
||||
# Options: vscode | antigravity | none
|
||||
type: vscode
|
||||
|
||||
# SSH access configuration
|
||||
ssh:
|
||||
enabled: false
|
||||
|
||||
# VNC display settings
|
||||
display:
|
||||
width: "1920"
|
||||
height: "1080"
|
||||
secureConnection: "0" # Set to "1" when TLS is not terminated upstream
|
||||
|
||||
# User configuration
|
||||
user:
|
||||
id: "1000"
|
||||
groupId: "1000"
|
||||
|
||||
# =============================================================================
|
||||
# INFRASTRUCTURE & RESOURCES
|
||||
# =============================================================================
|
||||
|
||||
# Storage configuration
|
||||
storage:
|
||||
size: 32Gi
|
||||
className: ceph-filesystem
|
||||
|
||||
# Resource allocation
|
||||
resources:
|
||||
requests:
|
||||
memory: "2Gi"
|
||||
cpu: "1000m"
|
||||
limits:
|
||||
memory: "8Gi"
|
||||
cpu: "4000m"
|
||||
|
||||
# Shared memory for Electron apps (Chrome, Antigravity)
|
||||
shm:
|
||||
sizeLimit: 2Gi
|
||||
|
||||
# Kubernetes cluster access via RBAC
|
||||
# Options: none | readonlyns | readwritens | readonly | readwrite
|
||||
clusterAccess: none
|
||||
|
||||
# =============================================================================
|
||||
# INTEGRATIONS
|
||||
# =============================================================================
|
||||
|
||||
# Happy Coder AI assistant configuration
|
||||
happy:
|
||||
serverUrl: "https://happy.farh.net"
|
||||
webappUrl: "https://happy-coder.farh.net"
|
||||
homeDir: "/config/userdata/.happy"
|
||||
experimental: "true"
|
||||
|
||||
# MCP (Model Context Protocol) server sidecars
|
||||
mcp:
|
||||
sidecars:
|
||||
# Kubernetes API access
|
||||
kubernetes:
|
||||
enabled: true
|
||||
image:
|
||||
repository: quay.io/containers/kubernetes_mcp_server
|
||||
tag: v0.0.57
|
||||
port: 8080
|
||||
resources:
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "50m"
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
cpu: "500m"
|
||||
|
||||
# Flux GitOps operations
|
||||
flux:
|
||||
enabled: true
|
||||
image:
|
||||
repository: ghcr.io/controlplaneio-fluxcd/flux-operator-mcp
|
||||
tag: v0.41.1
|
||||
port: 8081
|
||||
resources:
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "50m"
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
cpu: "500m"
|
||||
|
||||
# Web content fetching capabilities
|
||||
fetch:
|
||||
enabled: true
|
||||
image:
|
||||
repository: mcp/fetch
|
||||
tag: latest
|
||||
port: 8082
|
||||
resources:
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "50m"
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
cpu: "500m"
|
||||
|
||||
# Sequential thinking and problem-solving
|
||||
sequentialthinking:
|
||||
enabled: true
|
||||
image:
|
||||
repository: mcp/sequentialthinking
|
||||
tag: latest
|
||||
port: 8083
|
||||
resources:
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "50m"
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
cpu: "500m"
|
||||
|
||||
# Home Assistant smart home control
|
||||
homeassistant:
|
||||
enabled: false # Requires HOMEASSISTANT_URL and HOMEASSISTANT_TOKEN
|
||||
image:
|
||||
repository: ghcr.io/homeassistant-ai/ha-mcp
|
||||
tag: stable
|
||||
port: 8087
|
||||
resources:
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "50m"
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
cpu: "500m"
|
||||
|
||||
# PostgreSQL performance tuning
|
||||
pgtuner:
|
||||
enabled: false # Requires DATABASE_URI in secrets
|
||||
image:
|
||||
repository: dog830228/pgtuner_mcp
|
||||
tag: latest
|
||||
port: 8085
|
||||
resources:
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "50m"
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
cpu: "500m"
|
||||
|
||||
# Browser automation and web testing
|
||||
playwright:
|
||||
enabled: true
|
||||
image:
|
||||
repository: mcr.microsoft.com/playwright/mcp
|
||||
tag: latest
|
||||
port: 8086
|
||||
resources:
|
||||
requests:
|
||||
memory: "128Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "512Mi"
|
||||
cpu: "1000m"
|
||||
|
||||
# =============================================================================
|
||||
# SMART DEFAULTS & AUTO-DETECTION
|
||||
# =============================================================================
|
||||
|
||||
# Environment auto-detection based on name patterns
|
||||
# Automatically adjusts defaults for dev/test/prod/team environments
|
||||
autoDetect:
|
||||
environment: true # Auto-detect dev/prod/team from name
|
||||
storageClass: true # Auto-detect ReadWriteMany storage class
|
||||
resources: true # Auto-size resources based on enabled features
|
||||
|
||||
# Resource profiles (auto-selected based on environment and features)
|
||||
# Override specific values above to customize
|
||||
resourceProfile: auto # auto | small | medium | large | xlarge
|
||||
|
||||
# =============================================================================
|
||||
# ADVANCED CONFIGURATION
|
||||
# =============================================================================
|
||||
|
||||
# Custom env secret name (defaults to: devcontainer-{name}-secrets-env)
|
||||
envSecretName: ""
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
-11
@@ -1,11 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>Dev Container Helm Chart Repository</title></head>
|
||||
<body>
|
||||
<h1>Dev Container Helm Chart Repository</h1>
|
||||
<p>Add this repository to Helm:</p>
|
||||
<pre>helm repo add devcontainer https://farhoodliquor.github.io/devcontainer</pre>
|
||||
<p>Install the chart:</p>
|
||||
<pre>helm install mydev devcontainer/devcontainer --set name=mydev</pre>
|
||||
</body>
|
||||
</html>
|
||||
-286
@@ -1,286 +0,0 @@
|
||||
apiVersion: v1
|
||||
entries:
|
||||
devcontainer:
|
||||
- apiVersion: v2
|
||||
appVersion: latest
|
||||
created: "2026-03-11T12:15:44.634530101Z"
|
||||
description: Dev Container with AI coding agents and MCP sidecars
|
||||
digest: 0c46b5d7a889cc21c28b8b088eb6c0c785a3f9324a4b39d14813c4eefe3f8e7a
|
||||
keywords:
|
||||
- development
|
||||
- devcontainer
|
||||
- vscode
|
||||
- ai
|
||||
name: devcontainer
|
||||
type: application
|
||||
urls:
|
||||
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.6.0.tgz
|
||||
version: 2.6.0
|
||||
- apiVersion: v2
|
||||
appVersion: latest
|
||||
created: "2026-03-11T12:15:44.633861353Z"
|
||||
description: Dev Container with AI coding agents and MCP sidecars
|
||||
digest: 010294a1a9b4bb6a92c685d819a0cd34dd03bc111da715d0807799168d0aba66
|
||||
keywords:
|
||||
- development
|
||||
- devcontainer
|
||||
- vscode
|
||||
- ai
|
||||
name: devcontainer
|
||||
type: application
|
||||
urls:
|
||||
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.4.0.tgz
|
||||
version: 2.4.0
|
||||
- apiVersion: v2
|
||||
appVersion: latest
|
||||
created: "2026-03-11T12:15:44.633145718Z"
|
||||
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
|
||||
and dynamic deployment modes
|
||||
digest: a650e9c7a8feb961232aee4048d3bf0ff1d04c55100f51a7db138e1a0f8b524e
|
||||
keywords:
|
||||
- development
|
||||
- devcontainer
|
||||
- vscode
|
||||
- ai
|
||||
- knative
|
||||
- serverless
|
||||
name: devcontainer
|
||||
type: application
|
||||
urls:
|
||||
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.3.0.tgz
|
||||
version: 2.3.0
|
||||
- apiVersion: v2
|
||||
appVersion: latest
|
||||
created: "2026-03-11T12:15:44.631873087Z"
|
||||
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
|
||||
and dynamic deployment modes
|
||||
digest: f5c440846e7672239a6f7b14a393888988ef627d896bc967bfc018130d65921d
|
||||
keywords:
|
||||
- development
|
||||
- devcontainer
|
||||
- vscode
|
||||
- ai
|
||||
- knative
|
||||
- serverless
|
||||
name: devcontainer
|
||||
type: application
|
||||
urls:
|
||||
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.2.5.tgz
|
||||
version: 2.2.5
|
||||
- apiVersion: v2
|
||||
appVersion: latest
|
||||
created: "2026-03-11T12:15:44.6304485Z"
|
||||
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
|
||||
and dynamic deployment modes
|
||||
digest: 2bea7dc5c198a5b4dab0b74f0a75089210c7ba49b56176ba2af205b7dac3fe23
|
||||
keywords:
|
||||
- development
|
||||
- devcontainer
|
||||
- vscode
|
||||
- ai
|
||||
- knative
|
||||
- serverless
|
||||
name: devcontainer
|
||||
type: application
|
||||
urls:
|
||||
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.2.4.tgz
|
||||
version: 2.2.4
|
||||
- apiVersion: v2
|
||||
appVersion: latest
|
||||
created: "2026-03-11T12:15:44.62965028Z"
|
||||
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
|
||||
and dynamic deployment modes
|
||||
digest: afde89b22d7e4a5dfd4c918a06258d9f27f1b17493a70dba98d1ae544280505a
|
||||
keywords:
|
||||
- development
|
||||
- devcontainer
|
||||
- vscode
|
||||
- ai
|
||||
- knative
|
||||
- serverless
|
||||
name: devcontainer
|
||||
type: application
|
||||
urls:
|
||||
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.2.3.tgz
|
||||
version: 2.2.3
|
||||
- apiVersion: v2
|
||||
appVersion: latest
|
||||
created: "2026-03-11T12:15:44.628801186Z"
|
||||
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
|
||||
and dynamic deployment modes
|
||||
digest: 5b3b994b74da01579156021fcfb718c61989def7c16cafadb36e7ddc90cbeea7
|
||||
keywords:
|
||||
- development
|
||||
- devcontainer
|
||||
- vscode
|
||||
- ai
|
||||
- knative
|
||||
- serverless
|
||||
name: devcontainer
|
||||
type: application
|
||||
urls:
|
||||
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.2.2.tgz
|
||||
version: 2.2.2
|
||||
- apiVersion: v2
|
||||
appVersion: latest
|
||||
created: "2026-03-11T12:15:44.627970857Z"
|
||||
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
|
||||
and dynamic deployment modes
|
||||
digest: 8db383b24252edd37998c56bbba76793d1b6eeb37365a6894a713eef6af81210
|
||||
keywords:
|
||||
- development
|
||||
- devcontainer
|
||||
- vscode
|
||||
- ai
|
||||
- knative
|
||||
- serverless
|
||||
name: devcontainer
|
||||
type: application
|
||||
urls:
|
||||
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.2.1.tgz
|
||||
version: 2.2.1
|
||||
- apiVersion: v2
|
||||
appVersion: latest
|
||||
created: "2026-03-11T12:15:44.627169642Z"
|
||||
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
|
||||
and dynamic deployment modes
|
||||
digest: a1ea207bc96a35cc545d12fa8aca00452792de54e3fae74993260cd69afee0fa
|
||||
keywords:
|
||||
- development
|
||||
- devcontainer
|
||||
- vscode
|
||||
- ai
|
||||
- knative
|
||||
- serverless
|
||||
name: devcontainer
|
||||
type: application
|
||||
urls:
|
||||
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.2.0.tgz
|
||||
version: 2.2.0
|
||||
- apiVersion: v2
|
||||
appVersion: latest
|
||||
created: "2026-03-11T12:15:44.626328122Z"
|
||||
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
|
||||
and dynamic deployment modes
|
||||
digest: beac1182a39f158fb9aa1f3308b4b030bf378d612f2aa860f792fad62aa30321
|
||||
keywords:
|
||||
- development
|
||||
- devcontainer
|
||||
- vscode
|
||||
- ai
|
||||
- knative
|
||||
- serverless
|
||||
name: devcontainer
|
||||
type: application
|
||||
urls:
|
||||
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.1.1.tgz
|
||||
version: 2.1.1
|
||||
- apiVersion: v2
|
||||
appVersion: latest
|
||||
created: "2026-03-11T12:15:44.625121312Z"
|
||||
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
|
||||
and dynamic deployment modes
|
||||
digest: efb6cbd932a7ac082853d305e970db72e0086543a6963baabef16ebd2e8498f8
|
||||
keywords:
|
||||
- development
|
||||
- devcontainer
|
||||
- vscode
|
||||
- ai
|
||||
- knative
|
||||
- serverless
|
||||
name: devcontainer
|
||||
type: application
|
||||
urls:
|
||||
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.1.0.tgz
|
||||
version: 2.1.0
|
||||
- apiVersion: v2
|
||||
appVersion: latest
|
||||
created: "2026-03-11T12:15:44.623735055Z"
|
||||
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
|
||||
and dynamic deployment modes
|
||||
digest: 46f25124d9045802d0e50d34209d15a7fa15e1ef1c9d3f0e93ac4bb39b7c9b17
|
||||
keywords:
|
||||
- development
|
||||
- devcontainer
|
||||
- vscode
|
||||
- ai
|
||||
- knative
|
||||
- serverless
|
||||
name: devcontainer
|
||||
type: application
|
||||
urls:
|
||||
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.0.5.tgz
|
||||
version: 2.0.5
|
||||
- apiVersion: v2
|
||||
appVersion: latest
|
||||
created: "2026-03-11T12:15:44.622870733Z"
|
||||
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
|
||||
and dynamic deployment modes
|
||||
digest: db82381ffe831b07ce7777e8e6e05455a8eaeccfcd0afcd87825433a96cb2d65
|
||||
keywords:
|
||||
- development
|
||||
- devcontainer
|
||||
- vscode
|
||||
- ai
|
||||
- knative
|
||||
- serverless
|
||||
name: devcontainer
|
||||
type: application
|
||||
urls:
|
||||
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.0.4.tgz
|
||||
version: 2.0.4
|
||||
- apiVersion: v2
|
||||
appVersion: latest
|
||||
created: "2026-03-11T12:15:44.622004867Z"
|
||||
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
|
||||
and dynamic deployment modes
|
||||
digest: 872919ea64531b35dfa7f956d8a6e4130a1c7f0f80c50141b6f2d1cddd49682e
|
||||
keywords:
|
||||
- development
|
||||
- devcontainer
|
||||
- vscode
|
||||
- ai
|
||||
- knative
|
||||
- serverless
|
||||
name: devcontainer
|
||||
type: application
|
||||
urls:
|
||||
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.0.3.tgz
|
||||
version: 2.0.3
|
||||
- apiVersion: v2
|
||||
appVersion: latest
|
||||
created: "2026-03-11T12:15:44.621180038Z"
|
||||
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
|
||||
and dynamic deployment modes
|
||||
digest: 7cbb5379f8b41bc938a29b44c563757358c25fd843c75fb6d2f1a2b9365c0cf1
|
||||
keywords:
|
||||
- development
|
||||
- devcontainer
|
||||
- vscode
|
||||
- ai
|
||||
- knative
|
||||
- serverless
|
||||
name: devcontainer
|
||||
type: application
|
||||
urls:
|
||||
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.0.1-dev.tgz
|
||||
version: 2.0.1-dev
|
||||
- apiVersion: v2
|
||||
appVersion: latest
|
||||
created: "2026-03-11T12:15:44.620303012Z"
|
||||
description: Dev Container with AI coding agents and MCP sidecars - supports persistent
|
||||
and dynamic deployment modes
|
||||
digest: 5736c92626e2c3edb8e81ddba5be581360dbf3901ccc607db8bad238ea831cd0
|
||||
keywords:
|
||||
- development
|
||||
- devcontainer
|
||||
- vscode
|
||||
- ai
|
||||
- knative
|
||||
- serverless
|
||||
name: devcontainer
|
||||
type: application
|
||||
urls:
|
||||
- https://farhoodliquor.github.io/devcontainer/devcontainer-2.0.0-dev.tgz
|
||||
version: 2.0.0-dev
|
||||
generated: "2026-03-11T12:15:44.619306973Z"
|
||||
@@ -0,0 +1,30 @@
|
||||
# Antigravity Dev Container - Session Notes
|
||||
|
||||
## Key Architecture Facts
|
||||
- Image: `ghcr.io/cpfarhood/devcontainer:latest` (repo name is `devcontainer`, not `antigravity`)
|
||||
- Deployed via Helm chart (`chart/`), not kustomize anymore
|
||||
- Service must NOT be headless (`clusterIP: None`) — Cilium gateway can't route to headless services
|
||||
- `SECURE_CONNECTION=0` — TLS is terminated at the gateway, not the app
|
||||
- Container user is `user` (UID 1000) — baseimage-gui runs startapp.sh as `app` user, sudo is not available
|
||||
|
||||
## Deployment Method
|
||||
- **Primary**: Helm chart in `chart/` directory
|
||||
- **Makefile targets**: `helm-deploy`, `helm-delete`, `helm-logs`, `helm-shell`, `helm-port-forward`
|
||||
- **Old kustomize** (`k8s/` directory) has been removed — all deployments use Helm now
|
||||
- Chart published as OCI artifact to GHCR, reconciled by Flux
|
||||
|
||||
## MCP Sidecars
|
||||
- **Kubernetes MCP** (v0.0.57, port 8080): Only deployed when enabled AND `clusterAccess` != `none`
|
||||
- **Flux MCP** (v0.41.1, port 8081): Only deployed when enabled AND `clusterAccess` != `none`
|
||||
- **Home Assistant MCP** (6.7.1, port 8087): Disabled by default, requires secrets:
|
||||
- `homeassistant-url`: Base URL like `http://homeassistant.local:8123`
|
||||
- `homeassistant-token`: Long-lived access token
|
||||
- **Playwright MCP**: External service, not a sidecar
|
||||
- Configure via `mcpSidecars.<name>.enabled` in values
|
||||
- **Version Strategy**: All MCP images use pinned versions for stability (no `latest` tags)
|
||||
|
||||
## Common Gotchas
|
||||
- `baseimage-gui` creates user dynamically — don't hardcode usernames in scripts, use numeric UID/GID
|
||||
- `chown /home` fails (PVC root not owned by container) — only chown subdirectories
|
||||
- `sudo` not available in startapp.sh — script already runs as correct user
|
||||
- MCP sidecars need appropriate secrets and RBAC permissions to function
|
||||
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:recommended",
|
||||
":gitSignOff"
|
||||
],
|
||||
"semanticCommits": "enabled",
|
||||
"dependencyDashboard": true,
|
||||
"suppressNotifications": [
|
||||
"prEditedNotification"
|
||||
],
|
||||
"rebaseWhen": "conflicted",
|
||||
"commitMessagePrefix": "chore(deps):",
|
||||
"commitMessageAction": "update",
|
||||
"commitMessageTopic": "{{depName}}",
|
||||
"prConcurrentLimit": 5,
|
||||
"prHourlyLimit": 2,
|
||||
"schedule": [
|
||||
"before 6am on monday"
|
||||
],
|
||||
"packageRules": [
|
||||
{
|
||||
"description": "GitHub Actions",
|
||||
"matchManagers": [
|
||||
"github-actions"
|
||||
],
|
||||
"groupName": "github-actions",
|
||||
"additionalBranchPrefix": "github-actions-",
|
||||
"semanticCommitScope": "github-actions",
|
||||
"pinDigests": true
|
||||
},
|
||||
{
|
||||
"description": "Docker base image",
|
||||
"matchManagers": [
|
||||
"dockerfile"
|
||||
],
|
||||
"groupName": "docker",
|
||||
"additionalBranchPrefix": "docker-",
|
||||
"semanticCommitScope": "docker"
|
||||
},
|
||||
{
|
||||
"description": "Automerge patch updates",
|
||||
"matchUpdateTypes": [
|
||||
"patch"
|
||||
],
|
||||
"automerge": true,
|
||||
"automergeType": "pr",
|
||||
"platformAutomerge": true
|
||||
},
|
||||
{
|
||||
"description": "Automerge minor updates for stable packages",
|
||||
"matchUpdateTypes": [
|
||||
"minor"
|
||||
],
|
||||
"matchCurrentVersion": "!/^0/",
|
||||
"automerge": true,
|
||||
"automergeType": "pr",
|
||||
"platformAutomerge": true
|
||||
},
|
||||
{
|
||||
"description": "Separate major updates - require manual review",
|
||||
"matchUpdateTypes": [
|
||||
"major"
|
||||
],
|
||||
"automerge": false,
|
||||
"additionalBranchPrefix": "major-"
|
||||
}
|
||||
],
|
||||
"ignorePaths": [
|
||||
"**/node_modules/**"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#!/bin/sh
|
||||
# Start OpenSSH server when SSH=true.
|
||||
# Runs as root during container initialisation (cont-init.d).
|
||||
[ "${SSH:-false}" = "true" ] || exit 0
|
||||
|
||||
echo "=== SSH enabled: starting sshd ==="
|
||||
|
||||
HOME_DIR="/config/userdata"
|
||||
HOST_KEY_STORE="$HOME_DIR/.ssh/host_keys"
|
||||
|
||||
# Persist host keys on the home PVC so clients don't see a "host key
|
||||
# changed" warning after pod restarts.
|
||||
if [ -d "$HOST_KEY_STORE" ] && [ -n "$(ls "$HOST_KEY_STORE"/ssh_host_* 2>/dev/null)" ]; then
|
||||
# Restore previously generated host keys
|
||||
echo "Restoring SSH host keys from PVC..."
|
||||
cp "$HOST_KEY_STORE"/ssh_host_* /etc/ssh/
|
||||
chmod 600 /etc/ssh/ssh_host_*_key
|
||||
chmod 644 /etc/ssh/ssh_host_*_key.pub
|
||||
else
|
||||
# First boot: generate and save host keys to PVC
|
||||
echo "Generating SSH host keys (first boot)..."
|
||||
ssh-keygen -A 2>/dev/null || true
|
||||
mkdir -p "$HOST_KEY_STORE"
|
||||
cp /etc/ssh/ssh_host_* "$HOST_KEY_STORE/"
|
||||
chmod 700 "$HOST_KEY_STORE"
|
||||
chown -R 1000:1000 "$HOST_KEY_STORE"
|
||||
echo "SSH host keys saved to PVC."
|
||||
fi
|
||||
|
||||
# Populate authorized_keys from env var (injected via Kubernetes secret)
|
||||
if [ -n "$SSH_AUTHORIZED_KEYS" ]; then
|
||||
mkdir -p "$HOME_DIR/.ssh"
|
||||
chmod 700 "$HOME_DIR/.ssh"
|
||||
printf '%s\n' "$SSH_AUTHORIZED_KEYS" > "$HOME_DIR/.ssh/authorized_keys"
|
||||
chmod 600 "$HOME_DIR/.ssh/authorized_keys"
|
||||
chown -R 1000:1000 "$HOME_DIR/.ssh"
|
||||
echo "SSH authorized keys configured."
|
||||
else
|
||||
echo "WARNING: SSH_AUTHORIZED_KEYS not set — you will not be able to log in."
|
||||
fi
|
||||
|
||||
# Start sshd in background (root required to bind :22 and fork sessions)
|
||||
/usr/sbin/sshd -D &
|
||||
|
||||
echo "sshd started (PID $!)"
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
# Fix the app user (UID 1000) created by baseimage-gui at runtime.
|
||||
# baseimage-gui sets shell=/sbin/nologin and home=/dev/null, which
|
||||
# prevents VSCode from opening terminals.
|
||||
usermod -s /bin/bash app
|
||||
usermod -d /config/userdata app
|
||||
@@ -0,0 +1,109 @@
|
||||
#!/bin/bash
|
||||
# Initialize repository
|
||||
set -e
|
||||
|
||||
echo "=== Repository Initialization ==="
|
||||
|
||||
# Set up basic git configuration
|
||||
echo "Configuring git user settings..."
|
||||
# Use environment variables if provided, otherwise use defaults
|
||||
GIT_USER_NAME="${GIT_USER_NAME:-DevContainer User}"
|
||||
GIT_USER_EMAIL="${GIT_USER_EMAIL:-devcontainer@example.com}"
|
||||
|
||||
git config --global user.name "$GIT_USER_NAME"
|
||||
git config --global user.email "$GIT_USER_EMAIL"
|
||||
|
||||
# Set up git credentials early if GITHUB_TOKEN is provided
|
||||
# This ensures all git operations have proper authentication
|
||||
if [ -n "$GITHUB_TOKEN" ]; then
|
||||
echo "Setting up git credentials..."
|
||||
# Configure git to use credential store globally
|
||||
git config --global credential.helper store
|
||||
|
||||
# Create or update the credentials file
|
||||
CREDENTIALS_FILE="/config/userdata/.git-credentials"
|
||||
|
||||
# Support multiple git hosting providers
|
||||
# GitHub supports both oauth2 and token as username
|
||||
echo "https://oauth2:${GITHUB_TOKEN}@github.com" > "$CREDENTIALS_FILE"
|
||||
echo "https://${GITHUB_TOKEN}:x-oauth-basic@github.com" >> "$CREDENTIALS_FILE"
|
||||
echo "https://token:${GITHUB_TOKEN}@github.com" >> "$CREDENTIALS_FILE"
|
||||
|
||||
# GitLab format (if same token works)
|
||||
if [ -n "$GITLAB_HOST" ]; then
|
||||
echo "https://oauth2:${GITHUB_TOKEN}@${GITLAB_HOST}" >> "$CREDENTIALS_FILE"
|
||||
fi
|
||||
|
||||
chmod 600 "$CREDENTIALS_FILE"
|
||||
|
||||
# Also create a symlink in the home directory if it doesn't exist
|
||||
# This handles cases where git might look in different locations
|
||||
if [ ! -f "$HOME/.git-credentials" ] && [ "$HOME" != "/config/userdata" ]; then
|
||||
ln -sf "$CREDENTIALS_FILE" "$HOME/.git-credentials"
|
||||
fi
|
||||
|
||||
echo "Git credentials configured"
|
||||
else
|
||||
# Even without a token, ensure git has a proper credential helper configured
|
||||
# This prevents errors when credentials are added later
|
||||
echo "No GITHUB_TOKEN provided, configuring basic git settings..."
|
||||
git config --global credential.helper store
|
||||
|
||||
# Create an empty credentials file with proper permissions
|
||||
CREDENTIALS_FILE="/config/userdata/.git-credentials"
|
||||
touch "$CREDENTIALS_FILE"
|
||||
chmod 600 "$CREDENTIALS_FILE"
|
||||
|
||||
# Create symlink if needed
|
||||
if [ ! -f "$HOME/.git-credentials" ] && [ "$HOME" != "/config/userdata" ]; then
|
||||
ln -sf "$CREDENTIALS_FILE" "$HOME/.git-credentials"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if GITHUB_REPO is set
|
||||
if [ -z "$GITHUB_REPO" ]; then
|
||||
echo "GITHUB_REPO not set, skipping repository clone"
|
||||
WORKSPACE_DIR="/workspace/default"
|
||||
mkdir -p "$WORKSPACE_DIR"
|
||||
else
|
||||
# Parse repo name from URL
|
||||
REPO_NAME=$(basename "$GITHUB_REPO" .git)
|
||||
WORKSPACE_DIR="/workspace/$REPO_NAME"
|
||||
|
||||
echo "Repository: $GITHUB_REPO"
|
||||
echo "Target directory: $WORKSPACE_DIR"
|
||||
|
||||
# Check if repo already exists
|
||||
if [ -d "$WORKSPACE_DIR/.git" ]; then
|
||||
echo "Repository already exists, pulling latest changes..."
|
||||
cd "$WORKSPACE_DIR"
|
||||
git pull || echo "Pull failed, continuing anyway..."
|
||||
else
|
||||
echo "Cloning repository..."
|
||||
mkdir -p "$(dirname "$WORKSPACE_DIR")"
|
||||
|
||||
# Clone with token if provided
|
||||
if [ -n "$GITHUB_TOKEN" ]; then
|
||||
# Replace https://github.com/ with https://oauth2:token@github.com/
|
||||
CLONE_URL=$(echo "$GITHUB_REPO" | sed "s|https://github.com/|https://oauth2:${GITHUB_TOKEN}@github.com/|")
|
||||
git clone "$CLONE_URL" "$WORKSPACE_DIR"
|
||||
else
|
||||
git clone "$GITHUB_REPO" "$WORKSPACE_DIR"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Set ownership using numeric IDs (username may not exist yet in baseimage-gui)
|
||||
RUN_UID="${USER_ID:-1000}"
|
||||
RUN_GID="${GROUP_ID:-1000}"
|
||||
chown -R "$RUN_UID:$RUN_GID" "$WORKSPACE_DIR"
|
||||
|
||||
# Ensure home directory exists on the PVC (may be absent on a fresh volume)
|
||||
mkdir -p "$HOME"
|
||||
chown "$RUN_UID:$RUN_GID" "$HOME"
|
||||
|
||||
|
||||
# Export workspace directory for startapp.sh
|
||||
echo "$WORKSPACE_DIR" > /tmp/workspace-dir
|
||||
|
||||
echo "=== Initialization Complete ==="
|
||||
@@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
# Start application script for baseimage-gui
|
||||
set -e
|
||||
|
||||
echo "=== Starting Dev Container ==="
|
||||
|
||||
# Initialize repository
|
||||
/usr/local/bin/init-repo
|
||||
|
||||
# Get workspace directory
|
||||
if [ -f /tmp/workspace-dir ]; then
|
||||
WORKSPACE_DIR=$(cat /tmp/workspace-dir)
|
||||
else
|
||||
WORKSPACE_DIR="/workspace/default"
|
||||
fi
|
||||
|
||||
IDE="${IDE:-vscode}"
|
||||
echo "IDE mode: $IDE"
|
||||
echo "Workspace: $WORKSPACE_DIR"
|
||||
|
||||
case "$IDE" in
|
||||
antigravity)
|
||||
echo "Opening Google Antigravity in: $WORKSPACE_DIR"
|
||||
# --no-sandbox is required for Electron apps in Docker (no kernel sandbox available).
|
||||
# Explicit --user-data-dir and --extensions-dir pin config to the home PVC so
|
||||
# settings and the setup wizard state survive pod restarts.
|
||||
exec antigravity --no-sandbox \
|
||||
--user-data-dir "$HOME/.config/antigravity" \
|
||||
--extensions-dir "$HOME/.antigravity/extensions" \
|
||||
--new-window --wait "$WORKSPACE_DIR"
|
||||
;;
|
||||
none)
|
||||
echo "IDE=none: no IDE launched, keeping container alive."
|
||||
exec sleep infinity
|
||||
;;
|
||||
*)
|
||||
echo "Opening VSCode in: $WORKSPACE_DIR"
|
||||
exec code --new-window --wait "$WORKSPACE_DIR"
|
||||
;;
|
||||
esac
|
||||
Executable
+46
@@ -0,0 +1,46 @@
|
||||
#!/bin/bash
|
||||
# Test script to verify git credentials configuration
|
||||
|
||||
set -e
|
||||
|
||||
echo "=== Git Credentials Test ==="
|
||||
|
||||
# Check git configuration
|
||||
echo "1. Git user configuration:"
|
||||
git config --global user.name || echo " ❌ user.name not set"
|
||||
git config --global user.email || echo " ❌ user.email not set"
|
||||
|
||||
echo ""
|
||||
echo "2. Git credential helper:"
|
||||
git config --global credential.helper || echo " ❌ credential.helper not set"
|
||||
|
||||
echo ""
|
||||
echo "3. Credentials file locations:"
|
||||
CREDENTIALS_FILE="/config/userdata/.git-credentials"
|
||||
if [ -f "$CREDENTIALS_FILE" ]; then
|
||||
echo " ✓ $CREDENTIALS_FILE exists"
|
||||
echo " Permissions: $(stat -c %a $CREDENTIALS_FILE)"
|
||||
echo " Lines in file: $(wc -l < $CREDENTIALS_FILE)"
|
||||
else
|
||||
echo " ❌ $CREDENTIALS_FILE does not exist"
|
||||
fi
|
||||
|
||||
if [ -f "$HOME/.git-credentials" ]; then
|
||||
if [ -L "$HOME/.git-credentials" ]; then
|
||||
echo " ✓ $HOME/.git-credentials is a symlink to $(readlink -f $HOME/.git-credentials)"
|
||||
else
|
||||
echo " ✓ $HOME/.git-credentials exists (not a symlink)"
|
||||
fi
|
||||
else
|
||||
echo " ❌ $HOME/.git-credentials does not exist"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "4. Environment check:"
|
||||
echo " HOME=$HOME"
|
||||
echo " GITHUB_TOKEN=${GITHUB_TOKEN:+[SET]}"
|
||||
echo " GIT_USER_NAME=${GIT_USER_NAME:-[NOT SET]}"
|
||||
echo " GIT_USER_EMAIL=${GIT_USER_EMAIL:-[NOT SET]}"
|
||||
|
||||
echo ""
|
||||
echo "=== Test Complete ==="
|
||||
Reference in New Issue
Block a user