NutriFlow / backend

gitlab.nutriflow.app / nutriflow / backend · Pipeline #847

CI/CD generado por CULTIVA IA
Pipeline #847
passed · 8 min 42 s
Commit: 3f8a2bc"feat: añadir módulo de recetas personalizadas"
Rama: main
Autor: Carlos Méndez
Iniciado: 2026-06-15 · 09:14 UTC
Runner: nutriflow-runner-01 (GKE)
▶ build
▶ test
▶ security
▶ infra
▶ deploy
▶ notify

Build

2 jobs · 2 min 18 s
🚧
build:app
node:20-alpine
npm ci npm run build artifacts: dist/
1 min 04 s
🐗
build:docker
docker:24 + docker:24-dind
multi-stage Dockerfile CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
1 min 14 s

Test

2 jobs · 1 min 53 s
🧪
test:unit
node:20-alpine
jest --coverage cobertura: 87.4% cobertura XML → reports
1 min 12 s
🔎
test:lint
node:20-alpine
eslint + prettier
41 s

Security

3 jobs · 1 min 07 s
🛡
security:trivy-image
aquasec/trivy:0.58.0
HIGH, CRITICAL exit-code 1 0 vulnerabilidades críticas
28 s
📊
security:sast
template: Security/SAST.gitlab-ci.yml
SAST GitLab nativo allow_failure: true
22 s
📥
security:dependency-scan
template: Security/Dependency-Scanning.gitlab-ci.yml
npm audit 1 aviso low
17 s

Infra (Terraform)

3 jobs · 1 min 34 s
infra:validate
hashicorp/terraform:1.6.0
terraform init -backend=false terraform validate fmt -check
18 s
📄
infra:plan
hashicorp/terraform:1.6.0
terraform plan -out=tfplan artifacts: tfplan (1 day)
52 s
infra:apply
hashicorp/terraform:1.6.0
when: manual only: main terraform apply tfplan
24 s

Deploy

2 jobs · 1 min 40 s
🎉
deploy:staging
bitnami/kubectl:1.31
only: develop ns: nutriflow-stg rollout status ✓
48 s
🌟
deploy:production
bitnami/kubectl:1.31
when: manual only: main ns: nutriflow-prod
52 s

Notify

1 job · 4 s
🔔
notify:slack
alpine/curl:latest
on: success + failure $SLACK_WEBHOOK (masked) #deploys-nutriflow
4 s
.gitlab-ci.yml — NutriFlow Backend 📋 Copiar
# .gitlab-ci.yml · NutriFlow SaaS — generado por CULTIVA IA

stages:
  - build
  - test
  - security
  - infra
  - deploy
  - notify

# ── Variables globales ─────────────────────────────────────────
variables:
  DOCKER_DRIVER: overlay2
  DOCKER_TLS_CERTDIR: "/certs"
  TF_ROOT: $CI_PROJECT_DIR/terraform
  TF_VERSION: "1.6.0"
  KUBE_NAMESPACE_STG: nutriflow-stg
  KUBE_NAMESPACE_PROD: nutriflow-prod

# ── Template base Kubernetes ────────────────────────────────────
.kube_template: &kube_template
  image: bitnami/kubectl:1.31
  before_script:
    - kubectl config set-cluster k8s --server="$KUBE_URL"
        --insecure-skip-tls-verify=true
    - kubectl config set-credentials admin --token="$KUBE_TOKEN"
    - kubectl config set-context default --cluster=k8s --user=admin
    - kubectl config use-context default

# ── STAGE: BUILD ───────────────────────────────────────────────
build:app:
  stage: build
  image: node:20-alpine
  script:
    - npm ci
    - npm run build
  artifacts:
    paths: [dist/]
    expire_in: 1 hour
  cache:
    key: $CI_COMMIT_REF_SLUG
    paths: [node_modules/]
    policy: pull-push

build:docker:
  stage: build
  image: docker:24
  services: [docker:24-dind]
  before_script:
    - docker login -u $CI_REGISTRY_USER
        -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    - docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
        $CI_REGISTRY_IMAGE:latest
    - docker push $CI_REGISTRY_IMAGE:latest
  only: [main, develop]

# ── STAGE: TEST ────────────────────────────────────────────────
test:unit:
  stage: test
  image: node:20-alpine
  script:
    - npm ci
    - npm test -- --coverage
  coverage: '/Lines\s*:\s*(\d+\.\d+)%/'
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml

test:lint:
  stage: test
  image: node:20-alpine
  script:
    - npm ci
    - npm run lint
    - npm run format:check

# ── STAGE: SECURITY ────────────────────────────────────────────
include:
  - template: Security/SAST.gitlab-ci.yml
  - template: Security/Dependency-Scanning.gitlab-ci.yml

security:trivy-image:
  stage: security
  image: aquasec/trivy:0.58.0
  script:
    - trivy image --exit-code 1
        --severity HIGH,CRITICAL
        $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  allow_failure: false

# ── STAGE: INFRA (Terraform) ────────────────────────────────────
infra:validate:
  stage: infra
  image: hashicorp/terraform:$TF_VERSION
  script:
    - cd $TF_ROOT
    - terraform init -backend=false
    - terraform validate
    - terraform fmt -check

infra:plan:
  stage: infra
  image: hashicorp/terraform:$TF_VERSION
  script:
    - cd $TF_ROOT
    - terraform init
    - terraform plan -out=tfplan
  artifacts:
    paths: ["$TF_ROOT/tfplan"]
    expire_in: 1 day

infra:apply:
  stage: infra
  image: hashicorp/terraform:$TF_VERSION
  script:
    - cd $TF_ROOT
    - terraform init
    - terraform apply -auto-approve tfplan
  dependencies: [infra:plan]
  when: manual
  only: [main]

# ── STAGE: DEPLOY ──────────────────────────────────────────────
deploy:staging:
  <<: *kube_template
  stage: deploy
  script:
    - kubectl apply -f k8s/ -n $KUBE_NAMESPACE_STG
    - kubectl rollout status deployment/nutriflow-backend
        -n $KUBE_NAMESPACE_STG --timeout=120s
  environment:
    name: staging
    url: https://staging.nutriflow.app
  only: [develop]

deploy:production:
  <<: *kube_template
  stage: deploy
  script:
    - kubectl apply -f k8s/ -n $KUBE_NAMESPACE_PROD
    - kubectl rollout status deployment/nutriflow-backend
        -n $KUBE_NAMESPACE_PROD --timeout=180s
  environment:
    name: production
    url: https://app.nutriflow.app
  when: manual
  only: [main]

# ── STAGE: NOTIFY ──────────────────────────────────────────────
notify:slack:
  stage: notify
  image: alpine/curl:latest
  script:
    - |
      STATUS="✅ Pipeline #$CI_PIPELINE_ID passed"
      curl -X POST -H 'Content-type: application/json'
        --data "{\"text\":\"$STATUS · $CI_COMMIT_TITLE\"}"
        $SLACK_WEBHOOK
  when: always
  only: [main, develop]