From 37896b0c324fb79a0a9bd1e49fa403cb4404ed73 Mon Sep 17 00:00:00 2001 From: Tom Schraitle Date: Sat, 26 Jul 2025 11:28:55 +0200 Subject: [PATCH] Improve Dockerfile and documentation * Base the Docker image on openSUSE Leap 15.6 * Introduce `etc/docbuild/env.docker.toml` for a env config especially for Docker images * Update `.dockerignore` * Simplify the Dockerfile * Add new topic about building and running the Docker container --- .dockerignore | 25 +++- Dockerfile | 113 +++++++++++------- changelog.d/69.doc.rst | 1 + docs/source/developer/index.rst | 2 +- docs/source/developer/prepare-environment.rst | 6 +- docs/source/developer/work-with-docker.rst | 76 ++++++++++++ etc/docbuild/env.docker.toml | 90 ++++++++++++++ 7 files changed, 264 insertions(+), 49 deletions(-) create mode 100644 changelog.d/69.doc.rst create mode 100644 docs/source/developer/work-with-docker.rst create mode 100644 etc/docbuild/env.docker.toml diff --git a/.dockerignore b/.dockerignore index 86bf82d0..0a23eaf7 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,6 @@ # Version control .git +.github/ .gitignore # Dependencies & build artifacts @@ -12,6 +13,26 @@ build *.pyd # Development files +.cache +.config.toml +.config +.coverage +.coveragerc +ci/ +devel/ +Dockerfile* +.dockerignore +.editorconfig +env.*.toml +etc/ +.ipython/ +LICENSE +.pytest_cache +.pytest.ini +.python-Version +.ruff_cache +.ruff.toml +towncrier.toml contrib/ tmp/ .env @@ -19,7 +40,9 @@ tmp/ *.log coverage .venv - +.venv-* +*.patch +*.diff # IDE files .vscode diff --git a/Dockerfile b/Dockerfile index 903f36c2..54e030da 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,66 +1,89 @@ # Source: https://docs.astral.sh/uv/guides/integration/docker/#non-editable-installs # +# Build with a pre-configured DAPS toolchain image from openSUSE. +# This version builds a wheel in a builder stage and installs it in a +# clean runtime environment in the final stage. +# # Build it with: # $ docker build -t docbuild:latest . -# -- or -- -# $ docker buildx build -t docbuild:latest . # -# If you want to skip the jing installation step, use: -# $ docker build --build-arg WITH_JING=false -t docbuild:latest . -ARG PYTHON_VERSION=3.13-slim +ARG OPENSUSE_VERSION=15.6 +ARG IMAGE="registry.opensuse.org/documentation/containers/${OPENSUSE_VERSION}/opensuse-daps-toolchain:latest" -# ------- Stage 1: Build the environment ---------------- -FROM python:${PYTHON_VERSION} AS builder +# ------- Stage 1: Build the runtime environment ---------------- +FROM ${IMAGE} AS builder -# Create a non-root user +# Create a non-root user. RUN useradd -m app -USER app -# Install uv +# Install uv. COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ -# Change the working directory -WORKDIR /app +# Set the working directory to the user's home. +WORKDIR /home/app -# Install dependencies -RUN --mount=type=cache,target=/root/.cache/uv \ - --mount=type=bind,source=uv.lock,target=uv.lock \ - --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ - uv sync --frozen --no-install-project --no-editable +# Copy all project source files. +COPY . . -# Copy the project into the intermediate image -ADD --chown=app:app . /app - -# Sync the project +# Build the wheel, create a venv, and install the wheel into it. +# This is all done as root to avoid cache permission issues. RUN --mount=type=cache,target=/root/.cache/uv \ - uv sync --frozen --no-editable - -# ------- Stage 2: Build/provide the application -------- -FROM python:${PYTHON_VERSION} - -# Allow conditional installation of jing for XML validation -ARG WITH_JING=true - -# Install runtime dependencies like jing for XML validation -RUN if [ "$WITH_JING" = "true" ]; then \ - apt-get update && apt-get install -y --no-install-recommends jing && rm -rf /var/lib/apt/lists/*; \ - fi - -# Create a non-root user to match the builder stage + set -e; export HOME=/home/app && \ + uv build --wheel && \ + uv venv && \ + uv pip install dist/*.whl + +# Fix permissions for the runtime files. +RUN chown -R app:users /home/app + +# ------- Stage 2: Create the final, lean image -------- +FROM ${IMAGE} + +# --- OPTIMIZATION STEP --- +# As root, remove unnecessary files to reduce the final image size. +# This must be done as root, before creating the 'app' user. +# RUN set -x; \ +# rpm -v --erase --nodeps --force python3-cssselect python3 python3-base python3-lxml python3-gobject \ +# ca-certificates cracklib cups-config diffutils fdupes \ +# gio-branding-openSUSE gstreamer gtk2-tools gtk3-data gtk3-schema gtk3-tools \ +# hicolor-icon-theme info ncurses-utils netcfg openSUSE-release perl5 pinentry \ +# Mesa Mesa-dri Mesa-gallium Mesa-libEGL1 Mesa-libGL1 libglvnd libgstgl; \ +# rm -rf /usr/include \ +# /usr/lib/{browser-plugins,gstreamer-*,ca-certificates,keyboxd,locale,perl5,git,gpg-*,getconf,scdaemon,ssh,systemd,tmpfiles.d} \ +# /usr/local/* \ +# /usr/sbin/{fdisk,sfdisk,g13-syshelp,fsck.minix,partx,mkswap,zramctl} \ +# /var/log/* \ +# /var/cache/{zypp,ldconfig,fontconfig,cups} \ +# /var/adm/* \ +# /var/lib/{YaST2,alternatives,ca-certificates,selinux,xkb,misc} || true + +# --- DIAGNOSTIC STEP --- +# Add this temporary command to see the size of top-level directories +# before the cleanup step. This helps identify what is taking up space. +# RUN du -sh /usr/lib/* | sort -rh | head -n 20 > /du-usrlib-sort.txt + + +# Create the same non-root user. RUN useradd -m app -# Copy the environment, but not the source code -COPY --from=builder --chown=app:app /app/.venv /app/.venv +# Copy only the essential runtime directories from the builder. +# This results in a lean final image without build artifacts or source code. +COPY --from=builder --chown=app:users /home/app/.venv /home/app/.venv +COPY --from=builder --chown=app:users /home/app/.local /home/app/.local -# Set the working directory -WORKDIR /app +# Switch to the non-root user for security. +USER app -# Add the virtual environment's bin directory to the PATH -ENV PATH="/app/.venv/bin:${PATH}" +# Set the working directory. +WORKDIR /home/app -# Switch to the non-root user for security -USER app +# Set the PATH to include the virtual environment's bin directory. +ENV PATH="/home/app/.venv/bin:${PATH}" +ENV LANG=en_US.UTF-8 +ENV LC_ALL=en_US.UTF-8 +ENV TERM=xterm-256color -# Run the application -CMD ["docbuild"] +# Run the application. +# ENTRYPOINT [ "docbuild" ] +# CMD ["docbuild", "--env-config", "env-production.toml", "--help"] diff --git a/changelog.d/69.doc.rst b/changelog.d/69.doc.rst new file mode 100644 index 00000000..38f7aee9 --- /dev/null +++ b/changelog.d/69.doc.rst @@ -0,0 +1 @@ +Improve Dockerfile and documentation diff --git a/docs/source/developer/index.rst b/docs/source/developer/index.rst index 3eb8b486..1b5df692 100644 --- a/docs/source/developer/index.rst +++ b/docs/source/developer/index.rst @@ -28,7 +28,7 @@ This guide provides all the necessary information for contributing to the projec build-changelog create-release trigger-actions - + work-with-docker build-docs howto diff --git a/docs/source/developer/prepare-environment.rst b/docs/source/developer/prepare-environment.rst index 17045b61..7e03c8b3 100644 --- a/docs/source/developer/prepare-environment.rst +++ b/docs/source/developer/prepare-environment.rst @@ -75,7 +75,7 @@ The following steps are recommended to set up your development environment: uv venv --prompt venv313 --python 3.13 .venv Keep in mind that the Python version used in the virtual environment should match the version specified in the :file:`pyproject.toml` file. - + The example above uses Python 3.13, but you can adjust it according to your needs as long as it is compatible with the project. See file :file:`pyproject.toml` in ``project.requires-python`` for the exact version. @@ -101,6 +101,8 @@ The following steps are recommended to set up your development environment: After completing these steps, your development environment is ready to go. +.. _get-docserv-config: + Getting Docserv's Config Files ------------------------------ @@ -116,4 +118,4 @@ To get started, clone this repository to your local machine (you need VPN access git clone https://gitlab.suse.de/susedoc/docserv-config.git You will later need to point ``docbuild`` to the location of this cloned -repository in your configuration. \ No newline at end of file +repository in your configuration. diff --git a/docs/source/developer/work-with-docker.rst b/docs/source/developer/work-with-docker.rst new file mode 100644 index 00000000..71fa60fc --- /dev/null +++ b/docs/source/developer/work-with-docker.rst @@ -0,0 +1,76 @@ +.. _work-docker: + +Working with Docker Images +========================== + +Sometimes it is easier to work with a Docker container that contains all +the necessary dependencies. + + +Requirements +------------ + +To build a Docker image and use it as container, check the following +requirements: + +* Docker installed and running on your system: + + * For openSUSE: Run :command:`sudo zypper install docker` to install it and :command:`sudo systemctl start docker` to start the Docker daemon. + + * For MacOS: Refer to https://docs.docker.com/desktop/setup/install/mac-install/. + +* Clone the respective configuration repository. Refer to :ref:`get-docserv-config`. + + +Creating a Docker image +----------------------- + +To create a Docker image, use the following command: + +.. code-block:: shell-session + :caption: Building the image + + docker buildx build -t docbuild:latest . + +This creates a Docker image ``docbuild`` with the tag ``latest``. After the successful build, you see: + +.. code-block:: shell-session + :caption: Listing the Docker image + + $ docker image ls docbuild + REPOSITORY TAG IMAGE ID CREATED SIZE + docbuild latest c13d36935907 16 minutes ago 643MB + + +Running the Docker container +---------------------------- + +Before you start the Docker container, you need to collect some paths and file names: + +* The path to the configuration repository, marked as ``CONFIG_DIR``. +* The path to the environment file, marked as ``ENV_FILE``. Usually you want to use :file:`$PWD/etc/env-docker.toml` from this repository. +* The path to the cache directory, marked as ``CACHE_DIR``. Under Linux it's usually :file:`/var/cache/docbuild` +* The path of the target directory (the result of all ), marked as ``TARGET_DIR``. + + +.. admonition:: Make absolute paths + + Use absolute paths for the previous variables. + + +Running the Docker container based on the previous image, use this command: + +.. code-block:: shell-session + :caption: Running the Docker container + + export CONFIG_DIR="..." + export ENV_FILE="..." + export CACHE_DIR="..." + export TARGET_DIR="..." + docker run --it \ + -v $CONFIG_DIR:/etc/docbuild \ + -v $ENV_FILE:/app/.env-production.toml \ + -v $CACHE_DIR:/var/cache/docbuild/ \ + -v $TARGET_DIR:/data/docbuild/external-builds/ \ + docbuild:latest \ + DOCBUILD_COMMAND diff --git a/etc/docbuild/env.docker.toml b/etc/docbuild/env.docker.toml new file mode 100644 index 00000000..aea197c5 --- /dev/null +++ b/etc/docbuild/env.docker.toml @@ -0,0 +1,90 @@ +# TOML example configuration file +# + +[server] +# Section "server": deals with all config about server settings +name = "doc-suse-com" +role = "production" +host = "127.0.0.1" +# port = +enable_mail = true + + +[config] +# Section "config": general configuration +default_lang = "en-us" +languages = [ + 'de-de', + 'en-us', + 'es-es', + 'fr-fr', + 'ja-jp', + 'ko-kr', + 'pt-br', + 'zh-cn', +] +canonical_url_domain = "https://docs.example.com" + + +[paths] +# Section "paths": Defines several paths +# Paths can hold placeholders in brackets. +root_config_dir = "/etc/docbuild" +jinja_dir = "{root_config_dir}/jinja-doc-suse-com" +config_dir = "{root_config_dir}/config.d" +server_rootfiles_dir = "{root_config_dir}/server-root-files-doc-suse-com" +# +base_cache_dir = "/var/cache/docbuild" +base_server_cache_dir = "{base_cache_dir}/{server.name}" +base_tmp_dir = "{base_cache_dir}/tmp" +repo_dir = "{base_cache_dir}/repos/permanent-full/" +temp_repo_dir = "{base_cache_dir}/repos/temporary-branches/" +# cache_dir = "{base_cache_dir}/{server.name}" +meta_cache_dir = "{base_cache_dir}/{server.name}/meta" + +[paths.tmp] +# Section "paths.tmp": Definies temporary paths +# Paths can hold placeholders in brackets. +tmp_base_dir = "{paths.base_tmp_dir}" +tmp_dir = "{tmp_base_dir}/doc-example-com" +tmp_metadata_dir = "{tmp_dir}/metadata" +tmp_deliverable_dir = "{paths.tmp.tmp_dir}/deliverable/" +tmp_build_dir = "{tmp_dir}/build/{{product}}-{{docset}}-{{lang}}" +tmp_out_dir = "{tmp_dir}/out/" +log_dir = "{tmp_dir}/log/" +tmp_deliverable_name = "{{product}}_{{docset}}_{{lang}}_XXXXXX" + +[paths.target] +# Section "paths.target": Definies target paths +target_dir = "doc@10.100.60.1:/srv/docs" +backup_dir = "/data/docbuild/external-builds/" + + +[build] +# Section "build": General build parameters, independant from any specific + +[build.daps] +# Section "build.daps": Configuration for daps +command = "daps -vv" +meta = "daps -vv metadata --output {{output}}" +# html = "daps -vv --builddir='{paths.tmp.tmp_build_dir}' html" +# pdf = "daps -vv --builddir='{paths.tmp.tmp_build_dir}' pdf" + + +[build.container] +# Section "build.container": Configuration for container +container = "registry.opensuse.org/documentation/containers/15.6/opensuse-daps-toolchain:latest" + +[xslt-params] +# Section "xslt-params": Replaces /etc/docserv/xslt-params-doc-suse-com.txt file +# These keys are considered as XSLT parameters and passed +# to the transformation process +homepage = "https://documentation.suse.com/" +overview-page = "https://documentation.suse.com/" +overview-page-title = "documentation.suse.com" +external.js.onlineonly = "/docserv/res/extra.js" +show.edit.link = 1 +twittercards.twitter.account = "@SUSE" +generate.json-ld = 1 +search.description.length = 118 +socialmedia.description.length = 65