Files
mdm-ordinis/.gitlab-ci.yml
T
2026-05-10 21:56:35 +00:00

369 lines
15 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.
# =============================================================
# Ordinis — build & push images.
# Path-aware CI: backend и frontend jobs запускаются только когда меняются
# соответствующие пути. Trigger downstream передаёт BACKEND_IMAGE_TAG и/или
# FRONTEND_IMAGE_TAG; infra-pipeline применяет helm upgrade с per-service
# overrides — сервисы которые не пересобирались не катятся.
# =============================================================
stages:
- test
- build
- publish
- trigger-deploy
variables:
MAVEN_OPTS: "-Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn"
MAVEN_CLI_OPTS: "-B -ntp --batch-mode"
IMAGE_TAG: $CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA
REGISTRY: repo.nstart.cloud
IMAGE_REPO: terravault
DOCKER_BUILDKIT: "1"
cache:
key:
files: ["pom.xml"]
paths:
- .m2/repository
default:
retry:
max: 2
when:
- runner_system_failure
- stuck_or_timeout_failure
- script_failure
# Backend changes триггерят maven + docker для всех Java-сервисов + миграции.
.backend-changes: &backend-changes
- "ordinis-app/**/*"
- "ordinis-read-api/**/*"
- "ordinis-projection-writer/**/*"
- "ordinis-rest-api/**/*"
- "ordinis-domain/**/*"
- "ordinis-validation/**/*"
- "ordinis-events-api/**/*"
- "ordinis-outbox/**/*"
- "ordinis-auth/**/*"
- "ordinis-cuod-bundle/**/*"
- "ordinis-migrations/**/*"
- "pom.xml"
- ".gitlab-ci.yml"
# Frontend changes — только admin-ui.
.frontend-changes: &frontend-changes
- "ordinis-admin-ui/**/*"
# Documentation changes — Diplodoc-built integrator guide.
# docs-internal/ исключено явно: оно НЕ build'ится Diplodoc'ом и не должно
# триггерить docs job.
.docs-changes: &docs-changes
- "docs/**/*"
# ─── TEST ──────────────────────────────────────
maven-test:
stage: test
image: repo.nstart.cloud/library/maven:3.9.9-eclipse-temurin-21
script:
- mvn $MAVEN_CLI_OPTS verify
artifacts:
when: always
reports:
junit: '**/target/surefire-reports/TEST-*.xml'
expire_in: 1 week
rules:
- changes: *backend-changes
# Е2Е тесты через Testcontainers (Postgres+Kafka). Профиль `e2e` в
# ordinis-app/pom.xml включает их (default surefire excludes e2e директорию).
# Требует docker:dind service для testcontainers — обычный maven-test runner
# не имеет Docker.
maven-e2e:
stage: test
image: repo.nstart.cloud/library/maven:3.9.9-eclipse-temurin-21
services:
- name: repo.nstart.cloud/library/docker:29.1.2-dind
alias: docker
command: ["--tls=false"]
variables:
DOCKER_HOST: "tcp://docker:2375"
DOCKER_TLS_CERTDIR: ""
TESTCONTAINERS_HOST_OVERRIDE: "docker"
TESTCONTAINERS_RYUK_DISABLED: "true"
script:
- for i in $(seq 1 30); do docker -H tcp://docker:2375 info >/dev/null 2>&1 && break; sleep 1; done
- mvn $MAVEN_CLI_OPTS -pl ordinis-app -am -P e2e test
artifacts:
when: always
reports:
junit: '**/target/surefire-reports/TEST-*.xml'
expire_in: 1 week
rules:
- changes: *backend-changes
# Можно сделать allow_failure: true если e2e будут флакать — пока нет.
# ─── BENCH (manual JMH) ────────────────────────
# Manual trigger only — slow (~5-10 min) + noisy в CI history. Trigger через
# pipeline run UI с variable RUN_BENCH=true. Catches алгоритмическую регрессию
# на hot paths (parseRef, OnCloseAction, schema traversal).
maven-bench:
stage: test
image: repo.nstart.cloud/library/maven:3.9.9-eclipse-temurin-21
script:
- mvn $MAVEN_CLI_OPTS -P bench -pl ordinis-bench -am package -DskipTests
# Quick smoke: 1 fork, 2 warmups, 3 measurements × 2s. Total ~5 min.
# Если нужно надёжнее — bump до -f 2 -wi 5 -i 5 -r 5 (15 min).
- java -jar ordinis-bench/target/benchmarks.jar -f 1 -wi 2 -i 3 -w 1 -r 2
-rf json -rff bench-results.json | tee bench-results.txt
artifacts:
paths:
- bench-results.json
- bench-results.txt
expire_in: 5 days
rules:
- if: '$RUN_BENCH == "true"'
when: on_success
- if: '$CI_PIPELINE_SOURCE == "web"'
when: manual
allow_failure: true
# ─── BUILD (Maven jars) ────────────────────────
# maven-package всегда запускается: docker backend jobs нуждаются в jar даже
# при frontend-only PR (иначе backend образы не пересобрать → ImagePullBackOff
# в кластере при rolling update). BuildKit layer cache делает повторную сборку
# идентичных jar'ов ~30 sec.
maven-package:
stage: build
image: repo.nstart.cloud/library/maven:3.9.9-eclipse-temurin-21
script:
- mvn $MAVEN_CLI_OPTS package -DskipTests
artifacts:
paths:
- "**/target/*.jar"
- ordinis-migrations/src/main/resources/db/changelog/**
expire_in: 1 day
# ─── BUILD: Integrator Guide (sanity check) ────
# Sanity-check build для PRs и non-main branches. Полный image build
# идёт в publish stage (docker-docs). Здесь только verify что yfm
# не выдаёт errors на изменения docs/**.
build-docs:
stage: build
image: repo.nstart.cloud/library/node:20-alpine
before_script:
# npm install pnpm — corepack может hang на CI runner integrity checks.
- npm install -g --silent pnpm@9.15.4
script:
- cd docs
- pnpm install --frozen-lockfile
- pnpm build
- echo "Built $(find _dist -name '*.html' | wc -l) HTML pages, total size:"
- du -sh _dist
artifacts:
name: "ordinis-docs-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA"
paths:
- docs/_dist
expire_in: 1 week
rules:
- if: '$CI_COMMIT_BRANCH != "main"'
changes: *docs-changes
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
changes: *docs-changes
# ─── PUBLISH (Docker images) ───────────────────
# resource_group: serializes docker-* jobs across all concurrent pipelines на
# одном runner'е. 5 parallel docker builds (app/read-api/projection-writer/
# admin-ui/migrations) каждый поднимает свой DinD container с buildx — суммарно
# выжирали runner disk (~25Gi+) → "no space left on device" в /var/lib/docker
# на тяжёлых stages (см. pipeline 6066). Serialize'я добавляет ~10 min к
# pipeline'у, но вместо retry-roulette мы получаем стабильно зелёный CI.
# При scaling до multi-runner setup можно увеличить parallelism per-runner и
# переименовать group в per-runner identifier.
.docker_base:
stage: publish
image: repo.nstart.cloud/nstart/docker-cli-buildx:1.0.0
services:
- name: repo.nstart.cloud/library/docker:29.1.2-dind
alias: docker
command: ["--tls=false"]
resource_group: docker-builds
variables:
DOCKER_HOST: tcp://docker:2375
DOCKER_TLS_CERTDIR: ""
DOCKER_BUILDKIT: "1"
.publish_script: &publish_script |
for i in $(seq 1 30); do docker info >/dev/null 2>&1 && break; sleep 1; done
# Lighter cleanup: dangling layers only, build cache сохраняем чтобы BuildKit
# переиспользовал между serialized jobs. Раньше был `prune -af --volumes`
# после Nexus blob loss recovery (см. ниже) — сжирал inline cache, каждая
# сборка с нуля. Сейчас Nexus стабилен → cache сохраняем.
docker image prune -f >/dev/null 2>&1 || true
echo "$REGISTRY_PASSWORD" | docker login "$REGISTRY" -u "$REGISTRY_USER" --password-stdin
IMG="$REGISTRY/$IMAGE_REPO/$SVC"
# Cache strategy:
# - --cache-from $IMG:latest — BuildKit читает inline cache из previously
# pushed manifest'а. На повторных сборках cached слои переиспользуются,
# полная сборка падает с ~70-90s до 15-25s.
# - --build-arg BUILDKIT_INLINE_CACHE=1 — записывает cache metadata в
# pushed image manifest для следующего pipeline'а.
# - --pull — Nexus docker-proxy лениво re-fetch'ит base image с Docker Hub.
#
# Раньше был --no-cache как temporary recovery после Nexus blob loss
# incident (pipeline 6066). Nexus blob storage стабилизирован, --no-cache
# убран → -2 мин/service × 5 services = -10 мин на pipeline.
# Если Nexus снова потеряет blob refs — симптом: "blob unknown to registry"
# на manifest push. Recovery: вернуть --no-cache temporarily, сделать
# `nexus-cli script run cleanup-orphan-blobs` (или ручной touch через UI),
# потом убрать --no-cache обратно.
#
# Git info passed как build args чтобы Spring Boot build-info goal получил
# реальные значения (внутри Docker context .git/ исключён через .dockerignore,
# git-commit-id-maven-plugin не может прочесть). Backend Dockerfile'ы
# используют эти ARG'и в `mvn -Dgit.commit.id.abbrev=... -Dgit.branch=...`.
# Frontend / docs / migrations Dockerfile'ы их игнорируют (no-op в context'е).
docker pull "$IMG:latest" >/dev/null 2>&1 || true
docker build --pull -f "$DOCKERFILE" \
-t "$IMG:$IMAGE_TAG" -t "$IMG:latest" -t "$IMG:$CI_COMMIT_REF_SLUG" \
--cache-from "$IMG:latest" \
--build-arg BUILDKIT_INLINE_CACHE=1 \
--build-arg GIT_COMMIT="${CI_COMMIT_SHORT_SHA:-unknown}" \
--build-arg GIT_BRANCH="${CI_COMMIT_REF_NAME:-unknown}" \
--build-arg GIT_TAG="${CI_COMMIT_TAG:-none}" \
--build-arg GIT_TIME="${CI_COMMIT_TIMESTAMP:-unknown}" \
"$BUILD_CONTEXT"
docker push "$IMG:$IMAGE_TAG"
docker push "$IMG:latest"
docker push "$IMG:$CI_COMMIT_REF_SLUG"
docker-app:
extends: .docker_base
needs: [maven-package]
variables:
SVC: ordinis-app
DOCKERFILE: ordinis-app/Dockerfile
BUILD_CONTEXT: .
script:
- *publish_script
docker-read-api:
extends: .docker_base
needs: [maven-package]
variables:
SVC: ordinis-read-api
DOCKERFILE: ordinis-read-api/Dockerfile
BUILD_CONTEXT: .
script:
- *publish_script
docker-projection-writer:
extends: .docker_base
needs: [maven-package]
variables:
SVC: ordinis-projection-writer
DOCKERFILE: ordinis-projection-writer/Dockerfile
BUILD_CONTEXT: .
script:
- *publish_script
docker-migrations:
extends: .docker_base
needs: [maven-package]
variables:
SVC: ordinis-migrations
DOCKERFILE: ordinis-migrations/Dockerfile
BUILD_CONTEXT: ordinis-migrations/
script:
- *publish_script
docker-admin-ui:
extends: .docker_base
needs: []
variables:
SVC: ordinis-admin-ui
DOCKERFILE: ordinis-admin-ui/Dockerfile
BUILD_CONTEXT: ordinis-admin-ui/
script:
- *publish_script
# Раньше был path-filter, но это создавало дыру: если backend менялся, а
# frontend нет — резолвили общий тег и helm пытался катить admin-ui:<commit>
# которого не существовало. Layer-cache делает повторную сборку ~30 sec.
# Integrator Guide (Diplodoc) — multi-stage Dockerfile (yfm build → nginx serving).
# Path-filtered: rebuild только при изменении docs/. Если docs не менялся,
# helm берёт previous tag через --reset-then-reuse-values.
docker-docs:
extends: .docker_base
needs: []
variables:
SVC: ordinis-docs
DOCKERFILE: docs/Dockerfile
BUILD_CONTEXT: docs/
script:
- *publish_script
rules:
# На main + tag собираем БЕЗ path-filter. Иначе resolve-tag emit'ит
# docs=main-<sha>, helm пытается pull этого тега, image отсутствует
# (docker-docs не запускался) → atomic rollback с context deadline
# exceeded. Layer cache при отсутствии docs/* changes делает rebuild
# ~30 sec — приемлемая цена за гарантию что image для tag существует.
- if: '$CI_COMMIT_BRANCH == "main"'
- if: '$CI_COMMIT_TAG =~ /^v[0-9]+\.[0-9]+\.[0-9]+$/'
- if: '$CI_PIPELINE_SOURCE == "web"'
when: manual
# ─── TRIGGER DEPLOY ───────────────────────────
# Single resolve-tag (always runs) → один UPSTREAM_IMAGE_TAG. Path-filter
# работает на уровне docker-* jobs (backend skip если бэк не менялся,
# layers cache hit делает rebuild ~30 sec). Полное per-service splitting
# через dual dotenv не работает с GitLab trigger var interpolation.
resolve-tag:
stage: trigger-deploy
image: repo.nstart.cloud/library/alpine:3.20
script:
- echo "RESOLVED_IMAGE_TAG=$IMAGE_TAG" > resolved.env
# DOCS_IMAGE_TAG может быть пустым если docker-docs не пересобирался
# (path-filter no-match). Helm в infra оставит старый tag через
# --reset-then-reuse-values если переменная пустая.
- echo "RESOLVED_DOCS_TAG=$IMAGE_TAG" >> resolved.env
- cat resolved.env
artifacts:
reports:
dotenv: resolved.env
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
# На tag-пайплайне $CI_COMMIT_BRANCH пустой — но trigger-deploy-prod
# (тоже tag-only) needs: resolve-tag. Без этого правила prod deploy
# не запускается с ошибкой "resolve-tag does not exist in the pipeline".
- if: '$CI_COMMIT_TAG =~ /^v[0-9]+\.[0-9]+\.[0-9]+$/'
trigger-deploy-staging:
stage: trigger-deploy
needs: [resolve-tag]
trigger:
project: 2-6/2-6-4/terravault/ordinis-infra
branch: main
strategy: depend
variables:
UPSTREAM_IMAGE_TAG: $RESOLVED_IMAGE_TAG
DOCS_IMAGE_TAG: $RESOLVED_DOCS_TAG
DEPLOY_ENV: staging
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
trigger-deploy-prod:
stage: trigger-deploy
needs: [resolve-tag]
trigger:
project: 2-6/2-6-4/terravault/ordinis-infra
branch: main
strategy: depend
variables:
UPSTREAM_IMAGE_TAG: $RESOLVED_IMAGE_TAG
DOCS_IMAGE_TAG: $RESOLVED_DOCS_TAG
DEPLOY_ENV: prod
rules:
- if: '$CI_COMMIT_TAG =~ /^v[0-9]+\.[0-9]+\.[0-9]+$/'
when: manual