Skip to content

feat(api): public stats + shield summary endpoints for landing + Noah #233

feat(api): public stats + shield summary endpoints for landing + Noah

feat(api): public stats + shield summary endpoints for landing + Noah #233

Workflow file for this run

name: Build & Deploy to Hetzner
on:
push:
branches: [main]
paths-ignore:
- "**.md"
- ".gitignore"
- "LICENSE"
env:
REGISTRY: docker.io
IMAGE_NAME: buzzbd/buzz-bd-agent
jobs:
build-and-deploy:
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Supply chain security check
run: |
for lockfile in package-lock.json api/package-lock.json; do
if [ -f "$lockfile" ] && grep -q "plain-crypto-js" "$lockfile"; then
echo "BLOCKED: plain-crypto-js in $lockfile" && exit 1
fi
done
echo "Lockfile integrity: CLEAN"
- name: Extract version from package.json
id: version
run: |
VERSION=$(jq -r .version package.json)
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Building version: $VERSION"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: true
platforms: linux/amd64
tags: |
${{ env.IMAGE_NAME }}:v${{ steps.version.outputs.version }}
${{ env.IMAGE_NAME }}:latest
no-cache: true
- name: Deploy to Hetzner
uses: appleboy/ssh-action@v1
with:
host: 204.168.137.253
username: root
key: ${{ secrets.HETZNER_SSH_KEY }}
script_stop: true
command_timeout: 10m
script: |
set -e
IMAGE="buzzbd/buzz-bd-agent:latest"
echo ">>> Pulling ${IMAGE}"
docker pull "${IMAGE}"
cd /data/buzz
echo ">>> Stopping old container"
docker compose down --timeout 30 || true
echo ">>> Starting new container"
docker compose up -d
echo ">>> Waiting 180s for boot"
sleep 180
echo ">>> Health check (12 attempts, 15s apart)"
for i in 1 2 3 4 5 6 7 8 9 10 11 12; do
HTTP_CODE=$(curl -s -o /tmp/health.json -w '%{http_code}' http://localhost:3000/api/v1/health)
if [ "$HTTP_CODE" = "200" ]; then
echo "Health check PASSED on attempt $i"
cat /tmp/health.json
exit 0
fi
echo "Attempt $i: HTTP $HTTP_CODE - retrying in 15s..."
sleep 15
done
echo "HEALTH CHECK FAILED after 12 attempts"
exit 1
- name: Post-deploy — directive reload + announcement
if: success()
uses: appleboy/ssh-action@v1
with:
host: 204.168.137.253
username: root
key: ${{ secrets.HETZNER_SSH_KEY }}
command_timeout: 2m
script: |
# Collect system stats for announcement
HEALTH=$(curl -s http://localhost:3000/api/v1/health)
ENDPOINTS=$(echo "$HEALTH" | python3 -c "import sys,json; print(json.load(sys.stdin).get('endpoints',0))" 2>/dev/null || echo "?")
TABLES=$(echo "$HEALTH" | python3 -c "import sys,json; print(json.load(sys.stdin)['checks']['database']['tables'])" 2>/dev/null || echo "?")
AGENTS=$(curl -s http://localhost:3000/api/v1/agents | python3 -c "import sys,json; d=json.load(sys.stdin); print(sum(1 for a in d['agents'] if a['status']=='active'))" 2>/dev/null || echo "?")
echo "Stats: ${ENDPOINTS} endpoints, ${TABLES} tables, ${AGENTS} agents"
- name: Post-deploy — directive reload via Telegram
if: success()
env:
SENTINEL_BOT_TOKEN: ${{ secrets.SENTINEL_BOT_TOKEN }}
OGIE_CHAT_ID: ${{ secrets.OGIE_CHAT_ID }}
run: |
VERSION="v${{ steps.version.outputs.version }}"
if [ -n "$SENTINEL_BOT_TOKEN" ] && [ -n "$OGIE_CHAT_ID" ]; then
curl -s -X POST "https://api.telegram.org/bot${SENTINEL_BOT_TOKEN}/sendMessage" -d chat_id="${OGIE_CHAT_ID}" -d parse_mode=Markdown -d "text=🔄 *Directive Reload* — Buzz ${VERSION} deployed. Memory file updated. Reload on next cycle."
echo "Directive reload notification sent via Sentinel"
else
echo "SENTINEL_BOT_TOKEN or OGIE_CHAT_ID not configured — skipping"
fi
- name: Telegram notify — success
if: success()
env:
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
run: |
VERSION="v${{ steps.version.outputs.version }}"
# Deploy announcement
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-d chat_id=950395553 \
-d parse_mode=Markdown \
-d text="✅ *Buzz ${VERSION} deployed successfully*
🐝 *Deploy Summary*
Image: \`buzzbd/buzz-bd-agent:${VERSION}\`
Server: api.buzzbd.ai
Health: PASSED
Commit: [\`${GITHUB_SHA::7}\`](${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }})
📊 *System Status*
Endpoints | Tables | Agents: check /status
Sentinel: monitoring every 15 min
_Auto-deployed via GitHub Actions_
_Directive reload: automatic on next cron cycle_"
- name: Telegram notify — failure
if: failure()
env:
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
run: |
VERSION="v${{ steps.version.outputs.version }}"
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-d chat_id=950395553 \
-d parse_mode=Markdown \
-d text="❌ *Buzz ${VERSION} deploy FAILED*
Server: api.buzzbd.ai
Commit: [\`${GITHUB_SHA::7}\`](${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }})
Action: [${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
_Check GitHub Actions logs_"