From fa43164ba81d7f4792a020949d62bd3b29cfd8a9 Mon Sep 17 00:00:00 2001 From: Jakub Kaczmarzyk Date: Wed, 2 Sep 2020 15:35:18 -0400 Subject: [PATCH 1/5] add Dockerfile Docker provides a standard way of installing software. This is particularly useful with software that requires fairly complex dependencies, like Anki. The Dockerfile added in this commit builds a Docker image with Anki. The Docker image is based on `python:3.8`, which is based on a Debian Buster image. This Docker image can be useful to end users, because it can be installed on any system that has Docker (e.g., Windows, macOS, Linux), and it is also useful to developers who might not have -- or want to install -- the dependencies that Anki requires at compile time. To build the image, run `docker build --tag anki .` in the project root directory. To run the image with a GUI (on Unix-like systems), run ``` docker run --rm -it --env "DISPLAY=$DISPLAY" --volume /tmp/.X11-unix:/tmp/.X11-unix anki ``` One may also mount a user's Anki directory into the container and run the container as the current user. One may also run with Singularity (which typically has more transparent support for GUI applications and behaving as the current user) by first building the Docker image as above and then converting to Singularity Image Format (SIF) with ``` sudo singularity build anki.sif docker-daemon://anki:latest ``` Run the Singularity image with ``` singularity run anki.sif ``` --- Dockerfile | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..e70311cd6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,79 @@ +FROM python:3.8 AS builder + +ARG DEBIAN_FRONTEND="noninteractive" + +# Install rust. +ENV CARGO_HOME="/opt/cargo" \ + RUSTUP_HOME="/opt/rustup" +ENV PATH="$CARGO_HOME/bin:$PATH" +RUN curl -fsSL --proto '=https' --tlsv1.2 https://sh.rustup.rs \ + | sh -s -- -y --quiet --no-modify-path \ + && rustup update \ + && cargo install ripgrep + +# Install system dependencies. +RUN apt-get update \ + && apt-get install --yes --no-install-recommends \ + gettext \ + lame \ + mpv \ + portaudio19-dev \ + rsync \ + && rm -rf /var/lib/apt/lists/* + +# Install node and npm. +WORKDIR /opt/node +RUN curl -fsSL --proto '=https' https://nodejs.org/dist/v12.18.3/node-v12.18.3-linux-x64.tar.xz \ + | tar xJ --strip-components 1 +ENV PATH="/opt/node/bin:$PATH" + +# Install protoc. +WORKDIR /opt/protoc +RUN curl -fsSL --proto '=https' -O https://github.com/protocolbuffers/protobuf/releases/download/v3.11.4/protoc-3.11.4-linux-x86_64.zip \ + && unzip protoc-3.11.4-linux-x86_64.zip -x readme.txt \ + && rm protoc-3.11.4-linux-x86_64.zip +ENV PATH="/opt/protoc/bin:$PATH" + +# Build anki. +WORKDIR /opt/anki +COPY . . +RUN make develop \ + && make build + +# Build final image. +FROM python:3.8-slim + +# Install system dependencies. +RUN apt-get update \ + && apt-get install --yes --no-install-recommends \ + gettext \ + lame \ + libnss3 \ + libxcb-icccm4 \ + libxcb-image0 \ + libxcb-keysyms1 \ + libxcb-randr0 \ + libxcb-render-util0 \ + libxcb-xinerama0 \ + libxcb-xkb1 \ + libxkbcommon-x11-0 \ + libxcomposite1 \ + mpv \ + portaudio19-dev \ + rsync \ + && rm -rf /var/lib/apt/lists/* + +# Install pre-compiled Anki. +COPY --from=builder /opt/anki/dist/ /opt/anki/ +RUN python -m pip install --no-cache-dir \ + PyQtWebEngine \ + /opt/anki/*.whl \ + # Create an anki executable. + && printf "#!/usr/bin/env python\nimport aqt\naqt.run()\n" > /usr/local/bin/anki \ + && chmod +x /usr/local/bin/anki \ + # Create non-root user. + && useradd --create-home anki + +USER anki + +ENTRYPOINT ["/usr/local/bin/anki"] From 6e5519ef8014095f8732533aa003b6744aeb08c6 Mon Sep 17 00:00:00 2001 From: Jakub Kaczmarzyk Date: Wed, 2 Sep 2020 15:58:26 -0400 Subject: [PATCH 2/5] add Jakub Kaczmarzyk + rm whitespace --- CONTRIBUTORS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 81b10b43a..569f458ca 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -46,8 +46,9 @@ Matt Krump Alexander Presnyakov abdo aplaice -phwoo +phwoo Soren Bjornstad +Jakub Kaczmarzyk ******************** From de5feef0a41e6b98fc5b5d897ffe18007e6b0f88 Mon Sep 17 00:00:00 2001 From: Jakub Kaczmarzyk Date: Wed, 2 Sep 2020 16:12:52 -0400 Subject: [PATCH 3/5] fix jakub kaczmarzyk's email --- CONTRIBUTORS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 569f458ca..a25e4a6f3 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -48,7 +48,7 @@ abdo aplaice phwoo Soren Bjornstad -Jakub Kaczmarzyk +Jakub Kaczmarzyk ******************** From a22f671f3b03cd0f9b13591ff1e9c8f9c54410f2 Mon Sep 17 00:00:00 2001 From: Jakub Kaczmarzyk Date: Thu, 3 Sep 2020 09:21:02 -0400 Subject: [PATCH 4/5] add maintainer label --- Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index e70311cd6..163a9f5aa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -77,3 +77,5 @@ RUN python -m pip install --no-cache-dir \ USER anki ENTRYPOINT ["/usr/local/bin/anki"] + +LABEL maintainer="Jakub Kaczmarzyk " From d07185693ceecdab9e0fab0ce872092f9fe01359 Mon Sep 17 00:00:00 2001 From: Jakub Kaczmarzyk Date: Tue, 8 Sep 2020 14:58:25 -0400 Subject: [PATCH 5/5] Update Dockerfile and add README.docker - Fix permissions in `/opt` to allow non-root user to compile Anki (which requires reading and/or creating files below `/opt`). - Create `pythonbuilder` stage, which runs only `make build`. The wheels from this stage are copied into the final stage. - Add README.docker --- Dockerfile | 41 ++++++++++++++++----- README.docker | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 8 deletions(-) create mode 100644 README.docker diff --git a/Dockerfile b/Dockerfile index 163a9f5aa..51031c5bf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,17 @@ -FROM python:3.8 AS builder +ARG PYTHON_VERSION="3.8" -ARG DEBIAN_FRONTEND="noninteractive" +FROM python:$PYTHON_VERSION AS dependencies + +# Allow non-root users to install things and modify installations in /opt. +RUN chmod 777 /opt && chmod a+s /opt # Install rust. ENV CARGO_HOME="/opt/cargo" \ RUSTUP_HOME="/opt/rustup" ENV PATH="$CARGO_HOME/bin:$PATH" -RUN curl -fsSL --proto '=https' --tlsv1.2 https://sh.rustup.rs \ +RUN mkdir $CARGO_HOME $RUSTUP_HOME \ + && chmod a+rws $CARGO_HOME $RUSTUP_HOME \ + && curl -fsSL --proto '=https' --tlsv1.2 https://sh.rustup.rs \ | sh -s -- -y --quiet --no-modify-path \ && rustup update \ && cargo install ripgrep @@ -16,6 +21,16 @@ RUN apt-get update \ && apt-get install --yes --no-install-recommends \ gettext \ lame \ + libnss3 \ + libxcb-icccm4 \ + libxcb-image0 \ + libxcb-keysyms1 \ + libxcb-randr0 \ + libxcb-render-util0 \ + libxcb-xinerama0 \ + libxcb-xkb1 \ + libxkbcommon-x11-0 \ + libxcomposite1 \ mpv \ portaudio19-dev \ rsync \ @@ -34,14 +49,24 @@ RUN curl -fsSL --proto '=https' -O https://github.com/protocolbuffers/protobuf/r && rm protoc-3.11.4-linux-x86_64.zip ENV PATH="/opt/protoc/bin:$PATH" -# Build anki. +# Allow non-root users to install toolchains and update rust crates. +RUN chmod 777 $RUSTUP_HOME/toolchains $RUSTUP_HOME/update-hashes $CARGO_HOME/registry \ + && chmod -R a+rw $CARGO_HOME/registry \ + # Necessary for TypeScript. + && chmod a+w /home + +# Build anki. Use a separate image so users can build an image with build-time +# dependencies. +FROM dependencies AS builder WORKDIR /opt/anki COPY . . -RUN make develop \ - && make build +RUN make develop + +FROM builder AS pythonbuilder +RUN make build # Build final image. -FROM python:3.8-slim +FROM python:${PYTHON_VERSION}-slim # Install system dependencies. RUN apt-get update \ @@ -64,7 +89,7 @@ RUN apt-get update \ && rm -rf /var/lib/apt/lists/* # Install pre-compiled Anki. -COPY --from=builder /opt/anki/dist/ /opt/anki/ +COPY --from=pythonbuilder /opt/anki/dist/ /opt/anki/ RUN python -m pip install --no-cache-dir \ PyQtWebEngine \ /opt/anki/*.whl \ diff --git a/README.docker b/README.docker new file mode 100644 index 000000000..e098b9c4b --- /dev/null +++ b/README.docker @@ -0,0 +1,99 @@ +Anki in Docker +============== + +Docker provides a standard method of installing software. This is particularly helpful +when software requires a complex set of dependencies, like Anki. + +## Running Anki in Docker + +Build and then run the image. The `docker run` command below runs the image as the +current user, and it mounts the user's `$HOME` directory, which is where Anki stores +its local files. + +``` +docker build --tag anki . +xhost +local:root # Undo when done with `xhost -local:root` +docker run \ + --rm -it \ + --user 1000:1000 \ + --volume $HOME/.local/share:$HOME/.local/share:rw \ + --volume /etc/passwd:/etc/passwd:ro \ + --volume /tmp/.X11-unix:/tmp/.X11-unix:rw \ + --env DISPLAY=$DISPLAY \ + anki +xhost -local:root +``` + +## Developing Anki in Docker + +Build your local source tree in Docker. + +1. Build the Docker image with build-time dependencies. The Anki Dockerfile uses +multi-stage builds, so the target is the first stage, which includes only the +dependencies. + + ``` + docker build --tag anki:dependencies --target dependencies . + ``` + +2. Compile your source tree + + Start the image with dependencies in the background. It is important to run the + image as the current user, because otherwise, some files in the source tree will be + owned by root. Find user id with `id -u` and group ID with `id -g`. These values + are passed to `--user` as in `--user $(id -u):$(id -g)`. + + ``` + docker run --rm -it \ + --name ankibuilder \ + --detach \ + --workdir /work + --volume "$PWD":/work:rw \ + --user 1000:1000 \ + --volume /etc/passwd:/etc/passwd:ro \ + --volume /tmp/.X11-unix:/tmp/.X11-unix:rw \ + --env DISPLAY=$DISPLAY \ + anki:dependencies bash + ``` + + Allow the Docker container to use the local X server and show the GUI. + + ``` + xhost +local:root + ``` + + (Undo this when done with `xhost -local:root`) + + Compile. + + ``` + docker exec -it ankibuilder make run + ``` + + The Anki graphical user interface should appear. The first run will take some time + because Rust code has to be compiled and Python dependencies have to be downloaded, + etc. The following runs will be much faster. + + To compile without running the GUI, use `make develop`. + +3. Other common operations + + If system packages need to be installed, use `apt-get` as below. The Docker image + is based on a Debian Stable image. + + ``` + docker exec -it --user root ankibuilder apt-get update + docker exec -it --user root ankibuilder apt-get install PACKAGES + ``` + + An interactive bash shell can be started with + + ``` + docker exec -it ankibuilder bash + ``` + + or as root user + + ``` + docker exec -it --user root ankibuilder bash + ```