diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fe2f9ca..9a4c8fe 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -118,16 +118,10 @@ maven-package: - ordinis-migrations/src/main/resources/db/changelog/** expire_in: 1 day -# ─── BUILD: Integrator Guide (Diplodoc) ──────── -# Build static HTML documentation для consumers через @diplodoc/cli (yfm). -# Source — docs/ (Markdown + toc.yaml). Output — docs/_dist/ HTML. -# -# Triggers: -# - auto при изменении docs/** -# - manual через web UI (для force-rebuild без code changes) -# -# Artifact `docs/_dist/` сохраняется неделю — pages-deploy job (TBD, -# когда host решён — GitLab Pages либо k8s ingress) подхватит из него. +# ─── 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 @@ -146,9 +140,10 @@ build-docs: - docs/_dist expire_in: 1 week rules: - - changes: *docs-changes - - if: '$CI_PIPELINE_SOURCE == "web"' - when: manual + - if: '$CI_COMMIT_BRANCH != "main"' + changes: *docs-changes + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' + changes: *docs-changes # ─── PUBLISH (Docker images) ─────────────────── .docker_base: @@ -228,6 +223,24 @@ docker-admin-ui: # frontend нет — резолвили общий тег и helm пытался катить admin-ui: # которого не существовало. 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: + - if: '$CI_COMMIT_BRANCH == "main"' + changes: *docs-changes + - if: '$CI_PIPELINE_SOURCE == "web"' + when: manual + # ─── TRIGGER DEPLOY ─────────────────────────── # Single resolve-tag (always runs) → один UPSTREAM_IMAGE_TAG. Path-filter # работает на уровне docker-* jobs (backend skip если бэк не менялся, @@ -239,6 +252,10 @@ resolve-tag: 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: @@ -255,6 +272,7 @@ trigger-deploy-staging: strategy: depend variables: UPSTREAM_IMAGE_TAG: $RESOLVED_IMAGE_TAG + DOCS_IMAGE_TAG: $RESOLVED_DOCS_TAG DEPLOY_ENV: staging rules: - if: '$CI_COMMIT_BRANCH == "main"' @@ -268,6 +286,7 @@ trigger-deploy-prod: 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]+$/' diff --git a/docs/Dockerfile b/docs/Dockerfile new file mode 100644 index 0000000..08214dd --- /dev/null +++ b/docs/Dockerfile @@ -0,0 +1,33 @@ +# Multi-stage build для Ordinis Integrator Guide. +# Stage 1: build HTML через @diplodoc/cli (yfm). +# Stage 2: nginx serving static files на /docs/ path. + +ARG NODE_IMAGE=repo.nstart.cloud/library/node:20-alpine +ARG NGINX_IMAGE=repo.nstart.cloud/library/nginx:1.27-alpine + +# ─── Stage 1: Build HTML ───────────────────── +FROM ${NODE_IMAGE} AS builder +WORKDIR /docs + +# Layered cache: package.json + lockfile меняются реже чем content. +COPY package.json pnpm-lock.yaml ./ +RUN corepack enable \ + && corepack prepare pnpm@9.0.0 --activate \ + && pnpm install --frozen-lockfile + +# Copy остальное (toc, .yfm, markdown). +COPY .yfm toc.yaml index.md ./ +COPY integration ./integration + +RUN pnpm build + +# ─── Stage 2: nginx ────────────────────────── +FROM ${NGINX_IMAGE} +COPY --from=builder /docs/_dist /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf + +# Healthcheck для k8s readiness/liveness. +HEALTHCHECK --interval=10s --timeout=3s --start-period=5s --retries=3 \ + CMD wget -q -O - http://localhost:8080/healthz || exit 1 + +EXPOSE 8080 diff --git a/docs/nginx.conf b/docs/nginx.conf new file mode 100644 index 0000000..284f537 --- /dev/null +++ b/docs/nginx.conf @@ -0,0 +1,46 @@ +# Ordinis Integrator Guide — nginx serving static HTML на /docs/. +# Diplodoc генерирует relative links — alias корректно работает без rewrite. + +server { + listen 8080 default_server; + listen [::]:8080 default_server; + server_name _; + + access_log /dev/stdout; + error_log /dev/stderr; + + # K8s probe. + location = /healthz { + access_log off; + return 200 "ok\n"; + add_header Content-Type text/plain; + } + + # Documentation root. + location /docs/ { + alias /usr/share/nginx/html/; + index index.html; + try_files $uri $uri/ $uri.html =404; + + # HTML — short cache (5 min). Новые deploy'ы быстро видны. + expires 5m; + add_header Cache-Control "public, must-revalidate"; + + # Hash-named assets — long cache. Diplodoc делает content-hashing + # в _bundle/ что safe для immutable cache. + location ~* /_bundle/.*\.(css|js|woff2?|ttf|eot|svg|png|jpg|ico)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + } + + # Redirect /docs (без trailing slash) → /docs/. + location = /docs { + return 301 /docs/; + } + + # Security headers. + add_header X-Content-Type-Options "nosniff" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; +}