Skip to content

Commit

Permalink
ci: make containers reproducible
Browse files Browse the repository at this point in the history
Make containers reproducible by:

* using a dated debian tag; and
* enabling the snapshot server URL
* enabling SOURCE_DATE_EPOCH and container file timestamping
* using a tmpfs for /var/log
* removing unreproducible files

The instructions are heavily influenced by [1].

Add git `safe.directory` exemptions everywhere in case the
`/src` filesystem is mounted as a different uid. This is very
common in containerized build environments.

Also move each container into its own build context directory.
The filesystem layout now matches the container registry URL.

Containerization is an unending time sink—there is always
something more to be done.

[1]: siemens/kas@bb51bd7
  • Loading branch information
cbs228 committed Jan 23, 2025
1 parent e8d0f3a commit 2eb2eab
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@

FROM ghcr.io/cbs228/sameold/builder/rust:latest

RUN --mount=target=/var/lib/apt/lists,type=cache,sharing=locked \
ARG SOURCE_DATE_EPOCH

RUN --mount=target=/var/lib/apt,type=cache,sharing=locked \
--mount=target=/var/cache/apt,type=cache,sharing=locked \
--mount=destination=/var/log,type=tmpfs \
[ "$(dpkg --print-architecture)" != arm64 ] || exit 0; \
set -eux; \
apt-get update; \
Expand All @@ -14,7 +17,8 @@ RUN --mount=target=/var/lib/apt/lists,type=cache,sharing=locked \
gcc-aarch64-linux-gnu \
libc6-dev-arm64-cross \
libc6:arm64 \
;
; \
rm -f -- /etc/machine-id /var/cache/ldconfig/aux-cache

ARG RUST_TARGET="aarch64-unknown-linux-gnu"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@

FROM ghcr.io/cbs228/sameold/builder/rust:latest

RUN --mount=target=/var/lib/apt/lists,type=cache,sharing=locked \
ARG SOURCE_DATE_EPOCH

RUN --mount=target=/var/lib/apt,type=cache,sharing=locked \
--mount=target=/var/cache/apt,type=cache,sharing=locked \
--mount=destination=/var/log,type=tmpfs \
[ "$(dpkg --print-architecture)" != armhf ] || exit 0; \
set -eux; \
apt-get update; \
Expand All @@ -14,7 +17,8 @@ RUN --mount=target=/var/lib/apt/lists,type=cache,sharing=locked \
gcc-arm-linux-gnueabihf \
libc6-dev-armhf-cross \
libc6:armhf \
;
; \
rm -f -- /etc/machine-id /var/cache/ldconfig/aux-cache

ARG RUST_TARGET="armv7-unknown-linux-gnueabihf"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,41 @@
# Debian base image
#

ARG DEBIAN_SUITE=buster
# or buster-20240612-slim for a snapshot-capable, reproducible image
ARG DEBIAN_TAG=buster-slim

FROM docker.io/library/debian:${DEBIAN_SUITE}-slim
FROM docker.io/library/debian:${DEBIAN_TAG}

ARG DEBIAN_TAG
ARG SOURCE_DATE_EPOCH

# Create a non-root user for running builds
# GHA wants uid 1001
ARG UID=1001

RUN --mount=target=/var/lib/apt/lists,type=cache,sharing=locked \
RUN --mount=target=/var/lib/apt,type=cache,sharing=locked \
--mount=target=/var/cache/apt,type=cache,sharing=locked \
--mount=destination=/var/log,type=tmpfs \
set -eux; \
# create an unprivileged user for builds
useradd -u "$UID" --create-home --user-group builder; \
groupadd builder -g "$UID"; \
useradd builder -u "$UID" -g "$UID" --create-home; \
# build directories for any user
mkdir -m 1777 /src /install /cargo; \
# re-enable apt caching (we have a cache mount)
rm -f /etc/apt/apt.conf.d/docker-clean; \
# if tag contains numerics, like buster-20240612-slim, use the
# snapshot URL that's baked into the image
if echo "${DEBIAN_TAG}" | grep -q "[0-9]"; then \
sed -i -r \
-e 's/^deb/# deb/' \
-e 's|^#\s*(.*http://snapshot\.)|\1|' \
/etc/apt/sources.list; \
cat >&2 /etc/apt/sources.list; \
echo 'Acquire::Check-Valid-Until "false";' > /etc/apt/apt.conf.d/use-snapshot.conf; \
echo 'Acquire::Retries "10";' >> /etc/apt/apt.conf.d/use-snapshot.conf; \
echo 'Acquire::Retries::Delay::Maximum "600";' >> /etc/apt/apt.conf.d/use-snapshot.conf; \
fi; \
# enable packages from multiple architectures
dpkg --add-architecture amd64; \
dpkg --add-architecture armhf; \
Expand All @@ -36,8 +54,9 @@ RUN --mount=target=/var/lib/apt/lists,type=cache,sharing=locked \
qemu-user \
qemu-user-binfmt \
tar \
zstd \
;
zstd; \
git config --system --add safe.directory '*'; \
rm -f -- /etc/machine-id /var/cache/ldconfig/aux-cache

# Directories for sources and installed binaries
VOLUME ["/src", "/install"]
Expand Down
72 changes: 50 additions & 22 deletions .github/container/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
#
# run with --push to push the images.

now="$(date -u +'%Y-%m-%d')"
# set to disable cache
NO_CACHE="${NO_CACHE:-}"

DEBIAN_TAG="buster-20240612-slim"
CONTAINER_PREFIX="ghcr.io/cbs228"
CONTAINER_FQNAME="${CONTAINER_PREFIX}/sameold/builder/%s"
CONTAINER_TAGS=("$now" "latest")

RUST_VERSIONS=("1.84.0")

Expand Down Expand Up @@ -60,6 +61,28 @@ container_name() {
printf "$CONTAINER_FQNAME" "$1"
}

buildcontainer() {
# Usage: buildcontainer ARGS
#
# Run the $BUILDER to build a container image. Some standardized
# arguments are passed to every build.

if [[ $BUILDER =~ podman$ ]]; then
run "$BUILDER" build \
--build-arg SOURCE_DATE_EPOCH="$SOURCE_DATE_EPOCH" \
--timestamp "$SOURCE_DATE_EPOCH" \
${NO_CACHE:+--no-cache} \
"$@"
else
# docker mode; 100% untested
run "$BUILDER" buildx build \
--build-arg SOURCE_DATE_EPOCH="$SOURCE_DATE_EPOCH" \
--output type=docker,rewrite-timestamp=true \
${NO_CACHE:+--no-cache} \
"$@"
fi
}

tagall() {
# Usage: tagall SHORTNAME TAG0 TAG1 ...
#
Expand Down Expand Up @@ -115,54 +138,59 @@ while true; do
done

BUILDER="$(container_builder)"
selfdir="$(dirname "${0?}")"
selfdir="$(dirname "$(realpath -e "${0?}")")"

# set SOURCE_DATE_EPOCH if possible
SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(git log -1 --pretty=%ct -- "$selfdir" || date +%s)}"
export SOURCE_DATE_EPOCH

# tag with "latest" and SOURCE_DATE_EPOCH
CONTAINER_TAGS=("latest" "$(date --date '@'"$SOURCE_DATE_EPOCH" +'%Y-%m-%d')")

# build the base image
base_tag="$(container_name base):${CONTAINER_TAGS[0]}"

run "$BUILDER" build \
-f "${selfdir?}/Dockerfile.base" \
buildcontainer \
--build-arg DEBIAN_TAG="$DEBIAN_TAG" \
--tag "$base_tag" \
"${selfdir?}"
"${selfdir?}/base"

# add rust to the base image
rust_tag="$(container_name rust):${CONTAINER_TAGS[0]}"

run "$BUILDER" build \
-f "${selfdir?}/Dockerfile.rust" \
buildcontainer \
--from "$base_tag" \
--build-arg RUST_VERSIONS="${RUST_VERSIONS[*]}" \
--tag "$rust_tag" \
"${selfdir?}"
"${selfdir?}/rust"

# build architecture-specific images
for containerfile in "${selfdir}/"Dockerfile.rust.*; do
platform_triple="${containerfile##*.}"
for containerdir in "${selfdir?}/"*-*-*; do
[ -d "$containerdir" ] || continue

platform_triple="$(basename ${containerdir})"
cur_tag="$(container_name "$platform_triple"):${CONTAINER_TAGS[0]}"

run "$BUILDER" build \
buildcontainer \
--from "$rust_tag" \
-f "${containerfile}" \
--tag "${cur_tag}" \
"${selfdir?}"
"${containerdir}"
done

# if all builds succeed, apply remaining tags...
tagall base "${CONTAINER_TAGS[@]}"
tagall rust "${CONTAINER_TAGS[@]}"
for containerfile in "${selfdir}/"Dockerfile.rust.*; do
platform_triple="${containerfile##*.}"
for containerdir in "${selfdir?}/"*-*-*; do
[ -d "$containerdir" ] || continue

tagall "$platform_triple" "${CONTAINER_TAGS[@]}"
tagall "$(basename "$containerdir")" "${CONTAINER_TAGS[@]}"
done

[ -n "${push_images:-}" ] || exit 0

# ... and push
pushall base "${CONTAINER_TAGS[@]}"
pushall rust "${CONTAINER_TAGS[@]}"
for containerfile in "${selfdir}/"Dockerfile.rust.*; do
platform_triple="${containerfile##*.}"
for containerdir in "${selfdir?}/"*; do
[ -d "$containerdir" ] || continue

pushall "$platform_triple" "${CONTAINER_TAGS[@]}"
pushall "$(basename "$containerdir")" "${CONTAINER_TAGS[@]}"
done
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@

FROM ghcr.io/cbs228/sameold/builder/rust:latest

RUN --mount=target=/var/lib/apt/lists,type=cache,sharing=locked \
ARG SOURCE_DATE_EPOCH

RUN --mount=target=/var/lib/apt,type=cache,sharing=locked \
--mount=target=/var/cache/apt,type=cache,sharing=locked \
--mount=destination=/var/log,type=tmpfs \
[ "$(dpkg --print-architecture)" != i386 ] || exit 0; \
set -eux; \
apt-get update; \
Expand All @@ -14,7 +17,8 @@ RUN --mount=target=/var/lib/apt/lists,type=cache,sharing=locked \
gcc-i686-linux-gnu \
libc6-dev-i386-cross \
libc6:i386 \
;
; \
rm -f -- /etc/machine-id /var/cache/ldconfig/aux-cache

ARG RUST_TARGET="i686-unknown-linux-gnu"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

FROM ghcr.io/cbs228/sameold/builder/base:latest

ARG SOURCE_DATE_EPOCH

# Install rustup and platform-native target
#
# RUST_VERSIONS: list of version numbers or strings
Expand Down Expand Up @@ -52,6 +54,6 @@ RUN set -eux; \
(umask 022 && echo "$RUSTUP_ARCH" >/etc/rust-native-arch)

# Install scripts
COPY rootfiles/ /
COPY rootfiles /

LABEL org.opencontainers.image.description="A pinned Rust toolchain with rustup."
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@

FROM ghcr.io/cbs228/sameold/builder/rust:latest

RUN --mount=target=/var/lib/apt/lists,type=cache,sharing=locked \
ARG SOURCE_DATE_EPOCH

RUN --mount=target=/var/lib/apt,type=cache,sharing=locked \
--mount=target=/var/cache/apt,type=cache,sharing=locked \
--mount=destination=/var/log,type=tmpfs \
[ "$(dpkg --print-architecture)" != amd64 ] || exit 0; \
set -eux; \
apt-get update; \
Expand All @@ -14,7 +17,8 @@ RUN --mount=target=/var/lib/apt/lists,type=cache,sharing=locked \
gcc-x86-64-linux-gnu \
libc6-dev-amd64-cross \
libc6:amd64 \
;
; \
rm -f -- /etc/machine-id /var/cache/ldconfig/aux-cache

ARG RUST_TARGET="x86_64-unknown-linux-gnu"

Expand Down

0 comments on commit 2eb2eab

Please sign in to comment.