monitoring: versions vs deb.taler.net trixie; outside+inside
This commit is contained in:
parent
95499a7f59
commit
1181680a4b
1 changed files with 469 additions and 0 deletions
469
scripts/taler-monitoring/check_versions.sh
Executable file
469
scripts/taler-monitoring/check_versions.sh
Executable file
|
|
@ -0,0 +1,469 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Area versions-### — Taler packages vs deb.taler.net (trixie) + repo availability.
|
||||||
|
#
|
||||||
|
# Checks:
|
||||||
|
# 1) deb.taler.net apt endpoints reachable (InRelease / Packages / sample .deb)
|
||||||
|
# 2) containers can reach deb.taler.net (install path from inside)
|
||||||
|
# 3) installed taler*/libeufin*/libtaler*/libdonau* versions vs suite index
|
||||||
|
#
|
||||||
|
# Env:
|
||||||
|
# TALER_APT_SUITE=trixie
|
||||||
|
# TALER_APT_BASE=https://deb.taler.net/apt/debian
|
||||||
|
# TALER_APT_INDEX=…/dists/trixie/main/binary-amd64/Packages
|
||||||
|
# TALER_APT_TESTING_INDEX=…/dists/trixie-testing/…
|
||||||
|
# TALER_PKG_BEHIND=warn|error (default: core packages ERROR if behind, else warn)
|
||||||
|
# SKIP_SSH=1 skip container install checks
|
||||||
|
set -euo pipefail
|
||||||
|
ROOT=$(cd "$(dirname "$0")" && pwd)
|
||||||
|
# shellcheck source=lib.sh
|
||||||
|
source "$ROOT/lib.sh"
|
||||||
|
|
||||||
|
set_area versions
|
||||||
|
|
||||||
|
SUITE="${TALER_APT_SUITE:-trixie}"
|
||||||
|
APT_BASE="${TALER_APT_BASE:-https://deb.taler.net/apt/debian}"
|
||||||
|
APT_BASE="${APT_BASE%/}"
|
||||||
|
INDEX="${TALER_APT_INDEX:-${APT_BASE}/dists/${SUITE}/main/binary-amd64/Packages}"
|
||||||
|
INRELEASE="${TALER_APT_INRELEASE:-${APT_BASE}/dists/${SUITE}/InRelease}"
|
||||||
|
TESTING_INDEX="${TALER_APT_TESTING_INDEX:-${APT_BASE}/dists/${SUITE}-testing/main/binary-amd64/Packages}"
|
||||||
|
BEHIND_MODE="${TALER_PKG_BEHIND:-warn}"
|
||||||
|
|
||||||
|
tmp=$(mktemp -d)
|
||||||
|
trap 'rm -rf "$tmp"' EXIT
|
||||||
|
|
||||||
|
http_get() {
|
||||||
|
# usage: http_get URL [outfile] → prints http_code; body to outfile or /dev/null
|
||||||
|
local url="$1" out="${2:-/dev/null}"
|
||||||
|
curl -sS --max-time 30 -L -o "$out" -w '%{http_code}' "$url" 2>/dev/null || echo 000
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 1) OUTSIDE — deb.taler.net install source (runner / laptop, no SSH)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
section "versions · outside · deb.taler.net (${SUITE})"
|
||||||
|
|
||||||
|
# DNS
|
||||||
|
HOST_APT="${APT_BASE#https://}"; HOST_APT="${HOST_APT#http://}"; HOST_APT="${HOST_APT%%/*}"
|
||||||
|
if getent hosts "$HOST_APT" >/dev/null 2>&1 \
|
||||||
|
|| python3 -c "import socket; socket.getaddrinfo('${HOST_APT}', 443)" >/dev/null 2>&1; then
|
||||||
|
ip=$(python3 -c "import socket; print(socket.getaddrinfo('${HOST_APT}',443)[0][4][0])" 2>/dev/null || true)
|
||||||
|
ok "outside DNS ${HOST_APT}" "${ip:-resolved}"
|
||||||
|
else
|
||||||
|
fail "outside DNS ${HOST_APT}" "unresolvable — cannot use as apt source"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# HTTPS portal / apt root
|
||||||
|
code=$(http_get "https://${HOST_APT}/")
|
||||||
|
[ "$code" = "200" ] && ok "outside https://${HOST_APT}/" "HTTP $code" \
|
||||||
|
|| warn "outside https://${HOST_APT}/" "HTTP $code"
|
||||||
|
|
||||||
|
code=$(http_get "${APT_BASE}/")
|
||||||
|
case "$code" in
|
||||||
|
200|301|302) ok "outside apt base" "${APT_BASE}/ HTTP $code" ;;
|
||||||
|
*) fail "outside apt base" "${APT_BASE}/ HTTP $code" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Suite metadata (what apt update needs)
|
||||||
|
code=$(http_get "$INRELEASE" "$tmp/InRelease")
|
||||||
|
if [ "$code" = "200" ] && [ -s "$tmp/InRelease" ]; then
|
||||||
|
ok "outside InRelease" "${SUITE} HTTP 200 · $(wc -c <"$tmp/InRelease" | tr -d ' ') bytes"
|
||||||
|
else
|
||||||
|
fail "outside InRelease" "HTTP $code — $INRELEASE (apt update will fail)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
RELEASE_URL="${APT_BASE}/dists/${SUITE}/Release"
|
||||||
|
code=$(http_get "$RELEASE_URL" "$tmp/Release")
|
||||||
|
[ "$code" = "200" ] && ok "outside Release" "${SUITE} HTTP 200" \
|
||||||
|
|| warn "outside Release" "HTTP $code — $RELEASE_URL"
|
||||||
|
|
||||||
|
# Packages (plain) — required for version compare
|
||||||
|
code=$(http_get "$INDEX" "$tmp/Packages")
|
||||||
|
if [ "$code" = "200" ] && [ -s "$tmp/Packages" ] && grep -q '^Package: ' "$tmp/Packages"; then
|
||||||
|
n_pkg=$(grep -c '^Package: ' "$tmp/Packages" || true)
|
||||||
|
ok "outside Packages" "${SUITE} HTTP 200 · ${n_pkg} packages"
|
||||||
|
else
|
||||||
|
fail "outside Packages" "HTTP $code — $INDEX"
|
||||||
|
# still try more probes, but cannot compare versions without index
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Packages.gz (apt often prefers this)
|
||||||
|
PKGZ_URL="${APT_BASE}/dists/${SUITE}/main/binary-amd64/Packages.gz"
|
||||||
|
code=$(http_get "$PKGZ_URL" "$tmp/Packages.gz")
|
||||||
|
if [ "$code" = "200" ] && [ -s "$tmp/Packages.gz" ]; then
|
||||||
|
ok "outside Packages.gz" "${SUITE} HTTP 200 · $(wc -c <"$tmp/Packages.gz" | tr -d ' ') bytes"
|
||||||
|
else
|
||||||
|
warn "outside Packages.gz" "HTTP $code — apt may still use plain Packages"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Sample pool .deb downloadable (proves packages are installable, not just listed)
|
||||||
|
if [ -s "$tmp/Packages" ]; then
|
||||||
|
DEB_PATH=$(awk '
|
||||||
|
/^Package: taler-exchange$/ { p=1 }
|
||||||
|
p && /^Filename: / { sub(/^Filename: /,""); print; exit }
|
||||||
|
p && /^$/ { p=0 }
|
||||||
|
' "$tmp/Packages")
|
||||||
|
[ -z "$DEB_PATH" ] && DEB_PATH=$(awk '
|
||||||
|
/^Package: taler-merchant$/ { p=1 }
|
||||||
|
p && /^Filename: / { sub(/^Filename: /,""); print; exit }
|
||||||
|
p && /^$/ { p=0 }
|
||||||
|
' "$tmp/Packages")
|
||||||
|
if [ -n "$DEB_PATH" ]; then
|
||||||
|
DEB_URL="${APT_BASE}/${DEB_PATH}"
|
||||||
|
code=$(curl -sS --max-time 30 -o /dev/null -w '%{http_code}' -r 0-128 "$DEB_URL" 2>/dev/null || echo 000)
|
||||||
|
case "$code" in
|
||||||
|
200|206)
|
||||||
|
ok "outside pool .deb" "$(basename "$DEB_PATH") HTTP $code"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
fail "outside pool .deb" "HTTP $code — $DEB_URL (index ok but debs not fetchable)"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
warn "outside pool .deb" "no Filename for taler-exchange/merchant in index"
|
||||||
|
fi
|
||||||
|
|
||||||
|
for pkg in taler-exchange taler-merchant libeufin-bank; do
|
||||||
|
ver=$(awk -v p="$pkg" '
|
||||||
|
$0=="Package: "p { hit=1; next }
|
||||||
|
hit && /^Version: / { sub(/^Version: /,""); print; exit }
|
||||||
|
hit && /^$/ { hit=0 }
|
||||||
|
' "$tmp/Packages")
|
||||||
|
if [ -n "$ver" ]; then
|
||||||
|
ok "outside suite offers ${pkg}" "$ver"
|
||||||
|
else
|
||||||
|
fail "outside suite offers ${pkg}" "missing from ${SUITE} Packages"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
HAVE_TESTING=0
|
||||||
|
code=$(http_get "$TESTING_INDEX" "$tmp/Packages-testing")
|
||||||
|
if [ "$code" = "200" ] && [ -s "$tmp/Packages-testing" ] && grep -q '^Package: ' "$tmp/Packages-testing"; then
|
||||||
|
HAVE_TESTING=1
|
||||||
|
ok "outside ${SUITE}-testing Packages" "HTTP 200 (optional compare)"
|
||||||
|
else
|
||||||
|
info "outside ${SUITE}-testing Packages" "HTTP ${code:-000} (optional)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# TLS: certificate verify (curl default) already used above; explicit openssl probe if available
|
||||||
|
if command -v openssl >/dev/null 2>&1; then
|
||||||
|
if echo | openssl s_client -servername "$HOST_APT" -connect "${HOST_APT}:443" 2>/dev/null \
|
||||||
|
| grep -q 'Verify return code: 0'; then
|
||||||
|
ok "outside TLS ${HOST_APT}" "verify ok"
|
||||||
|
else
|
||||||
|
# curl succeeded with default CA — soft warn only
|
||||||
|
warn "outside TLS ${HOST_APT}" "openssl verify not clean (curl may still work)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Need Packages index for later compare
|
||||||
|
if [ ! -s "$tmp/Packages" ] || ! grep -q '^Package: ' "$tmp/Packages"; then
|
||||||
|
fail "outside Packages usable" "cannot continue version compare without suite index"
|
||||||
|
summary
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 2) INSIDE — containers can reach deb.taler.net (pasta / install path)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
section "versions · inside · containers → deb.taler.net"
|
||||||
|
|
||||||
|
if [ "${SKIP_SSH}" = "1" ]; then
|
||||||
|
warn "ssh" "SKIP_SSH=1 — skip container reachability + installed package compare"
|
||||||
|
info "outside-only" "deb.taler.net public checks completed above"
|
||||||
|
summary
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
if ! koopa_ssh_ok; then
|
||||||
|
err "ssh" "cannot reach ${KOOPA_SSH} — set SKIP_SSH=1 to skip container checks"
|
||||||
|
info "outside-only" "deb.taler.net public checks completed above"
|
||||||
|
summary
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
ok "ssh ${KOOPA_SSH}"
|
||||||
|
|
||||||
|
INRELEASE_URL="${APT_BASE}/dists/${SUITE}/InRelease"
|
||||||
|
# Write remote script to a file (avoids bash 3.2 parse bugs with case/;; inside $(…)<<heredoc).
|
||||||
|
{
|
||||||
|
printf 'INRELEASE_URL=%q\n' "$INRELEASE_URL"
|
||||||
|
cat <<'REMOTE'
|
||||||
|
set +e
|
||||||
|
resolve_ctr() {
|
||||||
|
local want="$1" c
|
||||||
|
c=$(podman ps --format '{{.Names}}' 2>/dev/null | grep -Fx "$want" | head -1)
|
||||||
|
if [ -n "$c" ]; then echo "$c"; return; fi
|
||||||
|
case "$want" in
|
||||||
|
*exchange*) podman ps --format '{{.Names}}' 2>/dev/null | grep -i exchange | head -1 ;;
|
||||||
|
*bank*) podman ps --format '{{.Names}}' 2>/dev/null | grep -iE 'hacktivism-bank|taler-bank' | head -1 ;;
|
||||||
|
*)
|
||||||
|
c=$(podman ps --format '{{.Names}}' 2>/dev/null | grep -E '^taler-hacktivism$' | head -1)
|
||||||
|
if [ -n "$c" ]; then echo "$c"; return; fi
|
||||||
|
podman ps --format '{{.Names}}' 2>/dev/null | grep -iE 'merchant|hacktivism' | grep -viE 'bank|exchange' | head -1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
for want in taler-hacktivism-exchange-ansible taler-hacktivism-bank taler-hacktivism; do
|
||||||
|
c=$(resolve_ctr "$want")
|
||||||
|
[ -n "$c" ] || continue
|
||||||
|
role=other
|
||||||
|
case "$c" in
|
||||||
|
*exchange*) role=exchange ;;
|
||||||
|
*bank*) role=bank ;;
|
||||||
|
*) role=merchant ;;
|
||||||
|
esac
|
||||||
|
code=$(podman exec "$c" curl -sS -m 12 -o /dev/null -w '%{http_code}' "$INRELEASE_URL" 2>/dev/null || echo 000)
|
||||||
|
echo "R|${role}|${c}|${code}"
|
||||||
|
if podman exec "$c" bash -lc 'grep -Rqs deb.taler.net /etc/apt/sources.list /etc/apt/sources.list.d 2>/dev/null'; then
|
||||||
|
echo "S|${role}|${c}|yes"
|
||||||
|
else
|
||||||
|
echo "S|${role}|${c}|no"
|
||||||
|
fi
|
||||||
|
podman exec "$c" dpkg-query -W -f '${Package}\t${Version}\n' 2>/dev/null \
|
||||||
|
| awk -v role="$role" -v ctr="$c" '
|
||||||
|
$1 ~ /^(taler-|libeufin-|libtaler|libdonau)/ {
|
||||||
|
printf "P|%s|%s|%s|%s\n", role, ctr, $1, $2
|
||||||
|
}'
|
||||||
|
done
|
||||||
|
REMOTE
|
||||||
|
} >"$tmp/remote-versions.sh"
|
||||||
|
|
||||||
|
REMOTE_OUT=$(koopa_ssh_bash 60 <"$tmp/remote-versions.sh" || true)
|
||||||
|
|
||||||
|
printf '%s\n' "$REMOTE_OUT" | grep -E '^R\|' >"$tmp/reach.tsv" || true
|
||||||
|
printf '%s\n' "$REMOTE_OUT" | grep -E '^S\|' >"$tmp/sources.tsv" || true
|
||||||
|
printf '%s\n' "$REMOTE_OUT" | grep -E '^P\|' >"$tmp/installed.tsv" || true
|
||||||
|
|
||||||
|
if [ -s "$tmp/reach.tsv" ]; then
|
||||||
|
while IFS='|' read -r _ role ctr code; do
|
||||||
|
if [ "$code" = "200" ]; then
|
||||||
|
ok "container ${role} → deb.taler.net" "${ctr} InRelease HTTP $code"
|
||||||
|
else
|
||||||
|
fail "container ${role} → deb.taler.net" "${ctr} InRelease HTTP $code — apt install/update will fail"
|
||||||
|
fi
|
||||||
|
done <"$tmp/reach.tsv"
|
||||||
|
else
|
||||||
|
warn "container → deb.taler.net" "no reachability rows"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -s "$tmp/sources.tsv" ]; then
|
||||||
|
while IFS='|' read -r _ role ctr has; do
|
||||||
|
if [ "$has" = "yes" ]; then
|
||||||
|
ok "container ${role} apt source" "${ctr} lists deb.taler.net"
|
||||||
|
else
|
||||||
|
warn "container ${role} apt source" "${ctr} no deb.taler.net in sources.list*"
|
||||||
|
fi
|
||||||
|
done <"$tmp/sources.tsv"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -s "$tmp/installed.tsv" ]; then
|
||||||
|
fail "installed packages" "none found in taler containers"
|
||||||
|
summary
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
n_inst=$(wc -l <"$tmp/installed.tsv" | tr -d ' ')
|
||||||
|
ok "collected installed packages" "${n_inst} rows"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 3) compare installed vs trixie (and note testing)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
section "versions · compare installed vs ${SUITE}"
|
||||||
|
|
||||||
|
export BEHIND_MODE HAVE_TESTING
|
||||||
|
export TALER_APT_SUITE="$SUITE"
|
||||||
|
CORE_PKGS="taler-exchange taler-exchange-database taler-merchant taler-merchant-webui libeufin-bank libeufin-common libtalerexchange libtalermerchant taler-terms-generator"
|
||||||
|
export CORE_PKGS
|
||||||
|
|
||||||
|
python3 - "$tmp/Packages" "$tmp/Packages-testing" "$tmp/installed.tsv" <<'PY' >"$tmp/py.out"
|
||||||
|
import sys, os
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
main_idx, test_idx, inst_path = sys.argv[1:4]
|
||||||
|
have_testing = os.environ.get("HAVE_TESTING", "0") == "1"
|
||||||
|
core = set(os.environ.get("CORE_PKGS", "").split())
|
||||||
|
suite = os.environ.get("TALER_APT_SUITE", "trixie")
|
||||||
|
|
||||||
|
def _ord_ch(c):
|
||||||
|
if c == "~":
|
||||||
|
return -1
|
||||||
|
if c.isdigit():
|
||||||
|
return 0
|
||||||
|
if c.isalpha():
|
||||||
|
return ord(c)
|
||||||
|
return ord(c) + 256
|
||||||
|
|
||||||
|
def _split_ver(v):
|
||||||
|
epoch = 0
|
||||||
|
if ":" in v:
|
||||||
|
e, v = v.split(":", 1)
|
||||||
|
try:
|
||||||
|
epoch = int(e)
|
||||||
|
except ValueError:
|
||||||
|
epoch = 0
|
||||||
|
if "-" in v:
|
||||||
|
upstream, deb = v.rsplit("-", 1)
|
||||||
|
else:
|
||||||
|
upstream, deb = v, ""
|
||||||
|
return epoch, upstream, deb
|
||||||
|
|
||||||
|
def _cmp_part(a, b):
|
||||||
|
ia = ib = 0
|
||||||
|
while ia < len(a) or ib < len(b):
|
||||||
|
while True:
|
||||||
|
ca = a[ia] if ia < len(a) and not a[ia].isdigit() else ""
|
||||||
|
cb = b[ib] if ib < len(b) and not b[ib].isdigit() else ""
|
||||||
|
if ca == "" and cb == "":
|
||||||
|
break
|
||||||
|
if ca == "" and cb:
|
||||||
|
return -1 if _ord_ch(cb) > 0 else (1 if cb == "~" else -1)
|
||||||
|
if cb == "" and ca:
|
||||||
|
return 1 if _ord_ch(ca) > 0 else (-1 if ca == "~" else 1)
|
||||||
|
oa, ob = _ord_ch(ca), _ord_ch(cb)
|
||||||
|
ia += 1
|
||||||
|
ib += 1
|
||||||
|
if oa != ob:
|
||||||
|
return (oa > ob) - (oa < ob)
|
||||||
|
sa = sb = ""
|
||||||
|
while ia < len(a) and a[ia].isdigit():
|
||||||
|
sa += a[ia]; ia += 1
|
||||||
|
while ib < len(b) and b[ib].isdigit():
|
||||||
|
sb += b[ib]; ib += 1
|
||||||
|
na = int(sa) if sa else 0
|
||||||
|
nb = int(sb) if sb else 0
|
||||||
|
if na != nb:
|
||||||
|
return (na > nb) - (na < nb)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def deb_cmp(a, b):
|
||||||
|
if a == b:
|
||||||
|
return 0
|
||||||
|
ea, ua, da = _split_ver(a)
|
||||||
|
eb, ub, db = _split_ver(b)
|
||||||
|
if ea != eb:
|
||||||
|
return (ea > eb) - (ea < eb)
|
||||||
|
c = _cmp_part(ua, ub)
|
||||||
|
if c:
|
||||||
|
return c
|
||||||
|
return _cmp_part(da, db)
|
||||||
|
|
||||||
|
def parse_packages(path):
|
||||||
|
pkgs = {}
|
||||||
|
if not path or not os.path.isfile(path) or os.path.getsize(path) == 0:
|
||||||
|
return pkgs
|
||||||
|
cur = ver = None
|
||||||
|
with open(path, encoding="utf-8", errors="replace") as f:
|
||||||
|
for line in f:
|
||||||
|
line = line.rstrip("\n")
|
||||||
|
if line.startswith("Package: "):
|
||||||
|
if cur and ver and (cur not in pkgs or deb_cmp(ver, pkgs[cur]) > 0):
|
||||||
|
pkgs[cur] = ver
|
||||||
|
cur = line[9:].strip()
|
||||||
|
ver = None
|
||||||
|
elif line.startswith("Version: ") and cur:
|
||||||
|
ver = line[9:].strip()
|
||||||
|
elif line == "" and cur:
|
||||||
|
if ver and (cur not in pkgs or deb_cmp(ver, pkgs[cur]) > 0):
|
||||||
|
pkgs[cur] = ver
|
||||||
|
cur = ver = None
|
||||||
|
if cur and ver and (cur not in pkgs or deb_cmp(ver, pkgs[cur]) > 0):
|
||||||
|
pkgs[cur] = ver
|
||||||
|
return pkgs
|
||||||
|
|
||||||
|
main = parse_packages(main_idx)
|
||||||
|
testing = parse_packages(test_idx) if have_testing else {}
|
||||||
|
|
||||||
|
by_pkg = defaultdict(list)
|
||||||
|
with open(inst_path, encoding="utf-8") as f:
|
||||||
|
for line in f:
|
||||||
|
line = line.strip()
|
||||||
|
if not line.startswith("P|"):
|
||||||
|
continue
|
||||||
|
parts = line.split("|")
|
||||||
|
if len(parts) < 5:
|
||||||
|
continue
|
||||||
|
_, role, ctr, pkg, ver = parts[:5]
|
||||||
|
by_pkg[pkg].append((role, ctr, ver))
|
||||||
|
|
||||||
|
for pkg in sorted(by_pkg.keys()):
|
||||||
|
versions = sorted({v for _, _, v in by_pkg[pkg]}, key=lambda v: v)
|
||||||
|
# pick "highest" via deb_cmp
|
||||||
|
inst = versions[0]
|
||||||
|
for v in versions[1:]:
|
||||||
|
if deb_cmp(v, inst) > 0:
|
||||||
|
inst = v
|
||||||
|
roles = ",".join(sorted({r for r, _, _ in by_pkg[pkg]}))
|
||||||
|
multi = len(set(versions)) > 1
|
||||||
|
trixie = main.get(pkg)
|
||||||
|
testv = testing.get(pkg)
|
||||||
|
status = "ok"
|
||||||
|
detail = ""
|
||||||
|
if multi:
|
||||||
|
status = "warn"
|
||||||
|
detail = "multiple installed: " + ", ".join(sorted(set(versions)))
|
||||||
|
if trixie is None:
|
||||||
|
if status == "ok":
|
||||||
|
status = "info"
|
||||||
|
detail = (detail + "; " if detail else "") + f"not in {suite} main index"
|
||||||
|
else:
|
||||||
|
c = deb_cmp(inst, trixie)
|
||||||
|
if c == 0:
|
||||||
|
detail = (detail + "; " if detail else "") + f"= {suite} {trixie}"
|
||||||
|
elif c < 0:
|
||||||
|
status = "behind"
|
||||||
|
detail = f"installed {inst} < {suite} {trixie}"
|
||||||
|
else:
|
||||||
|
status = "ahead"
|
||||||
|
detail = f"installed {inst} > {suite} {trixie}"
|
||||||
|
if testv and deb_cmp(inst, testv) == 0:
|
||||||
|
detail += f" (={suite}-testing)"
|
||||||
|
elif testv:
|
||||||
|
detail += f" (testing has {testv})"
|
||||||
|
core_flag = "1" if pkg in core else "0"
|
||||||
|
print(f"{status}|{pkg}|{inst}|{trixie or '-'}|{roles}|{core_flag}|{detail}")
|
||||||
|
PY
|
||||||
|
|
||||||
|
ok_n=0; ahead_n=0; behind_n=0
|
||||||
|
while IFS='|' read -r status pkg inst trixie roles core detail; do
|
||||||
|
[ -n "${status:-}" ] || continue
|
||||||
|
label="pkg ${pkg} (${roles})"
|
||||||
|
case "$status" in
|
||||||
|
ok)
|
||||||
|
ok "$label" "${inst}"
|
||||||
|
ok_n=$((ok_n + 1))
|
||||||
|
;;
|
||||||
|
ahead)
|
||||||
|
info "$label" "$detail"
|
||||||
|
ahead_n=$((ahead_n + 1))
|
||||||
|
;;
|
||||||
|
behind)
|
||||||
|
behind_n=$((behind_n + 1))
|
||||||
|
if [ "$core" = "1" ] || [ "$BEHIND_MODE" = "error" ]; then
|
||||||
|
fail "$label" "$detail"
|
||||||
|
else
|
||||||
|
warn "$label" "$detail"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
warn)
|
||||||
|
warn "$label" "$detail"
|
||||||
|
;;
|
||||||
|
info)
|
||||||
|
info "$label" "installed ${inst}${detail:+ — $detail}"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
info "$label" "$status $detail"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done <"$tmp/py.out"
|
||||||
|
|
||||||
|
# Core packages must exist somewhere
|
||||||
|
for need in taler-exchange libeufin-bank taler-merchant; do
|
||||||
|
if grep -qE "^P\|[^|]+\|[^|]+\|${need}\|" "$tmp/installed.tsv"; then
|
||||||
|
ok "core installed ${need}"
|
||||||
|
else
|
||||||
|
fail "core installed ${need}" "not present in any taler container"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
info "suite" "deb.taler.net ${SUITE} (testing_index=${HAVE_TESTING})"
|
||||||
|
info "tally" "match=${ok_n} ahead=${ahead_n} behind=${behind_n}"
|
||||||
|
|
||||||
|
summary
|
||||||
Loading…
Add table
Add a link
Reference in a new issue