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