From 1661b2b4d0c2a6f3a7e4b07f7aafec8ac52e6393 Mon Sep 17 00:00:00 2001 From: "a.tolmachev" Date: Sun, 3 May 2026 19:41:04 +0000 Subject: [PATCH] deploy: split community delivery manifest --- .github/workflows/deploy.yml | 2 +- TASKS.md | 3 +- deploy/community/.env.example | 36 ++++++++++ deploy/community/docker-compose.yml | 103 ++++++++++++++++++++++++++++ docs/commercial-boundaries.md | 6 ++ docs/deployment.md | 10 +-- docs/implementation-plan.md | 2 + 7 files changed, 156 insertions(+), 6 deletions(-) create mode 100644 deploy/community/.env.example create mode 100644 deploy/community/docker-compose.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 41fc16a..1e7782c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -114,7 +114,7 @@ jobs: run: | ssh -p "${{ secrets.DEPLOY_PORT }}" "${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}" \ "mkdir -p '${{ secrets.DEPLOY_PATH }}'" - rsync -az -e "ssh -p ${{ secrets.DEPLOY_PORT }}" docker-compose.yml \ + rsync -az -e "ssh -p ${{ secrets.DEPLOY_PORT }}" deploy/community/docker-compose.yml \ "${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}:${{ secrets.DEPLOY_PATH }}/docker-compose.yml" - name: Write environment file diff --git a/TASKS.md b/TASKS.md index 01e7385..8392fd0 100644 --- a/TASKS.md +++ b/TASKS.md @@ -39,8 +39,9 @@ Verification: Progress: - done: - gate for creating `2` private repositories is already documented before any physical commercial split + - canonical Community deployment manifest and env template now live under `deploy/community/*`, and public deploy uses that manifest instead of the root compose file - pending: - - extension seams and delivery manifests still need to move from planning into concrete packaging work + - remaining extension seams and packaging split still need to move from planning into concrete public/private manifests ## Planned diff --git a/deploy/community/.env.example b/deploy/community/.env.example new file mode 100644 index 0000000..afe4348 --- /dev/null +++ b/deploy/community/.env.example @@ -0,0 +1,36 @@ +POSTGRES_DB=crank +POSTGRES_USER=crank +POSTGRES_PASSWORD=change-me +POSTGRES_HOST=postgres +POSTGRES_PORT=5432 +POSTGRES_MAX_CONNECTIONS=20 +POSTGRES_MIN_CONNECTIONS=2 +POSTGRES_ACQUIRE_TIMEOUT_MS=5000 +POSTGRES_IDLE_TIMEOUT_MS=600000 +POSTGRES_MAX_LIFETIME_MS=1800000 +CRANK_ADMIN_API_IMAGE=crank/admin-api:dev +CRANK_MCP_SERVER_IMAGE=crank/mcp-server:dev +CRANK_UI_IMAGE=crank/ui:dev +CRANK_STORAGE_ROOT=/var/lib/crank/storage +CRANK_PUBLISH_BIND=127.0.0.1 +CRANK_ADMIN_BIND=0.0.0.0:3001 +CRANK_ADMIN_RATE_LIMIT_RPS=30 +CRANK_ADMIN_RATE_LIMIT_BURST=60 +CRANK_MCP_BIND=0.0.0.0:3002 +CRANK_MCP_REFRESH_MS=5000 +CRANK_MCP_RATE_LIMIT_RPS=60 +CRANK_MCP_RATE_LIMIT_BURST=120 +CRANK_RUNTIME_MAX_CONCURRENT_UNARY=64 +CRANK_RUNTIME_MAX_CONCURRENT_WINDOW=16 +CRANK_RUNTIME_MAX_CONCURRENT_SESSIONS=16 +CRANK_RUNTIME_MAX_CONCURRENT_JOBS=16 +CRANK_LOG_LEVEL=info +CRANK_MASTER_KEY=change-me-master-key +CRANK_SESSION_SECRET=change-me-session-secret +CRANK_PASSWORD_PEPPER=change-me-password-pepper +CRANK_SESSION_TTL_HOURS=24 +CRANK_BOOTSTRAP_ADMIN_EMAIL=owner@crank.local +CRANK_BOOTSTRAP_ADMIN_PASSWORD=change-me-admin-password +CRANK_BOOTSTRAP_ADMIN_DISPLAY_NAME=Crank Owner +CRANK_DEMO_SEED=false +CRANK_BASE_URL=https://crank.example.com diff --git a/deploy/community/docker-compose.yml b/deploy/community/docker-compose.yml new file mode 100644 index 0000000..fdaab1c --- /dev/null +++ b/deploy/community/docker-compose.yml @@ -0,0 +1,103 @@ +services: + postgres: + image: postgres:16-alpine + restart: unless-stopped + environment: + POSTGRES_DB: ${POSTGRES_DB:-crank} + POSTGRES_USER: ${POSTGRES_USER:-crank} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-crank} + volumes: + - postgres_data:/var/lib/postgresql/data + ports: + - "127.0.0.1:5432:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-crank} -d ${POSTGRES_DB:-crank}"] + interval: 10s + timeout: 5s + retries: 5 + + admin-api: + image: ${CRANK_ADMIN_API_IMAGE:-crank/admin-api:dev} + build: + context: ../.. + dockerfile: apps/admin-api/Dockerfile + restart: unless-stopped + environment: + POSTGRES_HOST: ${POSTGRES_HOST:-postgres} + POSTGRES_PORT: ${POSTGRES_PORT:-5432} + POSTGRES_DB: ${POSTGRES_DB:-crank} + POSTGRES_USER: ${POSTGRES_USER:-crank} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-crank} + CRANK_STORAGE_ROOT: ${CRANK_STORAGE_ROOT:-/var/lib/crank/storage} + CRANK_ADMIN_BIND: ${CRANK_ADMIN_BIND:-0.0.0.0:3001} + CRANK_LOG_LEVEL: ${CRANK_LOG_LEVEL:-info} + CRANK_MASTER_KEY: ${CRANK_MASTER_KEY} + CRANK_BASE_URL: ${CRANK_BASE_URL:-http://localhost:3000} + CRANK_SESSION_SECRET: ${CRANK_SESSION_SECRET} + CRANK_PASSWORD_PEPPER: ${CRANK_PASSWORD_PEPPER} + CRANK_SESSION_TTL_HOURS: ${CRANK_SESSION_TTL_HOURS:-24} + CRANK_BOOTSTRAP_ADMIN_EMAIL: ${CRANK_BOOTSTRAP_ADMIN_EMAIL} + CRANK_BOOTSTRAP_ADMIN_PASSWORD: ${CRANK_BOOTSTRAP_ADMIN_PASSWORD} + CRANK_BOOTSTRAP_ADMIN_DISPLAY_NAME: ${CRANK_BOOTSTRAP_ADMIN_DISPLAY_NAME:-Crank Owner} + CRANK_DEMO_SEED: ${CRANK_DEMO_SEED:-false} + depends_on: + postgres: + condition: service_healthy + volumes: + - artifact_storage:${CRANK_STORAGE_ROOT:-/var/lib/crank/storage} + ports: + - "${CRANK_PUBLISH_BIND:-127.0.0.1}:3001:3001" + healthcheck: + test: ["CMD", "curl", "--fail", "http://127.0.0.1:3001/health"] + interval: 15s + timeout: 5s + retries: 5 + + mcp-server: + image: ${CRANK_MCP_SERVER_IMAGE:-crank/mcp-server:dev} + build: + context: ../.. + dockerfile: apps/mcp-server/Dockerfile + restart: unless-stopped + environment: + POSTGRES_HOST: ${POSTGRES_HOST:-postgres} + POSTGRES_PORT: ${POSTGRES_PORT:-5432} + POSTGRES_DB: ${POSTGRES_DB:-crank} + POSTGRES_USER: ${POSTGRES_USER:-crank} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-crank} + CRANK_STORAGE_ROOT: ${CRANK_STORAGE_ROOT:-/var/lib/crank/storage} + CRANK_MCP_BIND: ${CRANK_MCP_BIND:-0.0.0.0:3002} + CRANK_MCP_REFRESH_MS: ${CRANK_MCP_REFRESH_MS:-5000} + CRANK_LOG_LEVEL: ${CRANK_LOG_LEVEL:-info} + CRANK_MASTER_KEY: ${CRANK_MASTER_KEY} + CRANK_BASE_URL: ${CRANK_BASE_URL:-http://localhost:3000} + depends_on: + postgres: + condition: service_healthy + volumes: + - artifact_storage:${CRANK_STORAGE_ROOT:-/var/lib/crank/storage} + ports: + - "${CRANK_PUBLISH_BIND:-127.0.0.1}:3002:3002" + healthcheck: + test: ["CMD", "curl", "--fail", "http://127.0.0.1:3002/health"] + interval: 15s + timeout: 5s + retries: 5 + + ui: + image: ${CRANK_UI_IMAGE:-crank/ui:dev} + build: + context: ../.. + dockerfile: apps/ui/Dockerfile + restart: unless-stopped + ports: + - "${CRANK_PUBLISH_BIND:-127.0.0.1}:3000:3000" + healthcheck: + test: ["CMD", "wget", "-qO-", "http://127.0.0.1:3000/"] + interval: 15s + timeout: 5s + retries: 5 + +volumes: + postgres_data: + artifact_storage: diff --git a/docs/commercial-boundaries.md b/docs/commercial-boundaries.md index f6fcd09..a0badc7 100644 --- a/docs/commercial-boundaries.md +++ b/docs/commercial-boundaries.md @@ -192,6 +192,12 @@ Private code должен подключаться как реализация - UI gating по capability flags; - отдельные delivery manifests для Community. +Первый обязательный шаг на практике: + +- canonical public deployment manifest должен жить отдельно от root development convenience files; +- Community release path должен опираться на `deploy/community/*`; +- private `Enterprise` и `Cloud` manifests не должны проектироваться как вариации того же root compose-файла. + ## 10. Связанные документы - `docs/product-editions.md` diff --git a/docs/deployment.md b/docs/deployment.md index 9eb1767..425b5f6 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -32,8 +32,9 @@ - `Dockerfile` для `apps/admin-api`; - `Dockerfile` для `apps/mcp-server`; - `Dockerfile` для `apps/ui` или отдельный production build flow для UI; -- `docker-compose.yml`; -- `.env.example`; +- `deploy/community/docker-compose.yml` как canonical Community deployment manifest; +- `deploy/community/.env.example` как canonical Community runtime env template; +- `docker-compose.yml` и root `.env.example` только как local development convenience layers, а не как future private delivery boundary; - `.github/workflows/ci.yml`; - `.github/workflows/deploy.yml`; - примеры reverse proxy конфигураций. @@ -160,7 +161,7 @@ services: ## 9. Требования к `docker-compose` -`docker-compose.yml` должен: +`deploy/community/docker-compose.yml` должен: - поднимать `postgres`, `admin-api`, `mcp-server`, `ui`; - использовать env vars вместо hardcoded config; @@ -229,7 +230,8 @@ CD для MVP должен: Монолитный `DEPLOY_ENV_FILE` больше не используется. -Workflow собирает `.env` на сервере из отдельных GitHub secrets. Для runtime-переменных нужно +Workflow доставляет именно `deploy/community/docker-compose.yml` как public Community deployment manifest +и собирает `.env` на сервере из отдельных GitHub secrets. Для runtime-переменных нужно создавать secrets с теми же именами, что и у приложений: - `POSTGRES_HOST` diff --git a/docs/implementation-plan.md b/docs/implementation-plan.md index e1af836..12d94b2 100644 --- a/docs/implementation-plan.md +++ b/docs/implementation-plan.md @@ -31,6 +31,8 @@ - в кодовой базе определена capability model по редакциям; - UI и API не обещают Community-функции, которых там не должно быть; - старые review-файлы не требуются как отдельный источник истины. +- canonical Community deployment manifest и env template вынесены в `deploy/community/*`; +- public CI/CD delivery path использует именно `deploy/community/*` как source of truth для Community поставки. ## 4. Этап 2. Community machine access completion