From ec3724b766fcdbcfec7c486b80d5c72bf3fb45b4 Mon Sep 17 00:00:00 2001 From: Vladimir Valekzhanin Date: Tue, 13 Oct 2026 18:23:46 +0300 Subject: [PATCH 1/2] =?UTF-8?q?ci:=20=D0=BA=D0=BE=D0=BD=D1=82=D0=B5=D0=B9?= =?UTF-8?q?=D0=BD=D0=B5=D1=80=D0=B8=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20?= =?UTF-8?q?=D1=84=D1=80=D0=BE=D0=BD=D1=82=D0=B5=D0=BD=D0=B4=20=D1=87=D0=B5?= =?UTF-8?q?=D1=80=D0=B5=D0=B7=20docker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #30 --- .dockerignore | 12 +++++++++ Dockerfile | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 35 ++++++++++++++++++++++++++ compose.yaml | 15 +++++++++++ 4 files changed, 132 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..5f6f0a9 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,12 @@ +# Контекст сборки Docker: не тащить лишнее в образ. +target/ +.git/ +.gitignore +.idea/ +.vscode/ +*.log +*.class +.env +.DS_Store +lefthook.yml +setup.ps1 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4c11c9a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,70 @@ +# Многостадийная сборка десктоп-клиента (JavaFX, Java 25, Maven). +# +# Сборка образа: +# docker build -t desktop-client . +# +# Запуск GUI (нужен X-сервер, иначе окно не отобразится): +# Linux: +# xhost +local:docker +# docker run --rm -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix:ro desktop-client +# macOS (XQuartz: разрешить подключения, затем): +# xhost +localhost +# docker run --rm -e DISPLAY=host.docker.internal:0 desktop-client +# Без X-сервера процесс стартует, но окно JavaFX не покажет (headless-контейнер). + +# ---------- stage 1: сборка jar ---------- +FROM maven:3-eclipse-temurin-25 AS build +WORKDIR /app + +# Сначала только дескрипторы — слой с зависимостями кешируется, +# пока не меняется pom.xml. +COPY pom.xml ./ +COPY .mvn/ .mvn/ +RUN mvn -B dependency:go-offline -DskipTests + +# Затем исходники и сборка без тестов +# (verify-плагины spotless/checkstyle не входят в фазу package). +COPY src/ src/ +COPY checkstyle.xml ./ +RUN mvn -B package -DskipTests \ + && mvn -B dependency:copy-dependencies -DoutputDirectory=target/libs -DincludeScope=runtime + +# ---------- stage 2: запуск ---------- +FROM eclipse-temurin:25-jre-jammy +LABEL org.opencontainers.image.description="JavaFX desktop-клиент" + +# Нативные зависимости JavaFX (GTK/GL/X11/шрифты) для Ubuntu Jammy. +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + libgtk-3-0 \ + libgl1 \ + libxtst6 \ + libxi6 \ + libxrender1 \ + libxrandr2 \ + libxinerama1 \ + libxcursor1 \ + libxcomposite1 \ + libxdamage1 \ + libasound2 \ + libatk1.0-0 \ + libatk-bridge2.0-0 \ + libcups2 \ + libdrm2 \ + libxkbcommon0 \ + libpango-1.0-0 \ + libcairo2 \ + fontconfig \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app +COPY --from=build /app/target/*.jar ./app.jar +COPY --from=build /app/target/libs/ ./libs/ + +# Дефолты для запуска без бэкенда можно переопределить через -e: +# docker run --rm -e MOCK_DATA=true ... desktop-client +ENV INITIAL_PAGE_PATH=/fxml/LoginForm.fxml \ + MOCK_DATA=false \ + JAVA_OPTS="-Dprism.order=sw -Djava.awt.headless=false --enable-native-access=ALL-UNNAMED" + +ENTRYPOINT ["sh", "-c", "exec java ${JAVA_OPTS} -cp '/app/app.jar:/app/libs/*' com.DesktopApplicationClientJava.Main"] diff --git a/README.md b/README.md index 98b8dbb..5b805cb 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,41 @@ MOCK_DATA=true mvn javafx:run Откроется окно 800×600 со стартовой страницы (по умолчанию вход, `LoginForm.fxml`). +### Запуск фронта в Docker + +Образ собирается многостадийно (`Dockerfile`): stage `build` +(`maven:3-eclipse-temurin-25`, `mvn -B package -DskipTests` с кешированием +зависимостей) и stage `runtime` (`eclipse-temurin:25-jre` + нативные +библиотеки JavaFX: GTK, GL, X11, шрифты). Точка входа запускает +`com.DesktopApplicationClientJava.Main` с classpath из собранного jar +и `target/libs`. + +```bash +# Сборка образа: +docker build -t desktop-client . + +# Запуск GUI на Linux (нужен X-сервер): +xhost +local:docker +docker run --rm -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix:ro desktop-client + +# Запуск GUI на macOS (XQuartz с разрешёнными подключениями): +xhost +localhost +docker run --rm -e DISPLAY=host.docker.internal:0 desktop-client + +# Через compose (профиль gui, тест-инфра по умолчанию не затрагивается): +docker compose --profile gui run --rm client +``` + +Переменные окружения (`-e`): `INITIAL_PAGE_PATH` (стартовая страница), +`MOCK_DATA=true` (работа без бэкенда), `JAVA_OPTS` (по умолчанию +софтверный рендеринг `-Dprism.order=sw` и `--enable-native-access=ALL-UNNAMED` +для нативных библиотек JavaFX). + +Ограничения: JavaFX — оконный GUI, в headless-контейнере без проброса +`DISPLAY`/X11-сокета окно не отобразится (процесс при этом стартует). +Клиент ходит в бэкенд по HTTP (порт `APP_PORT` на хосте), напрямую +к postgres/redis из `compose.yaml` не подключается. + ## Git hooks Перед каждым коммитом автоматически проверяются: линт (`checkstyle:check`), diff --git a/compose.yaml b/compose.yaml index 4d689b3..eeb7ec7 100644 --- a/compose.yaml +++ b/compose.yaml @@ -8,8 +8,23 @@ # docker compose up -d # With object storage (for file/export ITs): # docker compose --profile files up -d +# Desktop client (GUI needs an X server, see Dockerfile header / README): +# docker compose --profile gui run --rm client +# The client talks to the backend API over HTTP (host port from APP_PORT), +# not to postgres/redis directly, so it has no depends_on. services: + client: + build: . + image: desktop-client + profiles: [gui] + environment: + - INITIAL_PAGE_PATH=${INITIAL_PAGE_PATH:-/fxml/LoginForm.fxml} + - MOCK_DATA=${MOCK_DATA:-false} + - DISPLAY=${DISPLAY:-} + volumes: + - /tmp/.X11-unix:/tmp/.X11-unix:ro + postgres: image: postgres:16-alpine environment: From 416afecd30e837bde1b386b3570e32d8db1892d9 Mon Sep 17 00:00:00 2001 From: Vladimir Valekzhanin Date: Tue, 13 Oct 2026 18:30:38 +0300 Subject: [PATCH 2/2] =?UTF-8?q?ci:=20=D1=83=D0=B1=D1=80=D0=B0=D1=82=D1=8C?= =?UTF-8?q?=20=D0=B8=D0=B7=D0=B1=D1=8B=D1=82=D0=BE=D1=87=D0=BD=D1=8B=D0=B5?= =?UTF-8?q?=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D1=80=D0=B8?= =?UTF-8?q?=D0=B8=20=D0=B8=D0=B7=20docker-=D1=84=D0=B0=D0=B9=D0=BB=D0=BE?= =?UTF-8?q?=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 1 - Dockerfile | 28 ++++++++-------------------- README.md | 18 ++++++++++-------- compose.yaml | 4 +--- 4 files changed, 19 insertions(+), 32 deletions(-) diff --git a/.dockerignore b/.dockerignore index 5f6f0a9..4f6fa86 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,3 @@ -# Контекст сборки Docker: не тащить лишнее в образ. target/ .git/ .gitignore diff --git a/Dockerfile b/Dockerfile index 4c11c9a..5b39d1a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,29 +1,17 @@ -# Многостадийная сборка десктоп-клиента (JavaFX, Java 25, Maven). -# -# Сборка образа: -# docker build -t desktop-client . -# -# Запуск GUI (нужен X-сервер, иначе окно не отобразится): -# Linux: -# xhost +local:docker -# docker run --rm -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix:ro desktop-client -# macOS (XQuartz: разрешить подключения, затем): -# xhost +localhost -# docker run --rm -e DISPLAY=host.docker.internal:0 desktop-client -# Без X-сервера процесс стартует, но окно JavaFX не покажет (headless-контейнер). +# JavaFX десктоп-клиент. Для GUI нужен X-сервер, иначе окно не отобразится: +# Linux: xhost +local:docker && docker run --rm -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix:ro desktop-client +# macOS: XQuartz + xhost +localhost, затем docker run --rm -e DISPLAY=host.docker.internal:0 desktop-client +# Подробности — в README («Запуск фронта в Docker»). # ---------- stage 1: сборка jar ---------- FROM maven:3-eclipse-temurin-25 AS build WORKDIR /app -# Сначала только дескрипторы — слой с зависимостями кешируется, -# пока не меняется pom.xml. COPY pom.xml ./ COPY .mvn/ .mvn/ RUN mvn -B dependency:go-offline -DskipTests -# Затем исходники и сборка без тестов -# (verify-плагины spotless/checkstyle не входят в фазу package). +# spotless/checkstyle привязаны к verify, поэтому package достаточно. COPY src/ src/ COPY checkstyle.xml ./ RUN mvn -B package -DskipTests \ @@ -61,10 +49,10 @@ WORKDIR /app COPY --from=build /app/target/*.jar ./app.jar COPY --from=build /app/target/libs/ ./libs/ -# Дефолты для запуска без бэкенда можно переопределить через -e: -# docker run --rm -e MOCK_DATA=true ... desktop-client +# Дефолты переопределяются через -e (напр. MOCK_DATA=true для работы без бэкенда). ENV INITIAL_PAGE_PATH=/fxml/LoginForm.fxml \ MOCK_DATA=false \ JAVA_OPTS="-Dprism.order=sw -Djava.awt.headless=false --enable-native-access=ALL-UNNAMED" - +# prism.order=sw: в контейнере нет GPU; native-access: доступ JavaFX к нативным либам, обязательный на JDK 25. +# classpath: собранный jar + runtime-зависимости из target/libs (см. copy-dependencies выше). ENTRYPOINT ["sh", "-c", "exec java ${JAVA_OPTS} -cp '/app/app.jar:/app/libs/*' com.DesktopApplicationClientJava.Main"] diff --git a/README.md b/README.md index 5b805cb..bee569f 100644 --- a/README.md +++ b/README.md @@ -51,12 +51,13 @@ MOCK_DATA=true mvn javafx:run ### Запуск фронта в Docker -Образ собирается многостадийно (`Dockerfile`): stage `build` -(`maven:3-eclipse-temurin-25`, `mvn -B package -DskipTests` с кешированием -зависимостей) и stage `runtime` (`eclipse-temurin:25-jre` + нативные -библиотеки JavaFX: GTK, GL, X11, шрифты). Точка входа запускает -`com.DesktopApplicationClientJava.Main` с classpath из собранного jar -и `target/libs`. +Образ (`Dockerfile`): сборка в `maven:3-eclipse-temurin-25` +(`mvn -B package -DskipTests`; spotless/checkstyle живут в `verify`, +поэтому `package` достаточно), запуск в `eclipse-temurin:25-jre` +с нативными библиотеками JavaFX для Jammy (GTK/GL/X11/звук/шрифты). +Точка входа запускает `com.DesktopApplicationClientJava.Main`, +classpath — собранный jar + `target/libs` (runtime-зависимости, +разложенные через `dependency:copy-dependencies`). ```bash # Сборка образа: @@ -76,8 +77,9 @@ docker compose --profile gui run --rm client Переменные окружения (`-e`): `INITIAL_PAGE_PATH` (стартовая страница), `MOCK_DATA=true` (работа без бэкенда), `JAVA_OPTS` (по умолчанию -софтверный рендеринг `-Dprism.order=sw` и `--enable-native-access=ALL-UNNAMED` -для нативных библиотек JavaFX). +`-Dprism.order=sw` — в контейнере нет GPU — и +`--enable-native-access=ALL-UNNAMED` — доступ JavaFX к нативным либам, +обязательный на JDK 25). Ограничения: JavaFX — оконный GUI, в headless-контейнере без проброса `DISPLAY`/X11-сокета окно не отобразится (процесс при этом стартует). diff --git a/compose.yaml b/compose.yaml index eeb7ec7..744b1cd 100644 --- a/compose.yaml +++ b/compose.yaml @@ -8,10 +8,8 @@ # docker compose up -d # With object storage (for file/export ITs): # docker compose --profile files up -d -# Desktop client (GUI needs an X server, see Dockerfile header / README): +# Desktop client (GUI: нужен X-сервер, подробности в README; к бэкенду ходит по HTTP, поэтому без depends_on): # docker compose --profile gui run --rm client -# The client talks to the backend API over HTTP (host port from APP_PORT), -# not to postgres/redis directly, so it has no depends_on. services: client: