tests: Refactor Dockerfile to multi-stage (more caching during development)

The previous refactor traded build step caching for smaller image size,
which in turn made fast caching of built images possible, and allowed us
to speed up CI builds. But almost any change in Dockerfile required full
rebuild of everything (vim & taskwarrior), so changes to the Dockerfile
became more painful.

This commit refactors the Dockerfile to use multi-stage builds, which
brings build caching back: vim & taskwarrior are built in separate
stages, which are cached step by step, and then the build artifacts are
copied into the main tests image and runtime dependencies are installed.

There's a catch, of course: --cache-from doesn't work with multi-stage
images unless the experimental BuildKit backend and its inline cache
export are enabled. This requires docker 19.03, which shouldn't be hard
to obtain but isn't installed by default on Travis CI.
This commit is contained in:
Tomas Janousek 2020-07-07 19:33:57 +02:00 committed by Tomas Babej
parent ae588783c1
commit 13ee57540e
2 changed files with 65 additions and 78 deletions

View file

@ -36,7 +36,8 @@ jobs:
) | sha256sum | read -r tag _ ) | sha256sum | read -r tag _
docker login "$DOCKER_REGISTRY" -u "$GITHUB_USER" -p "$GITHUB_TOKEN" || : docker login "$DOCKER_REGISTRY" -u "$GITHUB_USER" -p "$GITHUB_TOKEN" || :
docker pull "$DOCKER_CACHE_IMAGE":"$tag" || : docker pull "$DOCKER_CACHE_IMAGE":"$tag" || :
docker build \ DOCKER_BUILDKIT=1 docker build \
--build-arg BUILDKIT_INLINE_CACHE=1 \
--cache-from "$DOCKER_CACHE_IMAGE":"$tag" \ --cache-from "$DOCKER_CACHE_IMAGE":"$tag" \
${ALPINE_VERSION:+--build-arg ALPINE_VERSION="$ALPINE_VERSION"} \ ${ALPINE_VERSION:+--build-arg ALPINE_VERSION="$ALPINE_VERSION"} \
${PYTHON_VERSION:+--build-arg PYTHON_VERSION="$PYTHON_VERSION"} \ ${PYTHON_VERSION:+--build-arg PYTHON_VERSION="$PYTHON_VERSION"} \

View file

@ -1,105 +1,91 @@
ARG ALPINE_VERSION=3.12 ARG ALPINE_VERSION=3.12
ARG PYTHON_VERSION=3 ARG PYTHON_VERSION=3
FROM python:${PYTHON_VERSION}-alpine${ALPINE_VERSION}
ARG TASK_VERSION=v2.5.1 ARG TASK_VERSION=v2.5.1
ARG VIM_VERSION=v8.2.0716 ARG VIM_VERSION=v8.2.0716
ARG VIMWIKI_VERSION=master ARG VIMWIKI_VERSION=master
RUN set -ex \
&& apk add --no-cache --virtual .taskwiki-test-deps \
git \
make \
tzdata \
xvfb-run \
&& ln -sf /usr/share/zoneinfo/Etc/UTC /etc/localtime \
&& true
RUN set -ex \ FROM python:${PYTHON_VERSION}-alpine${ALPINE_VERSION} AS build
\
&& : install vim $VIM_VERSION \
\ FROM build AS build-vim
&& apk add --no-cache --virtual .vim-build-deps \ RUN apk add --no-cache \
gcc \ gcc \
git \ git \
gtk+3.0-dev \ gtk+3.0-dev \
libxt-dev \ libxt-dev \
make \ make \
musl-dev \ musl-dev \
ncurses-dev \ ncurses-dev
patchelf \ ARG VIM_VERSION
&& cd /usr/src \ RUN git clone --depth 1 --recurse-submodules --shallow-submodules \
&& git clone --depth 1 --recurse-submodules --shallow-submodules --branch $VIM_VERSION \ --branch $VIM_VERSION https://github.com/vim/vim /usr/src/vim
https://github.com/vim/vim \ WORKDIR /usr/src/vim
&& cd vim \ # "backport" https://github.com/vim/vim/commit/16d7eced1a08565a9837db8067c7b9db5ed68854
&& sed -i -e '/#\s*undef _POSIX_THREADS/d' src/if_python3.c \ RUN sed -i -e '/#\s*undef _POSIX_THREADS/d' src/if_python3.c
&& ./configure --prefix=/opt/vim --enable-pythoninterp --enable-python3interp --enable-gui=gtk3 \ RUN ./configure --prefix=/opt/vim --enable-pythoninterp --enable-python3interp --enable-gui=gtk3
&& make -j$(nproc) \ RUN make -j$(nproc)
&& make install \ RUN make install
&& rm -rf /usr/src/vim \
&& patchelf --print-needed /opt/vim/bin/* \
| grep -v '^libpython' \ FROM build AS build-taskwarrior
| sort -u \ RUN apk add --no-cache \
| sed -e 's/^/so:/' \
| xargs -rt apk add --no-cache --virtual .vim-runtime-deps \
\
&& : install taskwarrior $TASK_VERSION \
\
&& apk add --no-cache --virtual .taskwarrior-build-deps \
cmake \ cmake \
g++ \ g++ \
gcc \ gcc \
git \ git \
make \ make \
patchelf \ util-linux-dev
util-linux-dev \ ARG TASK_VERSION
&& cd /usr/src \ RUN git clone --depth 1 --recurse-submodules --shallow-submodules \
&& git clone --depth 1 --recurse-submodules --shallow-submodules --branch $TASK_VERSION \ --branch $TASK_VERSION https://github.com/GothenburgBitFactory/taskwarrior /usr/src/taskwarrior
https://github.com/GothenburgBitFactory/taskwarrior \ WORKDIR /usr/src/taskwarrior
&& cd taskwarrior \ RUN cmake -DCMAKE_INSTALL_PREFIX=/opt/taskwarrior -DCMAKE_BUILD_TYPE=release -DENABLE_SYNC=OFF .
&& cmake -DCMAKE_INSTALL_PREFIX=/opt/taskwarrior -DCMAKE_BUILD_TYPE=release -DENABLE_SYNC=OFF . \ RUN make -j$(nproc)
&& make -j$(nproc) \ RUN make install
&& make install \
&& rm -rf /usr/src/taskwarrior \
&& patchelf --print-needed /opt/taskwarrior/bin/* \ FROM build AS build-pip
| sort -u \ # coverage needs to build a C extensions, otherwise it's slow
| sed -e 's/^/so:/' \ RUN apk add --no-cache \
| xargs -rt apk add --no-cache --virtual .taskwarrior-runtime-deps \
\
&& : install vimwiki $VIMWIKI_VERSION \
\
&& mkdir -p /root/.vim/bundle \
&& cd /root/.vim/bundle \
&& git clone --depth 1 --recurse-submodules --shallow-submodules --branch $VIMWIKI_VERSION \
https://github.com/vimwiki/vimwiki \
\
&& : install python test dependencies \
\
&& apk add --no-cache --virtual .python-coverage-build-deps \
gcc \ gcc \
musl-dev \ musl-dev
&& pip install \ RUN pip install --root=/opt/pip-root \
coverage \ coverage \
coveralls \ coveralls \
pytest \ pytest \
pytest-cov \ pytest-cov \
pytest-xdist \ pytest-xdist \
https://github.com/liskin/vimrunner-python/archive/8c19ff88050c09236e7519425bfae33c687483df.zip \ https://github.com/liskin/vimrunner-python/archive/8c19ff88050c09236e7519425bfae33c687483df.zip
\ COPY requirements.txt /tmp/taskwiki/requirements.txt
&& : clean up build dependencies \ RUN pip install --root=/opt/pip-root \
\ -r /tmp/taskwiki/requirements.txt
&& apk del --no-network .taskwarrior-build-deps \
&& apk del --no-network .vim-build-deps \
&& apk del --no-network .python-coverage-build-deps \
&& true
FROM build AS tests
RUN apk add --no-cache \
git \
make \
patchelf \
tzdata \
xvfb-run
RUN ln -sf /usr/share/zoneinfo/Etc/UTC /etc/localtime
COPY --from=build-vim /opt/vim/ /opt/vim/
COPY --from=build-taskwarrior /opt/taskwarrior/ /opt/taskwarrior/
COPY --from=build-pip /opt/pip-root/ /
# install runtime deps of vim/taskwarrior
RUN patchelf --print-needed /opt/*/bin/* \
| grep -v '^libpython' \
| sort -u \
| sed -e 's/^/so:/' \
| xargs -rt apk add --no-cache
ENV PATH=/opt/vim/bin:/opt/taskwarrior/bin:$PATH ENV PATH=/opt/vim/bin:/opt/taskwarrior/bin:$PATH
RUN set -ex \ RUN task --version && vim --version
&& task --version \
&& vim --version \ ARG VIMWIKI_VERSION
&& true RUN git clone --depth 1 --recurse-submodules --shallow-submodules \
--branch $VIMWIKI_VERSION https://github.com/vimwiki/vimwiki /root/.vim/bundle/vimwiki
COPY requirements.txt /root/.vim/bundle/taskwiki/
RUN pip install -r /root/.vim/bundle/taskwiki/requirements.txt
WORKDIR /root/.vim/bundle/taskwiki WORKDIR /root/.vim/bundle/taskwiki