#!/bin/bash

## Copyright (C) 2026 - 2026 ENCRYPTED SUPPORT LLC <adrelanos@whonix.org>
## See the file COPYING for copying conditions.

## AI-Assisted

## Runner-side helper that caches the derivative-maker docker image across CI
## runs. The image is built by docker/derivative-maker-docker-run's
## build_docker_image() via 'docker build', whose Dockerfile RUNs
## derivative-maker-docker-setup (an apt-get install of build-host tools that
## hits the mirrors directly - not the in-container approx cache). On ephemeral
## runners the image is rebuilt every run. Caching it lets build_docker_image's
## 'docker images' guard find it and skip the rebuild, so those packages are
## not re-downloaded.
##
## Complements ci/approx-cache-sidecar, which caches the OTHER apt workload:
## the in-container build's target-OS packages fetched through approx.
##
## Paired with an actions/cache step over $HOME/.dm-docker-image; the cache-key
## carries a hashFiles('docker/**') so a Dockerfile/setup change rebuilds. No
## restore-keys (G-A-007).
##
## Usage (from .github/workflows/local-reproducible-build-test.yml):
##   ./ci/dm-docker-image-cache load    # if cache-hit, before the build
##   ./ci/dm-docker-image-cache save    # if cache-miss, after the build

set -o errexit
set -o nounset
set -o pipefail
set -o errtrace
shopt -s inherit_errexit
shopt -s shift_verbose
export LC_ALL=C

## No sensible developer-machine invocation (it sudo-manages the docker image
## store); mirrors ci/approx-cache-sidecar's guard.
if [ "${CI:-}" != "true" ] && [ "${ALLOW_LOCAL:-}" != "true" ]; then
   printf '%s\n' "${BASH_SOURCE[0]}: refusing to run outside CI (CI != 'true'). Set ALLOW_LOCAL=true to override." >&2
   exit 1
fi

MYDIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" && pwd )"
img="$( "${MYDIR}"/../docker/derivative-maker-docker-image-ref )"
cache_dir="${HOME}/.dm-docker-image"
image_tar="${cache_dir}/image.tar"

runner_uid_gid="$(id -u):$(id -g)"

case "${1:-}" in
   load)
      ## Cache miss leaves no tar; nothing to load (build_docker_image will
      ## build the image itself).
      [ -f "${image_tar}" ] || exit 0
      ## If the daemon is unreachable, the build is doomed anyway; fail loudly
      ## WITHOUT deleting a possibly-good tar, so a transient daemon restart does
      ## not drop the cache.
      if ! sudo --non-interactive -- docker info >/dev/null 2>&1; then
         printf '%s\n' "${BASH_SOURCE[0]}: ERROR: docker daemon not reachable; leaving the cache tar in place." >&2
         exit 1
      fi
      ## Daemon is up, so a load failure now means a corrupted tar (partial
      ## write, disk/cache issue), which must NOT block CI: drop the bad tar so
      ## build_docker_image rebuilds the image instead of the job aborting.
      ## 'rm' (not safe-rm): a GitHub-hosted runner has no safe-rm installed,
      ## and the path is a fixed cache file, not user input.
      ## style-ok: no-safe-rm
      if ! sudo --non-interactive -- docker load --input "${image_tar}"; then
         printf '%s\n' "${BASH_SOURCE[0]}: WARNING: docker load failed; cache tar corrupted. Rebuilding the image." >&2
         rm --force -- "${image_tar}"
      fi
      ;;
   save)
      mkdir --parents -- "${cache_dir}"
      ## 'docker save' as root (the harness builds via 'sudo docker'); then hand
      ## the tar to the runner so actions/cache can read it on post. Save to a
      ## temp file and rename on success, so a mid-save failure (disk full, image
      ## deleted concurrently) never leaves a truncated image.tar behind that a
      ## later 'load' would treat as the cache.
      sudo --non-interactive -- docker save "${img}" --output "${image_tar}.tmp"
      sudo --non-interactive -- chown -- "${runner_uid_gid}" "${image_tar}.tmp"
      mv -- "${image_tar}.tmp" "${image_tar}"
      ;;
   *)
      printf '%s\n' "usage: ${BASH_SOURCE[0]} <load|save>" >&2
      exit 64
      ;;
esac
