3.9 KB124 lines
Blame
1name: Deploy Hub
2concurrency: 1
3on:
4 push:
5 branches:
6 - main
7 paths:
8 - "web/**"
9 - "hub-api/**"
10 - "api/**"
11 - "collab/mermaid/**"
12 - "hub/Caddyfile"
13 - "hub/docker-compose.yml"
14 - "docker/Dockerfile.grove-web"
15 - "docker/Dockerfile.grove-hub-api"
16 - "docker/Dockerfile.grove-api"
17
18steps:
19 - name: Build mermaid
20 image: node:22-alpine
21 run: |
22 cd collab/mermaid
23 npm install -g pnpm
24 pnpm install --frozen-lockfile --ignore-scripts
25 pnpm build:mermaid
26 cp -R packages/mermaid/dist ../../web/mermaid/dist
27
28 - name: Build web image
29 image: docker:27-dind
30 run: |
31 docker build \
32 -f docker/Dockerfile.grove-web \
33 --build-arg GROVE_HUB_API_URL=http://hub-api:4000 \
34 --build-arg GROVE_API_URL=http://grove-api:4000 \
35 -t $GROVE_REGISTRY/grove-web:latest \
36 .
37 docker push $GROVE_REGISTRY/grove-web:latest
38
39 - name: Build hub-api image
40 image: docker:27-dind
41 run: |
42 docker build \
43 -f docker/Dockerfile.grove-hub-api \
44 -t $GROVE_REGISTRY/grove-hub-api:latest \
45 .
46 docker push $GROVE_REGISTRY/grove-hub-api:latest
47
48 - name: Build api image
49 image: docker:27-dind
50 run: |
51 docker build \
52 -f docker/Dockerfile.grove-api \
53 -t $GROVE_REGISTRY/grove-api:latest \
54 .
55 docker push $GROVE_REGISTRY/grove-api:latest
56
57 - name: Deploy
58 image: docker:27-dind
59 run: |
60 REGISTRY=localhost:5000
61 # Save current API image as :previous BEFORE pulling the new one
62 docker tag $REGISTRY/grove-api:latest $REGISTRY/grove-api:previous 2>/dev/null || true
63 docker push $REGISTRY/grove-api:previous 2>/dev/null || true
64
65 # Update docker-compose.yml and Caddyfile on host
66 cat hub/docker-compose.yml | docker run --rm -i -v /opt/grove:/opt/grove alpine sh -c 'cat > /opt/grove/docker-compose.yml'
67 cat hub/Caddyfile | docker run --rm -i -v /opt/grove:/opt/grove alpine sh -c 'cat > /opt/grove/Caddyfile'
68
69 docker compose -f /opt/grove/docker-compose.yml pull hub-api grove-web grove-api
70 docker compose -f /opt/grove/docker-compose.yml up -d --no-deps hub-api grove-web
71 docker compose -f /opt/grove/docker-compose.yml exec -T caddy caddy reload --config /etc/caddy/Caddyfile
72 docker image prune -f
73
74 - name: Restart API
75 image: docker:27-dind
76 run: |
77 # grove-api IS the Canopy runner — restarting it kills this pipeline.
78 # We fire-and-forget a detached container that does a health-checked
79 # restart with automatic rollback on failure.
80 docker run -d --rm \
81 -v /var/run/docker.sock:/var/run/docker.sock \
82 -v /opt/grove:/opt/grove:ro \
83 --network host \
84 docker:27-dind \
85 sh -c '
86 COMPOSE="docker compose -f /opt/grove/docker-compose.yml"
87 REGISTRY=localhost:5000
88 IMAGE=$REGISTRY/grove-api
89 CONTAINER=grove-grove-api-1
90
91 # 1. Restart with new image
92 sleep 5
93 $COMPOSE up -d grove-api
94
95 # 2. Health check — poll for 30s using docker exec
96 ok=false
97 for i in $(seq 1 15); do
98 sleep 2
99 if docker exec $CONTAINER wget -qO- http://127.0.0.1:4000/health 2>/dev/null; then
100 ok=true
101 break
102 fi
103 done
104
105 if $ok; then
106 echo "API healthy after restart"
107 else
108 echo "API unhealthy — rolling back to previous image"
109 docker tag $IMAGE:previous $IMAGE:latest
110 docker push $IMAGE:latest
111 $COMPOSE pull grove-api
112 $COMPOSE up -d grove-api
113
114 # Wait for rollback to come up
115 for i in $(seq 1 10); do
116 sleep 2
117 if docker exec $CONTAINER wget -qO- http://127.0.0.1:4000/health 2>/dev/null; then
118 echo "Rollback successful"
119 break
120 fi
121 done
122 fi
123 '
124