"""Task: Full Stack Deployment Pipeline — EXPERT. Agent debugs multi-error scenarios spanning the entire stack: GHA workflow + Dockerfile + Kubernetes manifests. Multiple bugs per scenario requiring cross-file reasoning. """ from server.models import TaskDifficulty from server.tasks.base import BaseTask class PipelineFullTask(BaseTask): NAME = "Full Stack Deployment Pipeline" DESCRIPTION = "Debug complex multi-error deployment pipelines across GHA workflows, Dockerfiles, and Kubernetes manifests" DIFFICULTY = TaskDifficulty.HARD AVAILABLE_SECRETS = ["GITHUB_TOKEN", "DOCKER_USERNAME", "DOCKER_PASSWORD"] SCENARIOS = [ # Scenario 1: GHCR token missing env + K8s service selector mismatch { "id": "full_pipeline_ghcr_and_selector", "files": [ { "path": ".github/workflows/deploy.yml", "type": "workflow", "content": ( "name: Build and Deploy\n" "on:\n" " push:\n" " branches: [main]\n" "\n" "jobs:\n" " deploy:\n" " runs-on: ubuntu-latest\n" " steps:\n" " - uses: actions/checkout@v4\n" "\n" " - name: Build Docker image\n" " run: docker build -t ghcr.io/${{ github.repository }}:${{ github.sha }} .\n" "\n" " - name: Login to GHCR\n" " run: echo $GITHUB_TOKEN | docker login ghcr.io -u ${{ github.actor }} --password-stdin\n" "\n" " - name: Push image\n" " run: docker push ghcr.io/${{ github.repository }}:${{ github.sha }}\n" ), }, { "path": "Dockerfile", "type": "dockerfile", "content": ( "FROM node:20-alpine\n" "WORKDIR /app\n" "COPY package*.json ./\n" "RUN npm ci\n" "COPY . .\n" "EXPOSE 3000\n" 'CMD ["npm", "start"]\n' ), }, { "path": "package.json", "type": "other", "content": '{"name": "myapp", "scripts": {"start": "node server.js"}}', }, { "path": "k8s/deployment.yaml", "type": "kubernetes", "content": ( "apiVersion: apps/v1\n" "kind: Deployment\n" "metadata:\n" " name: myapp\n" "spec:\n" " replicas: 3\n" " selector:\n" " matchLabels:\n" " app: myapp\n" " template:\n" " metadata:\n" " labels:\n" " app: myapp\n" " spec:\n" " containers:\n" " - name: app\n" " image: ghcr.io/OWNER/REPO:TAG\n" " ports:\n" " - containerPort: 3000\n" ), }, { "path": "k8s/service.yaml", "type": "kubernetes", "content": ( "apiVersion: v1\n" "kind: Service\n" "metadata:\n" " name: myapp-service\n" "spec:\n" " selector:\n" " app: my-app\n" " ports:\n" " - port: 80\n" " targetPort: 3000\n" ), }, ], "error": { "phase": "pipeline_deploy", "message": ( "Run: Build and Deploy\n" "\n" "Step: Login to GHCR ✗\n" "Error: Cannot perform an interactive login from a non TTY device\n" "Error: GITHUB_TOKEN environment variable is not set\n" "\n" "---\n" "(If login had succeeded, deployment would also fail with:)\n" "Error: Service 'myapp-service' has no endpoints" ), }, "expected_fixes": [ { "file": ".github/workflows/deploy.yml", "type": "contains", "expected": "GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}", "hint": "GITHUB_TOKEN is used as shell variable but not mapped from secrets via env block", }, { "file": "k8s/service.yaml", "type": "contains", "expected": "app: myapp", "hint": "Service selector 'app: my-app' doesn't match Deployment label 'app: myapp'", }, ], }, # Scenario 2: Dockerfile missing WORKDIR + workflow missing checkout + K8s wrong port { "id": "full_pipeline_three_bugs", "files": [ { "path": ".github/workflows/ci.yml", "type": "workflow", "content": ( "name: CI Pipeline\n" "on:\n" " push:\n" " branches: [main]\n" "\n" "jobs:\n" " build:\n" " runs-on: ubuntu-latest\n" " steps:\n" " - name: Build image\n" " run: docker build -t myapp:${{ github.sha }} .\n" "\n" " - name: Run tests\n" " run: docker run myapp:${{ github.sha }} npm test\n" ), }, { "path": "Dockerfile", "type": "dockerfile", "content": ( "FROM node:18-alpine\n" "COPY package*.json ./\n" "RUN npm ci\n" "COPY . .\n" "EXPOSE 3000\n" 'CMD ["npm", "start"]\n' ), }, { "path": "package.json", "type": "other", "content": '{"name": "myapp", "scripts": {"start": "node server.js", "test": "jest"}}', }, { "path": "k8s/deployment.yaml", "type": "kubernetes", "content": ( "apiVersion: apps/v1\n" "kind: Deployment\n" "metadata:\n" " name: myapp\n" "spec:\n" " replicas: 2\n" " selector:\n" " matchLabels:\n" " app: myapp\n" " template:\n" " metadata:\n" " labels:\n" " app: myapp\n" " spec:\n" " containers:\n" " - name: app\n" " image: myapp:latest\n" " ports:\n" " - containerPort: 8080\n" ), }, { "path": "k8s/service.yaml", "type": "kubernetes", "content": ( "apiVersion: v1\n" "kind: Service\n" "metadata:\n" " name: myapp-svc\n" "spec:\n" " selector:\n" " app: myapp\n" " ports:\n" " - port: 80\n" " targetPort: 8080\n" ), }, ], "error": { "phase": "pipeline_deploy", "message": ( "Run: CI Pipeline\n" "\n" "Step: Build image ✗\n" "Error: Checkout must happen before Docker build steps\n" "(No actions/checkout@v4 step found before docker build)\n" "\n" "---\n" "Additionally:\n" "- Dockerfile: npm reports module resolution errors at runtime\n" "- K8s: Service returns connection refused when accessed" ), }, "expected_fixes": [ { "file": ".github/workflows/ci.yml", "type": "contains", "expected": "actions/checkout@v4", "hint": "Workflow needs a checkout step before docker build", }, { "file": "Dockerfile", "type": "contains", "expected": "WORKDIR /app", "hint": "Dockerfile needs WORKDIR /app before COPY commands", }, { "file": "k8s/deployment.yaml", "type": "contains", "expected": "containerPort: 3000", "hint": "Container port should be 3000 to match the app's EXPOSE/listen port", }, { "file": "k8s/service.yaml", "type": "contains", "expected": "targetPort: 3000", "hint": "Service targetPort should be 3000 to match container port", }, ], }, # Scenario 3: Wrong GHCR password secret + Dockerfile base image typo + K8s OOM { "id": "full_pipeline_ghcr_dockerfile_k8s", "files": [ { "path": ".github/workflows/release.yml", "type": "workflow", "content": ( "name: Release Pipeline\n" "on:\n" " release:\n" " types: [published]\n" "\n" "jobs:\n" " release:\n" " runs-on: ubuntu-latest\n" " steps:\n" " - uses: actions/checkout@v4\n" "\n" " - name: Login to GHCR\n" " run: echo ${{ secrets.DOCKER_PASSWORD }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin\n" "\n" " - name: Build\n" " run: docker build -t ghcr.io/${{ github.repository }}:${{ github.event.release.tag_name }} .\n" "\n" " - name: Push\n" " run: docker push ghcr.io/${{ github.repository }}:${{ github.event.release.tag_name }}\n" ), }, { "path": "Dockerfile", "type": "dockerfile", "content": ( "FROM python:3.9-slimm\n" "WORKDIR /app\n" "COPY requirements.txt .\n" "RUN pip install -r requirements.txt\n" "COPY . .\n" "EXPOSE 7860\n" 'CMD ["gunicorn", "app:app", "-b", "0.0.0.0:7860"]\n' ), }, { "path": "requirements.txt", "type": "requirements", "content": "flask==3.0.0\ngunicorn==21.2.0\n", }, { "path": "k8s/deployment.yaml", "type": "kubernetes", "content": ( "apiVersion: apps/v1\n" "kind: Deployment\n" "metadata:\n" " name: api\n" "spec:\n" " replicas: 3\n" " selector:\n" " matchLabels:\n" " app: api\n" " template:\n" " metadata:\n" " labels:\n" " app: api\n" " spec:\n" " containers:\n" " - name: api\n" " image: ghcr.io/myorg/myapp:latest\n" " ports:\n" " - containerPort: 7860\n" " resources:\n" " limits:\n" ' memory: "64Mi"\n' ' cpu: "100m"\n' ), }, ], "error": { "phase": "pipeline_deploy", "message": ( "Run: Release Pipeline\n" "\n" "Step: Login to GHCR ✗\n" "Error: GHCR requires GITHUB_TOKEN for authentication, not DOCKER_PASSWORD\n" "\n" "---\n" "Additional issues found:\n" "- Dockerfile: pull access denied for base image — repository does not exist\n" "- K8s: Pod in CrashLoopBackOff with exit code 137" ), }, "expected_fixes": [ { "file": ".github/workflows/release.yml", "type": "contains", "expected": "secrets.GITHUB_TOKEN", "hint": "GHCR uses GITHUB_TOKEN, not DOCKER_PASSWORD", }, { "file": "Dockerfile", "type": "not_contains", "expected": "python:3.9-slimm", "hint": "Base image tag has a typo: 'slimm' should be 'slim'", }, { "file": "k8s/deployment.yaml", "type": "contains", "expected": 'memory: "256Mi"', "hint": "Memory limit 64Mi is too low for gunicorn — increase to at least 256Mi", }, ], }, # Scenario 4: Missing permissions block + hardcoded K8s image + missing ingress class { "id": "full_pipeline_permissions_image_ingress", "files": [ { "path": ".github/workflows/deploy.yml", "type": "workflow", "content": ( "name: Deploy to Production\n" "on:\n" " push:\n" " branches: [main]\n" "\n" "jobs:\n" " build-and-push:\n" " runs-on: ubuntu-latest\n" " steps:\n" " - uses: actions/checkout@v4\n" "\n" " - name: Login to GHCR\n" " run: echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin\n" "\n" " - name: Build and push\n" " run: |\n" " docker build -t ghcr.io/${{ github.repository }}:${{ github.sha }} .\n" " docker push ghcr.io/${{ github.repository }}:${{ github.sha }}\n" ), }, { "path": "Dockerfile", "type": "dockerfile", "content": ( "FROM node:20-alpine\n" "WORKDIR /app\n" "COPY package*.json ./\n" "RUN npm ci\n" "COPY . .\n" "EXPOSE 3000\n" 'CMD ["npm", "start"]\n' ), }, { "path": "package.json", "type": "other", "content": '{"name": "app", "scripts": {"start": "node index.js"}}', }, { "path": "k8s/deployment.yaml", "type": "kubernetes", "content": ( "apiVersion: apps/v1\n" "kind: Deployment\n" "metadata:\n" " name: webapp\n" "spec:\n" " replicas: 3\n" " selector:\n" " matchLabels:\n" " app: webapp\n" " template:\n" " metadata:\n" " labels:\n" " app: webapp\n" " spec:\n" " containers:\n" " - name: webapp\n" " image: ghcr.io/OWNER/REPO:TAG\n" " ports:\n" " - containerPort: 3000\n" ), }, { "path": "k8s/service.yaml", "type": "kubernetes", "content": ( "apiVersion: v1\n" "kind: Service\n" "metadata:\n" " name: webapp-svc\n" "spec:\n" " selector:\n" " app: webapp\n" " ports:\n" " - port: 80\n" " targetPort: 3000\n" ), }, { "path": "k8s/ingress.yaml", "type": "kubernetes", "content": ( "apiVersion: networking.k8s.io/v1\n" "kind: Ingress\n" "metadata:\n" " name: webapp-ingress\n" "spec:\n" " rules:\n" " - host: webapp.example.com\n" " http:\n" " paths:\n" " - path: /\n" " pathType: Prefix\n" " backend:\n" " service:\n" " name: webapp-svc\n" " port:\n" " number: 80\n" ), }, ], "error": { "phase": "pipeline_deploy", "message": ( "Run: Deploy to Production\n" "\n" "Step: Build and push ✗\n" "Error: denied: permission_denied: write_package\n" "GITHUB_TOKEN does not have packages:write permission\n" "\n" "---\n" "Additional issues:\n" "- K8s Deployment image is hardcoded as 'ghcr.io/OWNER/REPO:TAG' — " "should reference the actual built image\n" "- Ingress has no ingressClassName — won't be picked up by nginx controller" ), }, "expected_fixes": [ { "file": ".github/workflows/deploy.yml", "type": "contains", "expected": "packages: write", "hint": "Add permissions block with 'packages: write' to allow GHCR push", }, { "file": "k8s/deployment.yaml", "type": "not_contains", "expected": "OWNER/REPO:TAG", "hint": "Replace hardcoded 'OWNER/REPO:TAG' placeholder with actual image reference", }, { "file": "k8s/ingress.yaml", "type": "contains", "expected": "ingressClassName: nginx", "hint": "Add ingressClassName: nginx to the Ingress spec", }, ], }, # Scenario 5: Workflow secrets not wired + Dockerfile wrong output dir + K8s probe port wrong { "id": "full_pipeline_secrets_build_probe", "files": [ { "path": ".github/workflows/build.yml", "type": "workflow", "content": ( "name: Build and Push\n" "on:\n" " push:\n" " branches: [main]\n" "\n" "jobs:\n" " build:\n" " runs-on: ubuntu-latest\n" " steps:\n" " - uses: actions/checkout@v4\n" "\n" " - name: Login to DockerHub\n" " run: echo $DOCKER_PASSWORD | docker login -u $DOCKER_USERNAME --password-stdin\n" "\n" " - name: Build\n" " run: docker build -t myuser/frontend:${{ github.sha }} .\n" "\n" " - name: Push\n" " run: docker push myuser/frontend:${{ github.sha }}\n" ), }, { "path": "Dockerfile", "type": "dockerfile", "content": ( "FROM node:20-alpine AS builder\n" "WORKDIR /app\n" "COPY package*.json ./\n" "RUN npm ci\n" "COPY . .\n" "RUN npm run build\n" "\n" "FROM nginx:alpine\n" "COPY --from=builder /app/dist /usr/share/nginx/html\n" "EXPOSE 80\n" 'CMD ["nginx", "-g", "daemon off;"]\n' ), }, { "path": "package.json", "type": "other", "content": '{"name": "frontend", "scripts": {"build": "react-scripts build", "start": "react-scripts start"}}', }, { "path": "k8s/deployment.yaml", "type": "kubernetes", "content": ( "apiVersion: apps/v1\n" "kind: Deployment\n" "metadata:\n" " name: frontend\n" "spec:\n" " replicas: 2\n" " selector:\n" " matchLabels:\n" " app: frontend\n" " template:\n" " metadata:\n" " labels:\n" " app: frontend\n" " spec:\n" " containers:\n" " - name: frontend\n" " image: myuser/frontend:latest\n" " ports:\n" " - containerPort: 80\n" " livenessProbe:\n" " httpGet:\n" " path: /healthz\n" " port: 3000\n" " initialDelaySeconds: 10\n" " periodSeconds: 5\n" ), }, ], "error": { "phase": "pipeline_deploy", "message": ( "Run: Build and Push\n" "\n" "Step: Login to DockerHub ✗\n" "Error: DOCKER_USERNAME and DOCKER_PASSWORD env vars are empty — " "secrets not wired via env block\n" "\n" "---\n" "Additional issues:\n" "- Dockerfile: COPY failed: stat app/dist: file does not exist " "(react-scripts outputs to 'build/' not 'dist/')\n" "- K8s: Liveness probe port 3000 doesn't match container port 80 " "(nginx listens on 80)" ), }, "expected_fixes": [ { "file": ".github/workflows/build.yml", "type": "contains", "expected": "DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}", "hint": "Docker login secrets need to be mapped via env block", }, { "file": ".github/workflows/build.yml", "type": "contains", "expected": "DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}", "hint": "Both DOCKER_USERNAME and DOCKER_PASSWORD must be in env block", }, { "file": "Dockerfile", "type": "contains", "expected": "COPY --from=builder /app/build", "hint": "react-scripts outputs to 'build/' not 'dist/'", }, { "file": "k8s/deployment.yaml", "type": "contains", "expected": "port: 80", "hint": "Liveness probe port should be 80 to match nginx container", }, ], }, ]