diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f747a9a47..4ba8a897b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,6 +52,9 @@ jobs: - name: Verify NX_BASE and NX_HEAD are set run: echo "BASE=$NX_BASE HEAD=$NX_HEAD" + - name: Pre-start Supabase for affected packages + run: ./scripts/ci-prestart-supabase.sh core client + - name: Quality gate (lint + typecheck + test) run: pnpm nx affected -t lint typecheck test --parallel --configuration=production --base="$NX_BASE" --head="$NX_HEAD" @@ -96,6 +99,9 @@ jobs: - name: Verify NX_BASE and NX_HEAD are set run: echo "BASE=$NX_BASE HEAD=$NX_HEAD" + - name: Pre-start Supabase for affected packages + run: ./scripts/ci-prestart-supabase.sh edge-worker + - name: Check if edge-worker e2e tests are affected id: check-affected run: | diff --git a/pkgs/client/project.json b/pkgs/client/project.json index 5457f929c..16df42cb7 100644 --- a/pkgs/client/project.json +++ b/pkgs/client/project.json @@ -5,6 +5,9 @@ "projectType": "library", "tags": [], "targets": { + "supabase:ci-marker": { + "executor": "nx:noop" + }, "build": { "executor": "nx:noop", "inputs": ["production", "^production"], @@ -202,7 +205,7 @@ "executor": "nx:run-commands", "cache": true, "inputs": ["production", "^production"], - "dependsOn": ["^build"], + "dependsOn": ["^build", "lint"], "options": { "cwd": "{projectRoot}", "command": "pnpm vitest --typecheck.only --run --config vitest.typecheck.config.ts" diff --git a/pkgs/client/scripts/prepare-supabase.sh b/pkgs/client/scripts/prepare-supabase.sh new file mode 100755 index 000000000..51b928754 --- /dev/null +++ b/pkgs/client/scripts/prepare-supabase.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# Prepares Supabase for client package by copying migrations and seed from core. +# Called automatically by scripts/supabase-start.sh before starting Supabase. + +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PKG_DIR="$(dirname "$SCRIPT_DIR")" + +echo "Preparing Supabase for client..." +mkdir -p "$PKG_DIR/supabase/migrations/" +rm -f "$PKG_DIR/supabase/migrations/"*.sql +cp "$PKG_DIR/../core/supabase/migrations/"*.sql "$PKG_DIR/supabase/migrations/" +cp "$PKG_DIR/../core/supabase/seed.sql" "$PKG_DIR/supabase/" +echo "Migrations and seed copied from core" diff --git a/pkgs/core/project.json b/pkgs/core/project.json index 8366c9f65..1979b12e2 100644 --- a/pkgs/core/project.json +++ b/pkgs/core/project.json @@ -19,6 +19,9 @@ ] }, "targets": { + "supabase:ci-marker": { + "executor": "nx:noop" + }, "dump-realtime-schema": { "executor": "nx:run-commands", "local": true, @@ -275,7 +278,7 @@ "executor": "nx:run-commands", "cache": true, "inputs": ["production", "^production"], - "dependsOn": ["^build"], + "dependsOn": ["^build", "lint"], "options": { "cwd": "{projectRoot}", "command": "pnpm vitest --typecheck.only --run --config vitest.typecheck.config.ts" diff --git a/pkgs/dsl/project.json b/pkgs/dsl/project.json index 80c18e4a1..742aff06f 100644 --- a/pkgs/dsl/project.json +++ b/pkgs/dsl/project.json @@ -40,7 +40,7 @@ "executor": "nx:run-commands", "cache": true, "inputs": ["production"], - "dependsOn": [], + "dependsOn": ["lint"], "options": { "cwd": "{projectRoot}", "command": "pnpm vitest --typecheck.only --run --config vitest.typecheck.config.ts" diff --git a/pkgs/edge-worker/project.json b/pkgs/edge-worker/project.json index ab8970df9..41ce69068 100644 --- a/pkgs/edge-worker/project.json +++ b/pkgs/edge-worker/project.json @@ -4,6 +4,9 @@ "sourceRoot": "pkgs/edge-worker", "projectType": "library", "targets": { + "supabase:ci-marker": { + "executor": "nx:noop" + }, "build": { "executor": "nx:noop", "dependsOn": ["^build"] diff --git a/pkgs/edge-worker/scripts/prepare-supabase.sh b/pkgs/edge-worker/scripts/prepare-supabase.sh new file mode 100755 index 000000000..d30bf805c --- /dev/null +++ b/pkgs/edge-worker/scripts/prepare-supabase.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# Prepares Supabase for edge-worker package by copying migrations and seed from core. +# Called automatically by scripts/supabase-start.sh before starting Supabase. + +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PKG_DIR="$(dirname "$SCRIPT_DIR")" + +echo "Preparing Supabase for edge-worker..." +mkdir -p "$PKG_DIR/supabase/migrations/" +rm -f "$PKG_DIR/supabase/migrations/"*.sql +cp "$PKG_DIR/../core/supabase/migrations/"*.sql "$PKG_DIR/supabase/migrations/" +cp "$PKG_DIR/../core/supabase/seed.sql" "$PKG_DIR/supabase/" +echo "Migrations and seed copied from core" diff --git a/scripts/ci-prestart-supabase.sh b/scripts/ci-prestart-supabase.sh new file mode 100755 index 000000000..c6fa042c2 --- /dev/null +++ b/scripts/ci-prestart-supabase.sh @@ -0,0 +1,112 @@ +#!/bin/bash +# scripts/ci-prestart-supabase.sh +# +# Pre-starts Supabase for affected packages in CI. +# First instance pulls Docker images, rest start in parallel. +# +# Usage: ./scripts/ci-prestart-supabase.sh [package2] ... +# Example: ./scripts/ci-prestart-supabase.sh core client +# Example: ./scripts/ci-prestart-supabase.sh edge-worker +# +# Env vars: +# NX_BASE - Base ref for affected check (default: origin/main) +# NX_HEAD - Head ref for affected check (default: HEAD) +# +# Optimization: Supabase start has two slow phases: +# 1. Docker image pull (~slow first time, then cached) +# 2. Container startup (~28s even with cached images) +# This script starts the first instance (pulls images), then starts +# the rest in parallel to save ~1 minute on CI runs. + +set -euo pipefail + +if [ $# -eq 0 ]; then + echo "Usage: $0 [package2] ..." + echo "Example: $0 core client" + exit 1 +fi + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +BASE=${NX_BASE:-origin/main} +HEAD=${NX_HEAD:-HEAD} + +# Map packages to their directories +declare -A PKG_DIRS=( + ["core"]="pkgs/core" + ["client"]="pkgs/client" + ["edge-worker"]="pkgs/edge-worker" + ["cli"]="pkgs/cli" +) + +# Get all affected projects with supabase:ci-marker target +AFFECTED=$(pnpm nx show projects --affected -t supabase:ci-marker --base="$BASE" --head="$HEAD" 2>/dev/null || echo "") + +# Filter to only packages specified as arguments +DIRS=() +for pkg in "$@"; do + if [ -z "${PKG_DIRS[$pkg]:-}" ]; then + echo "Warning: Unknown package '$pkg', skipping" + continue + fi + if echo "$AFFECTED" | grep -q "^${pkg}$"; then + DIRS+=("${PKG_DIRS[$pkg]}") + echo "Package '$pkg' is affected" + else + echo "Package '$pkg' is not affected, skipping" + fi +done + +if [ ${#DIRS[@]} -eq 0 ]; then + echo "No affected packages need Supabase" + exit 0 +fi + +echo "Starting Supabase for: ${DIRS[*]}" + +LOGDIR=$(mktemp -d) +trap "rm -rf $LOGDIR" EXIT + +# First one pulls Docker images (must complete before others) +echo "::group::Starting ${DIRS[0]} (pulling Docker images)" +if ! "$SCRIPT_DIR/supabase-start-locked.sh" "${DIRS[0]}" 2>&1 | tee "$LOGDIR/first.log"; then + echo "::endgroup::" + echo "::error::Failed to start ${DIRS[0]}" + exit 1 +fi +echo "::endgroup::" + +# Rest in parallel (images cached, ~28s each but concurrent) +if [ ${#DIRS[@]} -gt 1 ]; then + REMAINING=$((${#DIRS[@]} - 1)) + echo "Starting $REMAINING more instance(s) in parallel..." + + PIDS=() + for dir in "${DIRS[@]:1}"; do + name=$(basename "$dir") + "$SCRIPT_DIR/supabase-start-locked.sh" "$dir" > "$LOGDIR/$name.log" 2>&1 & + PIDS+=("$!:$dir:$name") + done + + FAILED=0 + for entry in "${PIDS[@]}"; do + pid=${entry%%:*} + rest=${entry#*:} + dir=${rest%%:*} + name=${rest#*:} + if ! wait "$pid"; then + echo "::error::Failed to start $dir" + echo "::group::$name log" + cat "$LOGDIR/$name.log" + echo "::endgroup::" + FAILED=1 + else + echo ":: $dir started" + fi + done + + if [ $FAILED -eq 1 ]; then + exit 1 + fi +fi + +echo "All Supabase instances started" diff --git a/scripts/supabase-start.sh b/scripts/supabase-start.sh index 3e67d5464..59d8cee0d 100755 --- a/scripts/supabase-start.sh +++ b/scripts/supabase-start.sh @@ -15,7 +15,10 @@ set -e # Behavior: # 1. Checks if Supabase is already running (fast path) # 2. If running: exits immediately -# 3. If not running: cleans up stale containers and starts fresh +# 3. If not running: +# a. Runs /scripts/prepare-supabase.sh if it exists (e.g., copy migrations) +# b. Cleans up stale containers +# c. Starts Supabase fresh # # Exit codes: # 0 - Success (Supabase is running) @@ -58,6 +61,13 @@ fi # Supabase is not running - need to start it echo -e "${YELLOW}Supabase is not running. Starting...${NC}" +# Run package-specific preparation if script exists +PREPARE_SCRIPT="$PROJECT_DIR/scripts/prepare-supabase.sh" +if [ -x "$PREPARE_SCRIPT" ]; then + echo -e "${YELLOW}Running prepare-supabase.sh...${NC}" + "$PREPARE_SCRIPT" +fi + # Clean up any stale containers first # This prevents errors from previous incomplete shutdowns echo -e "${YELLOW}Cleaning up any stale containers...${NC}"