dnf5's `dnf install <pkg>` is a no-op when the package is already
installed at ANY version — it does NOT auto-upgrade to the latest
available. The deploy script's install branch was therefore silently
leaving hosts on older builds even though needs_update correctly
reported an upgrade was available.
Add an is_installed() probe and an install_or_upgrade() helper that
picks the right verb: `dnf install` when fresh, `dnf upgrade` when
stale. Captured combined-stream output is exposed via __DNF_OUTPUT__
for the existing failure-diagnostic path.
Verified end-to-end against the live fleet: hanzalova/beast/benjy/
quadbrat all upgraded cleanly from prior prerelease NVRs to
0.1.16-0.1.20260519134302.git1866b99.fc43, validation script returned
"Paris" from all three neurons.
Followup (not in this commit): all hosts running helexa-neuron-*
need libcudnn.so.9 available at runtime. Currently:
- quadbrat: libcudnn9-cuda-13 RPM (rhel9 CUDA repo)
- beast: /usr/lib64/libcudnn.so.9 (manual install)
- benjy: needed rhel9 CUDA repo added + libcudnn9-cuda-13 installed
as part of this validation pass.
The spec currently excludes cuDNN from auto-detected deps. Should
add a Recommends:libcudnn9-cuda-13 (soft) and ensure the rhel9 CUDA
repo is configured on each neuron host, similar to how ensure_lair_repo
handles the unstable channel.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the string compare of 'git describe --tags' vs the binary's
self-reported --version (which lies about prereleases — every
0.1.16-* RPM reports just "0.1.16") with the dnf-native question of
"is the installed package current against what the repo offers".
Mechanism:
- installed_nvr(): rpm -q --qf '%{version}-%{release}' for the
resident package, falling back to "(not installed)". Capturing rpm's
output through a variable keeps its "package X is not installed"
stdout message out of the result on failure.
- needs_update(): probes rpm -q first (treats absent as "needs work"),
then asks dnf check-update --refresh -q. Other dnf failures collapse
into "needs update" so the subsequent install surfaces a real error
rather than this check swallowing one silently.
- ensure_lair_repo(): probes for /etc/yum.repos.d/lair-cafe-unstable.repo
and adds it with `dnf config-manager addrepo` when missing. The
upstream .repo file ships enabled=0 (unstable channel doesn't
auto-engage on fetch), so we then run `dnf config-manager setopt
lair-cafe-unstable.enabled=1` every run — cheap, idempotent.
- Cortex and neuron install branches now guard `systemctl stop` with
`[ ! -f /usr/lib/systemd/system/...service ] || sudo systemctl stop`
so fresh installs (no unit file yet) don't short-circuit the install
step under set -e.
- dnf output is captured into a variable and only printed (with a
[host] prefix per line) on failure, so success stays quiet and
failures show the actual diagnostic instead of being eaten by
&> /dev/null.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>