diff --git a/Makefile b/Makefile index dc1e70b51e..df5ed04c90 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,9 @@ # standalone install method -DOCKER_COMPOSE = docker-compose +DOCKER_COMPOSE ?= docker-compose # support new plugin installation for docker-compose -ifeq (, $(shell which docker-compose)) -DOCKER_COMPOSE = docker compose +ifeq (, $(shell command -v docker-compose 2>/dev/null)) +DOCKER_COMPOSE := docker compose endif ############################################################### @@ -196,8 +196,8 @@ dctest: .docker/minio .docker/postgres dcservicesup: .docker/minio .docker/postgres # launch all studio's dependent services using docker-compose - $(DOCKER_COMPOSE) -f docker-compose.yml -f docker-compose.alt.yml up minio postgres redis + $(DOCKER_COMPOSE) up minio postgres redis dcservicesdown: # stop services that were started using dcservicesup - $(DOCKER_COMPOSE) -f docker-compose.yml -f docker-compose.alt.yml down + $(DOCKER_COMPOSE) down diff --git a/docker-compose.alt.yml b/docker-compose.alt.yml deleted file mode 100644 index acf01a97e3..0000000000 --- a/docker-compose.alt.yml +++ /dev/null @@ -1,16 +0,0 @@ -version: '3.4' - -# Alternate config that maps services to host ports so you can run everything but the Django -# and webpack servers in Docker -# -# Usage: -# docker-compose -f docker-compose.yml -f docker-compose.alt.yml up minio postgres redis - -services: - postgres: - ports: - - "5432:5432" - - redis: - ports: - - "6379:6379" diff --git a/docker-compose.yml b/docker-compose.yml index 719fc797ed..dd3341f83e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,3 @@ -version: '3.4' - x-studio-environment: &studio-environment MPLBACKEND: ps @@ -15,7 +13,7 @@ x-studio-environment: CELERY_RESULT_BACKEND_ENDPOINT: redis CELERY_REDIS_PASSWORD: "" REMAP_SIGTERM: "SIGQUIT" - PROBER_STUDIO_BASE_URL: http://studio-app:8080/{path} + WEBPACK_DEV_HOST: 0.0.0.0 x-studio-worker: &studio-worker @@ -77,12 +75,16 @@ services: POSTGRES_USER: learningequality POSTGRES_PASSWORD: kolibri POSTGRES_DB: kolibri-studio + ports: + - "5432:5432" volumes: - pgdata:/var/lib/postgresql/data/pgdata - .docker/postgres:/docker-entrypoint-initdb.d redis: image: redis:6.0.9 + ports: + - "6379:6379" volumes: diff --git a/docker/Dockerfile.dev b/docker/Dockerfile.dev index f11338a199..b45370df4d 100644 --- a/docker/Dockerfile.dev +++ b/docker/Dockerfile.dev @@ -1,4 +1,4 @@ -FROM python:3.10-slim-bookworm +FROM ghcr.io/astral-sh/uv:python3.10-trixie-slim # Set the timezone RUN ln -fs /usr/share/zoneinfo/America/Los_Angeles /etc/localtime @@ -10,26 +10,28 @@ WORKDIR /src # System packages ############################################################## -ENV DEBIAN_FRONTEND noninteractive +ENV DEBIAN_FRONTEND=noninteractive # Default Python file.open file encoding to UTF-8 instead of ASCII, workaround for le-utils setup.py issue -ENV LANG C.UTF-8 +ENV LANG=C.UTF-8 + RUN apt-get update && \ - apt-get -y install \ - curl fish man \ - python3-pip python3-dev \ - gcc libpq-dev libssl-dev libffi-dev make git gettext libjpeg-dev ffmpeg - -# Pin, Download and install node 18.x -RUN apt-get update \ - && apt-get install -y ca-certificates curl gnupg \ - && mkdir -p /etc/apt/keyrings \ - && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \ - && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \ - && echo "Package: nodejs" >> /etc/apt/preferences.d/preferences \ - && echo "Pin: origin deb.nodesource.com" >> /etc/apt/preferences.d/preferences \ - && echo "Pin-Priority: 1001" >> /etc/apt/preferences.d/preferences\ - && apt-get update \ - && apt-get install -y nodejs + apt-get -y install --no-install-recommends \ + curl \ + fish \ + man \ + gcc \ + libpq-dev \ + libssl-dev \ + libffi-dev \ + make \ + git \ + gettext \ + libjpeg-dev \ + ffmpeg \ + && rm -rf /var/lib/apt/lists/* + +# Copy Node.js from its image (does a merge) +COPY --from=node:20-trixie-slim /usr/local /usr/local ################################################################################ @@ -42,11 +44,9 @@ RUN pnpm install # Python packages ############################################################## COPY requirements.txt requirements-dev.txt /src/ -RUN pip install --no-cache-dir --upgrade pip # install pinned deps from pip-tools into system -RUN pip install -r requirements.txt -RUN pip install -r requirements-dev.txt +RUN uv pip sync --system requirements.txt requirements-dev.txt ################################################################################ diff --git a/docker/Dockerfile.prod b/docker/Dockerfile.prod index a9477f90ed..1934f10c63 100644 --- a/docker/Dockerfile.prod +++ b/docker/Dockerfile.prod @@ -1,43 +1,52 @@ -FROM python:3.10-slim-bookworm +FROM node:20-trixie-slim AS node-builder + +WORKDIR /contentcuration + +COPY package.json pnpm-lock.yaml ./ +RUN corepack enable pnpm && pnpm install --frozen-lockfile + +COPY . . + +# Ensure expected bundles directory exists and run production frontend build +RUN mkdir -p contentcuration/static/js/bundles \ + && ln -s /contentcuration/node_modules /contentcuration/contentcuration/node_modules \ + && pnpm run build + + +FROM ghcr.io/astral-sh/uv:python3.10-trixie-slim # Set the timezone RUN ln -fs /usr/share/zoneinfo/America/Los_Angeles /etc/localtime -ENV DEBIAN_FRONTEND noninteractive +ENV DEBIAN_FRONTEND=noninteractive # Default Python file.open file encoding to UTF-8 instead of ASCII, workaround for le-utils setup.py issue -ENV LANG C.UTF-8 -RUN apt-get update && apt-get -y install python3-pip python3-dev gcc libpq-dev libssl-dev libffi-dev make git curl libjpeg-dev ffmpeg +ENV LANG=C.UTF-8 -# Pin, Download and install node 18.x RUN apt-get update \ - && apt-get install -y ca-certificates curl gnupg \ - && mkdir -p /etc/apt/keyrings \ - && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \ - && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \ - && echo "Package: nodejs" >> /etc/apt/preferences.d/preferences \ - && echo "Pin: origin deb.nodesource.com" >> /etc/apt/preferences.d/preferences \ - && echo "Pin-Priority: 1001" >> /etc/apt/preferences.d/preferences\ - && apt-get update \ - && apt-get install -y nodejs - -RUN corepack enable pnpm + && apt-get -y install --no-install-recommends \ + gcc \ + libpq-dev \ + libssl-dev \ + libffi-dev \ + make \ + git \ + curl \ + gettext \ + libjpeg-dev \ + ffmpeg \ + && rm -rf /var/lib/apt/lists/* -COPY ./package.json . -COPY ./pnpm-lock.yaml . -RUN pnpm install +WORKDIR /contentcuration -COPY requirements.txt . +COPY requirements.txt /contentcuration/ -RUN pip install --upgrade pip -RUN pip install --ignore-installed -r requirements.txt +# Install Python dependencies +RUN uv pip sync --system /contentcuration/requirements.txt -COPY . /contentcuration/ -WORKDIR /contentcuration +COPY . /contentcuration/ -# generate the node bundles -RUN mkdir -p contentcuration/static/js/bundles -RUN ln -s /node_modules /contentcuration/node_modules -RUN pnpm run build +# Copy compiled frontend static output from node-builder +COPY --from=node-builder /contentcuration/contentcuration/static /contentcuration/contentcuration/static ARG COMMIT_SHA ENV RELEASE_COMMIT_SHA=$COMMIT_SHA diff --git a/webpack.config.js b/webpack.config.js index d055936b79..7da3a2dd61 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -16,7 +16,12 @@ const WebpackRTLPlugin = require('kolibri-tools/lib/webpackRtlPlugin'); const { InjectManifest } = require('workbox-webpack-plugin'); -// Function to detect if running in WSL +const DEFAULT_WEBPACK_DEV_HOST = '127.0.0.1'; + +/** + * Function to detect if running in WSL + * @return {boolean} + */ function isWSL() { try { const version = fs.readFileSync('/proc/version', 'utf8'); @@ -26,14 +31,24 @@ function isWSL() { } } -// Function to get WSL IP address -function getWSLIP() { +/** + * Get the host for the webpack dev server. + * @return {string} + */ +function getWebpackDevHost() { + if (process.env.WEBPACK_DEV_HOST) { + return process.env.WEBPACK_DEV_HOST; + } + + if (!isWSL()) { + return DEFAULT_WEBPACK_DEV_HOST; + } + try { - const ip = execSync('hostname -I').toString().trim().split(' ')[0]; - return ip; + return execSync('hostname -I').toString().trim().split(' ')[0]; } catch (err) { console.warn('Failed to get WSL IP address:', err); - return '127.0.0.1'; + return DEFAULT_WEBPACK_DEV_HOST; } } @@ -60,11 +75,8 @@ module.exports = (env = {}) => { const pnpmNodeModules = path.join(rootDir, 'node_modules', '.pnpm', 'node_modules'); // Determine the appropriate dev server host and public path based on environment - const isWSLEnvironment = isWSL(); - const devServerHost = isWSLEnvironment ? '0.0.0.0' : '127.0.0.1'; - const devPublicPath = isWSLEnvironment ? - `http://${getWSLIP()}:4000/dist/` : - 'http://127.0.0.1:4000/dist/'; + const devServerHost = getWebpackDevHost(); + const devPublicPath = `http://${devServerHost}:4000/dist/`; const workboxPlugin = new InjectManifest({ swSrc: path.resolve(srcDir, 'serviceWorker/index.js'), @@ -120,10 +132,8 @@ module.exports = (env = {}) => { allowedHosts: [ '127.0.0.1', 'localhost', - ].concat( - // For WSL, allow the WSL IP address - isWSLEnvironment ? [getWSLIP()] : [] - ), + getWebpackDevHost(), + ] }, module: { rules: [