# W4 (Pinbox24) v42-prod image-build + deploy pipeline — PROPOSED `.gitlab-ci.yml`
# for GitLab repo `pinbox24/p24-back-ts` (default branch `development`).
#
# Faza 3C of docs/plans/w3-w4-cicd-full-deployment.md (issue radieu/p24-infra#3768),
# unblocked by #3542 (docker-compose-w4.yml committed to git). This is the
# GIT-TRACKED SOURCE OF TRUTH for the v42-prod build/deploy pipeline — the same
# pattern docker-compose-w4.yml uses: it mirrors the intended pipeline here in
# p24-infra first, then a human applies it to the GitLab repo (p24-infra has
# push/MR rights but NO merge rights on these app repos —
# docs/pinbox24/gitlab-repo-locations.md).
#
# BEFORE APPLYING: diff this against the repo's live `.gitlab-ci.yml`. Job names,
# stage names, the runner tag, and the ECR repo path must match reality. Apply via
# the single-file PUT endpoint (see docs/playbooks/w4-gitlab-ci-image-pipeline.md),
# NOT the multi-action Commits API (which times out on this runner).
#
# --- Design decisions honored (issue #3768 scope, 2026-07-11) ---
# Gap 8: persistent patches (postbookReport.helper.js, uploadAwsS3.helper.js,
#        gus-api-regon-wsdl/, ecosystem.config.js) are NOT baked into the image —
#        they stay volume-mounted at runtime via docker-compose-w4.yml. The build
#        stage produces the plain app image only.
# Gap 5: redis-v42 depends_on + healthcheck already live in docker-compose-w4.yml;
#        the deploy stage uses `docker compose up -d` so compose enforces
#        `condition: service_healthy` startup ordering (no ordering logic here).
# D6:    WebSocket-safe restarts run in a NIGHTLY-ONLY window (20:00–06:00 UTC).
#        `deploy:v42-prod` is `when: manual` (operator runs it inside the window);
#        `deploy:v42-prod:nightly` runs automatically for a CI schedule set to that
#        window. See the runbook for the WebSocket-drain rationale.
#
# --- Secrets ---
# NEVER hardcode credential values. AWS ECR auth uses GitLab CI/CD variables
# (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, protected+masked). Runtime app env is
# read from the SOPS-maintained env files the autoheal step (#3611) keeps populated
# in the builder dir — never sourced (special chars break `source`; use grep|cut).

stages:
  - build
  - deploy

variables:
  AWS_REGION: "eu-central-1"
  ECR_REGISTRY: "563740926945.dkr.ecr.eu-central-1.amazonaws.com"
  IMAGE_REPO: "v42-prod"
  # Builder checkout dir on the bms-1 shell runner (matches docker-compose-w4.yml).
  BUILDER_DIR: "/root/builds/7N4sbbrB/0/pinbox24/p24-back-ts"
  COMPOSE_FILE: "/root/builds/7N4sbbrB/0/pinbox24/p24-back-ts/docker-compose-w4.yml"

# All jobs run on the bms-1 shell runner (register tag `v42-bms1-autodeploy` on the
# runner — same pattern as `mailgun-bms1-autodeploy` / `s3-v2-bms1-autodeploy`, Faza 1).
default:
  tags:
    - v42-bms1-autodeploy

# --------------------------------------------------------------------------------
# BUILD — produce the v42-prod app image and push to ECR.
# Patches are NOT copied in (Gap 8); Dockerfile builds the plain TS app only.
# --------------------------------------------------------------------------------
build:v42-prod:
  stage: build
  rules:
    - if: '$CI_COMMIT_BRANCH == "development"'
    - if: '$CI_PIPELINE_SOURCE == "web"'          # allow manual pipeline trigger
  script:
    - echo "Building v42-prod image (patches stay volume-mounted — Gap 8)"
    - aws ecr get-login-password --region "$AWS_REGION"
        | docker login --username AWS --password-stdin "$ECR_REGISTRY"
    - export IMAGE_TAG="merged-$(date -u +%Y%m%d-%H%M)-${CI_COMMIT_SHORT_SHA}"
    - docker build -t "${ECR_REGISTRY}/${IMAGE_REPO}:${IMAGE_TAG}" -f Dockerfile .
    # Roll the tags compose consumes. Retag current :latest as :old-<date> first so a
    # rollback target always exists (v42-prod has no auto old- tag — see runbook).
    - docker pull "${ECR_REGISTRY}/${IMAGE_REPO}:latest" || true
    - docker tag "${ECR_REGISTRY}/${IMAGE_REPO}:latest"
        "${ECR_REGISTRY}/${IMAGE_REPO}:old-$(date -u +%Y%m%d-%H%M)" || true
    - docker push "${ECR_REGISTRY}/${IMAGE_REPO}:old-$(date -u +%Y%m%d-%H%M)" || true
    - docker tag "${ECR_REGISTRY}/${IMAGE_REPO}:${IMAGE_TAG}"
        "${ECR_REGISTRY}/${IMAGE_REPO}:latest"
    - docker push "${ECR_REGISTRY}/${IMAGE_REPO}:${IMAGE_TAG}"
    - docker push "${ECR_REGISTRY}/${IMAGE_REPO}:latest"
    - echo "Pushed ${IMAGE_REPO}:${IMAGE_TAG} (and :latest)"

# --------------------------------------------------------------------------------
# DEPLOY (manual) — operator runs this INSIDE the 20:00–06:00 UTC window (D6).
# compose enforces redis-v42 healthy-before-start ordering (Gap 5).
# --------------------------------------------------------------------------------
.deploy_script: &deploy_script
  - echo "Deploying v42-prod via compose (redis-v42 ordering enforced by compose — Gap 5)"
  - aws ecr get-login-password --region "$AWS_REGION"
      | docker login --username AWS --password-stdin "$ECR_REGISTRY"
  - docker compose -f "$COMPOSE_FILE" pull v42-prod
  # `up -d` (not `restart`) so the host env_file is re-read and depends_on:service_healthy
  # is honored. Persistent-patch bind-mounts (Gap 8) come from the compose file, unchanged.
  - docker compose -f "$COMPOSE_FILE" up -d v42-prod
  - sleep 15
  - docker ps --filter name=v42-prod --format '{{.Status}} {{.Image}}'
  # Health gate — fail the deploy if the app is not serving.
  - curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3000/health | grep -q '^200$'
  # Post-deploy env resync (#3874). GitLab's checkout/`git clean` wipes the SSH-delivered
  # env files (backend-environment.env / s3-environment.env / redis-*-environment.env) that
  # hold the ENTIRE runtime config — so the next container (re)start would boot with zero env
  # vars (root cause of the 2026-07-09 W3/W4 cascading outage). Re-trigger secrets-sync.yml so
  # those files are repopulated immediately after every deploy. Async dispatch (does not wait);
  # runs last so it never affects the health-gated deploy result. GH_TOKEN is a masked CI/CD
  # variable already provisioned on the bms-1 runner (Faza 1) — read from env by `gh`, never inlined.
  - gh workflow run secrets-sync.yml --repo radieu/p24-infra -f target=bms1-all

deploy:v42-prod:
  stage: deploy
  when: manual
  rules:
    - if: '$CI_COMMIT_BRANCH == "development"'
  script: *deploy_script

# Schedule-gated variant — attach a CI schedule set to the 20:00–06:00 UTC window
# for hands-off nightly rollout (D6). Only runs on schedule triggers.
deploy:v42-prod:nightly:
  stage: deploy
  rules:
    - if: '$CI_PIPELINE_SOURCE == "schedule"'
  script: *deploy_script
