Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
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.
124 lines
4.2 KiB
Bash
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 "$@"
|