# Install the potrace + agg development headers so `pypotrace` can build from source.
#
# Why a playbook and not a one-liner: `pypotrace` is an **sdist that compiles at install
# time**, so every machine and every CI runner that resolves it needs these headers present
# FIRST. That makes this a recurring per-box action, not a one-off. Requested by forseti
# (pewpewstudio) for `core.image_pipeline`, which vectorises raster art into laser-ready
# contours; the operator chose pypotrace over shelling out to the potrace binary (2026-08-28).
#
# Run:  scripts/elway infra-ops@<host> --playbook playbooks/install-potrace-headers.yaml
# Rerunnable: a second run shows the install `skipped`.
#
# ⚠ TWO THINGS THAT WILL SEND YOU DOWN THE WRONG PATH ON A BOX WHERE THIS FAILS
#
# 1. **Only libagg is a pkg-config consumer. potrace is not.**
#
#        libagg    /usr/lib/x86_64-linux-gnu/pkgconfig/libagg.pc   present
#        potrace   NO .pc file — found via /usr/include/potracelib.h and the library
#
#    So `pkg-config --exists potrace` returns FALSE on a correctly configured box. It looks
#    exactly like the cause and never is. The real build error names libagg and only libagg:
#
#        Package libagg was not found in the pkg-config search path.
#        Package 'libagg', required by 'virtual:world', not found
#
#    A wrong model that produces a plausible-looking diagnostic costs more than no model.
#
# 2. **libagg's pkg-config modversion disagrees with its Debian package version.**
#
#        pkg-config --modversion libagg   ->  2.7.0
#        dpkg version                     ->  1:2.6.1-r134+dfsg1-2+b1
#
#    Comparing those two numbers convinces you the wrong package is installed. It is not a
#    problem; it is upstream's version vs Debian's packaging of it.
#
# Both of these were learned on the nh3-dev install and are recorded here rather than in an
# althing thread, at forseti's suggestion, because a thread is not where the next person looks.

vars:
  probe_venv: /tmp/pypotrace-probe
  # The user whose toolchain builds the probe. The headers are installed system-wide
  # as root; the build check is a developer action and runs as this user.
  dev_user: lkraven

steps:
  - name: Install the potrace and agg development headers
    shell: sudo DEBIAN_FRONTEND=noninteractive apt-get install -y libpotrace-dev libagg-dev
    when: "! dpkg -s libpotrace-dev >/dev/null 2>&1 || ! dpkg -s libagg-dev >/dev/null 2>&1"

verify:
  - name: libagg's pkg-config file is discoverable (this is the one that actually gates the build)
    shell: pkg-config --exists libagg
    changed_when: "false"

  - name: potrace's header is present (NOT via pkg-config — it ships no .pc)
    shell: test -f /usr/include/potracelib.h
    changed_when: "false"

  - name: pypotrace COMPILES against them
    # ⚠ `uv` is NOT on a non-interactive ssh PATH — infra-ops gets
    # /usr/local/bin:/usr/bin:/bin:/usr/games and nothing else. It also lives in a
    # different place on every box: /home/lkraven/bin/uv on nh3-dev,
    # /home/infra-ops/.local/bin/uv on nh3-extdev. Search rather than assume, and say
    # so loudly if it is genuinely absent — a build probe that silently does not run
    # is the failure this whole playbook exists to prevent.
    shell: |
      # ...and on nh3-dev it is inside a 0700 home, so `test -x` from infra-ops fails
      # even with the right absolute path — the directory cannot be traversed. The
      # headers are system-wide (root's business); the build probe is a DEVELOPER
      # action and has to run as the user who owns the toolchain.
      RUNAS={{ dev_user }}
      UV=""
      for c in /home/{{ dev_user }}/bin/uv /home/{{ dev_user }}/.local/bin/uv /usr/local/bin/uv; do
        sudo -u "$RUNAS" test -x "$c" && { UV="$c"; break; }
      done
      [ -n "$UV" ] || { echo "no uv reachable as $RUNAS; cannot run the build probe"; exit 1; }
      echo "  using uv at $UV (as $RUNAS)"
      sudo -u "$RUNAS" rm -rf {{ probe_venv }}
      sudo -u "$RUNAS" "$UV" venv {{ probe_venv }} >/dev/null 2>&1
      sudo -u "$RUNAS" "$UV" pip install --python {{ probe_venv }}/bin/python pypotrace 2>&1 | tail -2
    changed_when: "false"

  - name: and the built extension actually TRACES, which a successful build does not prove
    # A square must come back as one curve of four CornerSegments. If the extension linked
    # against something wrong it can still import and return nonsense; the geometry is the
    # assertion, not the import.
    shell: |
      sudo -u {{ dev_user }} {{ probe_venv }}/bin/python -c "
      import numpy as np, potrace
      a = np.zeros((40,40), np.uint32); a[10:30,10:30] = 1
      curves = list(potrace.Bitmap(a).trace())
      segs = [s for c in curves for s in c]
      assert len(curves) == 1, curves
      assert len(segs) == 4, segs
      assert {type(s).__name__ for s in segs} == {'CornerSegment'}, segs
      "
    changed_when: "false"

  - name: Remove the probe venv
    shell: sudo -u {{ dev_user }} rm -rf {{ probe_venv }}
    changed_when: "false"
