adrienbrignon / mkdocs-exporter

⚡ The fastest and most configurable plugin for MkDocs, allowing seamless export of individual pages and/or entire documentation as PDF documents.
https://adrienbrignon.github.io/mkdocs-exporter/
MIT License
81 stars 8 forks source link

The `mkdocs-exporter` pypi library fails to install into the latest, official `mkdocs-material` Docker container image #28

Closed codrcodz closed 5 months ago

codrcodz commented 5 months ago

This issue from the microsoft/playwright-python project appears to be related.

TL;DR: Since mkdocs-exporter relies on playwright and (as of 2024-06-20) it does not provide install instructions or a package for Alpine, without deep diving into what Alpine system-level dependencies are required (and if they are even available via official Alpine repos), there is no way to install mkdocs-exporter into Alpine-based docker images. Those that attempt an install will receive playwright related errors below. Unfortunately, the offical mkdocs-material, is Alpine based at this time. So a supported theme (material) is only available to mkdocs-exporter+mkdocs-material users if they manually install mkdocs-material and mkdocs-exporter onto a playwright supported OS (like Debian). This issue provides a sample Dockerfile that does exactly this and works as of 2024-06-20 with the current latest versions of mkdocs-material, mkdocs-exporter, and the playwright Pypi packages; however, it would be great if mkdocs-exporter developers could figure out how to allow for Alpine based installs or (at a mimimum) advertise that they do not support Alpine-based deployments.

Expected Behavior

Given I attempt to install mkdocs-exporter into the official, latest docker.io/squidfunk/mkdocs-material:9.5.27 (v9.5.27 at the time of this bug report) Docker container image like so,

FROM docker.io/squidfunk/mkdocs-material:9.5.27
RUN pip install mkdocs-exporter

as described by the official mkdocs-material documentation for installing additional python plugin libraries,

When I execute a docker build command, like so,

 docker build -t mkdocs-material-with-exporter .

Then the Docker container image builds successfully and mkdocs-exporter functions as described in the documentation when using it to export mkdocs sites as PDFs.

Observed Behavior

Given I attempt to install mkdocs-exporter into the official, latest docker.io/squidfunk/mkdocs-material:9.5.27 (v9.5.27 at the time of this bug report) Docker container image like so,

FROM docker.io/squidfunk/mkdocs-material:9.5.27
RUN pip install mkdocs-exporter

as described by the official mkdocs-material documentation for installing additional python plugin libraries,

When I execute a docker build command, like so,

 docker build -t mkdocs-material-with-exporter .

Then the Docker container image fails to build, and provides the following error messages:

[+] Building 5.5s (6/6) FINISHED                                                                      docker:default
 => [internal] load build definition from Dockerfile                                                            0.0s
 => => transferring dockerfile: 117B                                                                            0.0s
 => [internal] load metadata for docker.io/squidfunk/mkdocs-material:9.5.27                                     0.5s
 => [auth] squidfunk/mkdocs-material:pull token for registry-1.docker.io                                        0.0s
 => [internal] load .dockerignore                                                                               0.0s
 => => transferring context: 2B                                                                                 0.0s
 => CACHED [1/2] FROM docker.io/squidfunk/mkdocs-material:9.5.27@sha256:257eca88da7f42242cd05e8cebf6d10ebd079e  0.0s
 => ERROR [2/2] RUN pip install mkdocs-exporter                                                                 4.9s
------                                                                                                               
 > [2/2] RUN pip install mkdocs-exporter:                                                                            
1.554 Collecting mkdocs-exporter                                                                                     
1.635   Downloading mkdocs_exporter-6.0.0-py3-none-any.whl.metadata (5.5 kB)                                         
1.752 Collecting beautifulsoup4>=4.12.2 (from mkdocs-exporter)                                                       
1.766   Downloading beautifulsoup4-4.12.3-py3-none-any.whl.metadata (3.8 kB)                                         
1.998 Collecting libsass>=0.22.0 (from mkdocs-exporter)
2.010   Downloading libsass-0.23.0.tar.gz (316 kB)
2.077      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 316.1/316.1 kB 5.3 MB/s eta 0:00:00
2.119   Preparing metadata (setup.py): started
2.740   Preparing metadata (setup.py): finished with status 'done'
3.005 Collecting lxml>=4.9 (from mkdocs-exporter)
3.019   Downloading lxml-5.2.2-cp311-cp311-musllinux_1_2_x86_64.whl.metadata (3.4 kB)
3.028 Requirement already satisfied: mkdocs>=1.4 in /usr/local/lib/python3.11/site-packages (from mkdocs-exporter) (1.6.0)
3.114 INFO: pip is looking at multiple versions of mkdocs-exporter to determine which version is compatible with other requirements. This could take a while.
3.114 Collecting mkdocs-exporter
3.127   Downloading mkdocs_exporter-5.3.1-py3-none-any.whl.metadata (5.8 kB)
3.243 Collecting importlib-metadata>=6.0 (from mkdocs-exporter)
3.258   Downloading importlib_metadata-7.1.0-py3-none-any.whl.metadata (4.7 kB)
3.355 Collecting importlib-resources>=5.0 (from mkdocs-exporter)
3.367   Downloading importlib_resources-6.4.0-py3-none-any.whl.metadata (3.9 kB)
3.377 Requirement already satisfied: mkdocs-material<10.0.0,>=9.4.14 in /usr/local/lib/python3.11/site-packages (from mkdocs-material[imaging]<10.0.0,>=9.4.14->mkdocs-exporter) (9.5.27)
3.456 Collecting nest-asyncio>=1.5.6 (from mkdocs-exporter)
3.469   Downloading nest_asyncio-1.6.0-py3-none-any.whl.metadata (2.8 kB)
3.480 Collecting mkdocs-exporter
3.495   Downloading mkdocs_exporter-5.3.0-py3-none-any.whl.metadata (5.8 kB)
3.536   Downloading mkdocs_exporter-5.2.1-py3-none-any.whl.metadata (5.6 kB)
3.567   Downloading mkdocs_exporter-5.2.0-py3-none-any.whl.metadata (5.6 kB)
3.602   Downloading mkdocs_exporter-5.1.0-py3-none-any.whl.metadata (5.4 kB)
3.637   Downloading mkdocs_exporter-5.0.0-py3-none-any.whl.metadata (5.4 kB)
3.669   Downloading mkdocs_exporter-4.1.0-py3-none-any.whl.metadata (5.4 kB)
3.694 Collecting importlib-metadata<5.0 (from mkdocs-exporter)
3.706   Downloading importlib_metadata-4.13.0-py3-none-any.whl.metadata (4.9 kB)
3.717 INFO: pip is still looking at multiple versions of mkdocs-exporter to determine which version is compatible with other requirements. This could take a while.
3.718 Collecting mkdocs-exporter
3.731   Downloading mkdocs_exporter-4.0.2-py3-none-any.whl.metadata (5.4 kB)
3.766   Downloading mkdocs_exporter-4.0.1-py3-none-any.whl.metadata (5.4 kB)
3.809   Downloading mkdocs_exporter-4.0.0-py3-none-any.whl.metadata (5.4 kB)
3.846   Downloading mkdocs_exporter-3.1.1-py3-none-any.whl.metadata (5.4 kB)
3.881   Downloading mkdocs_exporter-3.1.0-py3-none-any.whl.metadata (5.4 kB)
3.899 INFO: This is taking longer than usual. You might need to provide the dependency resolver with stricter constraints to reduce runtime. See https://pip.pypa.io/warnings/backtracking for guidance. If you want to abort this run, press Ctrl + C.
3.915   Downloading mkdocs_exporter-3.0.3-py3-none-any.whl.metadata (5.4 kB)
3.956   Downloading mkdocs_exporter-3.0.2-py3-none-any.whl.metadata (5.4 kB)
3.993   Downloading mkdocs_exporter-3.0.1-py3-none-any.whl.metadata (5.4 kB)
4.034   Downloading mkdocs_exporter-3.0.0-py3-none-any.whl.metadata (5.4 kB)
4.079   Downloading mkdocs_exporter-2.0.2-py3-none-any.whl.metadata (5.4 kB)
4.122   Downloading mkdocs_exporter-2.0.1-py3-none-any.whl.metadata (4.7 kB)
4.168   Downloading mkdocs_exporter-2.0.0-py3-none-any.whl.metadata (4.6 kB)
4.197   Downloading mkdocs_exporter-1.3.0-py3-none-any.whl.metadata (4.7 kB)
4.216 Requirement already satisfied: mkdocs-material-extensions<2.0.0,>=1.1.1 in /usr/local/lib/python3.11/site-packages (from mkdocs-exporter) (1.3.1)
4.231   Downloading mkdocs_exporter-1.2.2-py3-none-any.whl.metadata (4.4 kB)
4.264   Downloading mkdocs_exporter-1.2.1-py3-none-any.whl.metadata (4.4 kB)
4.302   Downloading mkdocs_exporter-1.2.0-py3-none-any.whl.metadata (4.4 kB)
4.345   Downloading mkdocs_exporter-1.1.0-py3-none-any.whl.metadata (4.5 kB)
4.386   Downloading mkdocs_exporter-1.0.2-py3-none-any.whl.metadata (4.4 kB)
4.420   Downloading mkdocs_exporter-1.0.1-py3-none-any.whl.metadata (4.1 kB)
4.454   Downloading mkdocs_exporter-1.0.0-py3-none-any.whl.metadata (4.0 kB)
4.490   Downloading mkdocs_exporter-0.0.2-py3-none-any.whl.metadata (4.0 kB)
4.521   Downloading mkdocs_exporter-0.0.1-py3-none-any.whl.metadata (1.4 kB)
4.534 ERROR: Cannot install mkdocs-exporter==0.0.1, mkdocs-exporter==0.0.2, mkdocs-exporter==1.0.0, mkdocs-exporter==1.0.1, mkdocs-exporter==1.0.2, mkdocs-exporter==1.1.0, mkdocs-exporter==1.2.0, mkdocs-exporter==1.2.1, mkdocs-exporter==1.2.2, mkdocs-exporter==1.3.0, mkdocs-exporter==2.0.0, mkdocs-exporter==2.0.1, mkdocs-exporter==2.0.2, mkdocs-exporter==3.0.0, mkdocs-exporter==3.0.1, mkdocs-exporter==3.0.2, mkdocs-exporter==3.0.3, mkdocs-exporter==3.1.0, mkdocs-exporter==3.1.1, mkdocs-exporter==4.0.0, mkdocs-exporter==4.0.1, mkdocs-exporter==4.0.2, mkdocs-exporter==4.1.0, mkdocs-exporter==5.0.0, mkdocs-exporter==5.1.0, mkdocs-exporter==5.2.0, mkdocs-exporter==5.2.1, mkdocs-exporter==5.3.0, mkdocs-exporter==5.3.1 and mkdocs-exporter==6.0.0 because these package versions have conflicting dependencies.
4.534 
4.534 The conflict is caused by:
4.534     mkdocs-exporter 6.0.0 depends on playwright>=1.33
4.534     mkdocs-exporter 5.3.1 depends on playwright>=1.33
4.534     mkdocs-exporter 5.3.0 depends on playwright>=1.33
4.534     mkdocs-exporter 5.2.1 depends on playwright>=1.33
4.534     mkdocs-exporter 5.2.0 depends on playwright>=1.33
4.534     mkdocs-exporter 5.1.0 depends on playwright>=1.33
4.534     mkdocs-exporter 5.0.0 depends on playwright>=1.33
4.534     mkdocs-exporter 4.1.0 depends on playwright>=1.33
4.534     mkdocs-exporter 4.0.2 depends on playwright>=1.33
4.534     mkdocs-exporter 4.0.1 depends on playwright>=1.33
4.534     mkdocs-exporter 4.0.0 depends on playwright>=1.33
4.534     mkdocs-exporter 3.1.1 depends on playwright>=1.33
4.534     mkdocs-exporter 3.1.0 depends on playwright>=1.33
4.534     mkdocs-exporter 3.0.3 depends on playwright>=1.33
4.534     mkdocs-exporter 3.0.2 depends on playwright>=1.33
4.534     mkdocs-exporter 3.0.1 depends on playwright>=1.33
4.534     mkdocs-exporter 3.0.0 depends on playwright>=1.33
4.534     mkdocs-exporter 2.0.2 depends on playwright>=1.33
4.534     mkdocs-exporter 2.0.1 depends on playwright>=1.33
4.534     mkdocs-exporter 2.0.0 depends on playwright>=1.33
4.534     mkdocs-exporter 1.3.0 depends on playwright>=1.33
4.534     mkdocs-exporter 1.2.2 depends on playwright>=1.33
4.534     mkdocs-exporter 1.2.1 depends on playwright>=1.33
4.534     mkdocs-exporter 1.2.0 depends on playwright>=1.33
4.534     mkdocs-exporter 1.1.0 depends on playwright>=1.33
4.534     mkdocs-exporter 1.0.2 depends on playwright>=1.33
4.534     mkdocs-exporter 1.0.1 depends on playwright>=1.33
4.534     mkdocs-exporter 1.0.0 depends on playwright>=1.33
4.534     mkdocs-exporter 0.0.2 depends on playwright>=1.33
4.534     mkdocs-exporter 0.0.1 depends on playwright>=1.33
4.534 
4.534 To fix this you could try to:
4.534 1. loosen the range of package versions you've specified
4.534 2. remove package versions to allow pip attempt to solve the dependency conflict
4.534 
4.535 ERROR: ResolutionImpossible: for help visit https://pip.pypa.io/en/latest/topics/dependency-resolution/#dealing-with-dependency-conflicts
------
Dockerfile:2
--------------------
   1 |     FROM docker.io/squidfunk/mkdocs-material:9.5.27
   2 | >>> RUN pip install mkdocs-exporter
   3 |     
--------------------
ERROR: failed to solve: process "/bin/sh -c pip install mkdocs-exporter" did not complete successfully: exit code: 1

Additional Notes

Running pip --debug install mkdocs-exporter on the RUN line of the Dockerfile yields a more robust error message with colorized output. I have not provided that here due to the length of the output, the custom formatting that will likely get mangled, and how easily reproducible it is on a developer's local machine.

I have attempted this same install procedure with downgraded versions of both the Docker container image and the mkdocs-exporter library, so I suspect it has been an issue for a while.

I have attempted explicitly installing playwright via pip install playwright inside the container and experienced similar errors.

$ docker run --rm -it --entrypoint '' docker.io/squidfunk/mkdocs-material:9.5.27 /bin/sh
/docs # pip install playwright
ERROR: Could not find a version that satisfies the requirement playwright (from versions: none)
ERROR: No matching distribution found for playwright

Attempting to install older versions of playwright explicitly (playwright==1.33.0, for example) yields the same results.

The docker.io/squidfunk/mkdocs-material:9.5.27 image uses Alpine 3.19.1, which is still supported by the Alpine team until 2025-05-09.

It looks like playwright is not installable on the upstream vanilla Alpine image either based on this test:

$ docker run --rm -it --entrypoint '' docker.io/alpine:3.19.1 /bin/sh
# apk add python3
# apk add py3-pip
# pip install --break-system-packages playwright

However, it has no issues on this Debian-based image:

$ docker run --rm -it --entrypoint '' docker.io/python:3-bookworm /bin/sh
# pip install playwright

I was able to get mkdocs-exporter working by translating the official mkdocs-material Dockerfile to a functionally equivalent Debian-based one, but that solution is not exactly ideal. For anyone else trying to solve this same issue in the interim, here is the Dockerfile:

FROM python:3.11-bookworm

# Build-time flags
ARG WITH_PLUGINS=true

# Environment variables
ENV PACKAGES=/usr/local/lib/python3.11/site-packages
ENV PYTHONDONTWRITEBYTECODE=1

# Set build directory
WORKDIR /tmp

# Copy files necessary for build
COPY mkdocs-material/material material
COPY mkdocs-material/package.json package.json
COPY mkdocs-material/README.md README.md
COPY mkdocs-material/*requirements.txt ./
COPY *requirements.txt ./
COPY mkdocs-material/pyproject.toml pyproject.toml

# Perform build and cleanup artifacts and caches
RUN \
  apt update \
&& \
  apt install -y \
    libcairo2-dev \
    libfreetype-dev \
    git \
    libjpeg-dev \
    openssh-client \
    tini \
    zlib1g-dev \
&& \
  apt install -v \
    gcc \
    libffi-dev \
&& \
  pip install --no-cache-dir --upgrade pip \
&& \
  if [ "${WITH_PLUGINS}" = "true" ]; then \
    pip install --no-cache-dir \
      mkdocs-material[recommended] \
      mkdocs-material[imaging] \
      mkdocs-exporter \
      mkdocs-macros-plugin; \
  fi \
&& \
  for theme in mkdocs readthedocs; do \
    rm -rf ${PACKAGES}/mkdocs/themes/$theme; \
    ln -s \
      ${PACKAGES}/material/templates \
      ${PACKAGES}/mkdocs/themes/$theme; \
  done \
&& \
  playwright install \
&& \
  playwright install-deps \
&& \
  find ${PACKAGES} \
    -type f \
    -path "*/__pycache__/*" \
    -exec rm -f {} \; \
&& \
  git config --system --add safe.directory /docs \
&& \
  git config --system --add safe.directory /site

# Set working directory
WORKDIR /docs

# Expose MkDocs development server port
EXPOSE 8000

# Start development server by default
ENTRYPOINT ["/usr/bin/tini", "--", "mkdocs"]
CMD ["serve", "--dev-addr=0.0.0.0:8000"]

Note: size-wise, this is nowhere as close to as slim as the Alpine-based image provided by squidfunk/mkdocs-material, and I am sure there is more refinement that can be done to make it slimmer, but it works (as of v9.5.27 of mkdocs-material). Here is the original Dockerfile that this one is based on. This Dockerfile assumes that you have done a git clone of squidfunk/mkdocs-material from Github and that repository is in a subdirectory relative to this Dockerfile.

The biggest differences between this Dockerfile and the official one from mkdocs-material are:

adrienbrignon commented 5 months ago

Hi @codrcodz,

Yes, this is an expected issue. As you've mentioned, Playwright is not supported on non-glibc based Linux distributions (including Alpine):

To work around this issue, MkDocs Exporter provides a Dockerfile that builds a compatible image based on a supported operating system (Ubuntu).