Files
molecule-ai-status/.gitea/workflows/uptime-probe.yml
T
hongmingandClaude Opus 5.5 cc47835750
CI / noop (pull_request) Successful in 1s
CI / Workflow YAML lint (pull_request) Successful in 2s
CI / pytest (pull_request) Successful in 6s
CI / all-required (pull_request) Successful in 2s
sop-checklist / all-items-acked (pull_request) ack advisory until agent-team — acked: 0/7 — missing: comprehensive-testing, local-postgres-e2e, staging-smoke, +4
sop-checklist-gate / gate (pull_request_target) Successful in 15s
Merge main into fix/domain-only-workflow-guidance-1012-1058; re-pin probe to dce40eb9
Brings the branch up to main 9ba444b8 (21 commits behind) and resolves the
seven conflicts with #51/#52/#53/#56. Also updates facts that had gone stale
since July.

Conflict resolution:
- .gitea/archive/legacy-per-repo-merge-queue/*: removed. #53 deleted the
  repo-local merge queue outright, so the archive copy is dropped too.
- .gitea/workflows/validate-workflows.yml: removed. #56 moved yaml-lint into
  ci.yml.
- site/app.js, site/vercel.json: main's versions. #52's two-rule adapter
  (history -> uptime-probe-results, everything else -> main) and its comments
  supersede this branch's hunks. site/ is unchanged against main.
- .gitea/workflows/ci.yml: main's file plus this branch's noop step and
  persist-credentials: false on the pytest checkout. Keeps main's
  "pip install -q pytest pyyaml" (tests/test_all_required.py needs PyYAML).
- .upptimerc.yml: main's nine-site list unchanged (docs probe stays on the
  canonical https://docs.enteros.ai). Keeps this branch's header and
  provider-neutral control-plane comments. Drops the owner/repo/
  status-website/assignees/i18n keys: the probe, site/app.js, aggregate.py
  and the status CD's assert-live read only `sites`.

Probe workflow hardening (molecule-ai/internal#1041):
- GOPROBE_REF is the full SHA dce40eb96fb36ad22322faa98e631564592ddb5a
  (molecule-ai-uptime-probe main today). It is fetched by SHA, checked with
  rev-parse, and logged as "probe source: <repo>@<sha>". 507560af differs
  from it only in .gitea/workflows/minimal-ci.yml.
- The job guard no longer depends on the event payload:
  if: github.event_name != 'workflow_dispatch' || github.ref == 'refs/heads/main'
  The checkout uses ref: main.
- actions/checkout and actions/setup-go are pinned to SHAs that the fleet
  already runs. secrets.GITEA_TOKEN is no longer passed to setup-go.
  The retired operator-host remediation text is replaced.

README: rewritten for today's pipeline. https://status.enteros.ai is the
public page and the retired status.moleculesai.app host 308s there. Probe
data sits on uptime-probe-results; config stays on main. The README states
that molecule-ai-status-cd rebuilds and redeploys the page on every main
change, with assert-live and auto-rollback.

tests/test_active_guidance.py now asserts main's adapter split, a 40-hex
GOPROBE_REF with the rev-parse check, the pinned action SHAs, the new guard,
and that no legacy queue files or .gitea/archive remain.

Local: pytest 77 passed; node --check site/app.js; all workflows and
.upptimerc.yml (9 sites) parse; git diff --check clean. Nine negative
controls each fail the expected test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:41:39 -07:00

223 lines
11 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: Uptime probe (Gitea-native replaces upptime)
# Runs the molecule-ai-uptime-probe binary on a 5-minute cadence,
# appends per-site JSONL results to history/, and commits generated data to the
# dedicated uptime-probe-results branch. Configuration remains on main. Replaces
# the five Upptime workflows that lived in this
# repo before they were moved to .github/workflows-disabled-post-suspension/
# (because
# every upptime call to api.github.com 401s post-2026-05-06 GitHub
# org suspension).
#
# See molecule-ai/molecule-ai-status#2 for the design rationale +
# molecule-ai/molecule-ai-uptime-probe for the probe binary itself.
#
# Self-health (heartbeat): writes history/.monitoring-heartbeat.jsonl with the
# current timestamp on every successful commit. If the last heartbeat is
# older than 15 minutes, the status page renders "Monitoring degraded"
# instead of falsely showing "All systems operational." This prevents a
# silent dark period when the runner pool is down and scheduled workflows
# are queuing (not firing).
on:
schedule:
# Every 5 minutes — matches the upptime default cadence.
- cron: "*/5 * * * *"
# Manual trigger for ad-hoc checks.
workflow_dispatch:
# Re-run when probe-list config changes so a new endpoint gets a
# baseline immediately, not at the next /5 mark.
push:
branches: [main]
paths: [".upptimerc.yml"]
permissions:
contents: write # required to commit history/ updates
jobs:
probe:
name: Probe + commit
# workflow_dispatch can target an arbitrary ref. Refuse it before checkout
# or write-capable steps; probes always use reviewed main. Keyed on
# event_name/ref (set for every trigger), not on event-payload fields, so a
# scheduled run can never be skipped by a payload shape change.
if: github.event_name != 'workflow_dispatch' || github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
# Concurrency: at most one probe run at a time per branch. Two
# cron firings overlapping would race on history/ commits.
concurrency:
group: uptime-probe-${{ github.ref }}
cancel-in-progress: true
steps:
- name: Checkout repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: main
fetch-depth: 1
persist-credentials: true
- name: Seed latest probe history
# Probe commits are published to uptime-probe-results instead of main.
# Seed history/ from that branch so each run appends to the latest
# persisted probe data, then publish with a lease in the commit step.
run: |
set -euo pipefail
if git fetch --depth 1 origin uptime-probe-results:refs/remotes/origin/uptime-probe-results; then
RESULTS_BASE=$(git rev-parse refs/remotes/origin/uptime-probe-results)
echo "RESULTS_BASE=$RESULTS_BASE" >> "$GITHUB_ENV"
git checkout refs/remotes/origin/uptime-probe-results -- history
echo "seeded history/ from uptime-probe-results@$RESULTS_BASE"
else
echo "RESULTS_BASE=" >> "$GITHUB_ENV"
echo "::notice::No uptime-probe-results branch yet; starting from main history/"
fi
- name: "Self-health: check heartbeat staleness"
# Read the last heartbeat timestamp from the previous commit.
# If it is more than 15 minutes old, emit a visible error so operators notice
# before the status page silently goes dark. The workflow keeps
# running (we still commit fresh data if we got this far), but
# the failure is visible in the Actions UI and will wake someone.
run: |
set -euo pipefail
HEARTBEAT="history/.monitoring-heartbeat.jsonl"
NOW_S=$(date +%s)
# Grace period: allow up to 15 minutes of staleness before alerting.
# 5-minute cadence + potential runner-queue delay + clock skew = 15 min is safe.
GRACE_S=900
if [ ! -f "$HEARTBEAT" ]; then
echo "::notice::First run — no heartbeat file yet. Creating."
else
LAST_TS=$(tail -1 "$HEARTBEAT" | python3 -c "import json,sys; print(json.load(sys.stdin)['timestamp'])" 2>/dev/null || echo "")
if [ -n "$LAST_TS" ]; then
LAST_S=$(date -d "$LAST_TS" +%s 2>/dev/null || echo 0)
DELTA_S=$((NOW_S - LAST_S))
if [ "$DELTA_S" -gt "$GRACE_S" ]; then
echo "::error::STALE MONITORING: last heartbeat was ${DELTA_S}s ago (limit: ${GRACE_S}s)"
echo "::error::Scheduled workflows may be queuing on a down runner pool."
echo "::error::Inspect this repository's Actions queue at https://git.moleculesai.app/molecule-ai/molecule-ai-status/actions"
echo "::error::Validate the assigned runner label against operator-config ops/runners; live runner changes require reviewed, bounded authorization (internal#1058)."
# Do NOT exit 1 — we still want to commit fresh data and keep the page alive.
# The failure is purely for alerting visibility.
echo "::notice::Continuing anyway to keep the page alive — monitoring has degraded."
else
echo "::notice::Heartbeat OK: last run was ${DELTA_S}s ago"
fi
fi
fi
- name: Write + trim heartbeat
# Always write a fresh heartbeat so the next run has a baseline.
# Include the workflow-run URL so operators can click through from an alert.
# Keep only the last 10 entries to prevent unbounded file growth
# (~10 entries × ~100 bytes × 288 runs/day × 365 days ≈ 1 MB/year — fine
# without trim, but trimming keeps diffs cheap and git history clean).
run: |
set -euo pipefail
mkdir -p history
export HB="history/.monitoring-heartbeat.jsonl"
python3 - <<'PY'
import datetime
import json
import os
hb = os.environ["HB"]
ts = datetime.datetime.now(datetime.timezone.utc).isoformat()
repo = os.environ.get("GITHUB_REPOSITORY") or "molecule-ai/molecule-ai-status"
run_id = os.environ.get("GITHUB_RUN_ID") or "0"
run_url = f"https://git.moleculesai.app/{repo}/actions/runs/{run_id}"
entry = {"timestamp": ts, "run_url": run_url}
with open(hb, "a", encoding="utf-8") as f:
f.write(json.dumps(entry) + "\n")
# Trim to last 10 entries (the file grows 1 line per run).
with open(hb, encoding="utf-8") as f:
lines = [line for line in f if line.strip()]
with open(hb, "w", encoding="utf-8") as f:
f.write("".join(lines[-10:]))
PY
- name: Setup Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version: '1.23'
- name: Install probe
# Build from the probe repository at an immutable, reviewed full commit
# SHA (molecule-ai/internal#1041): this job holds contents: write, so it
# must never build a moving branch. Advance the pin only through a
# reviewed PR that sets GOPROBE_REF to a full SHA on the probe's main;
# tests/test_active_guidance.py rejects branch and tag refs here.
run: |
set -euo pipefail
GOPROBE_REPO=https://git.moleculesai.app/molecule-ai/molecule-ai-uptime-probe.git
GOPROBE_REF=dce40eb96fb36ad22322faa98e631564592ddb5a
tmp=$(mktemp -d)
git clone --filter=blob:none --no-checkout "$GOPROBE_REPO" "$tmp/probe"
git -C "$tmp/probe" fetch --depth 1 origin "$GOPROBE_REF"
git -C "$tmp/probe" checkout --detach FETCH_HEAD
test "$(git -C "$tmp/probe" rev-parse HEAD)" = "$GOPROBE_REF"
echo "probe source: ${GOPROBE_REPO}@${GOPROBE_REF}"
(cd "$tmp/probe" && go build -o /usr/local/bin/uptime-probe ./cmd/probe)
/usr/local/bin/uptime-probe -h 2>&1 | sed -n '1,5p'
- name: Run probes
# Exit 1 from the probe when any site fails — but we don't
# want a single failing site to abort the workflow before the
# commit step. `|| true` swallows the non-zero exit; the
# failure shows up as success=false in the JSONL history,
# where the status page picks it up.
run: |
mkdir -p history
/usr/local/bin/uptime-probe \
-config .upptimerc.yml \
-history-dir history \
-timeout 30s \
> /tmp/run.json || true
echo "== run summary =="
jq -r '.[] | "\(.name): \(.status_code) \(.latency_ms)ms success=\(.success)"' /tmp/run.json || cat /tmp/run.json
- name: Aggregate probe results to Upptime format
# Reads history/<slug>.jsonl files, computes rolling uptime/response-time
# aggregates, and writes history/<slug>.yml + history/summary.json.
# See molecule-ai/molecule-ai-status#7.
run: |
set -euo pipefail
python3 scripts/aggregate.py --history-dir history
- name: Commit history changes
# Fails fast if Gitea is unhealthy rather than silently swallowing
# the push. The next /5 cron firing picks up where this left off once
# Gitea recovers. Also guarded against concurrent-run race: the
# workflow-level concurrency group (line ~42) ensures at most one
# probe run per branch is in-flight at any time.
run: |
set -euo pipefail
# Health gate: fail fast if Gitea is 502 or otherwise unreachable.
# The probe ran successfully; we just cannot persist the results yet.
GATEWAY="https://git.moleculesai.app"
HTTP_CODE=$(curl -s -A curl/8.4.0 -o /dev/null -w "%{http_code}" \
--max-time 10 "$GATEWAY/api/v1/version" || echo 000)
if [ "$HTTP_CODE" != "200" ]; then
echo "::error::Gitea unhealthy (HTTP $HTTP_CODE) — cannot push results."
echo "::error::Probe data is in history/. Next successful push after Gitea"
echo "::error::recovers will commit all buffered results."
exit 1
fi
git config user.name "uptime-probe[bot]"
git config user.email "uptime-probe@bots.moleculesai.app"
git add history/
if git diff --cached --quiet; then
echo "no history changes to commit"
exit 0
fi
git commit -m "chore(uptime): probe results $(date -u +%Y-%m-%dT%H:%M:%SZ)"
if [ -n "${RESULTS_BASE:-}" ]; then
git push \
--force-with-lease=refs/heads/uptime-probe-results:"$RESULTS_BASE" \
origin HEAD:uptime-probe-results
else
git push origin HEAD:uptime-probe-results
fi