apiVersion: apps/v1 kind: Deployment metadata: name: test-env-runner namespace: test-env labels: app: test-env-runner spec: replicas: 1 selector: matchLabels: app: test-env-runner template: metadata: labels: app: test-env-runner spec: serviceAccountName: runner-registrar initContainers: # Obtain registration token from Gitea API once, write to shared volume. # Uses the same token if .runner file already exists (idempotent). - name: register image: alpine/k8s:1.35.1 command: - sh - -c - | set -e # If already registered, skip if [ -f /data/.runner ]; then echo "Runner already registered, skipping." exit 0 fi # Get Gitea admin credentials USER=$(kubectl -n gitea get secret gitea-admin -o jsonpath='{.data.username}' | base64 -d) PASS=$(kubectl -n gitea get secret gitea-admin -o jsonpath='{.data.password}' | base64 -d) # Resolve Gitea pod IP (headless service) GITEA_POD_IP=$(kubectl -n gitea get pod -l app.kubernetes.io/name=gitea \ -o jsonpath='{.items[0].status.podIP}') GITEA_URL="http://${GITEA_POD_IP}:3000" # Wait for Gitea API for i in $(seq 1 30); do if curl -sf "$GITEA_URL/api/v1/version" > /dev/null 2>&1; then break fi echo "Waiting for Gitea API... ($i/30)" sleep 5 done # Get registration token TOKEN=$(curl -sf -X POST -u "$USER:$PASS" \ "$GITEA_URL/api/v1/user/actions/runners/registration-token" \ | sed 's/.*"token":"\([^"]*\)".*/\1/') if [ -z "$TOKEN" ]; then echo "ERROR: Failed to get registration token" exit 1 fi echo "Got token: ${TOKEN:0:8}..." # Write token for the runner container echo "$TOKEN" > /data/.registration-token echo "$GITEA_URL" > /data/.gitea-url echo "Token saved to /data/.registration-token" volumeMounts: - name: data mountPath: /data containers: # Docker-in-Docker sidecar (required for act_runner to execute workflows) - name: dind image: docker:27-dind securityContext: privileged: true env: - name: DOCKER_TLS_CERTDIR value: "" volumeMounts: - name: docker-socket mountPath: /var/run resources: requests: cpu: 100m memory: 256Mi limits: cpu: "2" memory: 2Gi - name: runner image: gitea/act_runner:0.2.11 command: - sh - -c - | # Wait for Docker daemon echo "Waiting for Docker daemon..." for i in $(seq 1 30); do if docker info > /dev/null 2>&1; then echo "Docker daemon is ready" break fi sleep 2 done # Register if not yet registered if [ ! -f /data/.runner ] && [ -f /data/.registration-token ]; then TOKEN=$(cat /data/.registration-token) GITEA_URL=$(cat /data/.gitea-url) echo "Registering runner at $GITEA_URL..." act_runner register --no-interactive \ --instance "$GITEA_URL" \ --token "$TOKEN" \ --name "test-env-runner" \ --labels "edt:docker://benadis/ar-edt-slim:latest,ubuntu-latest:docker://node:20-bullseye" fi # Start daemon exec act_runner daemon env: - name: DOCKER_HOST value: "unix:///var/run/docker.sock" - name: GITEA_INSTANCE_URL value: "http://gitea-http.gitea.svc.cluster.local:3000" volumeMounts: - name: docker-socket mountPath: /var/run - name: config mountPath: /config readOnly: true - name: data mountPath: /data resources: requests: cpu: 100m memory: 128Mi limits: cpu: "2" memory: 2Gi volumes: - name: docker-socket emptyDir: {} - name: config configMap: name: test-env-runner-config - name: data persistentVolumeClaim: claimName: runner-data --- # PVC for runner data — persists .runner registration across pod restarts apiVersion: v1 kind: PersistentVolumeClaim metadata: name: runner-data namespace: test-env spec: storageClassName: local-path accessModes: ["ReadWriteOnce"] resources: requests: storage: 1Gi