From 87d5803b55addee40c2ea1546f35a44d7bb77a3a Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 12 Oct 2026 19:02:45 +0000 Subject: [PATCH] =?UTF-8?q?Docker=20:=20image=20Rust=20+=20SPA=20publi?= =?UTF-8?q?=C3=A9e=20sur=20GHCR,=20tir=C3=A9e=20au=20d=C3=A9marrage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LG2LLJuswrrTTPKJmunEVf --- .dockerignore | 52 ++++---------- .github/workflows/ci.yml | 36 ++++++++++ .github/workflows/docker-publish.yml | 44 ++++++++++++ Dockerfile | 102 ++++++++++++--------------- docker-compose.yml | 13 ++-- 5 files changed, 146 insertions(+), 101 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/docker-publish.yml diff --git a/.dockerignore b/.dockerignore index cc1bf4a..6a3e3e4 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,38 +1,14 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# next.js -/.next/ -/out/ -build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* -.pnpm-debug.log* - -# local env files -.env*.local - -# vercel -.vercel - -# typescript -*.tsbuildinfo - -# BMad and Antigravity -_bmad-output/ -.gemini/ -.antigravityignore +.git +.github +.claude +.agent +.agents +_bmad +_bmad-output +node_modules +dist +data +server/target +*.md +!README.md +.env* diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..6edbe89 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,36 @@ +name: CI + +# Vérifie le serveur Rust et l'interface sur chaque pull request. +on: + pull_request: + push: + branches: [main] + +jobs: + server: + runs-on: ubuntu-latest + defaults: + run: + working-directory: server + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + components: clippy + - uses: Swatinem/rust-cache@v2 + with: + workspaces: server + - run: cargo build --locked + - run: cargo test --locked + + web: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + - run: npm ci + - run: npm run lint + - run: npm run build diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 0000000..676bf72 --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,44 @@ +name: Image Docker + +# Construit l'image à chaque mise à jour de main et la publie sur GHCR : +# le serveur la récupère au prochain `docker compose up -d` (pull_policy: always). +on: + push: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + packages: write + +concurrency: + group: docker-publish + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: docker/setup-buildx-action@v3 + + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Nom de l'image (minuscules) + id: image + run: echo "name=ghcr.io/${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT" + + - uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: | + ${{ steps.image.outputs.name }}:latest + ${{ steps.image.outputs.name }}:${{ github.sha }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/Dockerfile b/Dockerfile index c75a901..6aac4a6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,70 +1,58 @@ -FROM node:20-alpine AS base +# CollectFlow — image de production. +# +# 1. web : compile l'interface React (Vite) en fichiers statiques. +# 2. server : compile le serveur Rust (binaire unique, optimisé). +# 3. runner : Debian minimale + Chromium (extraction Qlik) + les deux livrables. +# +# L'image est construite et publiée par GitHub Actions à chaque push sur main +# (ghcr.io/r0m1k3/collectflow:latest) ; docker-compose la tire au démarrage. -# Install dependencies only when needed -FROM base AS deps -# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. -RUN apk add --no-cache libc6-compat +# --------------------------------------------------------------------------- +FROM node:22-alpine AS web WORKDIR /app - -# Install dependencies based on the preferred package manager -COPY package.json package-lock.json* ./ -RUN npm ci - -# Rebuild the source code only when needed -FROM base AS builder -WORKDIR /app -COPY --from=deps /app/node_modules ./node_modules +COPY package.json package-lock.json ./ +RUN npm ci --no-audit --no-fund COPY . . - -# Next.js telemetry is disabled -ENV NEXT_TELEMETRY_DISABLED=1 -# Limit workers and increase memory to prevent OOM -ENV NODE_OPTIONS="--max-old-space-size=4096" -ENV NEXT_PRIVATE_WORKER_THREADS=0 - RUN npm run build -# Production image, copy all the files and run next -FROM base AS runner +# --------------------------------------------------------------------------- +FROM rust:1-bookworm AS server +WORKDIR /build +# Dépendances d'abord : couche réutilisée tant que Cargo.lock ne change pas. +COPY server/Cargo.toml server/Cargo.lock ./ +RUN mkdir src && echo "fn main() {}" > src/main.rs \ + && cargo build --release --locked \ + && rm -rf src target/release/deps/collectflow* target/release/collectflow* +COPY server/ ./ +RUN cargo build --release --locked && cp target/release/collectflow /collectflow + +# --------------------------------------------------------------------------- +FROM debian:bookworm-slim AS runner WORKDIR /app -ENV NODE_ENV=production -# Next.js telemetry is disabled -ENV NEXT_TELEMETRY_DISABLED=1 +# Chromium pour l'extraction Qlik (websocket Engine ouvert dans la page) + polices. +RUN apt-get update \ + && apt-get install -y --no-install-recommends chromium ca-certificates fonts-liberation fonts-freefont-ttf tzdata \ + && rm -rf /var/lib/apt/lists/* -# Chromium pour l'extraction Qlik via Playwright (ws in-page) + dépendances headless -RUN apk add --no-cache chromium nss freetype harfbuzz ca-certificates ttf-freefont -ENV PLAYWRIGHT_CHROMIUM_PATH=/usr/bin/chromium-browser +# Même UID/GID que l'ancienne image Node : le volume /app/data existant reste lisible. +RUN groupadd --system --gid 1001 collectflow \ + && useradd --system --uid 1001 --gid collectflow --home-dir /app collectflow \ + && mkdir -p /app/data \ + && chown -R collectflow:collectflow /app -RUN addgroup --system --gid 1001 nodejs -RUN adduser --system --uid 1001 nextjs +COPY --from=server /collectflow /app/collectflow +COPY --from=web --chown=collectflow:collectflow /app/dist /app/web -COPY --from=builder /app/public ./public - -# Set the correct permission for prerender cache and data storage -RUN mkdir .next data -RUN chown nextjs:nodejs .next data - -# Automatically leverage output traces to reduce image size -# https://nextjs.org/docs/advanced-features/output-file-tracing -COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ -COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static - -# playwright-core n'est pas toujours tracé par le standalone — on le copie explicitement -COPY --from=deps --chown=nextjs:nodejs /app/node_modules/playwright-core ./node_modules/playwright-core - -# Grant write access to /app for settings persistence -RUN chown -R nextjs:nodejs /app - -COPY --from=builder --chown=nextjs:nodejs /app/scripts ./scripts - -USER nextjs +ENV PORT=5643 \ + HOSTNAME=0.0.0.0 \ + DATA_DIR=/app/data \ + STATIC_DIR=/app/web \ + PLAYWRIGHT_CHROMIUM_PATH=/usr/bin/chromium \ + RUST_LOG=info +USER collectflow EXPOSE 5643 +VOLUME ["/app/data"] -ENV PORT=5643 -# set hostname to localhost -ENV HOSTNAME="0.0.0.0" - -# Run the DB init script, then start the standalone server -CMD node scripts/db-init.js && node server.js +CMD ["/app/collectflow"] diff --git a/docker-compose.yml b/docker-compose.yml index 3d4866e..18b0e2c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,20 +1,21 @@ -version: "3.8" - services: web: + # Image publiée par GitHub Actions à chaque mise à jour de main. + # `pull_policy: always` : chaque `docker compose up -d` récupère la + # dernière version avant de (re)démarrer le conteneur. + image: ghcr.io/r0m1k3/collectflow:latest + pull_policy: always + # Construction locale possible : `docker compose build` (utilise le Dockerfile). build: context: . dockerfile: Dockerfile ports: - "5643:5643" environment: - - NODE_ENV=production - - AUTH_TRUST_HOST=true - AUTH_SECRET=my-super-secret-key-that-should-be-changed-in-production - DATABASE_URL=postgres://postgres:MOT_DE_PASSE_ICI@postgresbase:5432/collectflow - FF_API_BASE_URL=https://api.ffnancy.fr - - # Mais comme CollectFlow permet de le configurer depuis l'interface (settings), ce n'est pas strictement obligatoire au lancement. + # La base, Qlik et l'API FF se règlent aussi depuis l'interface (Paramètres). volumes: - collectflow-data:/app/data restart: unless-stopped