#!/bin/bash
# Upstream claims reproducible builds, so use that as the end-to-end test of
# the installed package: build the reproducible_debfs example twice, into two
# different work directories, and require the two filesystem tarballs to be
# byte-identical.
#
# This exercises nearly all of the package in one go - layer resolution, the
# dynamic-layer generator, the shipped bdebstrap and genimage, the mmdebstrap
# run under user namespaces and the deploy stage - and it fails loudly if the
# installed tree is missing anything the pipeline reaches for.
#
# The example pins SOURCE_DATE_EPOCH and builds from a Debian snapshot, so it
# is architecture-agnostic and stable over time. It needs network access to
# snapshot.debian.org and the privileges mmdebstrap wants for its mount
# namespace, hence needs-internet and isolation-machine.
set -uo pipefail

EXAMPLE=/usr/share/rpi-image-gen/examples/reproducible_debfs
TMP=${AUTOPKGTEST_TMP:-$(mktemp -d)}
SKIP=77

[ -d "$EXAMPLE" ] || {
   echo "${EXAMPLE} missing - is rpi-image-gen-doc installed?" >&2
   exit 1
}

# Separate an unusable environment from a broken package. Marked skippable, so
# bail out with 77 rather than reporting a failure we cannot attribute.
if ! unshare --user --map-root-user true 2>/dev/null ; then
   echo "SKIP: unprivileged user namespaces unavailable" >&2
   exit $SKIP
fi
if ! curl -fsS --max-time 60 -o /dev/null \
      "https://snapshot.debian.org/archive/debian/20251120T122224Z/" ; then
   echo "SKIP: snapshot.debian.org unreachable" >&2
   exit $SKIP
fi

# mmdebstrap runs apt, which drops to the unprivileged _apt user to fetch.
# Every ancestor of the work directory therefore has to be traversable by
# _apt, or apt reports the key directory as "not a directory" and the build
# fails with no hint as to why. AUTOPKGTEST_TMP, and anything from mktemp -d,
# is 0700.
#
# Open up what we own, then check the whole chain rather than assuming. A real
# isolation-machine runner hands us something world-traversable, but
# salsa-ci.yml tells you to run this test by hand, and that is where a 0700
# ancestor bites.
chmod a+rx "$TMP" || exit 1
# Only check the ancestors - do not chmod them. Running as root we would
# "own" / and /tmp, and quietly relaxing those is not this test's business.
untraversable=
d=$(dirname "$TMP")
while : ; do
   case $(stat -c %a "$d") in
      *[!157]) untraversable="$d" ;;
   esac
   [ "$d" = / ] && break
   d=$(dirname "$d")
done
if [ -n "$untraversable" ] ; then
   echo "SKIP: ${untraversable} is not traversable by the _apt user, which" >&2
   echo "      apt drops to when fetching. chmod a+rx it and rerun." >&2
   exit $SKIP
fi

build() {
   local dir="${TMP}/$1"
   mkdir -p "$dir" || return 1
   chmod a+rx "$dir" || return 1
   ( cd "$dir" && rpi-image-gen build -S "$EXAMPLE" -c build.yaml ) \
      > "${TMP}/$1.log" 2>&1
}

for run in first second ; do
   echo "== ${run} build"
   if ! build "$run" ; then
      echo "   FAILED - last 40 lines:"
      tail -40 "${TMP}/${run}.log" | sed 's/^/   | /'
      exit 1
   fi
done

# Assign the globs straight to arrays. Going via printf | mapfile would turn
# "no match" under nullglob into a one-element array holding an empty string,
# and the count check below would wave it through.
shopt -s nullglob
a=( "${TMP}"/first/work/chroot-*/trixie-reproducible.tgz )
b=( "${TMP}"/second/work/chroot-*/trixie-reproducible.tgz )

if (( ${#a[@]} != 1 || ${#b[@]} != 1 )) ; then
   echo "expected exactly one tarball per build, got ${#a[@]} and ${#b[@]}" >&2
   exit 1
fi

# Patch 0007 exists so that nothing is compiled on the user's machine. If a
# trigger comes back on a new upstream release, the build still succeeds - it
# just quietly costs a toolchain again - so check for it rather than trusting
# that the patch still applies where it used to.
for run in first second ; do
   if grep -q "Building host support took" "${TMP}/${run}.log" ; then
      echo "${run} build compiled host tools; patch 0007 is not holding" >&2
      grep -n "Building host support took" "${TMP}/${run}.log" >&2
      exit 1
   fi
done

echo "== comparing"
sha256sum "${a[0]}" "${b[0]}"
if cmp -s "${a[0]}" "${b[0]}" ; then
   echo "   ok - builds are byte-identical"
   exit 0
fi

echo "   FAIL - builds differ" >&2
if command -v diffoscope > /dev/null ; then
   diffoscope --max-report-size 200000 "${a[0]}" "${b[0]}" || true
fi
exit 1
