diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index cead94d..0ab5b35 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -142,10 +142,18 @@ jobs: steps: - uses: actions/checkout@v7 + # The gha cache backend needs a buildx builder (the default docker + # driver cannot export a cache). + - uses: docker/setup-buildx-action@v4 + - uses: docker/build-push-action@v7 with: context: . # The Dockerfile lives under docker/; the context stays the repo # root so COPY paths are unchanged file: docker/Dockerfile + # Every layer, the compiled dependencies included, kept in the + # Actions cache: builds on main fill it, every build reads it. + cache-from: type=gha + cache-to: type=gha,mode=max push: false diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1bfdfea..c6e68a2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -105,10 +105,18 @@ jobs: type=sha,prefix= type=raw,value=latest + # The gha cache backend needs a buildx builder (the default docker + # driver cannot export a cache). + - uses: docker/setup-buildx-action@v4 + - uses: docker/build-push-action@v7 with: context: . file: docker/Dockerfile + # Every layer, the compiled dependencies included, kept in the + # Actions cache: builds on main fill it, every build reads it. + cache-from: type=gha + cache-to: type=gha,mode=max push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} diff --git a/docker/Dockerfile b/docker/Dockerfile index 69c0f1c..09b6042 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -8,23 +8,32 @@ RUN apk add --no-cache git \ WORKDIR /ui RUN npm ci --no-audit --no-fund && npm run build:gitops -# Stage 2: build -FROM rust:1.96-slim-trixie AS builder - -RUN apt-get update && apt-get install -y pkg-config libssl-dev curl && rm -rf /var/lib/apt/lists/* - -WORKDIR /app -COPY Cargo.toml Cargo.lock ./ +# Stage 2: build, in two steps so a release only compiles what changed. +# cargo-chef reduces the manifests to a recipe (the service's own version +# masked, so release-please's bump does not count), and `cook` compiles the +# dependencies from it alone: that layer is reused, from the CI build cache, +# for as long as Cargo.lock's dependencies stay the same. Any change to them +# rebuilds the whole layer once. +# # --locked: build exactly the versions CI tested. Without it, cargo may # quietly re-resolve inside the image and ship dependency versions the test # suite never saw. -# -RUN mkdir src \ - && echo 'fn main() {}' > src/main.rs \ - && cargo build --release --locked && rm -rf src +FROM rust:1.96-slim-trixie AS chef +RUN apt-get update && apt-get install -y pkg-config libssl-dev curl && rm -rf /var/lib/apt/lists/* +RUN cargo install cargo-chef --version 0.1.78 --locked +WORKDIR /app + +FROM chef AS planner +COPY Cargo.toml Cargo.lock ./ +COPY src/ src/ +RUN cargo chef prepare --recipe-path recipe.json +FROM chef AS builder +COPY --from=planner /app/recipe.json recipe.json +RUN cargo chef cook --release --locked --recipe-path recipe.json +COPY Cargo.toml Cargo.lock ./ COPY src/ src/ -RUN touch src/main.rs && cargo build --release --locked +RUN cargo build --release --locked # Stage 3: runtime FROM debian:trixie-slim