Files
Claude-Code-Monitor/deployments/ci/gitlab-ci/.gitlab-ci.yml
T
nntrivi2001 57dc91585d feat: Claude Code Monitor — lanes, pipelines and a merged workspace
Internal SmartGift build of a Claude Code monitoring dashboard.

Lanes: a durable unit of parallel agent work, one per working directory,
tracked across session restarts. Managed lanes are git worktrees the
dashboard provisions and can reset or remove behind a three-check destroy
guard and a counted preflight; adopted lanes are directories you already
own and are never destroyable.

Pipelines: a lane moves through pipeline stages. A stage the agent declares
with evidence renders green; a stage inferred from the tool-event stream
renders dashed amber and never counts as done. Detection is forward-only
within a 30-minute window, and never writes the declared stage.

Workspace: one page at /run with a lane grid, the selected lane's pipeline,
and a full Claude console behind a disclosure.
2026-07-30 14:39:03 +07:00

324 lines
11 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ─────────────────────────────────────────────────────────────────────────────
# GitLab CI/CD Pipeline Claude Code Agent Monitor
#
# Stages: test → build → deploy-staging → deploy-production
# ─────────────────────────────────────────────────────────────────────────────
# ── Global settings ─────────────────────────────────────────────────────────
default:
image: node:22-alpine
interruptible: true
retry:
max: 1
when:
- runner_system_failure
- stuck_or_timeout_failure
variables:
NODE_VERSION: "22"
REGISTRY: "${CI_REGISTRY}"
IMAGE_NAME: "${CI_REGISTRY_IMAGE}/agent-monitor"
MCP_IMAGE_NAME: "${CI_REGISTRY_IMAGE}/agent-monitor-mcp"
HELM_CHART_PATH: "deployments/helm/agent-monitor"
APP_NAME: "agent-monitor"
# Kaniko cache
KANIKO_CACHE_ARGS: "--cache=true --cache-repo=${CI_REGISTRY_IMAGE}/cache"
stages:
- test
- build
- deploy-staging
- deploy-production
- rollback
# ── Cache configuration ────────────────────────────────────────────────────
.node_cache: &node_cache
cache:
key:
files:
- package-lock.json
paths:
- node_modules/
policy: pull-push
# ── Test stage ──────────────────────────────────────────────────────────────
lint:
stage: test
<<: *node_cache
script:
- npm ci --prefer-offline
- npm run format:check
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
test:server:
stage: test
<<: *node_cache
script:
- npm ci --prefer-offline
- npm run test:server
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
artifacts:
when: on_failure
paths:
- server/__tests__/
expire_in: 7 days
test:client:
stage: test
<<: *node_cache
script:
- npm ci --prefer-offline
- cd client && npm ci --prefer-offline
- npm run test:client
cache:
key:
files:
- client/package-lock.json
paths:
- client/node_modules/
policy: pull-push
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
test:mcp:
stage: test
<<: *node_cache
script:
- npm run mcp:install
- npm run mcp:typecheck
- npm run mcp:test
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
# ── Build stage ─────────────────────────────────────────────────────────────
.kaniko_build: &kaniko_build
stage: build
image:
name: gcr.io/kaniko-project/executor:v1.22.0-debug
entrypoint: [""]
before_script:
- mkdir -p /kaniko/.docker
- echo "{\"auths\":{\"${CI_REGISTRY}\":{\"auth\":\"$(printf "%s:%s" "${CI_REGISTRY_USER}" "${CI_REGISTRY_PASSWORD}" | base64)\"}}}" > /kaniko/.docker/config.json
build:app:
<<: *kaniko_build
script:
- >-
/kaniko/executor
--context "${CI_PROJECT_DIR}"
--dockerfile "${CI_PROJECT_DIR}/Dockerfile"
--destination "${IMAGE_NAME}:${CI_COMMIT_SHORT_SHA}"
--destination "${IMAGE_NAME}:${CI_COMMIT_REF_SLUG}"
--destination "${IMAGE_NAME}:latest"
${KANIKO_CACHE_ARGS}
--label "org.opencontainers.image.revision=${CI_COMMIT_SHA}"
--label "org.opencontainers.image.created=$(date -u +%Y-%m-%dT%H:%M:%SZ)"
--label "org.opencontainers.image.source=${CI_PROJECT_URL}"
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/
build:mcp:
<<: *kaniko_build
script:
- >-
/kaniko/executor
--context "${CI_PROJECT_DIR}"
--dockerfile "${CI_PROJECT_DIR}/mcp/Dockerfile"
--destination "${MCP_IMAGE_NAME}:${CI_COMMIT_SHORT_SHA}"
--destination "${MCP_IMAGE_NAME}:${CI_COMMIT_REF_SLUG}"
--destination "${MCP_IMAGE_NAME}:latest"
${KANIKO_CACHE_ARGS}
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/
# Security scan
security:scan:
stage: build
needs: ["build:app", "build:mcp"]
image:
name: aquasec/trivy:latest
entrypoint: [""]
script:
- trivy image --exit-code 1 --severity HIGH,CRITICAL --format table "${IMAGE_NAME}:${CI_COMMIT_SHORT_SHA}"
- trivy image --exit-code 1 --severity HIGH,CRITICAL --format table "${MCP_IMAGE_NAME}:${CI_COMMIT_SHORT_SHA}"
- trivy image --exit-code 1 --severity CRITICAL --format json --output trivy-app-report.json "${IMAGE_NAME}:${CI_COMMIT_SHORT_SHA}"
- trivy image --exit-code 1 --severity CRITICAL --format json --output trivy-mcp-report.json "${MCP_IMAGE_NAME}:${CI_COMMIT_SHORT_SHA}"
artifacts:
paths:
- trivy-app-report.json
- trivy-mcp-report.json
expire_in: 30 days
allow_failure: false
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/
# ── Deploy Staging ──────────────────────────────────────────────────────────
deploy:staging:
stage: deploy-staging
image:
name: alpine/helm:3.14.0
entrypoint: [""]
needs:
- build:app
- build:mcp
environment:
name: staging
url: https://staging.agent-monitor.example.com
on_stop: stop:staging
before_script:
- apk add --no-cache curl aws-cli kubectl
- aws eks update-kubeconfig --region "${AWS_REGION:-us-west-2}" --name "${EKS_CLUSTER_STAGING:-agent-monitor-staging}"
script:
- |
helm upgrade --install ${APP_NAME} ${HELM_CHART_PATH} \
--namespace agent-monitor-staging \
--create-namespace \
--set image.repository=${IMAGE_NAME} \
--set image.tag=${CI_COMMIT_SHORT_SHA} \
--set mcp.image.repository=${MCP_IMAGE_NAME} \
--set mcp.image.tag=${CI_COMMIT_SHORT_SHA} \
--set environment=staging \
--values ${HELM_CHART_PATH}/values-staging.yaml \
--wait \
--atomic \
--timeout 600s
- |
echo "Running health check..."
kubectl wait --for=condition=ready pod \
-l app.kubernetes.io/name=${APP_NAME} \
-n agent-monitor-staging \
--timeout=300s
echo "✔ Staging deployment successful"
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/
stop:staging:
stage: deploy-staging
image:
name: alpine/helm:3.14.0
entrypoint: [""]
environment:
name: staging
action: stop
before_script:
- apk add --no-cache aws-cli kubectl
- aws eks update-kubeconfig --region "${AWS_REGION:-us-west-2}" --name "${EKS_CLUSTER_STAGING:-agent-monitor-staging}"
script:
- helm uninstall ${APP_NAME} -n agent-monitor-staging --wait || true
when: manual
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
# ── Deploy Production ───────────────────────────────────────────────────────
deploy:production:
stage: deploy-production
image:
name: alpine/helm:3.14.0
entrypoint: [""]
needs:
- deploy:staging
environment:
name: production
url: https://agent-monitor.example.com
before_script:
- apk add --no-cache curl aws-cli kubectl
- aws eks update-kubeconfig --region "${AWS_REGION:-us-west-2}" --name "${EKS_CLUSTER_PRODUCTION:-agent-monitor-production}"
script:
# Pre-deploy backup
- |
POD=$(kubectl get pods -n agent-monitor-production \
-l app.kubernetes.io/name=${APP_NAME} \
--field-selector=status.phase=Running \
-o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "")
if [ -n "$POD" ]; then
echo "Creating pre-deploy backup..."
kubectl exec "$POD" -n agent-monitor-production -- \
sh -c "cp /app/data/dashboard.db /tmp/pre-deploy-backup.db" 2>/dev/null || true
fi
# Deploy
- |
helm upgrade --install ${APP_NAME} ${HELM_CHART_PATH} \
--namespace agent-monitor-production \
--create-namespace \
--set image.repository=${IMAGE_NAME} \
--set image.tag=${CI_COMMIT_SHORT_SHA} \
--set mcp.image.repository=${MCP_IMAGE_NAME} \
--set mcp.image.tag=${CI_COMMIT_SHORT_SHA} \
--set environment=production \
--values ${HELM_CHART_PATH}/values-production.yaml \
--wait \
--atomic \
--timeout 600s
# Health check
- |
echo "Running production health check..."
kubectl wait --for=condition=ready pod \
-l app.kubernetes.io/name=${APP_NAME} \
-n agent-monitor-production \
--timeout=300s
echo "✔ Production deployment successful"
when: manual
allow_failure: false
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/
# ── Rollback ────────────────────────────────────────────────────────────────
rollback:staging:
stage: rollback
image:
name: alpine/helm:3.14.0
entrypoint: [""]
environment:
name: staging
before_script:
- apk add --no-cache aws-cli kubectl
- aws eks update-kubeconfig --region "${AWS_REGION:-us-west-2}" --name "${EKS_CLUSTER_STAGING:-agent-monitor-staging}"
script:
- echo "Rolling back staging..."
- helm rollback ${APP_NAME} ${ROLLBACK_REVISION:-0} -n agent-monitor-staging --wait --timeout 300s
- |
kubectl wait --for=condition=ready pod \
-l app.kubernetes.io/name=${APP_NAME} \
-n agent-monitor-staging \
--timeout=300s
- echo "✔ Staging rollback complete"
when: manual
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
rollback:production:
stage: rollback
image:
name: alpine/helm:3.14.0
entrypoint: [""]
environment:
name: production
before_script:
- apk add --no-cache aws-cli kubectl
- aws eks update-kubeconfig --region "${AWS_REGION:-us-west-2}" --name "${EKS_CLUSTER_PRODUCTION:-agent-monitor-production}"
script:
- echo "⚠ Rolling back PRODUCTION..."
- helm history ${APP_NAME} -n agent-monitor-production --max 5
- helm rollback ${APP_NAME} ${ROLLBACK_REVISION:-0} -n agent-monitor-production --wait --timeout 300s
- |
kubectl wait --for=condition=ready pod \
-l app.kubernetes.io/name=${APP_NAME} \
-n agent-monitor-production \
--timeout=300s
- echo "✔ Production rollback complete"
when: manual
allow_failure: false
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH