-
-
Notifications
You must be signed in to change notification settings - Fork 9
228 lines (190 loc) · 6.97 KB
/
Copy pathprod.docs.plus.yml
File metadata and controls
228 lines (190 loc) · 6.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# ============================================================================
# Production CI/CD Pipeline - Traefik Zero-Downtime Deployment
# ============================================================================
#
# Architecture:
# Traefik (SSL/LB) → Services (auto-discovered via Docker labels)
#
# Deployment Strategy:
# Rolling update - Traefik handles graceful traffic shifting
# New containers start → health check passes → old containers removed
#
# ============================================================================
name: CI-Production
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
ENV_SOURCE: /opt/projects/prod.docs.plus/.env
ENV_FILE: .env.production
COMPOSE_FILE: docker-compose.prod.yml
DEPLOY_TAG: ${{ github.sha }}
jobs:
deploy:
name: 🚀 Deploy to Production
runs-on: prod.docs.plus
if: contains(github.event.head_commit.message, 'build') && (contains(github.event.head_commit.message, 'front') || contains(github.event.head_commit.message, 'back'))
steps:
- name: 📦 Checkout Code
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: 🥟 Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: 📥 Install Dependencies
run: bun install --frozen-lockfile
- name: 🔐 Prepare Environment
run: |
echo "📁 Copying environment file..."
if [ ! -f "${{ env.ENV_SOURCE }}" ]; then
echo "❌ .env file not found at ${{ env.ENV_SOURCE }}"
exit 1
fi
cp "${{ env.ENV_SOURCE }}" "${{ env.ENV_FILE }}"
# Add deploy tag
echo "DEPLOY_TAG=${{ env.DEPLOY_TAG }}" >> "${{ env.ENV_FILE }}"
# Validate required vars
for var in DATABASE_URL SUPABASE_URL SUPABASE_ANON_KEY; do
if ! grep -q "^${var}=" "${{ env.ENV_FILE }}"; then
echo "❌ ${var} not found in .env"
exit 1
fi
done
echo "✅ Environment prepared"
- name: 🏗️ Build Docker Images
run: |
echo "🔨 Building images with tag: ${{ env.DEPLOY_TAG }}"
# Load env vars for build args
set -a
source ${{ env.ENV_FILE }}
set +a
docker-compose -f ${{ env.COMPOSE_FILE }} \
--env-file ${{ env.ENV_FILE }} \
build --parallel
echo "✅ Images built"
- name: 🚀 Deploy Services
run: |
echo "🚀 Deploying services..."
# Deploy with rolling update
# Traefik automatically routes to healthy containers
docker-compose -f ${{ env.COMPOSE_FILE }} \
--env-file ${{ env.ENV_FILE }} \
up -d \
--remove-orphans \
--scale webapp=2 \
--scale rest-api=2 \
--scale hocuspocus-server=2 \
--scale hocuspocus-worker=1
echo "✅ Services deployed"
- name: ⏳ Wait for Health Checks
run: |
echo "⏳ Waiting for services to be healthy..."
sleep 30
# Check Traefik
if ! docker ps --filter "name=traefik" --filter "health=healthy" | grep -q traefik; then
echo "⚠️ Traefik not healthy yet, waiting..."
sleep 15
fi
echo "✅ Initial wait complete"
- name: 🩺 Verify Deployment
run: |
echo "🩺 Verifying deployment..."
# Check all services are running
SERVICES="traefik docsplus-redis"
for svc in $SERVICES; do
if ! docker ps --filter "name=$svc" | grep -q "$svc"; then
echo "❌ $svc is not running"
docker logs $svc --tail 50 2>/dev/null || true
exit 1
fi
done
# Check scaled services
for svc in webapp rest-api hocuspocus-server hocuspocus-worker; do
COUNT=$(docker ps --filter "name=${svc}" --format "{{.Names}}" | wc -l)
if [ "$COUNT" -eq 0 ]; then
echo "❌ No $svc containers running"
exit 1
fi
echo "✅ $svc: $COUNT container(s) running"
done
# Health check via Traefik
echo "🔍 Testing endpoints via Traefik..."
# Wait for SSL cert (first deploy might take a moment)
for i in {1..30}; do
if curl -sf -o /dev/null https://docs.plus/api/health 2>/dev/null; then
echo "✅ docs.plus is healthy"
break
fi
if [ $i -eq 30 ]; then
echo "⚠️ docs.plus health check timed out (may still be provisioning SSL)"
fi
sleep 5
done
echo "✅ Deployment verified"
- name: 🧹 Cleanup
run: |
# Remove old images
docker image prune -f --filter "until=24h"
# Remove unused volumes (careful!)
docker volume prune -f --filter "label!=keep"
echo "✅ Cleanup complete"
- name: 📊 Summary
run: |
echo "======================================"
echo "✅ DEPLOYMENT SUCCESSFUL"
echo "======================================"
echo "Tag: ${{ env.DEPLOY_TAG }}"
echo ""
echo "Services:"
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | head -20
echo ""
echo "URLs:"
echo " - https://docs.plus"
echo " - https://prodback.docs.plus"
echo "======================================"
- name: 🚨 Rollback on Failure
if: failure()
run: |
echo "⚠️ Deployment failed - attempting rollback..."
# Get previous working tag (if any)
PREV_TAG=$(docker images docsy-webapp --format "{{.Tag}}" | grep -v "${{ env.DEPLOY_TAG }}" | head -1)
if [ -n "$PREV_TAG" ] && [ "$PREV_TAG" != "latest" ]; then
echo "🔄 Rolling back to: $PREV_TAG"
sed -i "s/DEPLOY_TAG=.*/DEPLOY_TAG=$PREV_TAG/" "${{ env.ENV_FILE }}"
docker-compose -f ${{ env.COMPOSE_FILE }} \
--env-file ${{ env.ENV_FILE }} \
up -d --remove-orphans
echo "✅ Rollback complete"
else
echo "⚠️ No previous version to rollback to"
fi
# ===========================================================================
# UPTIME KUMA (Monitoring)
# ===========================================================================
deploy-uptime-kuma:
name: 🔔 Deploy Uptime Kuma
runs-on: prod.docs.plus
if: contains(github.event.head_commit.message, 'build') && contains(github.event.head_commit.message, 'uptime-kuma')
steps:
- name: 🚀 Deploy
run: |
docker network create docsplus-network 2>/dev/null || true
docker stop uptime-kuma 2>/dev/null || true
docker rm uptime-kuma 2>/dev/null || true
docker run -d \
--name uptime-kuma \
--network docsplus-network \
--restart unless-stopped \
-v uptime-kuma-data:/app/data \
--label "traefik.enable=true" \
--label "traefik.http.routers.uptime.rule=Host(\`status.docs.plus\`)" \
--label "traefik.http.routers.uptime.entrypoints=websecure" \
--label "traefik.http.routers.uptime.tls.certresolver=letsencrypt" \
--label "traefik.http.services.uptime.loadbalancer.server.port=3001" \
louislam/uptime-kuma:latest
sleep 15
echo "✅ Uptime Kuma deployed at https://status.docs.plus"