.canopy/deploy-hub.ymlblame
View source
80fafdf1name: Deploy Hub
409bc792concurrency: 1
80fafdf3on:
80fafdf4 push:
80fafdf5 branches:
80fafdf6 - main
80fafdf7 paths:
36387cc8 - "web/**"
36387cc9 - "hub-api/**"
36387cc10 - "api/**"
6dd74de11 - "collab/mermaid/**"
e4f07b812 - "hub/Caddyfile"
c35d0b613 - "hub/docker-compose.yml"
36387cc14 - "docker/Dockerfile.grove-web"
36387cc15 - "docker/Dockerfile.grove-hub-api"
36387cc16 - "docker/Dockerfile.grove-api"
80fafdf17
80fafdf18steps:
6dd74de19 - name: Build mermaid
6dd74de20 image: node:22-alpine
6dd74de21 run: |
6dd74de22 cd collab/mermaid
6dd74de23 npm install -g pnpm
04a7f4324 pnpm install --frozen-lockfile --ignore-scripts
6dd74de25 pnpm build:mermaid
6dd74de26 cp -R packages/mermaid/dist ../../web/mermaid/dist
6dd74de27
80fafdf28 - name: Build web image
80fafdf29 image: docker:27-dind
77ad68130 timeout: 1200
80fafdf31 run: |
80fafdf32 docker build \
36387cc33 -f docker/Dockerfile.grove-web \
80fafdf34 --build-arg GROVE_HUB_API_URL=http://hub-api:4000 \
f0bb19235 --build-arg GROVE_API_URL=http://grove-api:4000 \
5f0fbcf36 -t $GROVE_REGISTRY/grove-web:latest \
36387cc37 .
5f0fbcf38 docker push $GROVE_REGISTRY/grove-web:latest
80fafdf39
80fafdf40 - name: Build hub-api image
80fafdf41 image: docker:27-dind
80fafdf42 run: |
80fafdf43 docker build \
36387cc44 -f docker/Dockerfile.grove-hub-api \
5f0fbcf45 -t $GROVE_REGISTRY/grove-hub-api:latest \
36387cc46 .
5f0fbcf47 docker push $GROVE_REGISTRY/grove-hub-api:latest
80fafdf48
5f0fbcf49 - name: Build api image
5f0fbcf50 image: docker:27-dind
5f0fbcf51 run: |
5f0fbcf52 docker build \
36387cc53 -f docker/Dockerfile.grove-api \
5f0fbcf54 -t $GROVE_REGISTRY/grove-api:latest \
36387cc55 .
5f0fbcf56 docker push $GROVE_REGISTRY/grove-api:latest
5f0fbcf57
5f0fbcf58 - name: Deploy
5f0fbcf59 image: docker:27-dind
80fafdf60 run: |
e69316361 REGISTRY=localhost:5000
e69316362 # Save current API image as :previous BEFORE pulling the new one
e69316363 docker tag $REGISTRY/grove-api:latest $REGISTRY/grove-api:previous 2>/dev/null || true
e69316364 docker push $REGISTRY/grove-api:previous 2>/dev/null || true
e69316365
c35d0b666 # Update docker-compose.yml and Caddyfile on host
c35d0b667 cat hub/docker-compose.yml | docker run --rm -i -v /opt/grove:/opt/grove alpine sh -c 'cat > /opt/grove/docker-compose.yml'
c35d0b668 cat hub/Caddyfile | docker run --rm -i -v /opt/grove:/opt/grove alpine sh -c 'cat > /opt/grove/Caddyfile'
c35d0b669
0b4b58270 docker compose -f /opt/grove/docker-compose.yml pull hub-api grove-web grove-api
0b4b58271 docker compose -f /opt/grove/docker-compose.yml up -d --no-deps hub-api grove-web
2e1766972 docker compose -f /opt/grove/docker-compose.yml exec -T caddy caddy reload --config /etc/caddy/Caddyfile
5f0fbcf73 docker image prune -f
7422c6574
7422c6575 - name: Restart API
7422c6576 image: docker:27-dind
7422c6577 run: |
fb964da78 # grove-api IS the Canopy runner — restarting it kills this pipeline.
fb964da79 # We fire-and-forget a detached container that does a health-checked
fb964da80 # restart with automatic rollback on failure.
7422c6581 docker run -d --rm \
7422c6582 -v /var/run/docker.sock:/var/run/docker.sock \
7422c6583 -v /opt/grove:/opt/grove:ro \
7422c6584 --network host \
7422c6585 docker:27-dind \
fb964da86 sh -c '
fb964da87 COMPOSE="docker compose -f /opt/grove/docker-compose.yml"
fb964da88 REGISTRY=localhost:5000
fb964da89 IMAGE=$REGISTRY/grove-api
e69316390 CONTAINER=grove-grove-api-1
fb964da91
e69316392 # 1. Restart with new image
fb964da93 sleep 5
fb964da94 $COMPOSE up -d grove-api
fb964da95
e69316396 # 2. Health check — poll for 30s using docker exec
fb964da97 ok=false
fb964da98 for i in $(seq 1 15); do
fb964da99 sleep 2
e693163100 if docker exec $CONTAINER wget -qO- http://127.0.0.1:4000/health 2>/dev/null; then
fb964da101 ok=true
fb964da102 break
fb964da103 fi
fb964da104 done
fb964da105
fb964da106 if $ok; then
fb964da107 echo "API healthy after restart"
fb964da108 else
fb964da109 echo "API unhealthy — rolling back to previous image"
fb964da110 docker tag $IMAGE:previous $IMAGE:latest
fb964da111 docker push $IMAGE:latest
e693163112 $COMPOSE pull grove-api
fb964da113 $COMPOSE up -d grove-api
fb964da114
fb964da115 # Wait for rollback to come up
fb964da116 for i in $(seq 1 10); do
fb964da117 sleep 2
e693163118 if docker exec $CONTAINER wget -qO- http://127.0.0.1:4000/health 2>/dev/null; then
fb964da119 echo "Rollback successful"
fb964da120 break
fb964da121 fi
fb964da122 done
fb964da123 fi
fb964da124 '