#!/bin/bash # # Build a minideb image # # First we build the image as a tarball, then we import it and tag it. # # However we aim to allow our images to be reproduced. That means # we need more control over the import process. We also build and import # each image twice to confirm that our images are still reproducible. # # To reproduce an image you have to: # # - Produce exactly the same base tarball. `mkimage` will take care of that # for the same package inputs. # - Import the image with the same config (`CMD` etc.) # - Have the same creation date on the image. # # That last requirement leads us to some extra work to re-use timestamps. # # The steps are: # # 1. Pull image from Dockerhub and save creation date and image_id # 2. Build image locally and import it, setting creation date to the pulled one # 3. Build the image again and import it, also setting creation date to the pulled one # 4. Compare the built image ids. Error if they are not the same (Docker thinks images are different, thanks to checksum) # 5. Compare built image id with pulled image id. Both will have same creation date but may differ in checksum so ids may be different # - If the image is the same as the pulled one then nothing changed in this build # - If the image differs from the pulled one then: # - Re-import the locally built image with the current timestamp so it will be shown as a new image # - Tag the built image with the target tag, ready to push. set -e set -u set -o pipefail BASENAME=bitnami/minideb mkdir -p build log() { echo "$@" >&2 } build() { DIST=$1 PLATFORM=${2:-amd64} TAG=$DIST-$PLATFORM [ -f "debootstrap/$DIST" ] || (echo "buildall: Unknown distribution: $DIST" && exit 1) current_ts="$(date -u +%Y-%m-%dT%H:%M:%S.%NZ)" if docker pull "$BASENAME:$TAG" > /dev/null; then target_ts="$(docker inspect "$BASENAME:$TAG" | jq --raw-output ".[0].Created")" pulled_image_id="$(docker inspect "$BASENAME:$TAG" | jq --raw-output ".[0].Id")" else target_ts="$current_ts" pulled_image_id= fi log "============================================" log "Building $BASENAME:$TAG" log "============================================" ./mkimage "build/$TAG.tar" "$DIST" "$PLATFORM" built_image_id=$(./import "build/$TAG.tar" "$target_ts" "$PLATFORM") log "============================================" log "Running tests for $BASENAME:$TAG" log "============================================" ./test "$built_image_id" "$TAG" "$PLATFORM" log "============================================" log "Rebuilding $BASENAME:$TAG to test reproducibility" log "============================================" ./mkimage "build/${TAG}-repro.tar" "$DIST" "$PLATFORM" repro_image_id=$(./import "build/${TAG}-repro.tar" "$target_ts" "$PLATFORM") if [ "$repro_image_id" != "$built_image_id" ]; then log "$BASENAME:$TAG differs after a rebuild. Examine $built_image_id and $repro_image_id" log "to find the differences and fix the build to be reproducible again." log "Changes (- first build, + second build):" ./dockerdiff "$built_image_id" "$repro_image_id" || true exit 1 fi rm "build/${TAG}-repro.tar" if [ -n "$pulled_image_id" ]; then if [ "$built_image_id" != "$pulled_image_id" ]; then log "Image changed $built_image_id (new) != $pulled_image_id (old)" log "Changes (- old, + new):" ./dockerdiff "$pulled_image_id" "$built_image_id" || true # Re-import with the current timestamp so that the image shows # as new built_image_id="$(./import "build/$TAG.tar" "$current_ts" "$PLATFORM")" else log "Image didn't change" return fi fi docker tag "$built_image_id" "$BASENAME:$TAG" log "Tagged $built_image_id as $BASENAME:$TAG" } if [ -z "$1" ]; then echo "You must specify the dist to build" exit 1 fi build "$@"