diff --git a/tutor/commands/images.py b/tutor/commands/images.py index d5469f9263f..e8b43bbd2c8 100644 --- a/tutor/commands/images.py +++ b/tutor/commands/images.py @@ -34,7 +34,7 @@ def _add_core_images_to_build( image, os.path.join("build", image), tutor_config.get_typed(config, tag, str), - (), + ("--target=production",), ) ) diff --git a/tutor/templates/build/openedx/Dockerfile b/tutor/templates/build/openedx/Dockerfile index e58b1064722..22ed8e73fa2 100644 --- a/tutor/templates/build/openedx/Dockerfile +++ b/tutor/templates/build/openedx/Dockerfile @@ -1,5 +1,10 @@ # syntax=docker/dockerfile:1.4 -###### Minimal image with base system requirements for most stages + + +########################################################################################################################### +############## MINIMAL +############## Minimal image with base system requirements for most stages +########################################################################################################################### FROM docker.io/ubuntu:20.04 as minimal LABEL maintainer="Overhang.io " @@ -11,21 +16,31 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ ENV LC_ALL en_US.UTF-8 {{ patch("openedx-dockerfile-minimal") }} -###### Install python with pyenv in /opt/pyenv and create virtualenv in /openedx/venv + +########################################################################################################################### +############## PYTHON +############## Install python with pyenv in /opt/pyenv and create virtualenv in /openedx/venv +########################################################################################################################### FROM minimal as python + # https://github.com/pyenv/pyenv/wiki/Common-build-problems#prerequisites RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt,sharing=locked \ apt update && \ apt install -y libssl-dev zlib1g-dev libbz2-dev \ libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev \ - xz-utils tk-dev libffi-dev liblzma-dev python-openssl git + xz-utils tk-dev libffi-dev liblzma-dev python-openssl git \ + software-properties-common libmysqlclient-dev libxmlsec1-dev libgeos-dev + +ARG PYTHON_VERSION=3.8.15 +ENV PYENV_ROOT /opt/pyenv +ENV PATH /openedx/venv/bin:${PATH} +ENV VIRTUAL_ENV /openedx/venv/ +ENV XDG_CACHE_HOME /openedx/.cache # Install pyenv # https://www.python.org/downloads/ # https://github.com/pyenv/pyenv/releases -ARG PYTHON_VERSION=3.8.15 -ENV PYENV_ROOT /opt/pyenv RUN git clone https://github.com/pyenv/pyenv $PYENV_ROOT --branch v2.3.17 --depth 1 # Install Python @@ -34,8 +49,21 @@ RUN $PYENV_ROOT/bin/pyenv install $PYTHON_VERSION # Create virtualenv RUN $PYENV_ROOT/versions/$PYTHON_VERSION/bin/python -m venv /openedx/venv -###### Checkout edx-platform code +# Install the right version of pip/setuptools +RUN --mount=type=cache,target=/openedx/.cache/pip,sharing=shared \ + pip install \ + # https://pypi.org/project/setuptools/ + # https://pypi.org/project/pip/ + # https://pypi.org/project/wheel/ + setuptools==67.6.1 pip==23.0.1. wheel==0.40.0 + + +########################################################################################################################### +############## CODE +########################################################################################################################### FROM minimal as code + +###### Checkout edx-platform code ARG EDX_PLATFORM_REPOSITORY={{ EDX_PLATFORM_REPOSITORY }} ARG EDX_PLATFORM_VERSION={{ EDX_PLATFORM_VERSION }} RUN mkdir -p /openedx/edx-platform && \ @@ -56,12 +84,24 @@ RUN git config --global user.email "tutor@overhang.io" \ {# Example: RUN curl -fsSL https://github.com/openedx/edx-platform/commit/.patch | git am #} {{ patch("openedx-dockerfile-post-git-checkout") }} -##### Empty layer with just the repo at the root. -# This is useful when overriding the build context with a host repo: -# docker build --build-context edx-platform=/path/to/edx-platform +# TODO: Temporary +# Apply these edx-platform changes which have not yet merged. +RUN curl -fsSL https://github.com/openedx/edx-platform/pull/32835.patch | git am + + +########################################################################################################################### +############## EDX-PLATFORM +############## Empty layer with just the repo at the root. +############## This is useful when overriding the build context with a host repo: +############## docker build --build-context edx-platform=/path/to/edx-platform +########################################################################################################################### FROM scratch as edx-platform COPY --from=code /openedx/edx-platform / + +########################################################################################################################### +############## LOCALES +########################################################################################################################### ###### Download extra locales to /openedx/locale/contrib/locale FROM minimal as locales ARG OPENEDX_I18N_VERSION={{ OPENEDX_COMMON_VERSION }} @@ -72,24 +112,185 @@ RUN cd /tmp \ && mv openedx-i18n-*/edx-platform/locale /openedx/locale/contrib \ && rm -rf openedx-i18n* -###### Install python requirements in virtualenv -FROM python as python-requirements -ENV PATH /openedx/venv/bin:${PATH} -ENV VIRTUAL_ENV /openedx/venv/ -ENV XDG_CACHE_HOME /openedx/.cache -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ - --mount=type=cache,target=/var/lib/apt,sharing=locked \ - apt update \ - && apt install -y software-properties-common libmysqlclient-dev libxmlsec1-dev libgeos-dev -# Install the right version of pip/setuptools -RUN --mount=type=cache,target=/openedx/.cache/pip,sharing=shared \ - pip install \ - # https://pypi.org/project/setuptools/ - # https://pypi.org/project/pip/ - # https://pypi.org/project/wheel/ - setuptools==67.6.1 pip==23.0.1. wheel==0.40.0 +########################################################################################################################### +############## FRONTEND REQUIREMENTS +########################################################################################################################### +FROM python as requirements-frontend + +WORKDIR /openedx/edx-platform + +# Install python reqs for building assets +RUN --mount=type=bind,from=edx-platform,source=/requirements/edx/assets.txt,target=requirements/edx/assets.txt \ + pip install -r requirements/edx/assets.txt + +# Set up node env +ENV PATH /openedx/nodeenv/bin:/openedx/venv/bin:${PATH} +RUN nodeenv /openedx/nodeenv --node=16.14.0 --prebuilt + +# Install nodejs requirements +ARG NPM_REGISTRY={{ NPM_REGISTRY }} +RUN --mount=type=bind,from=edx-platform,source=/package.json,target=package.json \ + --mount=type=bind,from=edx-platform,source=/package-lock.json,target=package-lock.json \ + --mount=type=bind,from=edx-platform,source=/scripts/copy-node-modules.sh,target=scripts/copy-node-modules.sh \ + --mount=type=cache,target=/root/.npm,sharing=shared \ + npm clean-install --no-audit --registry=$NPM_REGISTRY + +# Set up static root. +# By default, CMS static root is assumed to be $STUDIO_ROOT_LMS/studio. +ENV STATIC_ROOT_LMS=/openedx/staticfiles +RUN mkdir -p "$STATIC_ROOT_LMS" + + +########################################################################################################################### +############## BUNDLES: PRODUCTION +########################################################################################################################### +FROM requirements-frontend as bundles-production + +# Run webpack +RUN --mount=type=bind,from=edx-platform,source=/package.json,target=package.json \ + --mount=type=bind,from=edx-platform,source=/package-lock.json,target=/openedx/edx-platform/package-lock.json \ + --mount=type=bind,from=edx-platform,source=/.babelrc,target=.babelrc \ + --mount=type=bind,from=edx-platform,source=/webpack.common.config.js,target=webpack.common.config.js \ + --mount=type=bind,from=edx-platform,source=/webpack-config/file-lists.js,target=webpack-config/file-lists.js \ + --mount=type=bind,from=edx-platform,source=/webpack.dev.config.js,target=webpack.dev.config.js \ + --mount=type=bind,from=edx-platform,source=/webpack.prod.config.js,target=webpack.prod.config.js \ + --mount=type=bind,from=edx-platform,source=/webpack.builtinblocks.config.js,target=webpack.builtinblocks.config.js \ + --mount=type=bind,from=edx-platform,source=/cms/djangoapps/pipeline_js/js/xmodule.js,target=cms/djangoapps/pipeline_js/js/xmodule.js \ + --mount=type=bind,from=edx-platform,source=/cms/static,target=cms/static \ + --mount=type=bind,from=edx-platform,source=/cms/templates/,target=cms/templates/ \ + --mount=type=bind,from=edx-platform,source=/common/static/js/,target=common/static/js \ + --mount=type=bind,from=edx-platform,source=/common/static/common,target=common/static/common,rw \ + --mount=type=bind,from=requirements-frontend,source=/openedx/edx-platform/common/static/common/js/vendor,target=common/static/common/js/vendor \ + --mount=type=bind,from=requirements-frontend,source=/openedx/edx-platform/common/static/common/css/vendor,target=common/static/common/css/vendor \ + --mount=type=bind,from=edx-platform,source=/lms/djangoapps/discussion/static,target=lms/djangoapps/discussion/static \ + --mount=type=bind,from=edx-platform,source=/lms/djangoapps/instructor/static,target=lms/djangoapps/instructor/static \ + --mount=type=bind,from=edx-platform,source=/lms/djangoapps/support/static/support,target=lms/djangoapps/support/static/support \ + --mount=type=bind,from=edx-platform,source=/lms/djangoapps/teams/static,target=lms/djangoapps/teams/static \ + --mount=type=bind,from=edx-platform,source=/lms/static/,target=lms/static/ \ + --mount=type=bind,from=edx-platform,source=/lms/templates/,target=lms/templates/ \ + --mount=type=bind,from=edx-platform,source=/openedx/features/announcements/static,target=openedx/features/announcements/static \ + --mount=type=bind,from=edx-platform,source=/openedx/features/course_bookmarks/static,target=openedx/features/course_bookmarks/static \ + --mount=type=bind,from=edx-platform,source=/openedx/features/course_experience/static,target=openedx/features/course_experience/static \ + --mount=type=bind,from=edx-platform,source=/openedx/features/course_search/static,target=openedx/features/course_search/static \ + --mount=type=bind,from=edx-platform,source=/openedx/features/learner_profile/static,target=openedx/features/learner_profile/static \ + --mount=type=bind,from=edx-platform,source=/xmodule/assets,target=xmodule/assets \ + --mount=type=bind,from=edx-platform,source=/xmodule/js,target=xmodule/js \ + npm run webpack + + +########################################################################################################################### +############## BUNDLES: DEVELOPMENT +########################################################################################################################### +FROM requirements-frontend as bundles-development + +# Run webpack +RUN --mount=type=bind,from=edx-platform,source=/package.json,target=package.json \ + --mount=type=bind,from=edx-platform,source=/package-lock.json,target=/openedx/edx-platform/package-lock.json \ + --mount=type=bind,from=edx-platform,source=/.babelrc,target=.babelrc \ + --mount=type=bind,from=edx-platform,source=/webpack.common.config.js,target=webpack.common.config.js \ + --mount=type=bind,from=edx-platform,source=/webpack-config/file-lists.js,target=webpack-config/file-lists.js \ + --mount=type=bind,from=edx-platform,source=/webpack.dev.config.js,target=webpack.dev.config.js \ + --mount=type=bind,from=edx-platform,source=/webpack.prod.config.js,target=webpack.prod.config.js \ + --mount=type=bind,from=edx-platform,source=/webpack.builtinblocks.config.js,target=webpack.builtinblocks.config.js \ + --mount=type=bind,from=edx-platform,source=/cms/djangoapps/pipeline_js/js/xmodule.js,target=cms/djangoapps/pipeline_js/js/xmodule.js \ + --mount=type=bind,from=edx-platform,source=/cms/static,target=cms/static \ + --mount=type=bind,from=edx-platform,source=/cms/templates/,target=cms/templates/ \ + --mount=type=bind,from=edx-platform,source=/common/static/js/,target=common/static/js \ + --mount=type=bind,from=edx-platform,source=/common/static/common,target=common/static/common,rw \ + --mount=type=bind,from=requirements-frontend,source=/openedx/edx-platform/common/static/common/js/vendor,target=common/static/common/js/vendor \ + --mount=type=bind,from=requirements-frontend,source=/openedx/edx-platform/common/static/common/css/vendor,target=common/static/common/css/vendor \ + --mount=type=bind,from=edx-platform,source=/lms/djangoapps/discussion/static,target=lms/djangoapps/discussion/static \ + --mount=type=bind,from=edx-platform,source=/lms/djangoapps/instructor/static,target=lms/djangoapps/instructor/static \ + --mount=type=bind,from=edx-platform,source=/lms/djangoapps/support/static/support,target=lms/djangoapps/support/static/support \ + --mount=type=bind,from=edx-platform,source=/lms/djangoapps/teams/static,target=lms/djangoapps/teams/static \ + --mount=type=bind,from=edx-platform,source=/lms/static/,target=lms/static/ \ + --mount=type=bind,from=edx-platform,source=/lms/templates/,target=lms/templates/ \ + --mount=type=bind,from=edx-platform,source=/openedx/features/announcements/static,target=openedx/features/announcements/static \ + --mount=type=bind,from=edx-platform,source=/openedx/features/course_bookmarks/static,target=openedx/features/course_bookmarks/static \ + --mount=type=bind,from=edx-platform,source=/openedx/features/course_experience/static,target=openedx/features/course_experience/static \ + --mount=type=bind,from=edx-platform,source=/openedx/features/course_search/static,target=openedx/features/course_search/static \ + --mount=type=bind,from=edx-platform,source=/openedx/features/learner_profile/static,target=openedx/features/learner_profile/static \ + --mount=type=bind,from=edx-platform,source=/xmodule/assets,target=xmodule/assets \ + --mount=type=bind,from=edx-platform,source=/xmodule/js,target=xmodule/js \ + npm run webpack-dev + + +########################################################################################################################### +############## CSS: PRODUCTION +########################################################################################################################### +FROM requirements-frontend as css-production + +ENV PATH ./node_modules/.bin:${PATH} + +# Compile default CSS +RUN --mount=type=bind,from=edx-platform,source=/package.json,target=package.json \ + --mount=type=bind,from=edx-platform,source=/scripts/compile_sass.py,target=scripts/compile_sass.py \ + --mount=type=bind,from=edx-platform,source=/common/static,target=common/static \ + --mount=type=bind,from=edx-platform,source=/lms/static/sass,target=lms/static/sass \ + --mount=type=bind,from=edx-platform,source=/lms/static/sass/partials,target=lms/static/sass/partials \ + --mount=type=bind,from=edx-platform,source=/lms/static/certificates/sass,target=lms/static/certificates/sass \ + --mount=type=bind,from=edx-platform,source=/cms/static/sass,target=cms/static/sass \ + --mount=type=bind,from=edx-platform,source=/cms/static/sass/partials,target=cms/static/sass/partials \ + --mount=type=bind,from=edx-platform,source=/xmodule/assets,target=xmodule/assets \ + npm run compile-sass -- --skip-themes + +# Compile themed CSS +ENV EDX_PLATFORM_THEME_DIRS=/openedx/themes +COPY --link --chown=app:app ./themes/ /openedx/themes/ +RUN --mount=type=bind,from=edx-platform,source=/package.json,target=package.json \ + --mount=type=bind,from=edx-platform,source=/scripts/compile_sass.py,target=scripts/compile_sass.py \ + --mount=type=bind,from=edx-platform,source=/common/static,target=common/static \ + --mount=type=bind,from=edx-platform,source=/lms/static/sass,target=lms/static/sass \ + --mount=type=bind,from=edx-platform,source=/lms/static/sass/partials,target=lms/static/sass/partials \ + --mount=type=bind,from=edx-platform,source=/lms/static/certificates/sass,target=lms/static/certificates/sass \ + --mount=type=bind,from=edx-platform,source=/cms/static/sass,target=cms/static/sass \ + --mount=type=bind,from=edx-platform,source=/cms/static/sass/partials,target=cms/static/sass/partials \ + --mount=type=bind,from=edx-platform,source=/xmodule/assets,target=xmodule/assets \ + npm run compile-sass -- --skip-default + + +########################################################################################################################### +############## CSS: DEVELOPMENT +########################################################################################################################### +FROM requirements-frontend as css-development + +ENV PATH ./node_modules/.bin:${PATH} + +COPY --link --from=edx-platform --chown=app:app /lms/static/css/vendor lms/static/css/vendor + +# Compile default CSS +RUN --mount=type=bind,from=edx-platform,source=/package.json,target=package.json \ + --mount=type=bind,from=edx-platform,source=/scripts/compile_sass.py,target=scripts/compile_sass.py \ + --mount=type=bind,from=edx-platform,source=/common/static,target=common/static \ + --mount=type=bind,from=edx-platform,source=/lms/static/sass,target=lms/static/sass \ + --mount=type=bind,from=edx-platform,source=/lms/static/sass/partials,target=lms/static/sass/partials \ + --mount=type=bind,from=edx-platform,source=/lms/static/certificates/sass,target=lms/static/certificates/sass \ + --mount=type=bind,from=edx-platform,source=/cms/static/sass,target=cms/static/sass \ + --mount=type=bind,from=edx-platform,source=/cms/static/sass/partials,target=cms/static/sass/partials \ + --mount=type=bind,from=edx-platform,source=/xmodule/assets,target=xmodule/assets \ + npm run compile-sass -- --skip-themes --env=dev + +# Compile themed CSS +ENV EDX_PLATFORM_THEME_DIRS=/openedx/themes +COPY --link --chown=app:app ./themes/ /openedx/themes/ +RUN --mount=type=bind,from=edx-platform,source=/package.json,target=package.json \ + --mount=type=bind,from=edx-platform,source=/scripts/compile_sass.py,target=scripts/compile_sass.py \ + --mount=type=bind,from=edx-platform,source=/common/static,target=common/static \ + --mount=type=bind,from=edx-platform,source=/lms/static/sass,target=lms/static/sass \ + --mount=type=bind,from=edx-platform,source=/lms/static/sass/partials,target=lms/static/sass/partials \ + --mount=type=bind,from=edx-platform,source=/lms/static/certificates/sass,target=lms/static/certificates/sass \ + --mount=type=bind,from=edx-platform,source=/cms/static/sass,target=cms/static/sass \ + --mount=type=bind,from=edx-platform,source=/cms/static/sass/partials,target=cms/static/sass/partials \ + --mount=type=bind,from=edx-platform,source=/xmodule/assets,target=xmodule/assets \ + npm run compile-sass -- --skip-default --env=dev + + +########################################################################################################################### +############## APPLICATION REQUIREMENTS +########################################################################################################################### +FROM requirements-frontend as requirements-application # Install base requirements RUN --mount=type=bind,from=edx-platform,source=/requirements/edx/base.txt,target=/openedx/edx-platform/requirements/edx/base.txt \ @@ -107,38 +308,44 @@ RUN --mount=type=cache,target=/openedx/.cache/pip,sharing=shared \ {{ patch("openedx-dockerfile-post-python-requirements") }} # Install private requirements: this is useful for installing custom xblocks. -COPY ./requirements/ /openedx/requirements +COPY --link ./requirements/ /openedx/requirements RUN --mount=type=cache,target=/openedx/.cache/pip,sharing=shared \ - cd /openedx/requirements/ \ - && touch ./private.txt \ - && pip install -r ./private.txt + cd /openedx/requirements/ \ + && touch ./private.txt \ + && pip install -r ./private.txt -{% for extra_requirements in OPENEDX_EXTRA_PIP_REQUIREMENTS %} -RUN --mount=type=cache,target=/openedx/.cache/pip,sharing=shared \ - pip install '{{ extra_requirements }}' +{% for extra_requirements in OPENEDX_EXTRA_PIP_REQUIREMENTS %}RUN --mount=type=cache,target=/openedx/.cache/pip,sharing=shared pip install '{{ extra_requirements }}' {% endfor %} -###### Install nodejs with nodeenv in /openedx/nodeenv -FROM python as nodejs-requirements -ENV PATH /openedx/nodeenv/bin:/openedx/venv/bin:${PATH} -# Install nodeenv with the version provided by edx-platform -# https://github.com/openedx/edx-platform/blob/master/requirements/edx/base.txt -# https://github.com/pyenv/pyenv/releases -RUN pip install nodeenv==1.7.0 -RUN nodeenv /openedx/nodeenv --node=16.14.0 --prebuilt +########################################################################################################################### +############## PRODUCTION REQUIREMENTS +############## Simply take the general requirements, and remove everything related to libsass. +############## Alternatively, we could have based requirements-application on minimal, thus keeping libsass out of it, +############## BUT that would have required requirements-development to recompile libsass, which is slow. +########################################################################################################################### +FROM requirements-application as requirements-production + +RUN rm -r /openedx/venv/lib/python3.8/site-packages/*sass* + + +########################################################################################################################### +############## DEVELOPMENT REQUIREMENTS +########################################################################################################################### +FROM requirements-application as requirements-development + +# Install dev requirements +RUN --mount=type=bind,from=edx-platform,source=/requirements/edx/development.txt,target=/openedx/edx-platform/requirements/edx/development.txt \ + --mount=type=cache,target=/openedx/.cache/pip,sharing=shared \ + pip install -r /openedx/edx-platform/requirements/edx/development.txt -# Install nodejs requirements -ARG NPM_REGISTRY={{ NPM_REGISTRY }} -WORKDIR /openedx/edx-platform -RUN --mount=type=bind,from=edx-platform,source=/package.json,target=/openedx/edx-platform/package.json \ - --mount=type=bind,from=edx-platform,source=/package-lock.json,target=/openedx/edx-platform/package-lock.json \ - --mount=type=bind,from=edx-platform,source=/scripts/copy-node-modules.sh,target=/openedx/edx-platform/scripts/copy-node-modules.sh \ - --mount=type=cache,target=/root/.npm,sharing=shared \ - npm clean-install --no-audit --registry=$NPM_REGISTRY -###### Production image with system and python requirements -FROM minimal as production +########################################################################################################################### +############## APPLICATION +############## Application image with system and python requirements. +############## Basis for final prod and dev images. +########################################################################################################################### +FROM minimal as application # Install system requirements RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ @@ -153,18 +360,15 @@ RUN if [ "$APP_USER_ID" = 0 ]; then echo "app user may not be root" && false; fi RUN useradd --home-dir /openedx --create-home --shell /bin/bash --uid ${APP_USER_ID} app USER ${APP_USER_ID} -# https://hub.docker.com/r/powerman/dockerize/tags +# Copy in backend code and requirements. +# Dockerize tags: https://hub.docker.com/r/powerman/dockerize/tags COPY --link --from=docker.io/powerman/dockerize:0.19.0 /usr/local/bin/dockerize /usr/local/bin/dockerize -COPY --chown=app:app --from=edx-platform / /openedx/edx-platform -COPY --chown=app:app --from=locales /openedx/locale /openedx/locale -COPY --chown=app:app --from=python /opt/pyenv /opt/pyenv -COPY --chown=app:app --from=python-requirements /openedx/venv /openedx/venv -COPY --chown=app:app --from=python-requirements /openedx/requirements /openedx/requirements -COPY --chown=app:app --from=nodejs-requirements /openedx/nodeenv /openedx/nodeenv -COPY --chown=app:app --from=nodejs-requirements /openedx/edx-platform/node_modules /openedx/node_modules - -# Symlink node_modules such that we can bind-mount the edx-platform repository -RUN ln -s /openedx/node_modules /openedx/edx-platform/node_modules +COPY --link --chown=app:app --from=edx-platform / /openedx/edx-platform +COPY --link --chown=app:app ./themes/ /openedx/themes/ +COPY --link --chown=app:app --from=locales /openedx/locale /openedx/locale +COPY --link --chown=app:app --from=python /opt/pyenv /opt/pyenv +COPY --link --chown=app:app --from=requirements-production /openedx/venv /openedx/venv +COPY --link --chown=app:app --from=requirements-production /openedx/requirements /openedx/requirements ENV PATH /openedx/venv/bin:./node_modules/.bin:/openedx/nodeenv/bin:${PATH} ENV VIRTUAL_ENV /openedx/venv/ @@ -190,6 +394,20 @@ COPY --chown=app:app ./locale/ /openedx/locale/user/locale/ RUN cd /openedx/locale/user && \ django-admin compilemessages -v1 +# Copy in frontend requirements. +COPY --link --chown=app:app --from=requirements-frontend \ + /openedx/nodeenv \ + /openedx/nodeenv +COPY --link --chown=app:app --from=requirements-frontend \ + /openedx/edx-platform/node_modules \ + /openedx/edx-platform/node_modules +COPY --link --chown=app:app --from=requirements-frontend \ + /openedx/edx-platform/common/static/common/js/vendor \ + /openedx/edx-platform/common/static/common/js/vendor +COPY --link --chown=app:app --from=requirements-frontend \ + /openedx/edx-platform/common/static/common/css/vendor \ + /openedx/edx-platform/common/static/common/css/vendor + # Compile i18n strings: in some cases, js locales are not properly compiled out of the box # and we need to do a pass ourselves. Also, we need to compile the djangojs.js files for # the downloaded locales. @@ -197,54 +415,57 @@ RUN ./manage.py lms --settings=tutor.i18n compilejsi18n RUN ./manage.py cms --settings=tutor.i18n compilejsi18n # Copy scripts -COPY --chown=app:app ./bin /openedx/bin +COPY --link --chown=app:app ./bin /openedx/bin RUN chmod a+x /openedx/bin/* ENV PATH /openedx/bin:${PATH} -{{ patch("openedx-dockerfile-pre-assets") }} - -# Collect production assets. By default, only assets from the default theme -# will be processed. This makes the docker image lighter and faster to build. -# Only the custom themes added to /openedx/themes will be compiled. -# Here, we don't run "paver update_assets" which is slow, compiles all themes -# and requires a complex settings file. Instead, we decompose the commands -# and run each one individually to collect the production static assets to -# /openedx/staticfiles. -ENV NO_PYTHON_UNINSTALL 1 -ENV NO_PREREQ_INSTALL 1 -# We need to rely on a separate openedx-assets command to accelerate asset processing. -# For instance, we don't want to run all steps of asset collection every time the theme -# is modified. -RUN openedx-assets xmodule \ - && openedx-assets npm \ - && openedx-assets webpack --env=prod \ - && openedx-assets common -COPY --chown=app:app ./themes/ /openedx/themes/ -RUN openedx-assets themes \ - && openedx-assets collect --settings=tutor.assets \ - # De-duplicate static assets with symlinks - && rdfind -makesymlinks true -followsymlinks true /openedx/staticfiles/ - # Create a data directory, which might be used (or not) RUN mkdir /openedx/data -# If this "canary" file is missing from a container, then that indicates that a -# local edx-platform was bind-mounted into that container, thus overwriting the -# canary. This information is useful during edx-platform initialisation. -RUN echo \ - "This copy of edx-platform was built into a Docker image." \ - > bindmount-canary - # service variant is "lms" or "cms" ENV SERVICE_VARIANT lms -ENV DJANGO_SETTINGS_MODULE lms.envs.tutor.production {{ patch("openedx-dockerfile") }} EXPOSE 8000 -###### Intermediate image with dev/test dependencies -FROM production as development + +########################################################################################################################### +############## PRODUCTION +########################################################################################################################### +FROM application as production + +# Copy in and collect production static assets. +COPY --link --chown=app:app --from=bundles-production /openedx/staticfiles /openedx/staticfiles +COPY --link --chown=app:app --from=bundles-production /openedx/edx-platform/common/static/bundles common/static/bundles +COPY --link --chown=app:app --from=css-production /openedx/edx-platform/lms/static/css lms/static/css +COPY --link --chown=app:app --from=css-production /openedx/edx-platform/lms/static/certificates/css lms/static/certificates/css +COPY --link --chown=app:app --from=css-production /openedx/edx-platform/cms/static/css cms/static/css +RUN ./manage.py lms collectstatic --noinput --settings=tutor.assets +RUN ./manage.py cms collectstatic --noinput --settings=tutor.assets + +# De-dupe static assets with synlinks +RUN rdfind -makesymlinks true -followsymlinks true /openedx/staticfiles/ + +# Default amount of uWSGI processes +ENV UWSGI_WORKERS=2 + +# Copy the default uWSGI configuration +COPY --chown=app:app settings/uwsgi.ini . + +# Default django settings +ENV DJANGO_SETTINGS_MODULE lms.envs.tutor.production + +# Run server +CMD uwsgi uwsgi.ini + +{{ patch("openedx-dockerfile-final") }} + + +########################################################################################################################### +############## DEVELOPMENT +########################################################################################################################### +FROM application as development # Install useful system requirements (as root) USER root @@ -254,9 +475,9 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ apt install -y vim iputils-ping dnsutils telnet USER app -# Install dev python requirements -RUN --mount=type=cache,target=/openedx/.cache/pip,sharing=shared \ - pip install -r requirements/edx/development.txt +# Copy in dev python requirements +COPY --link --chown=app:app --from=requirements-development /openedx/venv /openedx/venv + # https://pypi.org/project/ipdb/ # https://pypi.org/project/ipython RUN --mount=type=cache,target=/openedx/.cache/pip,sharing=shared \ @@ -265,12 +486,14 @@ RUN --mount=type=cache,target=/openedx/.cache/pip,sharing=shared \ # Add ipdb as default PYTHONBREAKPOINT ENV PYTHONBREAKPOINT=ipdb.set_trace -# Recompile static assets: in development mode all static assets are stored in edx-platform, -# and the location of these files is stored in webpack-stats.json. If we don't recompile -# static assets, then production assets will be served instead. -RUN rm -r /openedx/staticfiles && \ - mkdir /openedx/staticfiles && \ - openedx-assets webpack --env=dev +# Copy in development static assets. +# In development mode, edx-platform expects the files to be in the repo, and pointed to by webpack-stats.json. +COPY --link --chown=app:app --from=bundles-development /openedx/staticfiles/webpack-stats.json /openedx/staticfiles/webpack-stats.json +COPY --link --chown=app:app --from=bundles-development /openedx/staticfiles/studio/webpack-stats.json /openedx/staticfiles/studio/webpack-stats.json +COPY --link --chown=app:app --from=bundles-development /openedx/edx-platform/common/static/bundles common/static/bundles +COPY --link --chown=app:app --from=css-development /openedx/edx-platform/lms/static/css lms/static/css +COPY --link --chown=app:app --from=css-development /openedx/edx-platform/lms/static/certificates/css lms/static/certificates/css +COPY --link --chown=app:app --from=css-development /openedx/edx-platform/cms/static/css cms/static/css {{ patch("openedx-dev-dockerfile-post-python-requirements") }} @@ -278,18 +501,3 @@ RUN rm -r /openedx/staticfiles && \ ENV DJANGO_SETTINGS_MODULE lms.envs.tutor.development CMD ./manage.py $SERVICE_VARIANT runserver 0.0.0.0:8000 - -###### Final image with production cmd -FROM production as final - -# Default amount of uWSGI processes -ENV UWSGI_WORKERS=2 - -# Copy the default uWSGI configuration -COPY --chown=app:app settings/uwsgi.ini . - -# Run server -CMD uwsgi uwsgi.ini - -{{ patch("openedx-dockerfile-final") }} -