#!/usr/bin/env bash
#
# installtime.sh — can the installer layer a package into the target it just
# installed, or does that have to wait for the machine's first boot?
#
#   installtime.sh
#
# The question this answers. Alloy's hotfixable components cannot ship in the
# base image (see README), so an installed machine has to acquire them some
# other way, and Max chose 2026-08-14 that they travel on the ISO rather than
# over the network. That leaves one sub-decision: whether `alloy install` lays
# them down while the target is still mounted, or whether a first-boot service
# does it on the installed machine.
#
# Layering at install time is worth a measurement because it removes a whole
# moving part: no first-boot unit, no stamp file to decide whether it already
# ran, no extra reboot in front of a user who just finished installing, and no
# ordering constraint against greetd. `rpm-ostree install` takes --sysroot, so
# it is at least plausible. Whether it works against a target that has never
# been booted is the thing to find out.
#
# What it does: install n1 (a base carrying nothing), mount the result, put a
# repo and its .repo file inside the target, run rpm-ostree against that
# sysroot from a container, unmount, boot it, and see whether the package is
# there on the very first boot.
#
# ## RESULT 2026-08-14: NOT PROVEN, AND FIRST BOOT WAS CHOSEN INSTEAD
#
# It does not work from this harness, and the reason is D-Bus rather than
# anything about ostree. `rpm-ostree install` is a client that talks to
# rpm-ostreed over the system bus, and --sysroot does not make it standalone.
# Three attempts, each getting one step further:
#
#   no bus at all         error: Loading sysroot: Connecting to system bus:
#                         Could not connect: No such file or directory
#   dbus-daemon --system  error: Failed to invoke RegisterClient:
#                         GDBus.Error...Spawn.ChildExited: Launch helper
#                         exited with unknown return code 1
#   daemon started by     error: Object does not exist at path "/"
#   hand on that sysroot
#
# The measurement was abandoned there rather than pushed further, because the
# first-boot shape was already proven by the ordinary cases in README.md and
# the difference between them is one reboot.
#
# **Do not read this as "install-time layering is impossible."** The failures
# above are a container with no systemd fighting D-Bus activation. The real
# `alloy install` runs inside the ISO's live environment, which is a full
# Fedora system with systemd, a system bus and rpm-ostree already present, so
# the thing that blocked this harness may simply not be there. What stays
# genuinely unproven is whether rpm-ostreed will operate on a sysroot that is
# not the one it is running from. If the first-boot reboot ever becomes worth
# removing, that is the question to answer, and the right place to answer it is
# from a booted live ISO rather than from here.
set -euo pipefail

# shellcheck source=build/layertest/common.sh
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common.sh"

# shellcheck source=build/privilege.sh
. "$HERE/../privilege.sh"

MNT="$STATE/target"

cleanup() {
  privc umount "$MNT" 2>/dev/null || true
  [ -n "${LOOP:-}" ] && privc losetup -d "$LOOP" 2>/dev/null
  true
}
trap cleanup EXIT

"$HERE/install-disk.sh" n1

# -------------------------------------------------------------- mount it
# -P so the partition table is read and /dev/loopNp* appear. bootc's generic
# ext4 layout is ESP, boot, root; the root partition is the last and the big
# one, and it is the one carrying ostree/.
say ""
say "mounting the installed target"
LOOP="$(privc losetup --find --show -P "$STATE/disk.raw")"
privc mkdir -p "$MNT"

ROOT=""
for part in "$LOOP"p*; do
  privc mount "$part" "$MNT" 2>/dev/null || continue
  if privc test -d "$MNT/ostree"; then ROOT="$part"; break; fi
  privc umount "$MNT"
done
[ -n "$ROOT" ] || die "no partition on $LOOP carries an ostree sysroot"
say "sysroot is $ROOT"

# --------------------------------------------- put the repo inside the target
# /var, not /usr: /usr is the immutable ostree tree and belongs to the image,
# while /var is the machine's own writable state and survives every upgrade.
# This is where the ISO's copy of our RPMs would land during a real install.
#
# The deployment's /etc is where rpm-ostree reads repo config from, and under
# ostree that is a per-deployment directory rather than a symlink into /var.
DEPLOY="$(privc sh -c "ls -d $MNT/ostree/deploy/*/deploy/*/ | head -1")"
[ -n "$DEPLOY" ] || die "no deployment under $MNT/ostree/deploy"
say "deployment is ${DEPLOY#"$MNT"}"

privc mkdir -p "$MNT/ostree/deploy/default/var/lib/alloy" 2>/dev/null || true
VAR="$(privc sh -c "ls -d $MNT/ostree/deploy/*/var | head -1")"
privc mkdir -p "$VAR/lib/alloy"
privc cp -r "$STATE/repo" "$VAR/lib/alloy/rpm"

# file:// and not http://, which is the whole point: no network at install time
# and none at first boot either.
privc tee "$DEPLOY/etc/yum.repos.d/alloy-local.repo" >/dev/null <<'EOF'
[alloy-local]
name=Alloy components, from the installer medium
baseurl=file:///var/lib/alloy/rpm
enabled=1
gpgcheck=0
EOF

# ------------------------------------------------------ layer into the target
# In a container because the dev host has no rpm-ostree. --privileged and the
# host mount namespace, because it is about to operate on a mounted filesystem
# and drive device nodes underneath it.
say ""
say "=== rpm-ostree install --sysroot against the unbooted target ==="
set +e
privc podman run --rm --privileged --pid=host \
  --security-opt label=type:unconfined_t \
  -v /dev:/dev \
  -v "$MNT:/target" \
  registry.fedoraproject.org/fedora:43 bash -c '
    dnf -y install rpm-ostree >/dev/null 2>&1
    rpm-ostree --sysroot=/target install alloy-demo 2>&1 | tail -20
    exit "${PIPESTATUS[0]}"
  '
RESULT=$?
set -e
say "rpm-ostree exit: $RESULT"

cleanup
trap - EXIT

# ------------------------------------------------------------- boot and look
say ""
say "booting the target"
nohup "$HERE/vm.sh" >"$STATE/qemu.log" 2>&1 &
sleep 50

say "--- first boot, never booted before this ---"
# `|| true` on the last command, not decoration. `rpm -q` exits 1 for a package
# that is not installed, which is a perfectly good answer here and is exactly
# the answer this script expects to see; without it the absent case reports
# "guest did not come back" about a guest that came back and said so.
"$HERE/sshx" 'echo -n "base-mark: "; cat /usr/share/base-mark; \
  echo -n "binary:    "; alloy-demo 2>&1 || echo "(absent)"; \
  echo -n "rpm -q:    "; rpm -q alloy-demo 2>&1 || true' \
  || die "guest did not come back"
"$HERE/sshx" 'rpm-ostree status --json' | python3 "$HERE/readstate.py"
