#!/usr/bin/env bash
#
# host-recipe.sh — the per-machine mint recipe both image builders read.
#
# Sourced by build/build-image.sh and build/build-iso.sh alongside
# build/build-stamp.sh and build/privilege.sh.
#
# The dials that decide what a machine can do (PROFILE, LANGS, DB) all default
# to the smallest thing that boots. LANGS defaults to empty, so a default mint
# carries no compiler at all, and the C toolchain leaves with it. That is the
# right default for an image whose job is to run software, and it is a trap for
# the three machines in this tree that build it: a forgotten flag produces a
# build host that boots, looks healthy, and cannot compile.
#
# So the dials live in a file per machine. The difference between two machines
# is then a diff rather than a memory, and `--host fw13` is what a mint is
# asked for rather than four flags somebody has to remember in the right order.
#
# Format: KEY=VALUE, one per line. Blank lines and # comments are ignored, and
# so is trailing whitespace. Quotes are not stripped, because no dial takes a
# value that needs them.
#
# A value of the form @path reads the file instead. That exists for
# ALLOY_SSH_KEY: a machine that is reinstalled repeatedly should not need its
# key pasted in at each mint, and the recipe is committed to a public repo, so
# it names where the key lives rather than carrying it. ~ is expanded; a
# missing file is an error, since a medium minted with no key silently becomes
# one that needs somebody at the keyboard.
#
# Every key must name an ARG the Containerfile declares. That is checked here,
# against the Containerfile itself rather than a list kept in step by hand,
# because a typo'd key is silently dropped by podman and would mint exactly the
# image this file exists to prevent.
#
# Two reserved keys are not build args, because what they name is not something
# the Containerfile can be told. Both are consumed by build/build-iso.sh and
# ignored by build/build-image.sh.
#
#   ARCH     the medium's architecture. Not a cross-build: a machine builds its
#            own medium, per the standing rule that nothing here cross-compiles.
#   UPDATE_TARGET  where `bootc upgrade` fetches from on a machine installed
#            from this medium. Without one the installer writes the compiled-in
#            public registry, which install/image.rs calls a deliberate dead
#            end, and updates_scheduled() leaves the timer disabled because it
#            reads the same absent kernel parameter. So a machine minted without
#            it cannot be fixed without a `bootc switch` typed at the machine or
#            a second medium, at the moment it is newest. It was a flag anyone
#            could forget; a recipe key is a machine's own answer, kept.
#   NVIDIA   yes to install the derived alloy-nvidia image rather than Alloy
#            itself. The NVIDIA module is not a package the shipped Containerfile
#            can add: it has to be compiled against one exact kernel, so it lives
#            in a thin image `FROM` that kernel's digest (build/build-nvidia.sh,
#            ruled 2026-08-30 on alloy `2a382a75`). A dial would kernel-lock the
#            shipped image; a recipe key says which machine's medium carries the
#            derived one.

# Read a recipe into a BUILD_ARGS array, and set HOST_ARCH from a recipe that
# names one. Recipe args go in before anything the caller typed, so an explicit
# `--build-arg` on the command line is the last value podman sees and wins.
#
# Usage: host_recipe_args NAME BUILD_ARGS   (the array's NAME, not its contents)
host_recipe_args() {
  local host="$1"
  local -n out="$2"
  local file="$REPO_ROOT/build/hosts/$host.env"
  local containerfile="$REPO_ROOT/Containerfile"

  [ -f "$file" ] || {
    printf 'error: no recipe for host %s at %s\n' "$host" "$file" >&2
    printf 'known hosts: %s\n' "$(cd "$REPO_ROOT/build/hosts" 2>/dev/null \
      && ls -1 ./*.env 2>/dev/null | sed 's|^\./||; s|\.env$||' | tr '\n' ' ')" >&2
    return 1
  }

  HOST_ARCH=""
  HOST_NVIDIA=""
  HOST_UPDATE_TARGET=""
  local recipe=()
  local line key value
  while IFS= read -r line || [ -n "$line" ]; do
    line="${line%%#*}"
    line="${line%"${line##*[![:space:]]}"}"
    [ -n "$line" ] || continue
    case "$line" in
      *=*) ;;
      *) printf 'error: %s: not KEY=VALUE: %s\n' "$file" "$line" >&2; return 1 ;;
    esac
    key="${line%%=*}"
    value="${line#*=}"
    case "$value" in
      @*)
        local path="${value#@}"
        case "$path" in "~/"*) path="$HOME/${path#\~/}" ;; esac
        [ -f "$path" ] || {
          printf 'error: %s: %s reads %s, which does not exist\n' "$file" "$key" "$path" >&2
          return 1
        }
        value="$(tr -d '\r\n' < "$path")"
        [ -n "$value" ] || {
          printf 'error: %s: %s reads %s, which is empty\n' "$file" "$key" "$path" >&2
          return 1
        }
        ;;
    esac
    if [ "$key" = "ARCH" ]; then
      HOST_ARCH="$value"
      continue
    fi
    if [ "$key" = "UPDATE_TARGET" ]; then
      HOST_UPDATE_TARGET="$value"
      continue
    fi
    if [ "$key" = "NVIDIA" ]; then
      case "$value" in
        yes|no) HOST_NVIDIA="$value" ;;
        *) printf 'error: %s: NVIDIA is %s; expected yes or no\n' "$file" "$value" >&2; return 1 ;;
      esac
      continue
    fi
    grep -q "^ARG $key=" "$containerfile" || {
      printf 'error: %s: %s is not an ARG the Containerfile declares\n' "$file" "$key" >&2
      return 1
    }
    recipe+=(--build-arg "$key=$value")
  done < "$file"

  out=("${recipe[@]}" "${out[@]}")
}
