Skip to content

Commit

Permalink
Use docker cache mounts for apt, pip and cargo
Browse files Browse the repository at this point in the history
The cache mounts are cached using standard github actions cache
when building in the CI pipeline.

Note that the build stage no longer contains the whole source tree,
these are instead mounted into the build container when building to
avoid invalidating cached build container layers.
  • Loading branch information
mjpieters committed Jan 31, 2025
1 parent d517b1c commit 3c2dc0f
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 16 deletions.
40 changes: 40 additions & 0 deletions .github/workflows/build-docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ jobs:

# Login to DockerHub first, to avoid rate-limiting
- uses: docker/login-action@v3
# PRs from forks don't have access to secrets, disable this step in that case.
if: ${{ github.event.pull_request.head.repo.full_name == 'astral-sh/uv' }}
with:
username: astralshbot
password: ${{ secrets.DOCKERHUB_TOKEN_RO }}
Expand Down Expand Up @@ -79,6 +81,44 @@ jobs:
platform=${{ matrix.platform }}
echo "PLATFORM_TUPLE=${platform//\//-}" >> $GITHUB_ENV
- name: Docker build caches
uses: actions/cache@v4
id: docker-build-caches
with:
path: |
var-cache-apt
var-lib-apt
tool-caches
key: docker-caches-${{ matrix.platform }}-${{ hashFiles('Dockerfile') }}

- name: Docker Cargo target cache
uses: actions/cache@v4
id: docker-cargo-target-cache
with:
path: |
root-target
key: docker-cargo-target-cache-${{ matrix.platform }}-${{ hashFiles('Dockerfile', 'crates/**', 'Cargo.toml', 'Cargo.lock') }}

- name: Inject build caches into docker
uses: reproducible-containers/buildkit-cache-dance@v3
with:
cache-map: |
{
"var-cache-apt": "/var/cache/apt",
"var-lib-apt": "/var/lib/apt",
"tool-caches": {"target": "/buildkit-cache", "id": "tool-caches"}
}
skip-extraction: ${{ steps.docker-build-caches.outputs.cache-hit }}

- name: Inject Cargo caches into docker
uses: reproducible-containers/buildkit-cache-dance@v3
with:
cache-map: |
{
"root-target": "/root/target"
}
skip-extraction: ${{ steps.docker-cargo-target-cache.outputs.cache-hit }}

# Adapted from https://docs.docker.com/build/ci/github-actions/multi-platform/
- name: Build and push by digest
id: build
Expand Down
53 changes: 37 additions & 16 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,19 +1,32 @@
FROM --platform=$BUILDPLATFORM ubuntu AS build
ENV HOME="/root"
ENV HOME="/root" \
# Place tool-specific caches in the buildkit tool cache.
CARGO_HOME=/buildkit-cache/cargo \
CARGO_ZIGBUILD_CACHE_DIR=/buildkit-cache/cargo-zigbuild \
PIP_CACHE_DIR=/buildkit-cache/pip \
RUSTUP_HOME=/buildkit-cache/rustup \
ZIG_GLOBAL_CACHE_DIR=/buildkit-cache/zig
WORKDIR $HOME

RUN apt update \
RUN \
--mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
# remove the default docker-specific apt config that auto-deletes /var/apt/cache archives
rm -f /etc/apt/apt.conf.d/docker-clean && \
# and configure apt-get to keep downloaded archives in the cache
echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' >/etc/apt/apt.conf.d/keep-cache && \
apt update \
&& apt install -y --no-install-recommends \
build-essential \
curl \
python3-venv \
cmake \
&& apt clean \
&& rm -rf /var/lib/apt/lists/*
cmake

# Setup zig as cross compiling linker
RUN python3 -m venv $HOME/.venv
RUN .venv/bin/pip install cargo-zigbuild
RUN \
--mount=type=cache,target=/buildkit-cache,id="tool-caches" \
.venv/bin/pip install cargo-zigbuild
ENV PATH="$HOME/.venv/bin:$PATH"

# Install rust
Expand All @@ -25,21 +38,29 @@ RUN case "$TARGETPLATFORM" in \
esac

# Update rustup whenever we bump the rust version
ENV PATH="$CARGO_HOME/bin:$PATH"
COPY rust-toolchain.toml rust-toolchain.toml
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --target $(cat rust_target.txt) --profile minimal --default-toolchain none
ENV PATH="$HOME/.cargo/bin:$PATH"
# Installs the correct toolchain version from rust-toolchain.toml and then the musl target
RUN rustup target add $(cat rust_target.txt)
RUN \
--mount=type=cache,target=/buildkit-cache,id="tool-caches" \
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --target $(cat rust_target.txt) --profile minimal --default-toolchain none \
# Installs the correct toolchain version from rust-toolchain.toml and then the musl target
&& rustup target add $(cat rust_target.txt)

# Build
COPY crates crates
COPY ./Cargo.toml Cargo.toml
COPY ./Cargo.lock Cargo.lock
RUN case "${TARGETPLATFORM}" in \
RUN \
# bind mounts to access Cargo config, lock, and sources, without having to
# copy them into the build layer and so bloat the docker build cache
--mount=type=bind,source=crates,target=crates \
--mount=type=bind,source=Cargo.toml,target=Cargo.toml \
--mount=type=bind,source=Cargo.lock,target=Cargo.lock \
# Cache mounts to speed up builds
--mount=type=cache,target=$HOME/target/ \
--mount=type=cache,target=/buildkit-cache,id="tool-caches" \
case "${TARGETPLATFORM}" in \
"linux/arm64") export JEMALLOC_SYS_WITH_LG_PAGE=16;; \
esac && \
cargo zigbuild --bin uv --bin uvx --target $(cat rust_target.txt) --release
RUN cp target/$(cat rust_target.txt)/release/uv /uv \
cargo zigbuild --bin uv --bin uvx --target $(cat rust_target.txt) --release \
&& cp target/$(cat rust_target.txt)/release/uv /uv \
&& cp target/$(cat rust_target.txt)/release/uvx /uvx
# TODO(konsti): Optimize binary size, with a version that also works when cross compiling
# RUN strip --strip-all /uv
Expand Down

0 comments on commit 3c2dc0f

Please sign in to comment.