diff --git a/VERSION b/VERSION index db77e0e..7b4d9a4 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.10.5 +1.10.6 diff --git a/VERSIONS.md b/VERSIONS.md index 56645f3..979800b 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.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. | diff --git a/check_urls.sh b/check_urls.sh index 36520d4..c9df684 100755 --- a/check_urls.sh +++ b/check_urls.sh @@ -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' '; ' "$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" diff --git a/site-gen/console_to_html.py b/site-gen/console_to_html.py index fcfeb76..81a0531 100755 --- a/site-gen/console_to_html.py +++ b/site-gen/console_to_html.py @@ -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":