Rust Builkit CICD Pipeline
Last Update: 2025-02-26
The following files create an buildkit CICD pipeline with cross platform compilation support for x86-64
, aarch64
and riscv64gc
for Rust. Also, the program is compiled with musl, so the resulting image is as small as possible.
The tests output cobertura files (via cargo-tarpaulin
) for use in your git-platform (e.g. Gitlab).
Also, this whole pipeline has been run with podman in the background with docker-buildx installed but using the podman context, so it can be run without root privileges.
You will have to modify the pipeline further to fit your program and upload the resulting docker images to a registry.
Run the whole pipeline with:
docker buildx bake default lint clippy test
Your docker-bake.hcl
-file:
target "default" {
platforms = [
"linux/amd64",
"linux/arm64",
"linux/riscv64",
]
output = ["type=cacheonly"]
}
target "lint" {
target = "lint"
output = ["type=cacheonly"]
}
target "clippy" {
target = "clippy"
output = ["type=cacheonly"]
}
target "test" {
target = "test-results"
output = [
"type=local,dest=.",
]
}
Your Dockerfile
:
# syntax=docker/dockerfile:1
# Script for converting Go's TARGETPLATFORM to Rust's target triple
# (also comes with RUSTFLAGS settings)
FROM --platform=${BUILDPLATFORM} docker.io/python:3.14-rc-alpine AS build-env
ARG TARGETPLATFORM
RUN <<EOFCMD
python <(cat << EOF
import os
targetplatform = os.environ.get("TARGETPLATFORM")
default_flags = " -C target-feature=+crt-static"
with open ("/env", "w") as f:
f.write("#!/bin/sh\n")
if targetplatform == "linux/riscv64":
f.write("export RUST_TARGET=riscv64gc-unknown-linux-musl\n")
f.write("export RUSTFLAGS='-C linker=riscv64-linux-gnu-gcc" + default_flags + "'\n")
elif targetplatform == "linux/arm64":
f.write("export RUST_TARGET=aarch64-unknown-linux-musl\n")
f.write("export RUSTFLAGS='-C linker=aarch64-linux-gnu-gcc" + default_flags + "'\n")
elif targetplatform == "linux/amd64":
f.write("export RUST_TARGET=x86_64-unknown-linux-musl\n")
f.write("export RUSTFLAGS='" + default_flags + "'\n")
EOF
)
EOFCMD
RUN chmod uga+x /env
# Prepare the base image (for cross-compilation)
FROM --platform=${BUILDPLATFORM} docker.io/rust:1.85-bookworm AS base
# Install rust tools (with caching between target platforms)
RUN rustup component add rustfmt
RUN rustup component add clippy
RUN cargo install cargo-tarpaulin
# Install the correct compiler for the target platform
ARG TARGETPLATFORM
RUN apt-get update -y
RUN if [ "$TARGETPLATFORM" = "linux/riscv64" ]; then apt-get install -y gcc-riscv64-linux-gnu; fi
RUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then apt-get install -y gcc-aarch64-linux-gnu; fi
# Configure the rust target specific toolchain
COPY --from=build-env /env /env
RUN . /env; rustup target add $RUST_TARGET
FROM base AS toml-files
WORKDIR /src
RUN echo "// dummy file" > ./src/main.rs
COPY Cargo.toml Cargo.lock ./
FROM toml-files AS fetch
RUN . /env; cargo fetch --locked --target "$RUST_TARGET"
# Copy all source files to the container now
FROM fetch AS fetch-with-files
COPY . .
FROM fetch-with-files AS clippy
RUN cargo clippy --all-targets --all-features -- -D warnings
FROM fetch-with-files AS lint
RUN cargo fmt --check
FROM fetch-with-files AS test
RUN cargo-tarpaulin --engine llvm --out Html --out Xml --output-dir /target/coverage
FROM scratch AS test-results
COPY --from=test /target/coverage/ /target/coverage/
FROM fetch-with-files AS build
ARG TARGETPLATFORM
RUN --mount=type=cache,id=cargo-build-${TARGETPLATFORM},dst=/src/target . /env; cargo build --release --target $RUST_TARGET
RUN --mount=type=cache,id=cargo-build-${TARGETPLATFORM},dst=/src/target . /env; mkdir /app; cp -r ./target/$RUST_TARGET/release/* /app
RUN ls
FROM scratch AS final
COPY --from=build /app/ /app/
CMD ["/app/my-app"]
Your .dockerignore
:
/.git
/.gitignore
/target
**/target
/docker-backe.hcl
/Dockerfile
/.vscode
/docs
/README.md