feat: add multi-repo cloning, remove dynamic/serverless mode

Add githubRepos list field for cloning multiple repositories into a
single dev container with multi-root workspace file generation.
Remove the unused dynamic deployment mode (Knative, routing proxy,
serverless scripts) to simplify the chart to persistent-only.
Fix release workflow cache-to setting that violated the no-cache policy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
DevContainer User
2026-03-03 16:42:53 +00:00
parent a300e8e810
commit 60f96fc8da
27 changed files with 97 additions and 1972 deletions
+4 -2
View File
@@ -1,4 +1,3 @@
{{- if eq .Values.deploymentMode "persistent" }}
apiVersion: apps/v1
kind: Deployment
metadata:
@@ -84,6 +83,10 @@ spec:
- name: GITHUB_REPO
value: {{ .Values.githubRepo | quote }}
{{- end }}
{{- if .Values.githubRepos }}
- name: GITHUB_REPOS
value: {{ join "," .Values.githubRepos | quote }}
{{- end }}
envFrom:
- secretRef:
name: {{ include "devcontainer.envSecretName" . }}
@@ -305,4 +308,3 @@ spec:
- name: userhome
persistentVolumeClaim:
claimName: {{ include "devcontainer.pvcName" . }}
{{- end }}
-68
View File
@@ -1,68 +0,0 @@
{{- if and (eq .Values.deploymentMode "dynamic") .Values.dynamic.ingress.enabled .Values.dynamic.ingress.host }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "devcontainer.fullname" . }}-dynamic
labels:
{{- include "devcontainer.labels" . | nindent 4 }}
app.kubernetes.io/component: dynamic-ingress
annotations:
{{- if .Values.dynamic.ingress.className }}
kubernetes.io/ingress.class: {{ .Values.dynamic.ingress.className }}
{{- end }}
# SSL configuration
{{- if .Values.dynamic.ingress.tls.enabled }}
cert-manager.io/cluster-issuer: {{ .Values.dynamic.ingress.tls.issuer | quote }}
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
{{- end }}
# Authentik forward auth (if enabled)
{{- if .Values.dynamic.ingress.authentik.enabled }}
nginx.ingress.kubernetes.io/auth-url: {{ .Values.dynamic.ingress.authentik.authUrl | quote }}
nginx.ingress.kubernetes.io/auth-signin: {{ .Values.dynamic.ingress.authentik.signIn | quote }}
nginx.ingress.kubernetes.io/auth-response-headers: "X-Authentik-Username,X-Authentik-Groups,X-Authentik-Email,X-Authentik-Name"
nginx.ingress.kubernetes.io/auth-snippet: |
proxy_set_header X-Forwarded-Host $http_host;
{{- end }}
# WebSocket support for VNC connections
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
# Large file upload support (for file manager)
nginx.ingress.kubernetes.io/client-max-body-size: "100m"
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
# Custom server snippet for GitHub repo logging
nginx.ingress.kubernetes.io/server-snippet: |
location ~ ^/github/([^/]+/[^/]+) {
# Log the GitHub repo being accessed
access_log /var/log/nginx/devcontainer-access.log combined;
# Set additional headers for audit/monitoring
proxy_set_header X-GitHub-Repo-Requested https://github.com/$1;
proxy_set_header X-Request-Timestamp $time_iso8601;
proxy_set_header X-Client-IP $remote_addr;
}
spec:
{{- if .Values.dynamic.ingress.tls.enabled }}
tls:
- hosts:
- {{ .Values.dynamic.ingress.host }}
secretName: {{ .Values.dynamic.ingress.tls.secretName | default (printf "%s-tls" (include "devcontainer.fullname" .)) }}
{{- end }}
rules:
- host: {{ .Values.dynamic.ingress.host }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ include "devcontainer.fullname" . }}-routing-proxy
port:
number: 80
{{- end }}
-98
View File
@@ -1,98 +0,0 @@
{{- if eq .Values.deploymentMode "dynamic" }}
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: {{ include "devcontainer.fullname" . }}
labels:
{{- include "devcontainer.labels" . | nindent 4 }}
annotations:
# Knative scaling annotations
autoscaling.knative.dev/minScale: {{ .Values.dynamic.knative.minScale | quote }}
autoscaling.knative.dev/maxScale: {{ .Values.dynamic.knative.maxScale | quote }}
autoscaling.knative.dev/target: {{ .Values.dynamic.knative.target | quote }}
autoscaling.knative.dev/scale-to-zero-grace-period: {{ .Values.dynamic.knative.scaleToZeroGracePeriod | quote }}
spec:
template:
metadata:
labels:
{{- include "devcontainer.labels" . | nindent 8 }}
annotations:
# Container configuration
autoscaling.knative.dev/targetPort: "5800"
serving.knative.dev/timeoutSeconds: {{ .Values.dynamic.knative.timeoutSeconds | quote }}
# Scaling configuration
autoscaling.knative.dev/class: "kpa.autoscaling.knative.dev"
autoscaling.knative.dev/metric: "concurrency"
spec:
# Container startup timeout
timeoutSeconds: {{ .Values.dynamic.knative.timeoutSeconds }}
containers:
- name: devcontainer
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: 5800
name: vnc-web
env:
# Dynamic mode flags
- name: SERVERLESS_MODE
value: "true"
- name: DYNAMIC_GITHUB_ROUTING
value: "true"
- name: DEPLOYMENT_MODE
value: "dynamic"
# Standard configuration
- name: IDE
value: {{ .Values.ide.type | default "vscode" | 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 }}
# File manager (always enabled in dynamic mode for easy file transfer)
- name: WEB_FILE_MANAGER
value: "1"
- name: WEB_FILE_MANAGER_ALLOWED_PATHS
value: "/workspace,/tmp" # No persistent /config in dynamic mode
# Secret environment variables
envFrom:
- secretRef:
name: {{ include "devcontainer.envSecretName" . }}
optional: true
resources:
{{- toYaml .Values.dynamic.knative.resources | nindent 10 }}
volumeMounts:
- name: tmp-home
mountPath: /config
- name: shm
mountPath: /dev/shm
# Health probes (adjusted for dynamic mode startup time)
readinessProbe:
httpGet:
path: /
port: 5800
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 10
livenessProbe:
httpGet:
path: /
port: 5800
initialDelaySeconds: 120
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 3
volumes:
- name: tmp-home
emptyDir: {} # Ephemeral - each instance gets fresh home
- name: shm
emptyDir:
medium: Memory
sizeLimit: {{ .Values.shm.sizeLimit }}
{{- end }}
-2
View File
@@ -1,4 +1,3 @@
{{- if eq .Values.deploymentMode "persistent" }}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
@@ -16,4 +15,3 @@ spec:
resources:
requests:
storage: {{ .Values.storage.size }}
{{- end }}
-2
View File
@@ -1,4 +1,3 @@
{{- if eq .Values.deploymentMode "persistent" }}
{{- $access := .Values.clusterAccess | default "none" }}
{{- $name := include "devcontainer.fullname" . }}
{{- $ns := .Release.Namespace }}
@@ -96,4 +95,3 @@ roleRef:
{{- end }}
{{- end }}
{{- end }}
-66
View File
@@ -1,66 +0,0 @@
{{- if and (eq .Values.deploymentMode "dynamic") .Values.dynamic.routingProxy.enabled }}
---
# Routing proxy deployment for dynamic GitHub repo extraction
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "devcontainer.fullname" . }}-routing-proxy
labels:
{{- include "devcontainer.labels" . | nindent 4 }}
app.kubernetes.io/component: routing-proxy
spec:
replicas: {{ .Values.dynamic.routingProxy.replicas }}
selector:
matchLabels:
{{- include "devcontainer.selectorLabels" . | nindent 6 }}
app.kubernetes.io/component: routing-proxy
template:
metadata:
labels:
{{- include "devcontainer.labels" . | nindent 8 }}
app.kubernetes.io/component: routing-proxy
spec:
containers:
- name: routing-proxy
image: "{{ .Values.dynamic.routingProxy.image.repository }}:{{ .Values.dynamic.routingProxy.image.tag }}"
imagePullPolicy: {{ .Values.dynamic.routingProxy.image.pullPolicy }}
ports:
- containerPort: 8080
name: http
env:
- name: DEVCONTAINER_SERVICE_URL
value: "{{ include "devcontainer.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local"
resources:
{{- toYaml .Values.dynamic.routingProxy.resources | nindent 10 }}
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 2
periodSeconds: 5
---
# Service for routing proxy
apiVersion: v1
kind: Service
metadata:
name: {{ include "devcontainer.fullname" . }}-routing-proxy
labels:
{{- include "devcontainer.labels" . | nindent 4 }}
app.kubernetes.io/component: routing-proxy
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 8080
name: http
selector:
{{- include "devcontainer.selectorLabels" . | nindent 4 }}
app.kubernetes.io/component: routing-proxy
{{- end }}
-2
View File
@@ -1,4 +1,3 @@
{{- if eq .Values.deploymentMode "persistent" }}
apiVersion: v1
kind: Service
metadata:
@@ -21,4 +20,3 @@ spec:
{{- end }}
selector:
{{- include "devcontainer.labels" . | nindent 4 }}
{{- end }}