-
Notifications
You must be signed in to change notification settings - Fork 0
239 lines (216 loc) · 10.3 KB
/
deploy-dev.yml
File metadata and controls
239 lines (216 loc) · 10.3 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
229
230
231
232
233
234
235
236
237
238
239
name: Build and Deploy (Dev)
on:
push:
branches:
- dev
workflow_dispatch:
env:
REGISTRY: ghcr.io
ROUTER_IMAGE: ghcr.io/mongrel-intelligence/cascade-router
WORKER_IMAGE: ghcr.io/mongrel-intelligence/cascade-worker
DASHBOARD_IMAGE: ghcr.io/mongrel-intelligence/cascade-dashboard
jobs:
build-and-deploy:
name: Build and Deploy (Dev)
runs-on: self-hosted
environment: CI
steps:
- uses: actions/checkout@v4
- name: Log in to GitHub Container Registry
run: |
echo "${{ secrets.GHCR_PAT }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Build and push router image
run: |
docker build \
--label org.opencontainers.image.revision=${{ github.sha }} \
-f Dockerfile.router \
-t ${{ env.ROUTER_IMAGE }}:dev \
-t ${{ env.ROUTER_IMAGE }}:dev-${{ github.sha }} .
docker push ${{ env.ROUTER_IMAGE }}:dev
docker push ${{ env.ROUTER_IMAGE }}:dev-${{ github.sha }}
- name: Validate router image (smoke test)
run: |
docker run --rm ${{ env.ROUTER_IMAGE }}:dev-${{ github.sha }} \
node --check dist/router/index.js
- name: Build and push worker image
run: |
docker build \
--label org.opencontainers.image.revision=${{ github.sha }} \
-f Dockerfile.worker \
-t ${{ env.WORKER_IMAGE }}:dev \
-t ${{ env.WORKER_IMAGE }}:dev-${{ github.sha }} .
docker push ${{ env.WORKER_IMAGE }}:dev
docker push ${{ env.WORKER_IMAGE }}:dev-${{ github.sha }}
- name: Build and push dashboard image
run: |
docker build \
--label org.opencontainers.image.revision=${{ github.sha }} \
-f Dockerfile.dashboard \
-t ${{ env.DASHBOARD_IMAGE }}:dev \
-t ${{ env.DASHBOARD_IMAGE }}:dev-${{ github.sha }} .
docker push ${{ env.DASHBOARD_IMAGE }}:dev
docker push ${{ env.DASHBOARD_IMAGE }}:dev-${{ github.sha }}
- name: Build and deploy frontend to Cloudflare Pages (dev)
run: |
docker build -f Dockerfile.frontend \
--build-arg VITE_API_URL=https://dev.api.ca.sca.de.com \
-t cascade-frontend-dev:build .
# Ensure the Cloudflare Pages project exists (idempotent)
docker run --rm \
-e CLOUDFLARE_API_TOKEN="${{ secrets.CLOUDFLARE_API_TOKEN }}" \
-e CLOUDFLARE_ACCOUNT_ID="${{ secrets.CLOUDFLARE_ACCOUNT_ID }}" \
cascade-frontend-dev:build \
wrangler pages project create cascade-dashboard-dev --production-branch=main || true
docker run --rm \
-e CLOUDFLARE_API_TOKEN="${{ secrets.CLOUDFLARE_API_TOKEN }}" \
-e CLOUDFLARE_ACCOUNT_ID="${{ secrets.CLOUDFLARE_ACCOUNT_ID }}" \
cascade-frontend-dev:build \
wrangler pages deploy dist/web --project-name=cascade-dashboard-dev --branch=main
- name: Run database migrations (dev)
run: |
docker build --target=builder -f Dockerfile.dashboard -t cascade-migrator:dev .
docker run --rm \
-e DATABASE_URL="${{ secrets.DEV_DATABASE_URL }}" \
-e DATABASE_SSL=false \
cascade-migrator:dev \
./node_modules/.bin/drizzle-kit migrate
- name: Run trigger config migration (dev)
run: |
docker run --rm \
-e DATABASE_URL="${{ secrets.DEV_DATABASE_URL }}" \
-e DATABASE_SSL=false \
cascade-migrator:dev \
npx tsx tools/migrate-triggers.ts
- name: Run hooks migration (dev)
run: |
docker run --rm \
-e DATABASE_URL="${{ secrets.DEV_DATABASE_URL }}" \
-e DATABASE_SSL=false \
cascade-migrator:dev \
npx tsx tools/migrate-hooks.ts --apply
- name: Re-encrypt project credentials with project-scoped AAD (dev)
run: |
docker run --rm \
--env-file /opt/services/cascade-dev.env \
-e DATABASE_URL="${{ secrets.DEV_DATABASE_URL }}" \
-e DATABASE_SSL=false \
cascade-migrator:dev \
npx tsx tools/migrate-project-credentials-reencrypt.ts
- name: Configure DATABASE_SSL for dev (self-signed certificate)
run: |
# /opt/services/ is read-only for the runner process (sed -i fails).
# Run a container via the host Docker socket — it mounts the host path
# as a writable bind mount and can modify the file directly.
docker run --rm \
-v /opt/services:/mnt/services \
alpine:3 sh -c '
grep -v "^DATABASE_SSL=" /mnt/services/cascade-dev.env > /tmp/new.env 2>/dev/null || true
echo "DATABASE_SSL=false" >> /tmp/new.env
cp /tmp/new.env /mnt/services/cascade-dev.env
'
- name: Pull and restart cascade-router-dev
run: |
cd /opt/services
docker compose pull cascade-router-dev
docker compose up -d --force-recreate cascade-router-dev
- name: Verify cascade-router-dev is healthy
run: |
echo "Waiting for cascade-router-dev to start..."
for i in $(seq 1 30); do
if docker inspect cascade-router-dev --format '{{.State.Health.Status}}' 2>/dev/null | grep -q healthy; then
echo "cascade-router-dev is healthy"
exit 0
fi
if docker inspect cascade-router-dev --format '{{.State.Status}}' 2>/dev/null | grep -q restarting; then
echo "ERROR: cascade-router-dev is crashlooping!"
docker logs cascade-router-dev --tail 20
exit 1
fi
sleep 5
done
echo "ERROR: cascade-router-dev did not become healthy within 150s"
docker logs cascade-router-dev --tail 20
exit 1
- name: Verify cascade-router-dev image
run: |
EXPECTED=$(docker image inspect ${{ env.ROUTER_IMAGE }}:dev-${{ github.sha }} --format '{{.Id}}')
RUNNING=$(docker inspect cascade-router-dev --format '{{.Image}}')
if [ "$EXPECTED" != "$RUNNING" ]; then
echo "ERROR: cascade-router-dev is running a stale image!"
echo " Expected (dev-${{ github.sha }}): $EXPECTED"
echo " Running: $RUNNING"
exit 1
fi
echo "cascade-router-dev image verified (commit ${{ github.sha }})"
- name: Verify cascade-router-dev runtime revision and worker image
run: |
REVISION=$(docker inspect cascade-router-dev --format '{{ index .Config.Labels "org.opencontainers.image.revision" }}')
WORKER_IMAGE=$(docker inspect cascade-router-dev --format '{{range .Config.Env}}{{println .}}{{end}}' | grep '^WORKER_IMAGE=' | cut -d= -f2-)
if [ "$REVISION" != "${{ github.sha }}" ]; then
echo "ERROR: cascade-router-dev revision label does not match deployed commit!"
echo " Expected revision: ${{ github.sha }}"
echo " Running revision: $REVISION"
exit 1
fi
if [ "$WORKER_IMAGE" != "${{ env.WORKER_IMAGE }}:dev" ]; then
echo "ERROR: cascade-router-dev is configured to launch the wrong worker image!"
echo " Expected worker image: ${{ env.WORKER_IMAGE }}:dev"
echo " Running worker image: $WORKER_IMAGE"
exit 1
fi
echo "cascade-router-dev runtime verified"
- name: Pull and restart cascade-dashboard-dev
run: |
cd /opt/services
docker compose pull cascade-dashboard-dev
docker compose up -d --force-recreate cascade-dashboard-dev
- name: Verify cascade-dashboard-dev is healthy
run: |
echo "Waiting for cascade-dashboard-dev to start..."
for i in $(seq 1 30); do
if docker inspect cascade-dashboard-dev --format '{{.State.Health.Status}}' 2>/dev/null | grep -q healthy; then
echo "cascade-dashboard-dev is healthy"
exit 0
fi
if docker inspect cascade-dashboard-dev --format '{{.State.Status}}' 2>/dev/null | grep -q restarting; then
echo "ERROR: cascade-dashboard-dev is crashlooping!"
docker logs cascade-dashboard-dev --tail 20
exit 1
fi
sleep 5
done
echo "ERROR: cascade-dashboard-dev did not become healthy within 150s"
docker logs cascade-dashboard-dev --tail 20
exit 1
- name: Verify cascade-dashboard-dev image
run: |
EXPECTED=$(docker image inspect ${{ env.DASHBOARD_IMAGE }}:dev-${{ github.sha }} --format '{{.Id}}')
RUNNING=$(docker inspect cascade-dashboard-dev --format '{{.Image}}')
if [ "$EXPECTED" != "$RUNNING" ]; then
echo "ERROR: cascade-dashboard-dev is running a stale image!"
echo " Expected (dev-${{ github.sha }}): $EXPECTED"
echo " Running: $RUNNING"
exit 1
fi
echo "cascade-dashboard-dev image verified (commit ${{ github.sha }})"
- name: Verify cascade-dashboard-dev runtime revision
run: |
REVISION=$(docker inspect cascade-dashboard-dev --format '{{ index .Config.Labels "org.opencontainers.image.revision" }}')
if [ "$REVISION" != "${{ github.sha }}" ]; then
echo "ERROR: cascade-dashboard-dev revision label does not match deployed commit!"
echo " Expected revision: ${{ github.sha }}"
echo " Running revision: $REVISION"
exit 1
fi
echo "cascade-dashboard-dev runtime verified"
- name: Verify worker image tag
run: |
EXPECTED=$(docker image inspect ${{ env.WORKER_IMAGE }}:dev-${{ github.sha }} --format '{{.Id}}')
TAGGED=$(docker image inspect ${{ env.WORKER_IMAGE }}:dev --format '{{.Id}}')
if [ "$EXPECTED" != "$TAGGED" ]; then
echo "ERROR: worker :dev tag does not point to the expected build!"
echo " Expected (dev-${{ github.sha }}): $EXPECTED"
echo " :dev resolves to: $TAGGED"
exit 1
fi
echo "Worker image verified: :dev tag matches commit ${{ github.sha }}"