From b4bcc3b1965ac6cb8d7e4db2bdbf18c1a1117752 Mon Sep 17 00:00:00 2001 From: neobooru <50623835+neobooru@users.noreply.github.com> Date: Sat, 12 Oct 2024 00:50:37 +0200 Subject: [PATCH] Docker development setup with remote debugging --- client/build.js | 15 ++++++---- client/docker-start-dev.sh | 11 +++++++ client/js/main.js | 2 +- docker-compose.dev.yml | 58 +++++++++++++++++++++++++++++++++++++ server/Dockerfile | 29 +++++++++++++++++++ server/docker-start-dev.sh | 8 +++++ server/szurubooru/facade.py | 14 +++++++++ 7 files changed, 131 insertions(+), 6 deletions(-) create mode 100644 client/docker-start-dev.sh create mode 100644 docker-compose.dev.yml create mode 100644 server/docker-start-dev.sh diff --git a/client/build.js b/client/build.js index eaf28a54a..f603b6c76 100755 --- a/client/build.js +++ b/client/build.js @@ -315,8 +315,13 @@ function makeOutputDirs() { } function watch() { - let wss = new WebSocket.Server({ port: 8080 }); + let wss = new WebSocket.Server({ port: 8081 }); const liveReload = !process.argv.includes('--no-live-reload'); + const polling = process.argv.includes("--polling"); + + const chokidarOptions = { + usePolling: polling, + }; function emitReload() { if (liveReload) { @@ -329,7 +334,7 @@ function watch() { } } - chokidar.watch('./fonts/**/*').on('change', () => { + chokidar.watch('./fonts/**/*', chokidarOptions).on('change', () => { try { bundleBinaryAssets(); emitReload(); @@ -337,7 +342,7 @@ function watch() { console.error(pe.render(e)); } }); - chokidar.watch('./img/**/*').on('change', () => { + chokidar.watch('./img/**/*', chokidarOptions).on('change', () => { try { bundleWebAppFiles(); emitReload(); @@ -345,14 +350,14 @@ function watch() { console.error(pe.render(e)); } }); - chokidar.watch('./html/**/*.tpl').on('change', () => { + chokidar.watch('./html/**/*.tpl', chokidarOptions).on('change', () => { try { bundleHtml(); } catch (e) { console.error(pe.render(e)); } }); - chokidar.watch('./css/**/*.styl').on('change', () => { + chokidar.watch('./css/**/*.styl', chokidarOptions).on('change', () => { try { bundleCss() emitReload(); diff --git a/client/docker-start-dev.sh b/client/docker-start-dev.sh new file mode 100644 index 000000000..ab8563cd3 --- /dev/null +++ b/client/docker-start-dev.sh @@ -0,0 +1,11 @@ +#!/usr/bin/dumb-init /bin/sh + +# Integrate environment variables +sed -i "s|__BACKEND__|${BACKEND_HOST}|" \ + /etc/nginx/nginx.conf + +# Start server +nginx& + +# Watch source for changes and build app +npm run watch -- --polling diff --git a/client/js/main.js b/client/js/main.js index c5bdc537c..d1b59e6fa 100644 --- a/client/js/main.js +++ b/client/js/main.js @@ -3,7 +3,7 @@ const config = require("./config.js"); if (config.environment == "development") { - var ws = new WebSocket("ws://" + location.hostname + ":8080"); + var ws = new WebSocket("ws://" + location.hostname + ":8081"); ws.addEventListener("open", function (event) { console.log("Live-reloading websocket connected."); }); diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 000000000..d9d0fb3e7 --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,58 @@ +version: "2" + +services: + server: + build: + context: ./server + target: development + depends_on: + - sql + environment: + ## These should be the names of the dependent containers listed below, + ## or FQDNs/IP addresses if these services are running outside of Docker + POSTGRES_HOST: sql + ## Credentials for database: + POSTGRES_USER: + POSTGRES_PASSWORD: ## Commented Values are Default: + + #POSTGRES_DB: defaults to same as POSTGRES_USER + #POSTGRES_PORT: 5432 + #LOG_SQL: 0 (1 for verbose SQL logs) + DEBUG: 1 + WAIT_FOR_CLIENT: 0 + volumes: + - "data:/data" + - "./server/:/opt/app/" + ports: + - "5678:5678" + + client: + build: + context: ./client + target: development + depends_on: + - server + environment: + BACKEND_HOST: server + BASE_URL: + volumes: + - "data:/data:ro" + - "./client/:/opt/app/" + - "/opt/app/public/" + ports: + - "${PORT}:80" + # Port 8081 is used for the live-reload when the source code is changed. + - "8081:8081" + + sql: + image: postgres:11-alpine + restart: unless-stopped + environment: + POSTGRES_USER: + POSTGRES_PASSWORD: + volumes: + - "sql:/var/lib/postgresql/data" + +volumes: + data: + sql: diff --git a/server/Dockerfile b/server/Dockerfile index 3e4dadfba..7886f85e9 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -61,6 +61,35 @@ ENTRYPOINT ["pytest", "--tb=short"] CMD ["szurubooru/"] +FROM prereqs as development +WORKDIR /opt/app + +ARG PUID=1000 +ARG PGID=1000 + +RUN apk --no-cache add \ + dumb-init \ + py3-pip \ + py3-setuptools \ + py3-waitress \ + && pip3 install --no-cache-dir --disable-pip-version-check \ + hupper \ + debugpy \ + && mkdir -p /opt/app /data \ + && addgroup -g ${PGID} app \ + && adduser -SDH -h /opt/app -g '' -G app -u ${PUID} app \ + && chown -R app:app /opt/app /data + +USER app +CMD ["/opt/app/docker-start-dev.sh"] + +ARG PORT=6666 +ENV PORT=${PORT} +EXPOSE ${PORT} + +VOLUME ["/data/"] + + FROM prereqs as release WORKDIR /opt/app diff --git a/server/docker-start-dev.sh b/server/docker-start-dev.sh new file mode 100644 index 000000000..d7d76fb60 --- /dev/null +++ b/server/docker-start-dev.sh @@ -0,0 +1,8 @@ +#!/usr/bin/dumb-init /bin/sh +set -e +cd /opt/app + +alembic upgrade head + +echo "Starting szurubooru API on port ${PORT}" +exec hupper -m waitress --port ${PORT} szurubooru.facade:app diff --git a/server/szurubooru/facade.py b/server/szurubooru/facade.py index 4c8084f6d..a952ff2b5 100644 --- a/server/szurubooru/facade.py +++ b/server/szurubooru/facade.py @@ -162,4 +162,18 @@ def create_app() -> Callable[[Any, Any], Any]: return rest.application +if int(os.getenv("DEBUG", 0)) != 0: + try: + import debugpy + except ModuleNotFoundError: + raise RuntimeError("debugpy is not installed") + + # TODO: These print statements don't show up in our docker output. Why is that? + logging.info("debugpy listening on port 5678") + debugpy.listen(("0.0.0.0", 5678)) + + if int(os.getenv("WAIT_FOR_CLIENT", 0)) != 0: + logging.info("Waiting for debugger attach") + debugpy.wait_for_client() + app = create_app()