Skip to content

feat(wizard): add Linear as PM provider option in dashboard wizard (#… #646

feat(wizard): add Linear as PM provider option in dashboard wizard (#…

feat(wizard): add Linear as PM provider option in dashboard wizard (#… #646

Workflow file for this run

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 }}"