# ============================================================================= # DiDi Lot 2 (Backend) — Pipeline CI/CD GitLab # ============================================================================= # Etape: verify (typecheck) → test (unitare) → build (imagini Docker, versionate) # → publish (registry) → deploy (cu HEALTH-GATE) → rollback (manual) # # Variabile așteptate în GitLab (Settings → CI/CD → Variables): # CI_REGISTRY / CI_REGISTRY_USER / CI_REGISTRY_PASSWORD — registry-ul de imagini # DEPLOY_HOST — host-ul de deployment (SSH) # DEPLOY_USER — utilizatorul de deployment # Versionare imagini: $CI_COMMIT_SHORT_SHA (+ tag "latest" doar pe branch-ul implicit). # Rollback: job manual care re-etichetează imaginea anterioară (previous) ca latest # și redeployează — mecanismul de revenire cerut de documentație. # ============================================================================= stages: - verify - test - build - publish - deploy - rollback default: interruptible: true variables: DOCKER_TLS_CERTDIR: "/certs" AGENT_DIR: services/orchestration-layer/agent-v3 FRAMEWORK_DIR: services/orchestration-layer/didiFramework DASHBOARD_DIR: admin-dashboard # ---------- șabloane reutilizabile ---------- .node: image: node:20-alpine before_script: - node --version && npm --version .docker: image: docker:27 services: [docker:27-dind] before_script: - echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin "$CI_REGISTRY" # ============================================================================= # VERIFY — compilare TypeScript (fără emitere) pe toate serviciile # ============================================================================= verify:agent-v3: stage: verify extends: .node script: - cd $AGENT_DIR && npm ci --prefer-offline - npx tsc --noEmit rules: [{ changes: ["services/orchestration-layer/agent-v3/**/*"] }, { when: always }] verify:framework: stage: verify extends: .node script: - cd $FRAMEWORK_DIR && npm ci --prefer-offline - npx tsc --noEmit verify:dashboard: stage: verify extends: .node script: - cd $DASHBOARD_DIR && npm ci --prefer-offline - npm run type-check # ============================================================================= # TEST — teste unitare (vitest pe agent-v3; raport JUnit pentru GitLab) # ============================================================================= test:agent-v3: stage: test extends: .node script: - cd $AGENT_DIR && npm ci --prefer-offline - npx vitest run --reporter=default --reporter=junit --outputFile=vitest-junit.xml artifacts: when: always reports: junit: $AGENT_DIR/vitest-junit.xml expire_in: 30 days # validarea specificațiilor OpenAPI livrate (criteriul 6 — OpenAPI/Swagger) test:openapi: stage: test image: python:3.12-alpine script: - pip install --quiet openapi-spec-validator pyyaml - python -c "from openapi_spec_validator import validate; import yaml; validate(yaml.safe_load(open('$AGENT_DIR/openapi.yaml')))" - python -c "from openapi_spec_validator import validate; import yaml; validate(yaml.safe_load(open('$FRAMEWORK_DIR/openapi.yaml')))" # ============================================================================= # BUILD — imagini Docker versionate cu SHA-ul commit-ului # ============================================================================= build:agent-v3: stage: build extends: .docker script: - docker build -t "$CI_REGISTRY_IMAGE/agent-v3:$CI_COMMIT_SHORT_SHA" $AGENT_DIR - docker push "$CI_REGISTRY_IMAGE/agent-v3:$CI_COMMIT_SHORT_SHA" build:framework: stage: build extends: .docker script: - docker build -t "$CI_REGISTRY_IMAGE/didi-framework:$CI_COMMIT_SHORT_SHA" $FRAMEWORK_DIR - docker push "$CI_REGISTRY_IMAGE/didi-framework:$CI_COMMIT_SHORT_SHA" build:dashboard: stage: build extends: .docker script: - docker build -t "$CI_REGISTRY_IMAGE/didi-admin:$CI_COMMIT_SHORT_SHA" $DASHBOARD_DIR - docker push "$CI_REGISTRY_IMAGE/didi-admin:$CI_COMMIT_SHORT_SHA" # ============================================================================= # PUBLISH — pe branch-ul implicit, promovăm SHA-ul la :latest și păstrăm # :previous (ținta de rollback) # ============================================================================= publish:latest: stage: publish extends: .docker rules: [{ if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' }] script: - | for svc in agent-v3 didi-framework didi-admin; do # păstrează versiunea curentă ca :previous (rollback target) docker pull "$CI_REGISTRY_IMAGE/$svc:latest" || true docker tag "$CI_REGISTRY_IMAGE/$svc:latest" "$CI_REGISTRY_IMAGE/$svc:previous" || true docker push "$CI_REGISTRY_IMAGE/$svc:previous" || true # promovează build-ul curent docker pull "$CI_REGISTRY_IMAGE/$svc:$CI_COMMIT_SHORT_SHA" docker tag "$CI_REGISTRY_IMAGE/$svc:$CI_COMMIT_SHORT_SHA" "$CI_REGISTRY_IMAGE/$svc:latest" docker push "$CI_REGISTRY_IMAGE/$svc:latest" done # ============================================================================= # DEPLOY — cu HEALTH-GATE: deploy-ul e considerat reușit DOAR dacă # /api/v3/health/all răspunde healthy/degraded după repornire # ============================================================================= deploy:production: stage: deploy image: alpine:3.20 rules: [{ if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH', when: manual }] environment: { name: production } before_script: [ "apk add --no-cache openssh-client curl" ] script: - ssh -o StrictHostKeyChecking=accept-new "$DEPLOY_USER@$DEPLOY_HOST" " cd ~/didi/backend/services/orchestration-layer/agent-v3 && docker compose pull && docker compose up -d && cd ../didiFramework && docker compose pull && docker compose up -d" # HEALTH-GATE: max 120s până când platforma raportează sănătate - | for i in $(seq 1 24); do code=$(ssh "$DEPLOY_USER@$DEPLOY_HOST" "curl -sk -o /dev/null -w '%{http_code}' http://localhost:24803/api/v3/health/all" || echo 000) echo "health-gate încercarea $i: HTTP $code" [ "$code" = "200" ] && exit 0 sleep 5 done echo "HEALTH-GATE EȘUAT — deploy-ul NU este sănătos"; exit 1 # ============================================================================= # ROLLBACK — manual: repune imaginile :previous și redeployează # ============================================================================= rollback:production: stage: rollback extends: .docker rules: [{ if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH', when: manual }] environment: { name: production } script: - | for svc in agent-v3 didi-framework didi-admin; do docker pull "$CI_REGISTRY_IMAGE/$svc:previous" docker tag "$CI_REGISTRY_IMAGE/$svc:previous" "$CI_REGISTRY_IMAGE/$svc:latest" docker push "$CI_REGISTRY_IMAGE/$svc:latest" done - echo "Imaginile anterioare repromovate la :latest — rulați deploy:production pentru a le aplica."