diff --git a/.dockerignore b/.dockerignore index 2d25c3f..f3919cc 100644 --- a/.dockerignore +++ b/.dockerignore @@ -6,6 +6,13 @@ dist coverage .idea .vscode +.claude +.codex +.tmp .DS_Store *.log var +apps/ui/node_modules +apps/ui/playwright-report +apps/ui/test-results +apps/ui/.playwright diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 220ce14..9dcbf25 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -138,6 +138,29 @@ jobs: " fi + - name: Validate required environment variables + run: | + ssh "${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}" " + set -e + cd '${{ secrets.DEPLOY_PATH }}' + required_vars=' + CRANK_DATABASE_URL + CRANK_MASTER_KEY + CRANK_SESSION_SECRET + CRANK_PASSWORD_PEPPER + CRANK_BOOTSTRAP_ADMIN_EMAIL + CRANK_BOOTSTRAP_ADMIN_PASSWORD + CRANK_PUBLIC_BASE_URL + ' + for var in \$required_vars; do + value=\$(grep -E \"^\${var}=\" .env | tail -n1 | cut -d= -f2- || true) + if [ -z \"\$value\" ]; then + echo \"missing required env: \$var\" >&2 + exit 1 + fi + done + " + - name: Deploy with Docker Compose run: | ssh "${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}" " diff --git a/TASKS.md b/TASKS.md index a37df50..9c8d1d0 100644 --- a/TASKS.md +++ b/TASKS.md @@ -2,19 +2,21 @@ ## Current -### `feat/websocket-test-run-polish` +### `feat/rust-docker-build-cache` -Status: pending +Status: in_progress DoD: -- WebSocket wizard test-run shows reconnect / bounded-result semantics clearly -- WebSocket test-run flow is covered by local fixtures and Playwright -- `TASKS.md` stays aligned with the actual streaming branch state +- Rust Docker builds use a cache-friendly multi-stage layout +- `admin-api` and `mcp-server` reuse dependency layers across rebuilds +- noisy local artifacts are excluded from Docker build context +- deployment fails early when required secret-store env is missing ## Next -- `feat/live-authenticated-staging-entry` +- `feat/websocket-test-run-polish` ## Backlog +- `feat/websocket-test-run-polish` - `feat/live-authenticated-staging-entry` diff --git a/apps/admin-api/Dockerfile b/apps/admin-api/Dockerfile index ea3c405..78693ad 100644 --- a/apps/admin-api/Dockerfile +++ b/apps/admin-api/Dockerfile @@ -1,3 +1,57 @@ +FROM rust:1.85-bookworm AS deps + +WORKDIR /app + +COPY Cargo.toml Cargo.lock ./ +COPY apps/admin-api/Cargo.toml apps/admin-api/Cargo.toml +COPY apps/mcp-server/Cargo.toml apps/mcp-server/Cargo.toml +COPY crates/crank-core/Cargo.toml crates/crank-core/Cargo.toml +COPY crates/crank-schema/Cargo.toml crates/crank-schema/Cargo.toml +COPY crates/crank-mapping/Cargo.toml crates/crank-mapping/Cargo.toml +COPY crates/crank-proto/Cargo.toml crates/crank-proto/Cargo.toml +COPY crates/crank-registry/Cargo.toml crates/crank-registry/Cargo.toml +COPY crates/crank-runtime/Cargo.toml crates/crank-runtime/Cargo.toml +COPY crates/crank-adapter-rest/Cargo.toml crates/crank-adapter-rest/Cargo.toml +COPY crates/crank-adapter-graphql/Cargo.toml crates/crank-adapter-graphql/Cargo.toml +COPY crates/crank-adapter-grpc/Cargo.toml crates/crank-adapter-grpc/Cargo.toml +COPY crates/crank-adapter-grpc/build.rs crates/crank-adapter-grpc/build.rs +COPY crates/crank-adapter-grpc/proto crates/crank-adapter-grpc/proto +COPY crates/crank-adapter-websocket/Cargo.toml crates/crank-adapter-websocket/Cargo.toml +COPY crates/crank-adapter-soap/Cargo.toml crates/crank-adapter-soap/Cargo.toml + +RUN mkdir -p \ + apps/admin-api/src \ + apps/mcp-server/src \ + crates/crank-core/src \ + crates/crank-schema/src \ + crates/crank-mapping/src \ + crates/crank-proto/src \ + crates/crank-registry/src \ + crates/crank-runtime/src \ + crates/crank-adapter-rest/src \ + crates/crank-adapter-graphql/src \ + crates/crank-adapter-grpc/src \ + crates/crank-adapter-websocket/src \ + crates/crank-adapter-soap/src \ + && printf 'fn main() {}\n' > apps/admin-api/src/main.rs \ + && printf 'fn main() {}\n' > apps/mcp-server/src/main.rs \ + && printf 'pub fn placeholder() {}\n' > crates/crank-core/src/lib.rs \ + && printf 'pub fn placeholder() {}\n' > crates/crank-schema/src/lib.rs \ + && printf 'pub fn placeholder() {}\n' > crates/crank-mapping/src/lib.rs \ + && printf 'pub fn placeholder() {}\n' > crates/crank-proto/src/lib.rs \ + && printf 'pub fn placeholder() {}\n' > crates/crank-registry/src/lib.rs \ + && printf 'pub fn placeholder() {}\n' > crates/crank-runtime/src/lib.rs \ + && printf 'pub fn placeholder() {}\n' > crates/crank-adapter-rest/src/lib.rs \ + && printf 'pub fn placeholder() {}\n' > crates/crank-adapter-graphql/src/lib.rs \ + && printf 'pub fn placeholder() {}\n' > crates/crank-adapter-grpc/src/lib.rs \ + && printf 'pub fn placeholder() {}\n' > crates/crank-adapter-websocket/src/lib.rs \ + && printf 'pub fn placeholder() {}\n' > crates/crank-adapter-soap/src/lib.rs + +RUN --mount=type=cache,target=/usr/local/cargo/registry \ + --mount=type=cache,target=/usr/local/cargo/git/db \ + --mount=type=cache,target=/app/target \ + cargo build --release -p admin-api + FROM rust:1.85-bookworm AS builder WORKDIR /app @@ -6,7 +60,11 @@ COPY Cargo.toml Cargo.lock ./ COPY apps ./apps COPY crates ./crates -RUN cargo build --release -p admin-api +RUN --mount=type=cache,target=/usr/local/cargo/registry \ + --mount=type=cache,target=/usr/local/cargo/git/db \ + --mount=type=cache,target=/app/target \ + cargo build --release -p admin-api \ + && cp /app/target/release/admin-api /tmp/admin-api FROM debian:bookworm-slim @@ -16,7 +74,7 @@ RUN apt-get update \ WORKDIR /app -COPY --from=builder /app/target/release/admin-api /usr/local/bin/admin-api +COPY --from=builder /tmp/admin-api /usr/local/bin/admin-api ENV CRANK_ADMIN_BIND=0.0.0.0:3001 diff --git a/apps/mcp-server/Dockerfile b/apps/mcp-server/Dockerfile index e54ba59..733f6a1 100644 --- a/apps/mcp-server/Dockerfile +++ b/apps/mcp-server/Dockerfile @@ -1,3 +1,57 @@ +FROM rust:1.85-bookworm AS deps + +WORKDIR /app + +COPY Cargo.toml Cargo.lock ./ +COPY apps/admin-api/Cargo.toml apps/admin-api/Cargo.toml +COPY apps/mcp-server/Cargo.toml apps/mcp-server/Cargo.toml +COPY crates/crank-core/Cargo.toml crates/crank-core/Cargo.toml +COPY crates/crank-schema/Cargo.toml crates/crank-schema/Cargo.toml +COPY crates/crank-mapping/Cargo.toml crates/crank-mapping/Cargo.toml +COPY crates/crank-proto/Cargo.toml crates/crank-proto/Cargo.toml +COPY crates/crank-registry/Cargo.toml crates/crank-registry/Cargo.toml +COPY crates/crank-runtime/Cargo.toml crates/crank-runtime/Cargo.toml +COPY crates/crank-adapter-rest/Cargo.toml crates/crank-adapter-rest/Cargo.toml +COPY crates/crank-adapter-graphql/Cargo.toml crates/crank-adapter-graphql/Cargo.toml +COPY crates/crank-adapter-grpc/Cargo.toml crates/crank-adapter-grpc/Cargo.toml +COPY crates/crank-adapter-grpc/build.rs crates/crank-adapter-grpc/build.rs +COPY crates/crank-adapter-grpc/proto crates/crank-adapter-grpc/proto +COPY crates/crank-adapter-websocket/Cargo.toml crates/crank-adapter-websocket/Cargo.toml +COPY crates/crank-adapter-soap/Cargo.toml crates/crank-adapter-soap/Cargo.toml + +RUN mkdir -p \ + apps/admin-api/src \ + apps/mcp-server/src \ + crates/crank-core/src \ + crates/crank-schema/src \ + crates/crank-mapping/src \ + crates/crank-proto/src \ + crates/crank-registry/src \ + crates/crank-runtime/src \ + crates/crank-adapter-rest/src \ + crates/crank-adapter-graphql/src \ + crates/crank-adapter-grpc/src \ + crates/crank-adapter-websocket/src \ + crates/crank-adapter-soap/src \ + && printf 'fn main() {}\n' > apps/admin-api/src/main.rs \ + && printf 'fn main() {}\n' > apps/mcp-server/src/main.rs \ + && printf 'pub fn placeholder() {}\n' > crates/crank-core/src/lib.rs \ + && printf 'pub fn placeholder() {}\n' > crates/crank-schema/src/lib.rs \ + && printf 'pub fn placeholder() {}\n' > crates/crank-mapping/src/lib.rs \ + && printf 'pub fn placeholder() {}\n' > crates/crank-proto/src/lib.rs \ + && printf 'pub fn placeholder() {}\n' > crates/crank-registry/src/lib.rs \ + && printf 'pub fn placeholder() {}\n' > crates/crank-runtime/src/lib.rs \ + && printf 'pub fn placeholder() {}\n' > crates/crank-adapter-rest/src/lib.rs \ + && printf 'pub fn placeholder() {}\n' > crates/crank-adapter-graphql/src/lib.rs \ + && printf 'pub fn placeholder() {}\n' > crates/crank-adapter-grpc/src/lib.rs \ + && printf 'pub fn placeholder() {}\n' > crates/crank-adapter-websocket/src/lib.rs \ + && printf 'pub fn placeholder() {}\n' > crates/crank-adapter-soap/src/lib.rs + +RUN --mount=type=cache,target=/usr/local/cargo/registry \ + --mount=type=cache,target=/usr/local/cargo/git/db \ + --mount=type=cache,target=/app/target \ + cargo build --release -p mcp-server + FROM rust:1.85-bookworm AS builder WORKDIR /app @@ -6,7 +60,11 @@ COPY Cargo.toml Cargo.lock ./ COPY apps ./apps COPY crates ./crates -RUN cargo build --release -p mcp-server +RUN --mount=type=cache,target=/usr/local/cargo/registry \ + --mount=type=cache,target=/usr/local/cargo/git/db \ + --mount=type=cache,target=/app/target \ + cargo build --release -p mcp-server \ + && cp /app/target/release/mcp-server /tmp/mcp-server FROM debian:bookworm-slim @@ -16,7 +74,7 @@ RUN apt-get update \ WORKDIR /app -COPY --from=builder /app/target/release/mcp-server /usr/local/bin/mcp-server +COPY --from=builder /tmp/mcp-server /usr/local/bin/mcp-server ENV CRANK_MCP_BIND=0.0.0.0:3002 diff --git a/docker-compose.yml b/docker-compose.yml index 24760f7..85b6a75 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -63,6 +63,7 @@ services: CRANK_MCP_REFRESH_MS: ${CRANK_MCP_REFRESH_MS:-5000} CRANK_LOG_LEVEL: ${CRANK_LOG_LEVEL:-info} CRANK_SECRET_PROVIDER: ${CRANK_SECRET_PROVIDER:-env} + CRANK_MASTER_KEY: ${CRANK_MASTER_KEY} CRANK_PUBLIC_BASE_URL: ${CRANK_PUBLIC_BASE_URL:-http://localhost:3000} depends_on: postgres: diff --git a/docs/runtime-config.md b/docs/runtime-config.md index 447e028..509abd6 100644 --- a/docs/runtime-config.md +++ b/docs/runtime-config.md @@ -135,6 +135,9 @@ Demo/deployment: - `CRANK_MASTER_KEY` +`CRANK_MASTER_KEY` обязателен и для `admin-api`, и для `mcp-server`, потому что оба приложения +должны уметь резолвить `secret_id` в runtime. + Для опционального demo-seed: - `CRANK_DEMO_SEED=true`