fix: phase 0 quick wins — safety, naming, and portability
- Add helm.sh/resource-policy: keep to PVC (prevent data loss on uninstall) - Add fail guard for empty name value in Helm templates - Fix Makefile IMAGE_NAME from antigravity to devcontainer - Pin busybox:1.37, homeassistant:v6.7.1, playwright:v0.0.68 (was latest/stable) - Set imagePullPolicy: IfNotPresent on pinned sidecars - Remove fetch/sequentialthinking from .mcp.json (sidecars removed from chart) - Default storage.className to empty (use cluster default, was ceph-filesystem) - Default Happy Coder URLs to empty (was private farh.net endpoints) - Broaden githubRepo schema to accept GitLab/Gitea URLs - Add unknown IDE warning before VSCode fallback - Add mkdir -p before credential file write (fix fresh PVC boot) - Guard app user existence in cont-init-user.sh - Add NOTES.txt post-install template with port-forward and secret hints - Add standard app.kubernetes.io/* labels and separate selectorLabels Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -22,14 +22,6 @@
|
|||||||
"pgtuner": {
|
"pgtuner": {
|
||||||
"type": "sse",
|
"type": "sse",
|
||||||
"url": "http://localhost:8085/sse"
|
"url": "http://localhost:8085/sse"
|
||||||
},
|
|
||||||
"fetch": {
|
|
||||||
"type": "sse",
|
|
||||||
"url": "http://localhost:8082/sse"
|
|
||||||
},
|
|
||||||
"sequentialthinking": {
|
|
||||||
"type": "sse",
|
|
||||||
"url": "http://localhost:8083/sse"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# Variables
|
# Variables
|
||||||
REGISTRY ?= ghcr.io/cpfarhood
|
REGISTRY ?= ghcr.io/cpfarhood
|
||||||
IMAGE_NAME ?= antigravity
|
IMAGE_NAME ?= devcontainer
|
||||||
IMAGE_TAG ?= latest
|
IMAGE_TAG ?= latest
|
||||||
FULL_IMAGE = $(REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG)
|
FULL_IMAGE = $(REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG)
|
||||||
|
|
||||||
@@ -29,15 +29,15 @@ run:
|
|||||||
-e HAPPY_EXPERIMENTAL="true" \
|
-e HAPPY_EXPERIMENTAL="true" \
|
||||||
-v $(PWD)/home:/home \
|
-v $(PWD)/home:/home \
|
||||||
-v $(PWD)/workspace:/workspace \
|
-v $(PWD)/workspace:/workspace \
|
||||||
--name antigravity \
|
--name devcontainer \
|
||||||
$(FULL_IMAGE)
|
$(FULL_IMAGE)
|
||||||
@echo "Access at http://localhost:5800"
|
@echo "Access at http://localhost:5800"
|
||||||
|
|
||||||
# Stop the running container
|
# Stop the running container
|
||||||
stop:
|
stop:
|
||||||
@echo "Stopping antigravity container..."
|
@echo "Stopping devcontainer..."
|
||||||
docker stop antigravity || true
|
docker stop devcontainer || true
|
||||||
docker rm antigravity || true
|
docker rm devcontainer || true
|
||||||
|
|
||||||
# Clean up local volumes
|
# Clean up local volumes
|
||||||
clean: stop
|
clean: stop
|
||||||
@@ -81,7 +81,7 @@ helm-port-forward:
|
|||||||
|
|
||||||
# Show help
|
# Show help
|
||||||
help:
|
help:
|
||||||
@echo "Antigravity Dev Container Makefile"
|
@echo "Dev Container Makefile"
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo "Usage: make [target]"
|
@echo "Usage: make [target]"
|
||||||
@echo ""
|
@echo ""
|
||||||
@@ -101,7 +101,7 @@ help:
|
|||||||
@echo ""
|
@echo ""
|
||||||
@echo "Variables:"
|
@echo "Variables:"
|
||||||
@echo " REGISTRY - Docker registry (default: ghcr.io/cpfarhood)"
|
@echo " REGISTRY - Docker registry (default: ghcr.io/cpfarhood)"
|
||||||
@echo " IMAGE_NAME - Image name (default: antigravity)"
|
@echo " IMAGE_NAME - Image name (default: devcontainer)"
|
||||||
@echo " IMAGE_TAG - Image tag (default: latest)"
|
@echo " IMAGE_TAG - Image tag (default: latest)"
|
||||||
@echo " RELEASE_NAME - Helm release name (default: mydev)"
|
@echo " RELEASE_NAME - Helm release name (default: mydev)"
|
||||||
@echo " NAMESPACE - Kubernetes namespace (default: default)"
|
@echo " NAMESPACE - Kubernetes namespace (default: default)"
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
Dev Container "{{ .Values.name }}" has been deployed.
|
||||||
|
|
||||||
|
{{- if ne (.Values.ide.type | default "vscode") "none" }}
|
||||||
|
|
||||||
|
Access the IDE:
|
||||||
|
kubectl port-forward deployment/{{ include "devcontainer.fullname" . }} 5800:5800 -n {{ .Release.Namespace }}
|
||||||
|
Then open: http://localhost:5800
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- if .Values.ssh.enabled }}
|
||||||
|
|
||||||
|
SSH access:
|
||||||
|
kubectl port-forward deployment/{{ include "devcontainer.fullname" . }} 2222:22 -n {{ .Release.Namespace }}
|
||||||
|
Then: ssh -p 2222 user@localhost
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
Useful commands:
|
||||||
|
Logs: kubectl logs -f deployment/{{ include "devcontainer.fullname" . }} -n {{ .Release.Namespace }}
|
||||||
|
Shell: kubectl exec -it deployment/{{ include "devcontainer.fullname" . }} -n {{ .Release.Namespace }} -- bash
|
||||||
|
|
||||||
|
{{- if not (lookup "v1" "Secret" .Release.Namespace (include "devcontainer.envSecretName" .)) }}
|
||||||
|
|
||||||
|
Optional: Create a secret for GITHUB_TOKEN, VNC_PASSWORD, etc:
|
||||||
|
kubectl create secret generic {{ include "devcontainer.envSecretName" . }} \
|
||||||
|
--from-literal=GITHUB_TOKEN=ghp_xxx \
|
||||||
|
--from-literal=VNC_PASSWORD=changeme \
|
||||||
|
-n {{ .Release.Namespace }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
Note: The PVC "{{ include "devcontainer.pvcName" . }}" is protected from deletion on helm uninstall.
|
||||||
|
To remove it manually: kubectl delete pvc {{ include "devcontainer.pvcName" . }} -n {{ .Release.Namespace }}
|
||||||
@@ -2,6 +2,9 @@
|
|||||||
Resource name prefix: devcontainer-{name}
|
Resource name prefix: devcontainer-{name}
|
||||||
*/}}
|
*/}}
|
||||||
{{- define "devcontainer.fullname" -}}
|
{{- define "devcontainer.fullname" -}}
|
||||||
|
{{- if not .Values.name }}
|
||||||
|
{{- fail "values.name is required and must not be empty" }}
|
||||||
|
{{- end }}
|
||||||
{{- printf "devcontainer-%s" .Values.name }}
|
{{- printf "devcontainer-%s" .Values.name }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
@@ -25,6 +28,18 @@ Common labels
|
|||||||
{{- define "devcontainer.labels" -}}
|
{{- define "devcontainer.labels" -}}
|
||||||
app: devcontainer
|
app: devcontainer
|
||||||
instance: {{ .Values.name }}
|
instance: {{ .Values.name }}
|
||||||
|
app.kubernetes.io/name: devcontainer
|
||||||
|
app.kubernetes.io/instance: {{ .Values.name }}
|
||||||
|
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||||
|
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Selector labels — keep narrow since changing these requires recreating the Deployment
|
||||||
|
*/}}
|
||||||
|
{{- define "devcontainer.selectorLabels" -}}
|
||||||
|
app: devcontainer
|
||||||
|
instance: {{ .Values.name }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
{{/*
|
{{/*
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ spec:
|
|||||||
replicas: 1
|
replicas: 1
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
{{- include "devcontainer.labels" . | nindent 6 }}
|
{{- include "devcontainer.selectorLabels" . | nindent 6 }}
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
@@ -23,7 +23,7 @@ spec:
|
|||||||
{{- if and .Values.ide.type (eq .Values.ide.type "antigravity") }}
|
{{- if and .Values.ide.type (eq .Values.ide.type "antigravity") }}
|
||||||
initContainers:
|
initContainers:
|
||||||
- name: setup-userdata
|
- name: setup-userdata
|
||||||
image: busybox:latest
|
image: busybox:1.37
|
||||||
command: ['sh', '-c']
|
command: ['sh', '-c']
|
||||||
args:
|
args:
|
||||||
- |
|
- |
|
||||||
@@ -169,7 +169,7 @@ spec:
|
|||||||
{{- if .Values.mcp.sidecars.homeassistant.enabled }}
|
{{- if .Values.mcp.sidecars.homeassistant.enabled }}
|
||||||
- name: homeassistant-mcp
|
- name: homeassistant-mcp
|
||||||
image: "{{ .Values.mcp.sidecars.homeassistant.image.repository }}:{{ .Values.mcp.sidecars.homeassistant.image.tag }}"
|
image: "{{ .Values.mcp.sidecars.homeassistant.image.repository }}:{{ .Values.mcp.sidecars.homeassistant.image.tag }}"
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: IfNotPresent
|
||||||
command: ["fastmcp", "run", "--transport", "sse", "--host", "0.0.0.0", "--port", "{{ .Values.mcp.sidecars.homeassistant.port }}"]
|
command: ["fastmcp", "run", "--transport", "sse", "--host", "0.0.0.0", "--port", "{{ .Values.mcp.sidecars.homeassistant.port }}"]
|
||||||
ports:
|
ports:
|
||||||
- name: homeassistant
|
- name: homeassistant
|
||||||
@@ -203,7 +203,7 @@ spec:
|
|||||||
{{- if .Values.mcp.sidecars.pgtuner.enabled }}
|
{{- if .Values.mcp.sidecars.pgtuner.enabled }}
|
||||||
- name: pgtuner-mcp
|
- name: pgtuner-mcp
|
||||||
image: "{{ .Values.mcp.sidecars.pgtuner.image.repository }}:{{ .Values.mcp.sidecars.pgtuner.image.tag }}"
|
image: "{{ .Values.mcp.sidecars.pgtuner.image.repository }}:{{ .Values.mcp.sidecars.pgtuner.image.tag }}"
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always # pgtuner uses `latest` tag (no versioned releases available)
|
||||||
command: ["python", "-m", "pgtuner_mcp", "--mode", "sse", "--host", "0.0.0.0", "--port", "{{ .Values.mcp.sidecars.pgtuner.port }}"]
|
command: ["python", "-m", "pgtuner_mcp", "--mode", "sse", "--host", "0.0.0.0", "--port", "{{ .Values.mcp.sidecars.pgtuner.port }}"]
|
||||||
ports:
|
ports:
|
||||||
- name: pgtuner
|
- name: pgtuner
|
||||||
@@ -237,7 +237,7 @@ spec:
|
|||||||
{{- if .Values.mcp.sidecars.playwright.enabled }}
|
{{- if .Values.mcp.sidecars.playwright.enabled }}
|
||||||
- name: playwright-mcp
|
- name: playwright-mcp
|
||||||
image: "{{ .Values.mcp.sidecars.playwright.image.repository }}:{{ .Values.mcp.sidecars.playwright.image.tag }}"
|
image: "{{ .Values.mcp.sidecars.playwright.image.repository }}:{{ .Values.mcp.sidecars.playwright.image.tag }}"
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: IfNotPresent
|
||||||
command: ["node"]
|
command: ["node"]
|
||||||
args:
|
args:
|
||||||
- cli.js
|
- cli.js
|
||||||
|
|||||||
@@ -2,12 +2,16 @@ apiVersion: v1
|
|||||||
kind: PersistentVolumeClaim
|
kind: PersistentVolumeClaim
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ include "devcontainer.pvcName" . }}
|
name: {{ include "devcontainer.pvcName" . }}
|
||||||
|
annotations:
|
||||||
|
helm.sh/resource-policy: keep
|
||||||
labels:
|
labels:
|
||||||
{{- include "devcontainer.labels" . | nindent 4 }}
|
{{- include "devcontainer.labels" . | nindent 4 }}
|
||||||
spec:
|
spec:
|
||||||
accessModes:
|
accessModes:
|
||||||
- ReadWriteMany
|
- ReadWriteMany
|
||||||
|
{{- if .Values.storage.className }}
|
||||||
storageClassName: {{ .Values.storage.className }}
|
storageClassName: {{ .Values.storage.className }}
|
||||||
|
{{- end }}
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
storage: {{ .Values.storage.size }}
|
storage: {{ .Values.storage.size }}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
"githubRepo": {
|
"githubRepo": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "GitHub repository URL to clone",
|
"description": "GitHub repository URL to clone",
|
||||||
"pattern": "^https://github\\.com/.+/.+$"
|
"pattern": "^https?://.+/.+/.+$"
|
||||||
},
|
},
|
||||||
"ide": {
|
"ide": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@@ -108,7 +108,7 @@
|
|||||||
"description": "Storage class name (must support ReadWriteMany)"
|
"description": "Storage class name (must support ReadWriteMany)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["size", "className"]
|
"required": ["size"]
|
||||||
},
|
},
|
||||||
"resources": {
|
"resources": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@@ -143,12 +143,10 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"serverUrl": {
|
"serverUrl": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "uri",
|
|
||||||
"description": "Happy Coder server URL"
|
"description": "Happy Coder server URL"
|
||||||
},
|
},
|
||||||
"webappUrl": {
|
"webappUrl": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "uri",
|
|
||||||
"description": "Happy Coder webapp URL"
|
"description": "Happy Coder webapp URL"
|
||||||
},
|
},
|
||||||
"homeDir": {
|
"homeDir": {
|
||||||
@@ -161,7 +159,7 @@
|
|||||||
"description": "Enable experimental Happy features"
|
"description": "Enable experimental Happy features"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["serverUrl", "webappUrl", "homeDir", "experimental"]
|
"required": ["homeDir", "experimental"]
|
||||||
},
|
},
|
||||||
"mcp": {
|
"mcp": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|||||||
+5
-5
@@ -45,7 +45,7 @@ user:
|
|||||||
# Storage configuration
|
# Storage configuration
|
||||||
storage:
|
storage:
|
||||||
size: 32Gi
|
size: 32Gi
|
||||||
className: ceph-filesystem
|
className: "" # Empty string uses the cluster's default StorageClass (must support ReadWriteMany)
|
||||||
|
|
||||||
# Resource allocation
|
# Resource allocation
|
||||||
resources:
|
resources:
|
||||||
@@ -70,8 +70,8 @@ clusterAccess: none
|
|||||||
|
|
||||||
# Happy Coder AI assistant configuration
|
# Happy Coder AI assistant configuration
|
||||||
happy:
|
happy:
|
||||||
serverUrl: "https://happy.farh.net"
|
serverUrl: ""
|
||||||
webappUrl: "https://happy-coder.farh.net"
|
webappUrl: ""
|
||||||
homeDir: "/config/userdata/.happy"
|
homeDir: "/config/userdata/.happy"
|
||||||
experimental: "true"
|
experimental: "true"
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ mcp:
|
|||||||
enabled: false # Requires HOMEASSISTANT_URL and HOMEASSISTANT_TOKEN
|
enabled: false # Requires HOMEASSISTANT_URL and HOMEASSISTANT_TOKEN
|
||||||
image:
|
image:
|
||||||
repository: ghcr.io/homeassistant-ai/ha-mcp
|
repository: ghcr.io/homeassistant-ai/ha-mcp
|
||||||
tag: stable
|
tag: v6.7.1
|
||||||
port: 8087
|
port: 8087
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
@@ -145,7 +145,7 @@ mcp:
|
|||||||
enabled: true
|
enabled: true
|
||||||
image:
|
image:
|
||||||
repository: mcr.microsoft.com/playwright/mcp
|
repository: mcr.microsoft.com/playwright/mcp
|
||||||
tag: latest
|
tag: v0.0.68
|
||||||
port: 8086
|
port: 8086
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
|
|||||||
@@ -2,5 +2,9 @@
|
|||||||
# Fix the app user (UID 1000) created by baseimage-gui at runtime.
|
# Fix the app user (UID 1000) created by baseimage-gui at runtime.
|
||||||
# baseimage-gui sets shell=/sbin/nologin and home=/dev/null, which
|
# baseimage-gui sets shell=/sbin/nologin and home=/dev/null, which
|
||||||
# prevents VSCode from opening terminals.
|
# prevents VSCode from opening terminals.
|
||||||
usermod -s /bin/bash app
|
if id app >/dev/null 2>&1; then
|
||||||
usermod -d /config/userdata app
|
usermod -s /bin/bash app
|
||||||
|
usermod -d /config/userdata app
|
||||||
|
else
|
||||||
|
echo "WARNING: 'app' user not found, skipping usermod" >&2
|
||||||
|
fi
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ if [ -n "$GITHUB_TOKEN" ]; then
|
|||||||
|
|
||||||
# Create or update the credentials file
|
# Create or update the credentials file
|
||||||
CREDENTIALS_FILE="/config/userdata/.git-credentials"
|
CREDENTIALS_FILE="/config/userdata/.git-credentials"
|
||||||
|
mkdir -p "$(dirname "$CREDENTIALS_FILE")"
|
||||||
|
|
||||||
# Support multiple git hosting providers
|
# Support multiple git hosting providers
|
||||||
# GitHub supports both oauth2 and token as username
|
# GitHub supports both oauth2 and token as username
|
||||||
@@ -51,6 +52,7 @@ else
|
|||||||
|
|
||||||
# Create an empty credentials file with proper permissions
|
# Create an empty credentials file with proper permissions
|
||||||
CREDENTIALS_FILE="/config/userdata/.git-credentials"
|
CREDENTIALS_FILE="/config/userdata/.git-credentials"
|
||||||
|
mkdir -p "$(dirname "$CREDENTIALS_FILE")"
|
||||||
touch "$CREDENTIALS_FILE"
|
touch "$CREDENTIALS_FILE"
|
||||||
chmod 600 "$CREDENTIALS_FILE"
|
chmod 600 "$CREDENTIALS_FILE"
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ case "$IDE" in
|
|||||||
exec sleep infinity
|
exec sleep infinity
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
|
if [ "$IDE" != "vscode" ]; then
|
||||||
|
echo "WARNING: Unknown IDE value '$IDE', defaulting to VSCode"
|
||||||
|
fi
|
||||||
echo "Opening VSCode in: $WORKSPACE_DIR"
|
echo "Opening VSCode in: $WORKSPACE_DIR"
|
||||||
exec code --new-window --wait "$WORKSPACE_DIR"
|
exec code --new-window --wait "$WORKSPACE_DIR"
|
||||||
;;
|
;;
|
||||||
|
|||||||
Reference in New Issue
Block a user