diff --git a/VERSION b/VERSION index 092afa1..84cc529 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.17.0 +1.18.0 diff --git a/VERSIONS.md b/VERSIONS.md index 0dd6528..b8fb8d4 100644 --- a/VERSIONS.md +++ b/VERSIONS.md @@ -17,6 +17,7 @@ Git tags: `vMAJOR.FEATURE.FIX` (e.g. `v1.8.0`). File `VERSION` omits the `v` pre | Tag | Date (UTC) | Notes | |-----|------------|--------| +| **v1.18.0** | 2026-07-19 | **Feature:** mattermost — client compatibility matrix: **mobile**, **desktop**, **vendor support** floors (default ≥10.11.0, ERROR) + **web** WARN on EOL; env `MATTERMOST_DESKTOP/SUPPORT_MIN_SERVER`, `MATTERMOST_CLIENT_CHECK`. | | **v1.17.0** | 2026-07-19 | **Feature:** mattermost phase — check **server version** (`X-Version-Id`) against **mobile store min** (default **≥10.11.0**); ERROR when too old (newest Android/iOS apps break); env `MATTERMOST_MOBILE_MIN_SERVER` / `MATTERMOST_MOBILE_CHECK=0`. | | **v1.16.0** | 2026-07-19 | **Feature:** mon sticky — blue **info** + green **ok** filter badges; suite **version tag under status pill** (not blue badge); filters `#filter-info` / `#filter-ok`. | | **v1.15.8** | 2026-07-19 | **Bugfix:** mon HTML filter JS — drop literal box-drawing / `\-` regex that caused Python SyntaxWarning (and weird glyphs in suite logs); use Unicode code-point loop instead. | diff --git a/check_mattermost.sh b/check_mattermost.sh index 54798fb..a61bed7 100755 --- a/check_mattermost.sh +++ b/check_mattermost.sh @@ -5,12 +5,19 @@ # Override: MATTERMOST_PUBLIC=https://mattermost.example.org # MATTERMOST_HOST=mattermost.example.org # -# Mobile compatibility (feature): -# Current Mattermost Android/iOS store apps require a minimum *server* version -# (default MATTERMOST_MOBILE_MIN_SERVER=10.11.0 — Play Store / mobile docs 2026). -# Server older than that → ERROR (users on newest app cannot rely on the server). -# Override: MATTERMOST_MOBILE_MIN_SERVER=x.y.z -# MATTERMOST_MOBILE_CHECK=0 to skip +# Client compatibility (feature) — server min for current *official* clients: +# Android / iOS store apps → default ≥ 10.11.0 (Play/App Store + mobile docs 2026) +# Desktop (Win/Mac/Linux) → default ≥ 10.11.0 (current Desktop / ESR pairing) +# Vendor support floor → default ≥ 10.11.0 (ESR; older = unsupported) +# Web SPA → served by this server (always loads) but WARN if +# server is below support floor (security/EOL). +# +# Env: +# MATTERMOST_CLIENT_CHECK=1|0 master switch (default 1) +# MATTERMOST_MOBILE_CHECK=1|0 alias / legacy (if set 0 and CLIENT unset → off) +# MATTERMOST_MOBILE_MIN_SERVER default 10.11.0 +# MATTERMOST_DESKTOP_MIN_SERVER default 10.11.0 +# MATTERMOST_SUPPORT_MIN_SERVER default 10.11.0 (vendor-supported floor) # # Outside-only (no SSH). Phase name: mattermost # @@ -22,9 +29,19 @@ source "$ROOT/lib.sh" set_area mattermost section "mattermost · public chat (outside-in)" -# Minimum server version for current mobile apps (Mattermost docs / Play Store). +# Defaults: current official clients + vendor ESR floor (2026-07 docs). : "${MATTERMOST_MOBILE_MIN_SERVER:=10.11.0}" -: "${MATTERMOST_MOBILE_CHECK:=1}" +: "${MATTERMOST_DESKTOP_MIN_SERVER:=10.11.0}" +: "${MATTERMOST_SUPPORT_MIN_SERVER:=10.11.0}" +# Master switch: CLIENT_CHECK, else legacy MOBILE_CHECK, else on +if [ -n "${MATTERMOST_CLIENT_CHECK:-}" ]; then + : +elif [ -n "${MATTERMOST_MOBILE_CHECK:-}" ]; then + MATTERMOST_CLIENT_CHECK="${MATTERMOST_MOBILE_CHECK}" +else + MATTERMOST_CLIENT_CHECK=1 +fi +: "${MATTERMOST_MOBILE_CHECK:=${MATTERMOST_CLIENT_CHECK}}" if [ -n "${MATTERMOST_PUBLIC:-}" ]; then BASE="${MATTERMOST_PUBLIC%/}" @@ -38,7 +55,7 @@ HOST=${HOST#http://} HOST=${HOST%%/*} info "target" "$BASE" -info "mobile policy" "newest store apps require server ≥ ${MATTERMOST_MOBILE_MIN_SERVER} (MATTERMOST_MOBILE_CHECK=${MATTERMOST_MOBILE_CHECK})" +info "client policy" "mobile≥${MATTERMOST_MOBILE_MIN_SERVER} · desktop≥${MATTERMOST_DESKTOP_MIN_SERVER} · support≥${MATTERMOST_SUPPORT_MIN_SERVER} (CLIENT_CHECK=${MATTERMOST_CLIENT_CHECK})" tmp=$(mktemp -d) trap 'rm -rf "$tmp"' EXIT @@ -90,16 +107,11 @@ PY fi fi -# --- Server version vs current mobile app requirement --- -set_group version -if [ "${MATTERMOST_MOBILE_CHECK}" = "0" ]; then - info "mobile compat" "skipped (MATTERMOST_MOBILE_CHECK=0)" -elif [ -z "$MM_SERVER_VER" ]; then - warn "server version" "could not parse X-Version-Id from /api/v4/system/ping (mobile min=${MATTERMOST_MOBILE_MIN_SERVER})" -else - info "server version" "X-Version-Id → ${MM_SERVER_VER} (mobile apps require ≥ ${MATTERMOST_MOBILE_MIN_SERVER})" - # Compare MAJOR.MINOR.PATCH (semver-ish); returns 0 if server >= min - if python3 - "$MM_SERVER_VER" "$MATTERMOST_MOBILE_MIN_SERVER" <<'PY' +# --- Server version vs official client families (mobile / desktop / support / web) --- +set_group clients +mm_ver_ge() { + # $1=server $2=need → 0 if server >= need + python3 - "$1" "$2" <<'PY' import sys def parse(v: str): @@ -113,21 +125,50 @@ def parse(v: str): parts.append(0) return tuple(parts) -server, need = parse(sys.argv[1]), parse(sys.argv[2]) -sys.exit(0 if server >= need else 1) +sys.exit(0 if parse(sys.argv[1]) >= parse(sys.argv[2]) else 1) PY - then - ok "mobile compat" \ - "server ${MM_SERVER_VER} ≥ ${MATTERMOST_MOBILE_MIN_SERVER} · current Android/iOS store apps OK" - else - # Hard ERROR: newest Play Store app refuses / breaks on unsupported servers - fail "mobile compat" \ - "server ${MM_SERVER_VER} < ${MATTERMOST_MOBILE_MIN_SERVER} (required by current Mattermost Android/iOS store apps)" \ - "users on latest mobile app get connect failures/crashes · upgrade Mattermost server · override MATTERMOST_MOBILE_MIN_SERVER= or MATTERMOST_MOBILE_CHECK=0" - fi +} + +# One policy line: id|min_env_default|severity(error|warn)|human label +# severity: error → fail; warn → warn +_mm_client_policies() { + printf '%s\n' \ + "mobile|${MATTERMOST_MOBILE_MIN_SERVER}|error|Android/iOS store apps (Mattermost Mobile)" \ + "desktop|${MATTERMOST_DESKTOP_MIN_SERVER}|error|Desktop app (Windows/macOS/Linux official)" \ + "support|${MATTERMOST_SUPPORT_MIN_SERVER}|error|vendor-supported server floor (ESR/current)" \ + "web|${MATTERMOST_SUPPORT_MIN_SERVER}|warn|browser webapp (loads with server, but EOL server is insecure/unsupported)" +} + +if [ "${MATTERMOST_CLIENT_CHECK}" = "0" ]; then + info "client compat" "skipped (MATTERMOST_CLIENT_CHECK=0 / MATTERMOST_MOBILE_CHECK=0)" +elif [ -z "$MM_SERVER_VER" ]; then + warn "server version" "could not parse X-Version-Id from /api/v4/system/ping — cannot judge client compatibility" +else + info "server version" "X-Version-Id → ${MM_SERVER_VER}" + while IFS='|' read -r cid cmin csever clabel; do + [ -n "$cid" ] || continue + if mm_ver_ge "$MM_SERVER_VER" "$cmin"; then + ok "${cid} compat" "server ${MM_SERVER_VER} ≥ ${cmin} · ${clabel} OK" + else + detail="server ${MM_SERVER_VER} < ${cmin} · ${clabel}" + hint="upgrade Mattermost server · set MATTERMOST_${cid^^}_MIN_SERVER= or MATTERMOST_CLIENT_CHECK=0" + # Fix hint for support/web keys + case "$cid" in + mobile) hint="upgrade server · MATTERMOST_MOBILE_MIN_SERVER= / MATTERMOST_CLIENT_CHECK=0" ;; + desktop) hint="upgrade server · MATTERMOST_DESKTOP_MIN_SERVER= / MATTERMOST_CLIENT_CHECK=0" ;; + support) hint="server below vendor support floor · MATTERMOST_SUPPORT_MIN_SERVER= / MATTERMOST_CLIENT_CHECK=0" ;; + web) hint="web SPA is served by this old server (works) but platform is unsupported · upgrade recommended" ;; + esac + if [ "$csever" = "error" ]; then + fail "${cid} compat" "$detail" "$hint" + else + warn "${cid} compat" "$detail · $hint" + fi + fi + done < <(_mm_client_policies) fi -# Client config (optional): BuildDate / advertised Android min if server sets it +# Client config (optional): BuildDate / advertised Android/iOS min if server sets it set_group client-config code=$(mm_get "$BASE/api/v4/config/client?format=old" "$tmp/client.json") if [ "$code" = "200" ] && [ -s "$tmp/client.json" ]; then @@ -142,18 +183,27 @@ PY import json, sys with open(sys.argv[1]) as f: d = json.load(f) -# empty strings are common on older servers print((d.get("AndroidMinVersion") or "").strip()) PY +) + ios_min=$(python3 - "$tmp/client.json" <<'PY' 2>/dev/null +import json, sys +with open(sys.argv[1]) as f: + d = json.load(f) +print((d.get("IosMinVersion") or d.get("IOSMinVersion") or "").strip()) +PY ) if [ -n "$build" ]; then - info "client config" "BuildDate/Version=${build}${and_min:+ · AndroidMinVersion=${and_min}}" + info "client config" "BuildDate/Version=${build}${and_min:+ · AndroidMinVersion=${and_min}}${ios_min:+ · IosMinVersion=${ios_min}}" else info "client config" "HTTP 200 (no BuildDate field)" fi if [ -n "$and_min" ] && [ "$and_min" != "0.0.0" ]; then info "android min (server)" "server advertises AndroidMinVersion=${and_min}" fi + if [ -n "$ios_min" ] && [ "$ios_min" != "0.0.0" ]; then + info "ios min (server)" "server advertises IosMinVersion=${ios_min}" + fi else info "client config" "HTTP ${code:-?} — optional" fi @@ -197,7 +247,7 @@ else info "tls cert" "openssl not available — skip expiry check" fi -info "hint" "surface catalog also lists $HOST; this phase checks SPA + /api/v4/system/ping + mobile server min (${MATTERMOST_MOBILE_MIN_SERVER})" +info "hint" "surface catalog also lists $HOST; client floors: mobile/desktop/support ≥${MATTERMOST_MOBILE_MIN_SERVER}/${MATTERMOST_DESKTOP_MIN_SERVER}/${MATTERMOST_SUPPORT_MIN_SERVER}" # Non-zero when any fail/err so parent phase + mon pages mark ERROR if [ "${FAIL_N:-0}" -gt 0 ]; then exit 1 diff --git a/site-gen/console_to_html.py b/site-gen/console_to_html.py index 2e81a13..7ec303f 100755 --- a/site-gen/console_to_html.py +++ b/site-gen/console_to_html.py @@ -148,11 +148,12 @@ def ui(lang: str, key: str, **kwargs) -> str: "stage_i6": "devtesting: fake-franken CHF via rusty.taler-ops.ch (geniban + fake-incoming)", "pages_section_lead": "Monitoring pages themselves (suite):", "mm_title": "Mattermost chat health", - "mm_summary": "mattermost.taler.net · SPA + ping · mobile server min · TLS", + "mm_summary": "mattermost.taler.net · SPA + ping · client floors (mobile/desktop/support) · TLS", "mm_i1": "Outside-in phase mattermost (default https://mattermost.taler.net)", - "mm_i2": "Landing SPA, /api/v4/system/ping, X-Version-Id vs mobile min (default ≥10.11.0), /login, TLS", - "mm_i3": "Newest Android/iOS store apps require server ≥ MATTERMOST_MOBILE_MIN_SERVER — ERROR if older", - "mm_i4": "Reported on taler-monitoring-surface HTML (no separate /taler-monitoring-mattermost* page)", + "mm_i2": "Landing SPA, /api/v4/system/ping, X-Version-Id vs client mins (mobile/desktop/support, default ≥10.11.0)", + "mm_i3": "ERROR if server too old for current Android/iOS store or Desktop apps; WARN for web on EOL server", + "mm_i4": "Env: MATTERMOST_MOBILE/DESKTOP/SUPPORT_MIN_SERVER, MATTERMOST_CLIENT_CHECK=0 to skip", + "mm_i5": "Reported on taler-monitoring-surface HTML (no separate /taler-monitoring-mattermost* page)", "mail_title": "Mail (MX / SMTP / IMAP)", "mail_summary": "firefly.gnunet.org + anastasis.taler-systems.com · mail-catalog.conf", "mail_i1": "Outside-in phase mail: MX, SMTP/IMAP ports and handshakes, SPF/DMARC", @@ -257,11 +258,12 @@ def ui(lang: str, key: str, **kwargs) -> str: "pages_i6": "Hôte de cette page : {host} · libellé : {label}", "pages_section_lead": "Les pages de monitoring elles-mêmes (suite) :", "mm_title": "Santé du chat Mattermost", - "mm_summary": "mattermost.taler.net · SPA + ping · min serveur mobile · TLS", + "mm_summary": "mattermost.taler.net · SPA + ping · seuils clients (mobile/desktop/support) · TLS", "mm_i1": "Phase outside-in mattermost (défaut https://mattermost.taler.net)", - "mm_i2": "SPA, /api/v4/system/ping, X-Version-Id vs min mobile (≥10.11.0), /login, TLS", - "mm_i3": "Apps store Android/iOS récentes exigent serveur ≥ MATTERMOST_MOBILE_MIN_SERVER — ERROR si plus vieux", - "mm_i4": "Rapporté sur la page surface HTML", + "mm_i2": "SPA, /api/v4/system/ping, X-Version-Id vs seuils clients (mobile/desktop/support, défaut ≥10.11.0)", + "mm_i3": "ERROR si serveur trop vieux pour apps store Android/iOS ou Desktop ; WARN web sur serveur EOL", + "mm_i4": "Env : MATTERMOST_MOBILE/DESKTOP/SUPPORT_MIN_SERVER, MATTERMOST_CLIENT_CHECK=0 pour ignorer", + "mm_i5": "Rapporté sur la page surface HTML", "mail_title": "Mail (MX / SMTP / IMAP)", "mail_summary": "firefly.gnunet.org + anastasis.taler-systems.com · mail-catalog.conf", "mail_i1": "Phase outside-in mail : MX, ports SMTP/IMAP et handshakes, SPF/DMARC", @@ -737,6 +739,7 @@ def monitoring_scope( ui(lang, "mm_i2"), ui(lang, "mm_i3"), ui(lang, "mm_i4"), + ui(lang, "mm_i5"), ] + _pages_items(lang, host, label) return { "kind": "mattermost",