Files
AetherForge/scripts/ci-docker-mining.sh
AetherForge a32860b0d9
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
feat: alive UI wave, galaxy presence, spread and fleet enhancements
Dashboard ambient layer, comrade presence, Mission Deck and War Room, Emberwake supply chain, spread/docs publishing, fleet policy and modules API, CI docker mining, and refreshed USB pack.
2026-06-04 22:36:17 -07:00

124 lines
4.2 KiB
Bash

#!/usr/bin/env bash
# CI Docker mining proof — Linux agent connects and reports hashrate > 0.
# Uses docker/data test wallet + fleet secret (see docker/README.md).
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
COMPOSE_FILE="${ROOT}/docker/docker-compose.yml"
BASE_URL="${AETHERFORGE_DOCKER_BASE_URL:-http://127.0.0.1:18989}"
E2E_USER="${AETHERFORGE_E2E_USER:-testuser}"
E2E_PASS="${AETHERFORGE_E2E_PASS:-testpass}"
EXPECTED_WORKER="${AETHERFORGE_DOCKER_WORKER:-docker-e2e-linux}"
WAIT_SECONDS="${AETHERFORGE_DOCKER_WAIT_SEC:-180}"
POLL_INTERVAL="${AETHERFORGE_DOCKER_POLL_SEC:-5}"
AUTH_HEADER="Authorization: Basic $(printf '%s:%s' "$E2E_USER" "$E2E_PASS" | base64 | tr -d '\n')"
DEADLINE=0
log() { printf '[ci-docker-mining] %s\n' "$*"; }
fail() { log "ERROR: $*"; exit 1; }
require_docker() {
if ! command -v docker >/dev/null 2>&1; then
fail "docker not found — install Docker Engine 24+ or run on a GHA ubuntu-latest runner"
fi
if ! docker compose version >/dev/null 2>&1; then
fail "docker compose plugin not found"
fi
}
teardown() {
local code=$?
log "Tearing down compose (exit=$code)…"
docker compose -f "$COMPOSE_FILE" down --rmi local -v --remove-orphans 2>/dev/null || true
if [[ $code -ne 0 ]]; then
log "Recent server logs:"
docker compose -f "$COMPOSE_FILE" logs --tail=80 server 2>/dev/null || true
log "Recent agent logs:"
docker compose -f "$COMPOSE_FILE" logs --tail=80 agent 2>/dev/null || true
fi
exit "$code"
}
wait_for_health() {
while (( SECONDS < DEADLINE )); do
if curl -sf "${BASE_URL}/api/v1/health" | grep -q '"status"[[:space:]]*:[[:space:]]*"ok"'; then
log "Server healthy at ${BASE_URL}/api/v1/health"
return 0
fi
sleep "$POLL_INTERVAL"
done
fail "server not healthy within ${WAIT_SECONDS}s (${BASE_URL}/api/v1/health)"
}
assert_mining() {
local agents_json stats_json
while (( SECONDS < DEADLINE )); do
agents_json="$(curl -sf -H "$AUTH_HEADER" "${BASE_URL}/api/v1/agents" || true)"
stats_json="$(curl -sf -H "$AUTH_HEADER" "${BASE_URL}/api/v1/dashboard/stats" || true)"
if [[ -n "$agents_json" && -n "$stats_json" ]]; then
local online_hr
online_hr="$(python3 - <<'PY' "$agents_json" "$EXPECTED_WORKER"
import json, sys
agents = json.loads(sys.argv[1])
worker = sys.argv[2]
hits = []
for a in agents:
if a.get("status") != "online":
continue
hr = max(
float(a.get("hashrate_15s") or 0),
float(a.get("hashrate_1m") or 0),
float(a.get("hashrate_15m") or 0),
)
if hr > 0:
hits.append((a.get("id"), a.get("worker_name") or a.get("name"), hr))
if worker:
named = [h for h in hits if h[1] == worker]
if named:
print(f"{named[0][0]}|{named[0][1]}|{named[0][2]:.2f}")
sys.exit(0)
if hits:
print(f"{hits[0][0]}|{hits[0][1]}|{hits[0][2]:.2f}")
PY
)" || true)"
if [[ -n "$online_hr" ]]; then
IFS='|' read -r agent_id worker_name hashrate <<<"$online_hr"
log "PASS: online agent ${agent_id} (${worker_name}) hashrate=${hashrate} H/s"
log "Fleet stats: $(printf '%s' "$stats_json" | python3 -c 'import json,sys; s=json.load(sys.stdin); print(f"online={s.get(\"online_agents\",0)} total_hr={s.get(\"total_hashrate\",0):.2f}")')"
return 0
fi
local online_count total_hr
online_count="$(printf '%s' "$stats_json" | python3 -c 'import json,sys; print(json.load(sys.stdin).get("online_agents",0))')"
total_hr="$(printf '%s' "$stats_json" | python3 -c 'import json,sys; print(json.load(sys.stdin).get("total_hashrate",0))')"
log "Waiting… online_agents=${online_count} total_hashrate=${total_hr} (need online + hashrate>0)"
else
log "Waiting… agents/stats API not ready"
fi
sleep "$POLL_INTERVAL"
done
fail "no online agent with hashrate>0 within ${WAIT_SECONDS}s (expected worker: ${EXPECTED_WORKER})"
}
main() {
require_docker
trap teardown EXIT
cd "$ROOT"
log "Starting docker compose (build may take several minutes)"
docker compose -f "$COMPOSE_FILE" up --build -d
DEADLINE=$((SECONDS + WAIT_SECONDS))
log "Waiting up to ${WAIT_SECONDS}s for health + mining proof…"
wait_for_health
assert_mining
log "Docker mining proof succeeded"
}
main "$@"