release 1.10.6: clean stage mon pages (no git noise, stage sticky, CHECK_BANK)

This commit is contained in:
Hernâni Marques 2026-07-19 01:38:33 +02:00
parent 1d71766a64
commit ef4dde4946
No known key found for this signature in database
GPG key ID: CB5738652768F7E9
5 changed files with 135 additions and 50 deletions

View file

@ -1 +1 @@
1.10.5
1.10.6

View file

@ -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.10.6** | 2026-07-19 | **Fix:** stage mon HTML quality — suite update out of public log; filter git noise; stage-specific sticky (not GOA); `CHECK_BANK=0` / `MERCHANT_REQUIRED` for mytops stage scope. |
| **v1.10.5** | 2026-07-19 | **Fix:** aptdeploy monpages **job-only** (own `/taler-monitoring-aptdeploy*`); full GOA mon inventory + bare URL checks stay on **hacktivism** general run — no bank/exchange/surface monpages noise on aptdeploy HTML. |
| **v1.10.4** | 2026-07-19 | **Fix:** suite **auto-upgrade strict** (`SUITE_UPDATE_STRICT=1` default — abort on fetch fail); monpages ERROR on **bootstrap** HTML; ship mytops-stage timer/service units; host-agent refuses stale tree. |
| **v1.10.3** | 2026-07-19 | **Bugfix:** sticky ERROR only from check badges (`┌ ERROR`) / `ERROR monpages`; ignore git `HEAD is now at …` commit subjects that mention ERROR. |

View file

@ -303,14 +303,18 @@ check_perf() {
esac
}
# Bank first (wallet-critical paths before UI chrome)
check_perf "perf bank /taler-integration/config" "$BANK_PUBLIC/taler-integration/config"
check_perf "perf bank /config" "$BANK_PUBLIC/config"
if [ "${CHECK_LANDING:-1}" = "1" ]; then
check_perf "perf bank /intro/" "$BANK_PUBLIC/intro/"
check_perf "perf bank /intro/stats.json" "$BANK_PUBLIC/intro/stats.json" 200
# Bank first (wallet-critical paths before UI chrome) — skip when CHECK_BANK=0 (e.g. mytops stage mon)
if [ "${CHECK_BANK:-1}" = "1" ]; then
check_perf "perf bank /taler-integration/config" "$BANK_PUBLIC/taler-integration/config"
check_perf "perf bank /config" "$BANK_PUBLIC/config"
if [ "${CHECK_LANDING:-1}" = "1" ]; then
check_perf "perf bank /intro/" "$BANK_PUBLIC/intro/"
check_perf "perf bank /intro/stats.json" "$BANK_PUBLIC/intro/stats.json" 200
fi
check_perf "perf bank /webui/" "$BANK_PUBLIC/webui/" 200,301,302
else
info "perf bank" "skipped (CHECK_BANK=0)"
fi
check_perf "perf bank /webui/" "$BANK_PUBLIC/webui/" 200,301,302
# Exchange
check_perf "perf exchange /config" "$EXCHANGE_PUBLIC/config"
@ -719,13 +723,17 @@ esac
# --- bank ---
set_group bank
if [ "${LOCAL_STACK:-1}" = "0" ]; then
if [ "${CHECK_BANK:-1}" != "1" ]; then
info "bank" "skipped (CHECK_BANK=0 — not in scope for this mon job)"
code=""
elif [ "${LOCAL_STACK:-1}" = "0" ]; then
check_url_soft "bank /config" 200 "$BANK_PUBLIC/config"
code=$(http_body "$BANK_PUBLIC/config" "$tmp/bc.json")
else
check_url "bank /config" 200 "$BANK_PUBLIC/config"
code=$(http_body "$BANK_PUBLIC/config" "$tmp/bc.json")
fi
code=$(http_body "$BANK_PUBLIC/config" "$tmp/bc.json")
if [ "$code" = "200" ]; then
if [ "${CHECK_BANK:-1}" = "1" ] && [ "$code" = "200" ]; then
expect_currency "bank" "$tmp/bc.json"
if json_has_alt_unit_names "$tmp/bc.json" >/tmp/alt-bank.$$ 2>&1; then
ok "bank /config alt_unit_names" "$(tr '\n' '; ' </tmp/alt-bank.$$ | sed 's/; $//')"
@ -773,25 +781,28 @@ if [ "$code" = "200" ]; then
fi
# Bank legal docs (landing nginx via Caddy /terms* /privacy* or /intro/*)
check_legal_doc "bank /terms" "$BANK_PUBLIC/terms" "terms|GOA|exploration|bank|FADP|revDSG"
# Prefer /privacy; fall back to /intro/privacy.html for older deploys
code=$(http_code "$BANK_PUBLIC/privacy")
if [ "$code" = "200" ]; then
check_legal_doc "bank /privacy" "$BANK_PUBLIC/privacy" "privacy|FADP|revDSG|data|GOA|bank"
else
if [ "${LOCAL_STACK:-1}" = "1" ]; then
check_legal_doc "bank /privacy (or /intro/privacy.html)" \
"$BANK_PUBLIC/intro/privacy.html" "privacy|FADP|revDSG|data|GOA|bank"
# still report bare /privacy failure for local
warn "bank /privacy" "HTTP $code — prefer Caddy handle /privacy* → landing"
if [ "${CHECK_BANK:-1}" = "1" ]; then
check_legal_doc "bank /terms" "$BANK_PUBLIC/terms" "terms|GOA|exploration|bank|FADP|revDSG"
# Prefer /privacy; fall back to /intro/privacy.html for older deploys
code=$(http_code "$BANK_PUBLIC/privacy")
if [ "$code" = "200" ]; then
check_legal_doc "bank /privacy" "$BANK_PUBLIC/privacy" "privacy|FADP|revDSG|data|GOA|bank"
else
check_url_soft "bank /privacy" 200 "$BANK_PUBLIC/privacy"
if [ "${LOCAL_STACK:-1}" = "1" ]; then
check_legal_doc "bank /privacy (or /intro/privacy.html)" \
"$BANK_PUBLIC/intro/privacy.html" "privacy|FADP|revDSG|data|GOA|bank"
# still report bare /privacy failure for local
warn "bank /privacy" "HTTP $code — prefer Caddy handle /privacy* → landing"
else
check_url_soft "bank /privacy" 200 "$BANK_PUBLIC/privacy"
fi
fi
fi
# --- merchant ---
set_group merchant
if [ "${LOCAL_STACK:-1}" = "0" ]; then
# MERCHANT_REQUIRED=1: hard ERROR if /config fails even when LOCAL_STACK=0 (mytops stage)
if [ "${LOCAL_STACK:-1}" = "0" ] && [ "${MERCHANT_REQUIRED:-0}" != "1" ]; then
check_url_soft "merchant /config" 200 "$MERCHANT_PUBLIC/config"
else
check_url "merchant /config" 200 "$MERCHANT_PUBLIC/config"

View file

@ -145,7 +145,32 @@ mkdir -p "$LOG_DIR" "$HTML_BASE"
STAMP=$(date +%Y%m%d-%H%M%S)
LOG="$LOG_DIR/run-${STAMP}.log"
UPD_LOG="$LOG_DIR/suite-update-${STAMP}.log"
: >"$LOG"
# --- suite auto-upgrade BEFORE the HTML log (git noise must not pollute mon pages) ---
OVERLAY_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/taler-monitoring/suite-overlay"
if [ -f "$AGENT_MON/taler-monitoring.sh" ]; then
mkdir -p "$OVERLAY_DIR"
rsync -a \
--exclude secrets.env --exclude 'android-test/artifacts' --exclude 'android-test/apks' \
--exclude 'android-test/out*' --exclude '__pycache__' \
"$AGENT_MON/" "$OVERLAY_DIR/" 2>/dev/null || true
fi
# shellcheck source=update-suite.sh
if ! source "$AGENT_DIR/update-suite.sh" >"$UPD_LOG" 2>&1; then
echo "ERROR: suite auto-update failed — refusing to run with stale tree" >&2
echo " detail: $UPD_LOG" >&2
tail -n 30 "$UPD_LOG" >&2 || true
echo " fix git/network or set SUITE_UPDATE_STRICT=0 only as emergency" >&2
exit 1
fi
if [ -d "${SUITE_DIR:-}/host-agent" ]; then
AGENT_DIR="${SUITE_DIR}/host-agent"
fi
# Now open the public HTML log (clean agent header + suite summary only)
if command -v stdbuf >/dev/null 2>&1; then
exec > >(stdbuf -oL -eL tee -a "$LOG") 2>&1
else
@ -157,30 +182,9 @@ echo "env_file=$ENV_FILE"
echo "domain=$TALER_DOMAIN · phases=$PHASES · RUN_TIMEOUT=${RUN_TIMEOUT}s"
echo "hosts=$MON_HOSTS"
echo "flags INSIDE_PODMAN=${INSIDE_PODMAN:-} INSIDE_MODE=${INSIDE_MODE:-} LOCAL_STACK=${LOCAL_STACK:-} SKIP_SSH=${SKIP_SSH:-} INSIDE_PROFILE=${INSIDE_PROFILE:-}"
# --- refresh from Forgejo; re-apply suite-overlay if Forgejo lacks needed phases ---
OVERLAY_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/taler-monitoring/suite-overlay"
# preserve capable mon before reset wipes tracked files
if [ -f "$AGENT_MON/taler-monitoring.sh" ]; then
mkdir -p "$OVERLAY_DIR"
rsync -a \
--exclude secrets.env --exclude 'android-test/artifacts' --exclude 'android-test/apks' \
--exclude 'android-test/out*' --exclude '__pycache__' \
"$AGENT_MON/" "$OVERLAY_DIR/" 2>/dev/null || true
fi
# shellcheck source=update-suite.sh
# Strict suite auto-upgrade (default): abort if Forgejo fetch/reset fails so
# hosts never keep publishing old / bootstrap HTML indefinitely.
if ! source "$AGENT_DIR/update-suite.sh"; then
echo "ERROR: suite auto-update failed — refusing to run with stale tree" >&2
echo " fix git/network or set SUITE_UPDATE_STRICT=0 only as emergency" >&2
exit 1
fi
# Prefer host-agent scripts from the *updated* suite tree
if [ -d "${SUITE_DIR:-}/host-agent" ]; then
AGENT_DIR="${SUITE_DIR}/host-agent"
fi
# One-line suite pin (no git M/checkout noise)
echo "suite ${SUITE_VERSION:-?} @ ${COMMIT_SHORT:-?} ${COMMIT_URL:-}"
echo " dir=${SUITE_DIR:-}"
SUITE_MON="${SUITE_DIR}"
ROOT="$SUITE_MON"

View file

@ -76,6 +76,13 @@ def ui(lang: str, key: str, **kwargs) -> str:
"pages_i4": "Phase monpages: outside-in check (existence + top/bottom content markers; not merchant JSON code 21)",
"pages_i5": "Host-agent: staging → DEPLOY_WWW_ROOT; reverse-proxy must serve /monitoring* and /taler-monitoring-surface*",
"pages_i6": "This page host: {host} · path label: {label}",
"stage_title": "mytops stage monitoring · {host}",
"stage_summary": "betel only · CHF stage stack · not lifeline/prod · not GOA",
"stage_i1": "Host: betel (stage). Public mon HTML: stage.my.taler-ops.ch + stage.taler-ops.ch /monitoring/",
"stage_i2": "Focus: stage merchant ({host}) + exchange.stage.taler-ops.ch; bank optional (CHECK_BANK)",
"stage_i3": "Phases: {phases} · suite auto-upgrade each timer run",
"stage_i4": "Not GOA: no surface/aptdeploy/mail pages · no koopa-admin-log",
"stage_i5": "Timer: taler-monitoring-mytops-stage.timer (4h) · user taler-monitoring",
"pages_section_lead": "Monitoring pages themselves (suite):",
"mm_title": "Mattermost chat health",
"mm_summary": "mattermost.taler.net · SPA + /api/v4/system/ping · TLS",
@ -150,6 +157,13 @@ def ui(lang: str, key: str, **kwargs) -> str:
"pages_i3": "GOA : /monitoring/ bank+exchange+taler ; surface + aptdeploy sur taler.hacktivism.ch — mail/mattermost dans surface",
"pages_i4": "Phase monpages : existence + marqueurs haut/bas (pas JSON merchant code 21)",
"pages_i5": "Host-agent : staging → DEPLOY_WWW_ROOT ; le reverse-proxy sert /monitoring* et /taler-monitoring-surface*",
"stage_title": "Surveillance mytops stage · {host}",
"stage_summary": "betel uniquement · pile CHF stage · pas lifeline/prod · pas GOA",
"stage_i1": "Hôte : betel (stage). HTML mon : stage.my.taler-ops.ch + stage.taler-ops.ch /monitoring/",
"stage_i2": "Focus : merchant stage ({host}) + exchange.stage ; bank optionnel (CHECK_BANK)",
"stage_i3": "Phases : {phases} · auto-upgrade suite à chaque minuterie",
"stage_i4": "Pas GOA : pas de pages surface/aptdeploy/mail · pas de koopa-admin-log",
"stage_i5": "Minuterie : taler-monitoring-mytops-stage.timer (4 h) · user taler-monitoring",
"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",
@ -569,6 +583,24 @@ def monitoring_scope(
}
phase_txt = phases or "urls · inside · versions"
# mytops stage (betel) — not GOA sticky text
if re.search(r"stage\.(my\.)?taler-ops\.ch|stage\.taler-ops\.ch|mytops-stage", host + " " + log_text, re.I):
items = [
ui(lang, "stage_i1"),
ui(lang, "stage_i2", host=host),
ui(lang, "stage_i3", phases=phase_txt),
ui(lang, "stage_i4"),
ui(lang, "stage_i5"),
ui(lang, "pages_i6", host=host, label=label),
]
return {
"kind": "stage",
"lang": lang,
"title": ui(lang, "stage_title", host=host),
"summary": ui(lang, "stage_summary"),
"items": items,
}
role = "stack"
if host.startswith("bank.") or "bank." in host:
role = "bank"
@ -602,6 +634,35 @@ def monitoring_scope(
}
def is_log_noise_line(ln: str) -> bool:
"""Drop git/update chatter that must not appear in public mon HTML."""
s = ln.strip()
if not s:
return False
low = s.lower()
if s.startswith("HEAD is now at"):
return True
if s.startswith("Your branch is") or s.startswith("branch '") or s.startswith('branch "'):
return True
if "set up to track" in low or "fast-forward" in low:
return True
if re.match(r"^M\t", s) or re.match(r"^M\s+\S", s):
return True # git status "M path"
if low.startswith("from https://") or low.startswith("from git@"):
return True
if "aktualisierung erzwungen" in low or "force update" in low:
return True
if low.startswith("fetch origin") or low.startswith("already at ") or low.startswith("upgraded "):
# keep "suite vX @ commit" lines from agent header; drop raw fetch chatter
if low.startswith("already at ") or low.startswith("upgraded "):
return True
if low.startswith("fetch origin"):
return True
if "ihr branch ist" in low or "bereits auf" in low:
return True
return False
def sticky_bar_html(
*,
generated_at: str,
@ -972,7 +1033,11 @@ def build_html(
suite_version: str = "",
suite_version_url: str = "",
) -> str:
raw_lines = [strip_ansi(l).rstrip("\n") for l in log_text.splitlines()]
raw_lines = [
strip_ansi(l).rstrip("\n")
for l in log_text.splitlines()
if not is_log_noise_line(strip_ansi(l))
]
while raw_lines and not raw_lines[-1].strip():
raw_lines.pop()
@ -1367,7 +1432,11 @@ def main() -> None:
generated_at = now.strftime("%Y-%m-%d %H:%M:%SZ")
title = args.title or f"{args.page_label} {args.mode} · {args.hostname}"
raw_for_counts = [strip_ansi(l).rstrip("\n") for l in log_text.splitlines()]
raw_for_counts = [
strip_ansi(l).rstrip("\n")
for l in log_text.splitlines()
if not is_log_noise_line(strip_ansi(l))
]
n_err, n_warn, _, _ = count_status(raw_for_counts)
if args.mode == "redirect":