FROM rust:1.96-slim-trixie AS chef
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates pkg-config perl make cmake clang mold git \
&& rm -rf /var/lib/apt/lists/*
ENV RUSTFLAGS="-C linker=clang -C link-arg=-fuse-ld=mold"
# cargo's own git fetch waits forever on a stalled knot; the cli gives up after
# 30s under 1KiB/s and cargo retries it
ENV CARGO_NET_GIT_FETCH_WITH_CLI=true \
CARGO_NET_RETRY=5 \
GIT_HTTP_LOW_SPEED_LIMIT=1024 \
GIT_HTTP_LOW_SPEED_TIME=30
RUN rustup toolchain install 1.96.0 \
--profile minimal \
--component rustfmt \
--component clippy \
--target wasm32-unknown-unknown \
--no-self-update
ENV CC=clang
ENV CXX=clang++
RUN cargo install cargo-chef --version 0.1.78 --locked
WORKDIR /src
FROM chef AS source
COPY Cargo.toml Cargo.lock rust-toolchain.toml ./
COPY lexicons ./lexicons
COPY crates ./crates
COPY bobbin ./bobbin
# keep image lighter by not copying in shuttle, knotmirror, knot2
# does however need some cargo toml patchings
RUN sed -i \
-e 's/, "shuttle"//' \
-e 's/, "knotmirror\/crates\/\*"//' \
-e 's/, "knot2\/crates\/\*"//' \
-e '/^knot-[a-z0-9-]* = { path = "knot2\/crates\//d' \
-e '/^knotmirror-[a-z0-9-]* = { path = "knotmirror\/crates\//d' \
-e '/^gix-pack = { path = "knot2\/third_party\/gix-pack" }/d' \
-e '/^gix-imara-diff = { path = "knot2\/third_party\/gix-imara-diff" }/d' \
Cargo.toml
# the recipe only changes with manifests and the lockfile, so a source-only
# change reuses the cooked dependency layer
FROM source AS planner
RUN cargo chef prepare --recipe-path recipe.json
# one "name url rev" line per git source, so fetching them caches on the pins alone
RUN awk -F'"' '/^name = /{n=$2} /^source = "git\+/{split($2, s, /[?#]/); sub(/^git\+/, "", s[1]); sub(/^rev=/, "", s[2]); print n, s[1], s[2]}' Cargo.lock \
| sort -u -k2,3 | sort -u -k2,2 > git-deps.txt
# the git dependencies come from a knot that is sometimes slow, so they get a
# layer that only a pin change invalidates
FROM chef AS git-deps
COPY --from=planner /src/git-deps.txt ./
RUN mkdir -p /tmp/git-deps/src && touch /tmp/git-deps/src/lib.rs \
&& { printf '[package]\nname = "git-deps"\nversion = "0.0.0"\nedition = "2021"\n\n[dependencies]\n'; \
awk '{ printf "%s = { git = \"%s\", rev = \"%s\" }\n", $1, $2, $3 }' git-deps.txt; } > /tmp/git-deps/Cargo.toml \
&& cargo fetch --manifest-path /tmp/git-deps/Cargo.toml \
&& rm -rf /tmp/git-deps
FROM chef AS builder
ARG BOBBIN_PROFILE=release
COPY --from=git-deps /usr/local/cargo/git /usr/local/cargo/git
COPY --from=planner /src/recipe.json recipe.json
COPY rust-toolchain.toml ./
RUN cargo chef cook --profile ${BOBBIN_PROFILE} --recipe-path recipe.json --bin bobbin --package bobbin
COPY --from=source /src .
RUN cargo build --profile ${BOBBIN_PROFILE} --bin bobbin --package bobbin
RUN if [ "${BOBBIN_PROFILE}" = "release" ]; then strip target/${BOBBIN_PROFILE}/bobbin; fi
FROM debian:trixie-slim
ARG BOBBIN_PROFILE=release
# apt's and dpkg's logs carry the install time
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
&& rm -rf /var/lib/apt/lists/* /var/log/apt /var/log/dpkg.log
COPY --from=builder /src/target/${BOBBIN_PROFILE}/bobbin /usr/local/bin/bobbin
ENV BOBBIN_BIND=0.0.0.0:8090
EXPOSE 8090
ENTRYPOINT ["/usr/local/bin/bobbin"]