Deploy & Verify HYDRA #232
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Deploy & Verify HYDRA | |
| on: | |
| push: | |
| branches: [master] | |
| schedule: | |
| - cron: '0 */6 * * *' | |
| workflow_dispatch: | |
| jobs: | |
| deploy-verify: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Check if service is already live | |
| id: health_check | |
| continue-on-error: true | |
| run: | | |
| echo "Checking https://hydra-api-nlnj.onrender.com/health ..." | |
| HTTP_CODE=$(curl -s -o /tmp/health_response.json -w "%{http_code}" \ | |
| --max-time 15 \ | |
| https://hydra-api-nlnj.onrender.com/health 2>/dev/null || echo "000") | |
| echo "HTTP status: $HTTP_CODE" | |
| if [ "$HTTP_CODE" = "200" ]; then | |
| echo "status=live" >> $GITHUB_OUTPUT | |
| echo "✅ Service is LIVE" | |
| cat /tmp/health_response.json | python3 -m json.tool 2>/dev/null || cat /tmp/health_response.json | |
| else | |
| echo "status=down" >> $GITHUB_OUTPUT | |
| echo "⚠️ Service returned HTTP $HTTP_CODE — may be sleeping or not deployed" | |
| fi | |
| - name: Trigger Render deploy hook (if secret exists) | |
| if: steps.health_check.outputs.status != 'live' | |
| continue-on-error: true | |
| env: | |
| RENDER_DEPLOY_HOOK: ${{ secrets.RENDER_DEPLOY_HOOK }} | |
| run: | | |
| if [ -n "$RENDER_DEPLOY_HOOK" ]; then | |
| echo "🚀 Triggering Render deploy hook..." | |
| curl -s -X POST "$RENDER_DEPLOY_HOOK" --max-time 30 | |
| echo "" | |
| echo "Deploy hook triggered. Waiting for service to start..." | |
| else | |
| echo "ℹ️ No RENDER_DEPLOY_HOOK secret configured." | |
| echo "" | |
| echo "To enable auto-deploy, either:" | |
| echo " 1. Connect this repo to Render at https://render.com (uses render.yaml blueprint)" | |
| echo " 2. Or add a RENDER_DEPLOY_HOOK secret with your Render deploy hook URL" | |
| echo "" | |
| echo "The render.yaml blueprint is ready — Render will auto-configure everything." | |
| fi | |
| - name: Wait for service to come up | |
| if: steps.health_check.outputs.status != 'live' | |
| id: wait_check | |
| run: | | |
| echo "Waiting for service to respond (checking every 30s for up to 5 min)..." | |
| for i in 1 2 3 4 5 6 7 8 9 10; do | |
| sleep 30 | |
| HTTP_CODE=$(curl -s -o /tmp/health_retry.json -w "%{http_code}" \ | |
| --max-time 15 \ | |
| https://hydra-api-nlnj.onrender.com/health 2>/dev/null || echo "000") | |
| echo "Attempt $i: HTTP $HTTP_CODE" | |
| if [ "$HTTP_CODE" = "200" ]; then | |
| echo "status=live" >> $GITHUB_OUTPUT | |
| echo "✅ Service is now LIVE!" | |
| cat /tmp/health_retry.json | python3 -m json.tool 2>/dev/null || true | |
| exit 0 | |
| fi | |
| done | |
| echo "status=down" >> $GITHUB_OUTPUT | |
| echo "❌ Service did not come up within 5 minutes" | |
| - name: Verify all endpoints respond | |
| if: steps.health_check.outputs.status == 'live' || steps.wait_check.outputs.status == 'live' | |
| run: | | |
| BASE="https://hydra-api-nlnj.onrender.com" | |
| echo "=== Endpoint Verification ===" | |
| echo -n "GET /health ... " | |
| curl -s -o /dev/null -w "%{http_code}" "$BASE/health" --max-time 10 | |
| echo "" | |
| echo -n "GET /pricing ... " | |
| curl -s -o /dev/null -w "%{http_code}" "$BASE/pricing" --max-time 10 | |
| echo "" | |
| echo -n "GET /docs ... " | |
| curl -s -o /dev/null -w "%{http_code}" "$BASE/docs" --max-time 10 | |
| echo "" | |
| echo -n "GET /openapi.json ... " | |
| curl -s -o /dev/null -w "%{http_code}" "$BASE/openapi.json" --max-time 10 | |
| echo "" | |
| echo -n "GET /.well-known/x402.json ... " | |
| curl -s -o /dev/null -w "%{http_code}" "$BASE/.well-known/x402.json" --max-time 10 | |
| echo "" | |
| echo -n "GET /.well-known/ai-plugin.json ... " | |
| curl -s -o /dev/null -w "%{http_code}" "$BASE/.well-known/ai-plugin.json" --max-time 10 | |
| echo "" | |
| echo -n "GET /.well-known/mcp.json ... " | |
| curl -s -o /dev/null -w "%{http_code}" "$BASE/.well-known/mcp.json" --max-time 10 | |
| echo "" | |
| echo -n "GET /.well-known/agent.json ... " | |
| curl -s -o /dev/null -w "%{http_code}" "$BASE/.well-known/agent.json" --max-time 10 | |
| echo "" | |
| echo -n "GET /.well-known/agent-card.json ... " | |
| curl -s -o /dev/null -w "%{http_code}" "$BASE/.well-known/agent-card.json" --max-time 10 | |
| echo "" | |
| echo -n "GET /.well-known/llms.txt ... " | |
| curl -s -o /dev/null -w "%{http_code}" "$BASE/.well-known/llms.txt" --max-time 10 | |
| echo "" | |
| echo -n "GET /mcp (MCP server) ... " | |
| curl -s -o /dev/null -w "%{http_code}" "$BASE/mcp" --max-time 10 | |
| echo "" | |
| echo -n "GET /v1/markets (free discovery) ... " | |
| curl -s -o /dev/null -w "%{http_code}" "$BASE/v1/markets" --max-time 10 | |
| echo "" | |
| echo -n "GET /v1/markets/discovery (free) ... " | |
| curl -s -o /dev/null -w "%{http_code}" "$BASE/v1/markets/discovery" --max-time 10 | |
| echo "" | |
| echo -n "POST /v1/regulatory/scan (expect 402) ... " | |
| curl -s -o /dev/null -w "%{http_code}" -X POST "$BASE/v1/regulatory/scan" \ | |
| -H "Content-Type: application/json" \ | |
| -d '{"business_description":"test crypto exchange in Wyoming","jurisdiction":"US"}' \ | |
| --max-time 10 | |
| echo "" | |
| echo -n "POST /v1/fed/signal (expect 402) ... " | |
| curl -s -o /dev/null -w "%{http_code}" -X POST "$BASE/v1/fed/signal" \ | |
| -H "Content-Type: application/json" \ | |
| -d '{}' \ | |
| --max-time 10 | |
| echo "" | |
| echo "" | |
| echo "Free endpoints should return 200. Paid endpoints should return 402." | |
| echo "402 = x402 payment required (correct behavior — payment gateway working)." | |
| - name: Report deployment status (fails on stale deploy) | |
| if: always() | |
| run: | | |
| echo "============================================" | |
| echo " HYDRA DEPLOYMENT STATUS REPORT" | |
| echo "============================================" | |
| BASE="https://hydra-api-nlnj.onrender.com" | |
| # Reachability ≠ current code. Compare the LIVE version to the repo's | |
| # APP_VERSION and confirm a discovery manifest serves. This catches the | |
| # "frozen on an old build" failure that plain 200-checks miss. | |
| EXPECTED=$(grep -E 'APP_VERSION' config/settings.py | head -1 | sed -E 's/.*"([0-9.]+)".*/\1/') | |
| LIVE=$(curl -s --max-time 20 "$BASE/health" | python3 -c "import sys,json;print(json.load(sys.stdin).get('version',''))" 2>/dev/null || echo "") | |
| X402=$(curl -s -o /dev/null -w "%{http_code}" --max-time 20 "$BASE/.well-known/x402.json" || echo "000") | |
| echo "Repo APP_VERSION : ${EXPECTED:-unknown}" | |
| echo "Live /health ver : ${LIVE:-unreachable}" | |
| echo "x402 manifest : $X402 (expect 200)" | |
| echo "" | |
| FAIL=0 | |
| if [ -z "$LIVE" ]; then echo "❌ Service unreachable."; FAIL=1; fi | |
| if [ -n "$EXPECTED" ] && [ -n "$LIVE" ] && [ "$LIVE" != "$EXPECTED" ]; then | |
| echo "❌ STALE DEPLOY: live=$LIVE but repo=$EXPECTED — Render is NOT serving current master." | |
| echo " Fix in Render dashboard: confirm service is connected to master with" | |
| echo " autoDeploy on, check the Deploys tab for build failures, then Manual Deploy" | |
| echo " (Clear build cache & deploy). render.yaml blueprint is ready." | |
| FAIL=1 | |
| fi | |
| if [ "$X402" != "200" ]; then | |
| echo "❌ Discovery manifest /.well-known/x402.json → $X402 (should be 200)." | |
| FAIL=1 | |
| fi | |
| if [ "$FAIL" = "1" ]; then | |
| echo "::error::HYDRA deployment is stale or broken — see details above." | |
| exit 1 | |
| fi | |
| echo "✅ HYDRA is LIVE and CURRENT (v$LIVE), discovery manifests serving." | |
| echo "============================================" |