+73
.github/workflows/deploy.yaml
+73
.github/workflows/deploy.yaml
···
1
+
name: Deploy Control
2
+
3
+
on:
4
+
push:
5
+
branches:
6
+
- main
7
+
workflow_dispatch:
8
+
9
+
jobs:
10
+
deploy:
11
+
runs-on: ubuntu-latest
12
+
steps:
13
+
- uses: actions/checkout@v3
14
+
15
+
- name: Setup Tailscale
16
+
uses: tailscale/github-action@v3
17
+
with:
18
+
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
19
+
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
20
+
tags: tag:ci
21
+
use-cache: "true"
22
+
23
+
- name: Configure SSH
24
+
run: |
25
+
mkdir -p ~/.ssh
26
+
echo "StrictHostKeyChecking no" >> ~/.ssh/config
27
+
28
+
- name: Deploy to server
29
+
run: |
30
+
ssh control@terebithia << 'EOF'
31
+
cd /var/lib/control/app
32
+
git fetch --all
33
+
git reset --hard origin/main
34
+
bun install
35
+
sudo /run/current-system/sw/bin/systemctl restart control.service
36
+
EOF
37
+
38
+
- name: Health check
39
+
run: |
40
+
HEALTH_URL="https://control.dunkirk.sh/health"
41
+
MAX_RETRIES=6
42
+
RETRY_DELAY=5
43
+
44
+
for i in $(seq 1 $MAX_RETRIES); do
45
+
echo "Health check attempt $i/$MAX_RETRIES..."
46
+
47
+
RESPONSE=$(curl -s -w "\n%{http_code}" "$HEALTH_URL" || echo "000")
48
+
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
49
+
BODY=$(echo "$RESPONSE" | head -n-1)
50
+
51
+
if [ "$HTTP_CODE" = "200" ]; then
52
+
# Validate response contains "status":"ok"
53
+
if echo "$BODY" | grep -q '"status":"ok"'; then
54
+
echo "✅ Service is healthy (HTTP $HTTP_CODE)"
55
+
echo "Response: $BODY"
56
+
exit 0
57
+
else
58
+
echo "❌ Health check returned 200 but invalid body"
59
+
echo "Response: $BODY"
60
+
fi
61
+
else
62
+
echo "❌ Health check failed with HTTP $HTTP_CODE"
63
+
echo "Response: $BODY"
64
+
fi
65
+
66
+
if [ $i -lt $MAX_RETRIES ]; then
67
+
echo "Retrying in ${RETRY_DELAY}s..."
68
+
sleep $RETRY_DELAY
69
+
fi
70
+
done
71
+
72
+
echo "❌ Health check failed after $MAX_RETRIES attempts"
73
+
exit 1
+4
src/index.ts
+4
src/index.ts
···
39
39
});
40
40
});
41
41
42
+
app.get("/health", (c) => {
43
+
return c.json({ status: "ok" });
44
+
});
45
+
42
46
// Kill-check endpoint for Caddy to call before proxying protected routes
43
47
// Returns 200 to allow, 503 to block
44
48
// No auth required - this is called by Caddy internally