590b55f7d8
pypotrace is an sdist that compiles at install time, so every machine and every CI runner resolving it needs these headers first. That makes it a recurring per-box action rather than the one-off it arrived as. Two things learned installing it on nh3-dev are recorded here rather than left in an althing thread, at forseti's suggestion, because a thread is not where the next person looks: Only libagg is a pkg-config consumer. potrace ships no .pc file and is found via potracelib.h directly, so `pkg-config --exists potrace` returns false on a correctly configured box. It looks exactly like the cause and never is. libagg's pkg-config modversion is 2.7.0 while its Debian package version is 1:2.6.1-r134. Comparing those two numbers convinces you the wrong package is installed. The verify phase asserts the geometry, not the import: a square must come back as one curve of four CornerSegments. An extension linked against the wrong thing can import cleanly and return nonsense, so a successful build is not evidence the module works. Getting the build probe to run took three passes and the reason is worth keeping. uv is not on a non-interactive ssh PATH; it is in a different place on each box; and on nh3-dev it sits inside a 0700 home, so even the correct absolute path fails `test -x` for the ssh user because the directory cannot be traversed. The headers are system-wide and root's business, but the build check is a developer action and has to run as the user who owns the toolchain.
101 lines
4.9 KiB
YAML
101 lines
4.9 KiB
YAML
# 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"
|