#!/usr/bin/env bash # SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2026 Bryan Joshua Pedini # tests/docker/snapshot.sh - record the host's Docker image set BEFORE tests run, # so tests/docker/cleanup.sh can restore it afterwards. # # Writes, next to this script: # images-before.tsv repo:tagimage-id (docker images --no-trunc, sorted) # image-ids-before.txt every image id, incl. untagged (docker images -a -q --no-trunc, sort -u) # # An existing snapshot is NEVER overwritten without --force: running `make test` # twice must not "launder" images pulled by the first run into the baseline. # Without --force an existing snapshot is kept and the script exits 0, so # `make docker-snapshot` / `make test` can call it every time. # # usage: snapshot.sh [--force] set -euo pipefail prog=${0##*/} here=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) tags_file=$here/images-before.tsv ids_file=$here/image-ids-before.txt usage() { cat <&2 exit 1 } force=0 for arg in "$@"; do case $arg in --force) force=1 ;; -h | --help) usage; exit 0 ;; *) printf '%s: unknown argument: %s\n' "$prog" "$arg" >&2; usage >&2; exit 1 ;; esac done if [[ -e $tags_file && -e $ids_file ]]; then if ((!force)); then printf '%s: snapshot already exists (taken %s), NOT overwriting it.\n' \ "$prog" "$(date -r "$tags_file" '+%Y-%m-%d %H:%M:%S %Z')" >&2 printf '%s: to re-baseline on purpose: %s --force\n' "$prog" "$0" >&2 exit 0 fi elif [[ -e $tags_file || -e $ids_file ]]; then ((force)) || die "incomplete snapshot (only one of ${tags_file##*/}, ${ids_file##*/} exists); inspect it, then re-run with --force" fi docker info >/dev/null 2>&1 || die "cannot talk to the Docker daemon (is it running, are you in the docker group?)" tmp_tags=$(mktemp "$tags_file.XXXXXX.tmp") tmp_ids=$(mktemp "$ids_file.XXXXXX.tmp") trap 'rm -f -- "$tmp_tags" "$tmp_ids"' EXIT # Exactly the commands of the original manual snapshot (plain `sort`, so the # order follows the caller's locale, byte-identical to running them by hand). # Consumers must not depend on the order: cleanup.sh reads both files into maps. docker images --no-trunc --format '{{.Repository}}:{{.Tag}}\t{{.ID}}' | sort >"$tmp_tags" docker images -a -q --no-trunc | sort -u >"$tmp_ids" if [[ -e $tags_file && -e $ids_file ]]; then # --force over an existing snapshot: show what the baseline gains, so an # accidental re-baseline after a test run is at least visible. gained_tags=$(LC_ALL=C comm -13 <(LC_ALL=C sort "$tags_file") <(LC_ALL=C sort "$tmp_tags")) gained_ids=$(LC_ALL=C comm -13 <(LC_ALL=C sort -u "$ids_file") <(LC_ALL=C sort -u "$tmp_ids")) if [[ -n $gained_tags || -n $gained_ids ]]; then printf '%s: WARNING: the new baseline adopts images that were not in the old one:\n' "$prog" >&2 if [[ -n $gained_tags ]]; then mapfile -t lines <<<"$gained_tags"; printf ' tag %s\n' "${lines[@]}" >&2; fi if [[ -n $gained_ids ]]; then mapfile -t lines <<<"$gained_ids"; printf ' id %s\n' "${lines[@]}" >&2; fi fi fi chmod 0644 "$tmp_tags" "$tmp_ids" mv -f -- "$tmp_tags" "$tags_file" mv -f -- "$tmp_ids" "$ids_file" trap - EXIT printf '%s: snapshot written: %d tags -> %s, %d ids -> %s\n' "$prog" \ "$(wc -l <"$tags_file")" "${tags_file#"$PWD"/}" "$(wc -l <"$ids_file")" "${ids_file#"$PWD"/}"