Skip to content

Instantly share code, notes, and snippets.

@jamesaphoenix
Created June 4, 2025 16:56
Show Gist options
  • Select an option

  • Save jamesaphoenix/b1717507a3cc914086e2a40316736d80 to your computer and use it in GitHub Desktop.

Select an option

Save jamesaphoenix/b1717507a3cc914086e2a40316736d80 to your computer and use it in GitHub Desktop.
#!/bin/bash
set -e
# Colors for output
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m' # No Color
# Get schema from environment or use 'public'
SCHEMA=${DATABASE_SCHEMA:-public}
echo -e "${GREEN}🌱 Generating schema-aware seed file for: $SCHEMA${NC}"
echo -e "${YELLOW}πŸ“‹ Using shared auth.users (no email transformation)${NC}"
# For worktree schemas, we handle auth.users differently - we share them
if [ "$SCHEMA" != "public" ]; then
echo -e "${YELLOW}πŸ”„ Ensuring shared auth.users exist...${NC}"
USER_COUNT=$(psql "${DATABASE_URL}" -t -c "SELECT COUNT(*) FROM auth.users;" | xargs)
if [ "$USER_COUNT" -eq "0" ]; then
echo -e "${YELLOW}⚠️ No users in auth.users. Running public seed first...${NC}"
# Run the original users seed file in public schema to populate auth.users
for seed_file in packages/supabase/supabase/seeds/*.sql; do
if [ -f "$seed_file" ]; then
filename=$(basename "$seed_file")
if [[ "$filename" == "02_users_and_identities.sql" ]]; then
echo -e "${YELLOW}Seeding auth.users from: $filename${NC}"
psql "${DATABASE_URL}" -c "SET search_path TO public,auth;" -f "$seed_file"
fi
fi
done
else
echo -e "${GREEN}βœ… Found $USER_COUNT users in auth.users${NC}"
fi
fi
# Now seed the schema-specific tables
for seed_file in packages/supabase/supabase/seeds/*.sql; do
if [ -f "$seed_file" ]; then
filename=$(basename "$seed_file")
echo -e "${YELLOW}Processing: $filename${NC}"
# Skip auth.users seeding for non-public schemas (we're using shared auth.users)
if [[ "$filename" == "02_users_and_identities.sql" ]] && [ "$SCHEMA" != "public" ]; then
echo -e "${YELLOW}β†ͺ️ Skipping auth.users seeding (using shared users)${NC}"
# Instead, sync the users table in our schema with auth.users
echo -e "${YELLOW}πŸ”„ Syncing ${SCHEMA}.users with auth.users${NC}"
psql "${DATABASE_URL}" << EOF
-- Sync users from auth.users to schema-specific users table
INSERT INTO ${SCHEMA}.users (id, email, created_at, updated_at)
SELECT
au.id,
au.email,
au.created_at,
au.updated_at
FROM auth.users au
WHERE NOT EXISTS (
SELECT 1 FROM ${SCHEMA}.users u WHERE u.id = au.id
)
ON CONFLICT (id) DO UPDATE SET
email = EXCLUDED.email,
updated_at = EXCLUDED.updated_at;
-- Log the sync
DO \$\$
DECLARE
user_count INTEGER;
BEGIN
SELECT COUNT(*) INTO user_count FROM ${SCHEMA}.users;
RAISE NOTICE 'Synced % users to %.users', user_count, '${SCHEMA}';
END\$\$;
EOF
echo -e "${GREEN}βœ… User sync completed${NC}"
else
# For other seed files, run them with schema context
# Set search path to prioritize our schema, then public, then auth
echo -e "${YELLOW}πŸ”§ Running with schema search path: ${SCHEMA},public,auth${NC}"
psql "${DATABASE_URL}" -c "SET search_path TO ${SCHEMA},public,auth;" -f "$seed_file"
fi
fi
done
# Verify the setup
echo -e "${GREEN}πŸ“Š Verification:${NC}"
psql "${DATABASE_URL}" << EOF
-- Check user counts
DO \$\$
DECLARE
auth_user_count INTEGER;
schema_user_count INTEGER;
BEGIN
SELECT COUNT(*) INTO auth_user_count FROM auth.users;
-- Only check schema users if not in public schema
IF '${SCHEMA}' != 'public' THEN
SELECT COUNT(*) INTO schema_user_count FROM ${SCHEMA}.users;
RAISE NOTICE 'auth.users count: %', auth_user_count;
RAISE NOTICE '${SCHEMA}.users count: %', schema_user_count;
IF auth_user_count != schema_user_count THEN
RAISE WARNING 'User counts do not match! This might indicate a sync issue.';
END IF;
ELSE
RAISE NOTICE 'auth.users count: %', auth_user_count;
RAISE NOTICE 'Running in public schema - no sync needed';
END IF;
END\$\$;
EOF
echo -e "${GREEN}βœ… Schema-aware seeding complete for: $SCHEMA${NC}"
echo -e "${YELLOW}ℹ️ Note: Using shared auth.users across all schemas (no email transformation)${NC}"
#!/bin/bash
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
function show_help {
echo -e "${BLUE}OctoSpark Worktree Manager${NC}"
echo ""
echo "Usage: $0 [command] [options]"
echo ""
echo "Commands:"
echo " create <branch-name> Create a new worktree with schema isolation"
echo " list List all worktrees and their schemas"
echo " switch <name> Switch to a worktree directory"
echo " remove <name> Remove a worktree and its schema"
echo " help Show this help message"
echo ""
echo "Examples:"
echo " $0 create feature-auth"
echo " $0 list"
echo " $0 switch feature-auth"
echo " $0 remove feature-auth"
}
function create_worktree {
local BRANCH_NAME=$1
if [ -z "$BRANCH_NAME" ]; then
echo -e "${RED}❌ Please provide a branch name${NC}"
echo "Usage: $0 create <branch-name>"
exit 1
fi
local WORKTREE_PATH="worktrees/$BRANCH_NAME"
# Check if worktree already exists
if [ -d "$WORKTREE_PATH" ]; then
echo -e "${RED}❌ Worktree already exists at: $WORKTREE_PATH${NC}"
exit 1
fi
echo -e "${GREEN}🌳 Creating worktree: $BRANCH_NAME${NC}"
# Create the worktree
git worktree add "$WORKTREE_PATH" -b "$BRANCH_NAME" 2>/dev/null || {
# If branch already exists, just check it out
git worktree add "$WORKTREE_PATH" "$BRANCH_NAME"
}
echo -e "${GREEN}βœ… Worktree created at: $WORKTREE_PATH${NC}"
echo ""
echo -e "${YELLOW}Next steps (run from this directory):${NC}"
echo -e "1. ./scripts/setup-worktree-root.sh $WORKTREE_PATH"
echo -e "2. $WORKTREE_PATH/run-migrations.sh"
echo -e "3. $WORKTREE_PATH/seed-worktree.sh"
echo -e "4. cd $WORKTREE_PATH && pnpm dev (or use $WORKTREE_PATH/dev-worktree.sh)"
}
function list_worktrees {
echo -e "${BLUE}πŸ“‹ Active Worktrees:${NC}"
echo ""
# List git worktrees
git worktree list | while read -r line; do
local path=$(echo "$line" | awk '{print $1}')
local branch=$(echo "$line" | awk '{print $3}' | tr -d '[]')
local worktree_name=$(basename "$path")
# Skip the main worktree
if [ "$worktree_name" == "octospark-services" ]; then
continue
fi
# Generate schema name
local schema="wt_$(echo $worktree_name | tr '-' '_' | tr '[:upper:]' '[:lower:]')"
echo -e "${GREEN}$worktree_name${NC}"
echo -e " Path: $path"
echo -e " Branch: $branch"
echo -e " Schema: $schema"
# Check if schema exists in database
if psql postgresql://postgres:postgres@localhost:54322/postgres -tAc "SELECT 1 FROM information_schema.schemata WHERE schema_name = '$schema'" 2>/dev/null | grep -q 1; then
echo -e " Database: ${GREEN}βœ“ Schema exists${NC}"
else
echo -e " Database: ${YELLOW}⚠ Schema not created${NC}"
fi
# Check Docker containers
local web_container="octospark-web-$worktree_name"
local api_container="octospark-api-$worktree_name"
if docker ps --format "{{.Names}}" | grep -q "^$web_container$"; then
local web_port=$(docker ps --format "table {{.Names}}\t{{.Ports}}" | grep "^$web_container" | grep -oE '0.0.0.0:[0-9]+' | head -1 | cut -d: -f2)
echo -e " Web: ${GREEN}βœ“ Running${NC} on port ${web_port:-unknown}"
else
echo -e " Web: ${YELLOW}β—‹ Not running${NC}"
fi
if docker ps --format "{{.Names}}" | grep -q "^$api_container$"; then
local api_port=$(docker ps --format "table {{.Names}}\t{{.Ports}}" | grep "^$api_container" | grep -oE '0.0.0.0:[0-9]+' | head -1 | cut -d: -f2)
echo -e " API: ${GREEN}βœ“ Running${NC} on port ${api_port:-unknown}"
else
echo -e " API: ${YELLOW}β—‹ Not running${NC}"
fi
echo ""
done
}
function switch_worktree {
local WORKTREE_NAME=$1
if [ -z "$WORKTREE_NAME" ]; then
echo -e "${RED}❌ Please provide a worktree name${NC}"
echo "Usage: $0 switch <name>"
exit 1
fi
local WORKTREE_PATH="worktrees/$WORKTREE_NAME"
if [ ! -d "$WORKTREE_PATH" ]; then
echo -e "${RED}❌ Worktree not found: $WORKTREE_NAME${NC}"
echo "Available worktrees:"
list_worktrees
exit 1
fi
echo -e "${GREEN}Switching to worktree: $WORKTREE_NAME${NC}"
echo "cd $WORKTREE_PATH"
echo ""
echo -e "${YELLOW}Run this command to switch:${NC}"
echo -e "${BLUE}cd $WORKTREE_PATH${NC}"
}
function remove_worktree {
local WORKTREE_NAME=$1
if [ -z "$WORKTREE_NAME" ]; then
echo -e "${RED}❌ Please provide a worktree name${NC}"
echo "Usage: $0 remove <name>"
exit 1
fi
local WORKTREE_PATH="worktrees/$WORKTREE_NAME"
local SCHEMA="wt_$(echo $WORKTREE_NAME | tr '-' '_' | tr '[:upper:]' '[:lower:]')"
if [ ! -d "$WORKTREE_PATH" ]; then
echo -e "${RED}❌ Worktree not found: $WORKTREE_NAME${NC}"
exit 1
fi
# Get the branch name for this worktree
local BRANCH_NAME=""
local worktree_info=$(git worktree list | grep "$WORKTREE_PATH")
if [ -n "$worktree_info" ]; then
BRANCH_NAME=$(echo "$worktree_info" | awk '{print $3}' | tr -d '[]')
fi
echo -e "${YELLOW}⚠️ This will remove:${NC}"
echo -e " - Worktree at: $WORKTREE_PATH"
echo -e " - Database schema: $SCHEMA"
if [ -n "$BRANCH_NAME" ]; then
echo -e " - Git branch: $BRANCH_NAME"
fi
# Check for running Docker containers
local web_container="octospark-web-$WORKTREE_NAME"
local api_container="octospark-api-$WORKTREE_NAME"
local running_containers=false
if docker ps --format "{{.Names}}" | grep -q "^$web_container$"; then
echo -e " - Docker container: $web_container"
running_containers=true
fi
if docker ps --format "{{.Names}}" | grep -q "^$api_container$"; then
echo -e " - Docker container: $api_container"
running_containers=true
fi
echo ""
read -p "Are you sure? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo -e "${YELLOW}Cancelled${NC}"
exit 0
fi
# Stop Docker containers first
if [ "$running_containers" = true ]; then
echo -e "${GREEN}Stopping Docker containers...${NC}"
docker stop "$web_container" "$api_container" 2>/dev/null || true
docker rm "$web_container" "$api_container" 2>/dev/null || true
# Also stop any related containers (like cloud tasks emulator)
docker ps --format "{{.Names}}" | grep "$WORKTREE_NAME" | xargs -r docker stop 2>/dev/null || true
docker ps -a --format "{{.Names}}" | grep "$WORKTREE_NAME" | xargs -r docker rm 2>/dev/null || true
fi
# Run cleanup script if it exists
if [ -f "$WORKTREE_PATH/cleanup-worktree.sh" ]; then
echo -e "${GREEN}Running cleanup script...${NC}"
(cd "$WORKTREE_PATH" && ./cleanup-worktree.sh)
else
# Manual cleanup
echo -e "${GREEN}Dropping schema: $SCHEMA${NC}"
psql postgresql://postgres:postgres@localhost:54322/postgres -c "DROP SCHEMA IF EXISTS $SCHEMA CASCADE;" 2>/dev/null || true
fi
# Remove git worktree
echo -e "${GREEN}Removing git worktree...${NC}"
git worktree remove "$WORKTREE_PATH" --force
# Ensure the directory is completely removed
if [ -d "$WORKTREE_PATH" ]; then
echo -e "${GREEN}Cleaning up worktree directory...${NC}"
rm -rf "$WORKTREE_PATH"
fi
# Delete the associated branch if it exists
if [ -n "$BRANCH_NAME" ] && git branch | grep -q "^\s*$BRANCH_NAME$"; then
echo -e "${GREEN}Deleting git branch: $BRANCH_NAME${NC}"
git branch -D "$BRANCH_NAME" 2>/dev/null || {
echo -e "${YELLOW}⚠️ Could not delete branch $BRANCH_NAME (it may be merged or already deleted)${NC}"
}
elif [ -n "$BRANCH_NAME" ]; then
echo -e "${YELLOW}ℹ️ Branch $BRANCH_NAME was already deleted or doesn't exist locally${NC}"
fi
echo -e "${GREEN}βœ… Worktree and associated branch removed successfully${NC}"
}
# Main command handling
case "${1:-help}" in
create)
create_worktree "$2"
;;
list)
list_worktrees
;;
switch)
switch_worktree "$2"
;;
remove)
remove_worktree "$2"
;;
help|--help|-h)
show_help
;;
*)
echo -e "${RED}Unknown command: $1${NC}"
echo ""
show_help
exit 1
;;
esac
#!/bin/bash
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Check if a worktree path is provided
if [ $# -eq 0 ]; then
echo -e "${RED}❌ Please provide a worktree path${NC}"
echo -e "Usage: $0 <worktree-path>"
echo -e "Example: $0 worktrees/feature-auth"
exit 1
fi
WORKTREE_PATH=$1
# Check if worktree exists
if [ ! -d "$WORKTREE_PATH" ]; then
echo -e "${RED}❌ Worktree not found at: $WORKTREE_PATH${NC}"
echo -e "Create it first with: ./scripts/manage-worktree.sh create <branch-name>"
exit 1
fi
# Get worktree name from path
WORKTREE_NAME=$(basename "$WORKTREE_PATH")
echo -e "${GREEN}πŸš€ Setting up worktree with schema isolation${NC}"
echo -e "${YELLOW}πŸ“‹ Worktree: $WORKTREE_NAME${NC}"
echo -e "${YELLOW}πŸ“‹ Path: $WORKTREE_PATH${NC}"
# Check if Supabase is running
if ! curl -s http://localhost:54321/health > /dev/null 2>&1; then
echo -e "${RED}❌ Supabase local stack is not running. Start it with: pnpm db:start${NC}"
exit 1
fi
# Generate schema name
WORKTREE_SCHEMA="wt_$(echo $WORKTREE_NAME | tr '-' '_' | tr '[:upper:]' '[:lower:]')"
echo -e "${YELLOW}πŸ“‹ Schema: $WORKTREE_SCHEMA${NC}"
# Create schema in database with extensions
echo -e "${GREEN}πŸ“¦ Creating database schema: $WORKTREE_SCHEMA${NC}"
psql postgresql://postgres:postgres@localhost:54322/postgres << EOF
-- Create schema if not exists
CREATE SCHEMA IF NOT EXISTS $WORKTREE_SCHEMA;
-- Grant usage on schema
GRANT USAGE ON SCHEMA $WORKTREE_SCHEMA TO postgres, anon, authenticated, service_role;
GRANT ALL ON SCHEMA $WORKTREE_SCHEMA TO postgres;
GRANT CREATE ON SCHEMA $WORKTREE_SCHEMA TO postgres;
-- Important: The uuid-ossp extension is already installed in the extensions schema by Supabase
-- We need to reference it properly in our search_path or create wrapper functions
-- Create wrapper function for uuid_generate_v4() in our schema
CREATE OR REPLACE FUNCTION $WORKTREE_SCHEMA.uuid_generate_v4()
RETURNS uuid
LANGUAGE sql
VOLATILE
AS \$\$
SELECT extensions.uuid_generate_v4();
\$\$;
-- Grant execute permissions
GRANT EXECUTE ON FUNCTION $WORKTREE_SCHEMA.uuid_generate_v4() TO postgres, anon, authenticated, service_role;
-- Also create gen_random_uuid wrapper (alternative UUID function)
CREATE OR REPLACE FUNCTION $WORKTREE_SCHEMA.gen_random_uuid()
RETURNS uuid
LANGUAGE sql
VOLATILE
AS \$\$
SELECT gen_random_uuid();
\$\$;
GRANT EXECUTE ON FUNCTION $WORKTREE_SCHEMA.gen_random_uuid() TO postgres, anon, authenticated, service_role;
-- Create wrapper functions for pgcrypto functions (gen_salt, crypt)
CREATE OR REPLACE FUNCTION $WORKTREE_SCHEMA.gen_salt(text)
RETURNS text
LANGUAGE sql
STABLE STRICT
AS \$\$
SELECT extensions.gen_salt(\$1);
\$\$;
CREATE OR REPLACE FUNCTION $WORKTREE_SCHEMA.gen_salt(text, integer)
RETURNS text
LANGUAGE sql
STABLE STRICT
AS \$\$
SELECT extensions.gen_salt(\$1, \$2);
\$\$;
CREATE OR REPLACE FUNCTION $WORKTREE_SCHEMA.crypt(text, text)
RETURNS text
LANGUAGE sql
STABLE STRICT
AS \$\$
SELECT extensions.crypt(\$1, \$2);
\$\$;
GRANT EXECUTE ON FUNCTION $WORKTREE_SCHEMA.gen_salt(text) TO postgres, anon, authenticated, service_role;
GRANT EXECUTE ON FUNCTION $WORKTREE_SCHEMA.gen_salt(text, integer) TO postgres, anon, authenticated, service_role;
GRANT EXECUTE ON FUNCTION $WORKTREE_SCHEMA.crypt(text, text) TO postgres, anon, authenticated, service_role;
EOF
# Get Supabase keys
SUPABASE_ANON_KEY=$(cat packages/supabase/supabase/.temp/gotrue/anon.key 2>/dev/null || echo "")
SUPABASE_SERVICE_ROLE_KEY=$(cat packages/supabase/supabase/.temp/gotrue/service.key 2>/dev/null || echo "")
if [ -z "$SUPABASE_ANON_KEY" ] || [ -z "$SUPABASE_SERVICE_ROLE_KEY" ]; then
# Try to get from supabase status
echo -e "${YELLOW}⚠️ Reading keys from supabase status${NC}"
SUPABASE_ANON_KEY=$(cd packages/supabase && supabase status 2>/dev/null | grep "anon key" | cut -d: -f2 | xargs || echo "")
SUPABASE_SERVICE_ROLE_KEY=$(cd packages/supabase && supabase status 2>/dev/null | grep "service_role key" | cut -d: -f2 | xargs || echo "")
fi
if [ -z "$SUPABASE_ANON_KEY" ] || [ -z "$SUPABASE_SERVICE_ROLE_KEY" ]; then
echo -e "${YELLOW}⚠️ Could not read Supabase keys - update .env.worktree manually${NC}"
SUPABASE_ANON_KEY="your-anon-key"
SUPABASE_SERVICE_ROLE_KEY="your-service-role-key"
fi
# Create .env.worktree file in the worktree
echo -e "${GREEN}πŸ“ Creating .env.worktree configuration${NC}"
cat > "$WORKTREE_PATH/.env.worktree" << EOF
# Worktree Configuration
WORKTREE_NAME=$WORKTREE_NAME
WORKTREE_SCHEMA=$WORKTREE_SCHEMA
DATABASE_SCHEMA=$WORKTREE_SCHEMA
NEXT_PUBLIC_DATABASE_SCHEMA=$WORKTREE_SCHEMA
# Database URLs with schema search path
DATABASE_URL=postgresql://postgres:postgres@localhost:54322/postgres?options=-c%20search_path%3D$WORKTREE_SCHEMA,public
DIRECT_URL=postgresql://postgres:postgres@localhost:54322/postgres?options=-c%20search_path%3D$WORKTREE_SCHEMA,public
# Supabase Configuration
NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321
NEXT_PUBLIC_SUPABASE_ANON_KEY=$SUPABASE_ANON_KEY
SUPABASE_SERVICE_ROLE_KEY=$SUPABASE_SERVICE_ROLE_KEY
SUPABASE_DB_URL=postgresql://postgres:postgres@localhost:54322/postgres?options=-c%20search_path%3D$WORKTREE_SCHEMA,public
# API Configuration
NEXT_PUBLIC_API_URL=http://localhost:8080
EOF
# Create docker-compose for this worktree
echo -e "${GREEN}🐳 Creating docker-compose configuration${NC}"
# Generate unique ports for this worktree
# Use a hash of the worktree name to generate consistent ports
HASH=$(echo -n "$WORKTREE_NAME" | md5sum | cut -c1-4)
PORT_OFFSET=$(printf "%d" "0x$HASH")
# Ensure port offset is in a reasonable range (1000-9000)
PORT_OFFSET=$((($PORT_OFFSET % 8000) + 1000))
# Calculate ports
WEB_PORT=$((3000 + $PORT_OFFSET))
API_PORT=$((8080 + $PORT_OFFSET))
# Get the absolute root directory
ROOT_DIR="$(cd "$(dirname "$0")/../" && pwd)"
# Create a standalone docker-compose file for the worktree
cat > "$WORKTREE_PATH/docker-compose.worktree.yml" << EOF
# Generated docker-compose for worktree: $WORKTREE_NAME
# Standalone configuration to avoid port conflicts
services:
# API service - development with hot reload
api-dev-$WORKTREE_NAME:
build:
context: $ROOT_DIR
dockerfile: apps/api/Dockerfile
target: dev
container_name: octospark-api-$WORKTREE_NAME
ports:
- "$API_PORT:8080"
env_file:
- $ROOT_DIR/apps/api/.env
environment:
- NODE_ENV=development
- PORT=8080
- DATABASE_SCHEMA=$WORKTREE_SCHEMA
- DATABASE_URL=postgresql://postgres:[email protected]:54322/postgres?options=-c%20search_path%3D$WORKTREE_SCHEMA,public
- SUPABASE_DB_URL=postgresql://postgres:[email protected]:54322/postgres?options=-c%20search_path%3D$WORKTREE_SCHEMA,public
- NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321
- NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0
- IS_DOCKER=true
- REDIS_URL=redis://redis-$WORKTREE_NAME:6379
volumes:
- $ROOT_DIR/apps/api/src:/app/apps/api/src
- $ROOT_DIR/packages:/app/packages
- /app/node_modules
- /app/apps/api/node_modules
networks:
- supabase_network_cursordevkit_octospark
depends_on:
- redis-$WORKTREE_NAME
extra_hosts:
- "host.docker.internal:host-gateway"
restart: unless-stopped
profiles:
- api-dev
- dev
- full-stack
# Web service - Next.js with hot reload
web-dev-$WORKTREE_NAME:
build:
context: $ROOT_DIR
dockerfile: apps/web/Dockerfile
container_name: octospark-web-$WORKTREE_NAME
ports:
- "$WEB_PORT:3000"
env_file:
- $ROOT_DIR/apps/web/.env.local
environment:
- NODE_ENV=development
- PORT=3000
- DATABASE_SCHEMA=$WORKTREE_SCHEMA
- NEXT_PUBLIC_DATABASE_SCHEMA=$WORKTREE_SCHEMA
- NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321
- NEXT_PUBLIC_API_URL=http://localhost:$API_PORT
- IS_DOCKER=true
- WATCHPACK_POLLING=true
- REDIS_URL=redis://redis-$WORKTREE_NAME:6379
volumes:
- $ROOT_DIR/apps/web/app:/app/apps/web/app
- $ROOT_DIR/apps/web/components:/app/apps/web/components
- $ROOT_DIR/apps/web/features:/app/apps/web/features
- $ROOT_DIR/apps/web/hooks:/app/apps/web/hooks
- $ROOT_DIR/apps/web/lib:/app/apps/web/lib
- $ROOT_DIR/apps/web/public:/app/apps/web/public
- $ROOT_DIR/apps/web/stores:/app/apps/web/stores
- $ROOT_DIR/apps/web/types:/app/apps/web/types
- $ROOT_DIR/apps/web/utils:/app/apps/web/utils
- $ROOT_DIR/apps/web/integration:/app/apps/web/integration
- $ROOT_DIR/apps/web/__tests__:/app/apps/web/__tests__
- $ROOT_DIR/packages:/app/packages
- /app/node_modules
- /app/apps/web/node_modules
- /app/apps/web/.next
networks:
- supabase_network_cursordevkit_octospark
depends_on:
- api-dev-$WORKTREE_NAME
extra_hosts:
- "host.docker.internal:host-gateway"
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
profiles:
- web-dev
- dev
- full-stack
# Redis service
redis-$WORKTREE_NAME:
image: redis:alpine
container_name: redis-$WORKTREE_NAME
ports:
- "$((6379 + $PORT_OFFSET)):6379"
networks:
- supabase_network_cursordevkit_octospark
restart: unless-stopped
profiles:
- dev
- api-dev
- full-stack
networks:
supabase_network_cursordevkit_octospark:
external: true
EOF
# Update the .env.worktree with the new ports
cat >> "$WORKTREE_PATH/.env.worktree" << EOF
# Worktree-specific ports
WEB_PORT=$WEB_PORT
API_PORT=$API_PORT
NEXT_PUBLIC_API_URL=http://localhost:$API_PORT
EOF
# Create helper scripts that can be run from root
echo -e "${GREEN}πŸ“„ Creating helper scripts${NC}"
# Create scripts directory
mkdir -p "$WORKTREE_PATH/scripts"
# Create init-worktree-schema.sh script
cat > "$WORKTREE_PATH/scripts/init-worktree-schema.sh" << 'EOF'
#!/bin/bash
# Initialize worktree schema with proper extensions
# This script ensures all required extensions are available in the worktree schema
set -euo pipefail
# Colors for output
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'
# Get the directory of this script
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
# Check if schema name is provided
if [ -z "${1:-}" ]; then
echo -e "${RED}Error: Schema name required${NC}"
echo "Usage: $0 <schema_name>"
exit 1
fi
SCHEMA_NAME="$1"
echo -e "${GREEN}Initializing schema: $SCHEMA_NAME${NC}"
# Create schema and install extensions
psql postgresql://postgres:postgres@localhost:54322/postgres << EOSQL
-- Create schema if it doesn't exist
CREATE SCHEMA IF NOT EXISTS $SCHEMA_NAME;
-- Install extensions in the public schema (they're global)
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
CREATE EXTENSION IF NOT EXISTS "vector";
-- Grant usage on extensions
GRANT USAGE ON SCHEMA public TO postgres, anon, authenticated, service_role;
GRANT USAGE ON SCHEMA $SCHEMA_NAME TO postgres, anon, authenticated, service_role;
GRANT ALL ON SCHEMA $SCHEMA_NAME TO postgres;
-- Create wrapper functions for extensions in the schema
CREATE OR REPLACE FUNCTION $SCHEMA_NAME.uuid_generate_v4()
RETURNS uuid
LANGUAGE sql
VOLATILE
AS \\\$\\\$
SELECT extensions.uuid_generate_v4();
\\\$\\\$;
CREATE OR REPLACE FUNCTION $SCHEMA_NAME.gen_random_uuid()
RETURNS uuid
LANGUAGE sql
VOLATILE
AS \\\$\\\$
SELECT gen_random_uuid();
\\\$\\\$;
-- Grant execute permissions on wrapper functions
GRANT EXECUTE ON FUNCTION $SCHEMA_NAME.uuid_generate_v4() TO postgres, anon, authenticated, service_role;
GRANT EXECUTE ON FUNCTION $SCHEMA_NAME.gen_random_uuid() TO postgres, anon, authenticated, service_role;
-- Verify extensions are accessible (session-specific search path)
SET search_path TO $SCHEMA_NAME, public;
SELECT extname, extnamespace::regnamespace
FROM pg_extension
WHERE extname IN ('uuid-ossp', 'pgcrypto', 'vector');
-- Test uuid generation works
SELECT $SCHEMA_NAME.uuid_generate_v4() as test_uuid;
EOSQL
echo -e "${GREEN}βœ… Schema initialized successfully${NC}"
EOF
chmod +x "$WORKTREE_PATH/scripts/init-worktree-schema.sh"
# Create clean-worktree-schema.sh script
cat > "$WORKTREE_PATH/scripts/clean-worktree-schema.sh" << 'EOF'
#!/bin/bash
# Clean and reset worktree schema
# This allows for a fresh migration run
set -euo pipefail
# Colors for output
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'
# Get the directory of this script
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
# Source environment to get schema name
source "$ROOT_DIR/.env.worktree"
echo -e "${YELLOW}⚠️ This will DROP and recreate schema: $WORKTREE_SCHEMA${NC}"
echo -e "${YELLOW}All data in this schema will be lost!${NC}"
read -p "Are you sure? (y/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo -e "${RED}Aborted${NC}"
exit 1
fi
echo -e "${GREEN}Cleaning schema: $WORKTREE_SCHEMA${NC}"
# Drop and recreate schema
psql postgresql://postgres:postgres@localhost:54322/postgres << EOSQL
-- Drop schema cascade (removes all objects)
DROP SCHEMA IF EXISTS $WORKTREE_SCHEMA CASCADE;
-- Recreate schema
CREATE SCHEMA $WORKTREE_SCHEMA;
-- Grant permissions
GRANT ALL ON SCHEMA $WORKTREE_SCHEMA TO postgres;
GRANT USAGE ON SCHEMA $WORKTREE_SCHEMA TO anon, authenticated, service_role;
EOSQL
echo -e "${GREEN}βœ… Schema cleaned successfully${NC}"
echo -e "${YELLOW}Run './run-migrations.sh' to apply migrations${NC}"
EOF
chmod +x "$WORKTREE_PATH/scripts/clean-worktree-schema.sh"
# Create a run-migrations script
cat > "$WORKTREE_PATH/run-migrations.sh" << 'EOF'
#!/bin/bash
set -e
# Get the directory of this script
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# For worktree, the packages are in the worktree directory itself
ROOT_DIR="$SCRIPT_DIR"
# Source environment
source "$SCRIPT_DIR/.env.worktree"
echo "Running migrations for schema: $WORKTREE_SCHEMA"
echo "From root directory: $ROOT_DIR"
# Initialize schema with extensions first
echo "Initializing schema with required extensions..."
"$SCRIPT_DIR/scripts/init-worktree-schema.sh" "$WORKTREE_SCHEMA"
# Run migrations with the schema-specific search path
cd "$ROOT_DIR/packages/supabase"
for migration in supabase/migrations/*.sql; do
if [ -f "$migration" ]; then
echo "Running: $(basename $migration)"
# Use search_path in the connection string and ensure extensions are accessible
psql "postgresql://postgres:postgres@localhost:54322/postgres?options=-c%20search_path%3D$WORKTREE_SCHEMA,public" -f "$migration"
fi
done
cd "$SCRIPT_DIR"
echo "βœ… Migrations completed"
EOF
chmod +x "$WORKTREE_PATH/run-migrations.sh"
# Create a seed data script
cat > "$WORKTREE_PATH/seed-worktree.sh" << 'EOF'
#!/bin/bash
set -e
# Get the directory of this script
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Source environment
source "$SCRIPT_DIR/.env.worktree"
echo "Seeding data for schema: $WORKTREE_SCHEMA"
# Use the fixed schema-aware seed generator that doesn't transform emails
if [ -f "$ROOT_DIR/scripts/generate-schema-seed-fixed.sh" ]; then
cd "$ROOT_DIR"
DATABASE_SCHEMA=$WORKTREE_SCHEMA DATABASE_URL=$DATABASE_URL ./scripts/generate-schema-seed-fixed.sh
elif [ -f "$ROOT_DIR/scripts/generate-schema-seed.sh" ]; then
cd "$ROOT_DIR"
DATABASE_SCHEMA=$WORKTREE_SCHEMA DATABASE_URL=$DATABASE_URL ./scripts/generate-schema-seed.sh
else
echo "⚠️ Schema-aware seed script not found"
echo " Falling back to standard seed files..."
# Fallback: Run seed files with schema context
cd "$ROOT_DIR"
for seed_file in packages/supabase/supabase/seeds/*.sql; do
if [ -f "$seed_file" ]; then
echo "Running: $(basename $seed_file)"
psql "$DATABASE_URL" -c "SET search_path TO $WORKTREE_SCHEMA,public,auth,extensions;" -f "$seed_file"
fi
done
fi
echo "βœ… Seed data loaded"
EOF
chmod +x "$WORKTREE_PATH/seed-worktree.sh"
# Create test runner script
cat > "$WORKTREE_PATH/test-worktree.sh" << 'EOF'
#!/bin/bash
set -e
# Get the directory of this script
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Source environment
source "$SCRIPT_DIR/.env.worktree"
echo "Running tests for worktree: $WORKTREE_NAME (schema: $WORKTREE_SCHEMA)"
# Export environment variables for tests
export DATABASE_SCHEMA=$WORKTREE_SCHEMA
export DATABASE_URL=$DATABASE_URL
# Run different test suites from root
cd "$ROOT_DIR"
case "${1:-all}" in
integration)
echo "Running integration tests..."
echo " - Web app integration tests..."
cd apps/web && pnpm test:integration
cd "$ROOT_DIR"
;;
e2e)
echo "Running e2e tests..."
cd apps/web && pnpm test:e2e
cd "$ROOT_DIR"
;;
unit)
echo "Running unit tests..."
echo " - Root unit tests..."
pnpm test
echo " - Core services tests..."
cd packages/core-services && pnpm test
cd "$ROOT_DIR"
echo " - API tests..."
cd apps/api && pnpm test
cd "$ROOT_DIR"
;;
web)
echo "Running web app tests..."
cd apps/web && pnpm test
cd "$ROOT_DIR"
;;
api)
echo "Running API tests..."
cd apps/api && pnpm test
cd "$ROOT_DIR"
;;
core)
echo "Running core services tests..."
cd packages/core-services && pnpm test
cd "$ROOT_DIR"
;;
all)
echo "Running all tests..."
echo " - Root tests..."
pnpm test
echo " - Core services tests..."
cd packages/core-services && pnpm test
cd "$ROOT_DIR"
echo " - API tests..."
cd apps/api && pnpm test
cd "$ROOT_DIR"
echo " - Web app tests..."
cd apps/web && pnpm test
cd "$ROOT_DIR"
echo " - Integration tests..."
cd apps/web && pnpm test:integration
cd "$ROOT_DIR"
;;
*)
echo "Usage: $0 [integration|e2e|unit|web|api|core|all]"
echo " integration - Run web app integration tests"
echo " e2e - Run end-to-end tests"
echo " unit - Run all unit tests (root, core, api)"
echo " web - Run web app tests only"
echo " api - Run API tests only"
echo " core - Run core services tests only"
echo " all - Run all tests"
exit 1
;;
esac
EOF
chmod +x "$WORKTREE_PATH/test-worktree.sh"
# Create dev runner script
cat > "$WORKTREE_PATH/dev-worktree.sh" << 'EOF'
#!/bin/bash
# Get the directory of this script
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Source environment
source "$SCRIPT_DIR/.env.worktree"
# Colors for output
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'
# Parse command
CMD="${1:-up}"
case "$CMD" in
up|start)
echo -e "${GREEN}Starting services for worktree: $WORKTREE_NAME${NC}"
echo -e "${YELLOW}Web: http://localhost:$WEB_PORT${NC}"
echo -e "${YELLOW}API: http://localhost:$API_PORT${NC}"
cd "$SCRIPT_DIR"
docker-compose -f docker-compose.worktree.yml --profile dev up -d
# Show container status
docker ps --filter "name=octospark-.*-$WORKTREE_NAME"
;;
down|stop)
echo -e "${YELLOW}Stopping services for worktree: $WORKTREE_NAME${NC}"
cd "$SCRIPT_DIR"
docker-compose -f docker-compose.worktree.yml down
;;
restart)
echo -e "${YELLOW}Restarting services for worktree: $WORKTREE_NAME${NC}"
cd "$SCRIPT_DIR"
docker-compose -f docker-compose.worktree.yml restart
;;
logs)
echo -e "${GREEN}Showing logs for worktree: $WORKTREE_NAME${NC}"
shift
cd "$SCRIPT_DIR"
docker-compose -f docker-compose.worktree.yml logs -f "$@"
;;
status)
echo -e "${GREEN}Services for worktree: $WORKTREE_NAME${NC}"
echo -e "${YELLOW}Web: http://localhost:$WEB_PORT${NC}"
echo -e "${YELLOW}API: http://localhost:$API_PORT${NC}"
docker ps --filter "name=octospark-.*-$WORKTREE_NAME" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
;;
*)
echo -e "${RED}Usage: $0 [up|down|restart|logs|status]${NC}"
echo -e " up/start - Start services in background"
echo -e " down/stop - Stop and remove services"
echo -e " restart - Restart services"
echo -e " logs - Show logs (optionally specify service)"
echo -e " status - Show service status"
exit 1
;;
esac
EOF
chmod +x "$WORKTREE_PATH/dev-worktree.sh"
# Create a cleanup script
cat > "$WORKTREE_PATH/cleanup-worktree.sh" << 'EOF'
#!/bin/bash
set -e
# Get the directory of this script
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# Source environment
source "$SCRIPT_DIR/.env.worktree"
echo "Cleaning up worktree: $WORKTREE_NAME (schema: $WORKTREE_SCHEMA)"
# Stop any running containers
cd "$SCRIPT_DIR"
docker-compose -f docker-compose.worktree.yml down 2>/dev/null || true
# Drop the schema (with CASCADE to remove all objects)
read -p "Drop schema $WORKTREE_SCHEMA? This will delete all data! (y/N): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
psql postgresql://postgres:postgres@localhost:54322/postgres -c "DROP SCHEMA IF EXISTS $WORKTREE_SCHEMA CASCADE;"
echo "βœ… Schema dropped"
fi
# Remove generated files
rm -f "$SCRIPT_DIR/.env.worktree" "$SCRIPT_DIR/docker-compose.override.yml" \
"$SCRIPT_DIR/run-migrations.sh" "$SCRIPT_DIR/seed-worktree.sh" \
"$SCRIPT_DIR/test-worktree.sh" "$SCRIPT_DIR/dev-worktree.sh" \
"$SCRIPT_DIR/cleanup-worktree.sh"
echo "βœ… Cleanup complete"
EOF
chmod +x "$WORKTREE_PATH/cleanup-worktree.sh"
# Copy environment files from main branch if they exist
echo -e "${GREEN}πŸ“‹ Copying environment files from main branch${NC}"
# Copy API .env file if it exists
if [ -f "apps/api/.env" ]; then
cp "apps/api/.env" "$WORKTREE_PATH/apps/api/.env"
echo -e "${GREEN}βœ“ Copied apps/api/.env${NC}"
else
echo -e "${YELLOW}⚠ No apps/api/.env found in main branch${NC}"
fi
# Copy Web .env.local file if it exists
if [ -f "apps/web/.env.local" ]; then
cp "apps/web/.env.local" "$WORKTREE_PATH/apps/web/.env.local"
# Update the API URL in the web env file to use the worktree-specific port
if command -v sed >/dev/null 2>&1; then
sed -i.bak "s|NEXT_PUBLIC_API_URL=.*|NEXT_PUBLIC_API_URL=http://localhost:$API_PORT|g" "$WORKTREE_PATH/apps/web/.env.local"
rm -f "$WORKTREE_PATH/apps/web/.env.local.bak"
echo -e "${GREEN}βœ“ Copied and updated apps/web/.env.local${NC}"
else
echo -e "${GREEN}βœ“ Copied apps/web/.env.local (manual port update needed)${NC}"
fi
else
echo -e "${YELLOW}⚠ No apps/web/.env.local found in main branch${NC}"
fi
# Add gitignore entries if not already present
if ! grep -q ".env.worktree" "$WORKTREE_PATH/.gitignore" 2>/dev/null; then
echo -e "\n# Worktree specific files" >> "$WORKTREE_PATH/.gitignore"
echo ".env.worktree" >> "$WORKTREE_PATH/.gitignore"
echo "docker-compose.worktree.yml" >> "$WORKTREE_PATH/.gitignore"
echo "run-migrations.sh" >> "$WORKTREE_PATH/.gitignore"
echo "seed-worktree.sh" >> "$WORKTREE_PATH/.gitignore"
echo "test-worktree.sh" >> "$WORKTREE_PATH/.gitignore"
echo "dev-worktree.sh" >> "$WORKTREE_PATH/.gitignore"
echo "cleanup-worktree.sh" >> "$WORKTREE_PATH/.gitignore"
echo -e "${GREEN}πŸ“ Added worktree files to .gitignore${NC}"
fi
echo -e "${GREEN}βœ… Worktree setup complete!${NC}"
echo ""
echo -e "Next steps (run from the root directory):"
echo -e "1. Run migrations: ${YELLOW}$WORKTREE_PATH/run-migrations.sh${NC}"
echo -e "2. Seed data: ${YELLOW}$WORKTREE_PATH/seed-worktree.sh${NC}"
echo -e "3. Start development:"
echo -e " - Docker: ${YELLOW}$WORKTREE_PATH/dev-worktree.sh${NC}"
echo -e " - Without Docker: ${YELLOW}source $WORKTREE_PATH/.env.worktree && pnpm dev${NC}"
echo -e "4. Run tests: ${YELLOW}$WORKTREE_PATH/test-worktree.sh [integration|e2e|unit|all]${NC}"
echo -e "5. When done: ${YELLOW}$WORKTREE_PATH/cleanup-worktree.sh${NC}"
echo ""
echo -e "Schema: ${GREEN}$WORKTREE_SCHEMA${NC}"
echo -e "Database: ${GREEN}postgresql://localhost:54322${NC}"
#!/bin/bash
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
echo -e "${GREEN}πŸš€ Setting up worktree with schema isolation${NC}"
# Check if we're in a git worktree or if it's a regular branch checkout
IS_WORKTREE=false
if [ -f .git ]; then
# This is a worktree (.git is a file pointing to the git directory)
IS_WORKTREE=true
WORKTREE_NAME=$(basename $(pwd))
elif [ -d .git ]; then
# This is the main repository or a regular checkout
# Check if we're in a worktree directory (under worktrees/)
if [[ $(pwd) == *"/worktrees/"* ]]; then
IS_WORKTREE=true
WORKTREE_NAME=$(basename $(pwd))
else
echo -e "${YELLOW}⚠️ Running in main repository. Using branch name for schema.${NC}"
WORKTREE_NAME=$(git branch --show-current)
if [ "$WORKTREE_NAME" == "main" ] || [ "$WORKTREE_NAME" == "master" ]; then
echo -e "${RED}❌ Cannot use schema isolation on main/master branch${NC}"
echo -e "${YELLOW} Please create a feature branch or worktree${NC}"
exit 1
fi
fi
else
echo -e "${RED}❌ Not in a git repository${NC}"
exit 1
fi
# Check if Supabase is running
if ! curl -s http://localhost:54321/health > /dev/null 2>&1; then
echo -e "${RED}❌ Supabase local stack is not running. Start it with: pnpm db:start${NC}"
exit 1
fi
# Generate schema name
WORKTREE_SCHEMA="wt_$(echo $WORKTREE_NAME | tr '-' '_' | tr '[:upper:]' '[:lower:]')"
echo -e "${YELLOW}πŸ“‹ Branch/Worktree: $WORKTREE_NAME${NC}"
echo -e "${YELLOW}πŸ“‹ Schema: $WORKTREE_SCHEMA${NC}"
# Create schema in database with extensions
echo -e "${GREEN}πŸ“¦ Creating database schema: $WORKTREE_SCHEMA${NC}"
psql postgresql://postgres:postgres@localhost:54322/postgres << EOF
-- Create schema if not exists
CREATE SCHEMA IF NOT EXISTS $WORKTREE_SCHEMA;
-- Grant usage on schema
GRANT USAGE ON SCHEMA $WORKTREE_SCHEMA TO postgres, anon, authenticated, service_role;
GRANT ALL ON SCHEMA $WORKTREE_SCHEMA TO postgres;
GRANT CREATE ON SCHEMA $WORKTREE_SCHEMA TO postgres;
-- Important: The uuid-ossp extension is already installed in the extensions schema by Supabase
-- We need to reference it properly in our search_path or create wrapper functions
-- Create wrapper function for uuid_generate_v4() in our schema
CREATE OR REPLACE FUNCTION $WORKTREE_SCHEMA.uuid_generate_v4()
RETURNS uuid
LANGUAGE sql
VOLATILE
AS \$\$
SELECT extensions.uuid_generate_v4();
\$\$;
-- Grant execute permissions
GRANT EXECUTE ON FUNCTION $WORKTREE_SCHEMA.uuid_generate_v4() TO postgres, anon, authenticated, service_role;
-- Also create gen_random_uuid wrapper (alternative UUID function)
CREATE OR REPLACE FUNCTION $WORKTREE_SCHEMA.gen_random_uuid()
RETURNS uuid
LANGUAGE sql
VOLATILE
AS \$\$
SELECT gen_random_uuid();
\$\$;
GRANT EXECUTE ON FUNCTION $WORKTREE_SCHEMA.gen_random_uuid() TO postgres, anon, authenticated, service_role;
EOF
# Get Supabase keys
SUPABASE_ANON_KEY=$(cat packages/supabase/supabase/.temp/gotrue/anon.key 2>/dev/null || echo "")
SUPABASE_SERVICE_ROLE_KEY=$(cat packages/supabase/supabase/.temp/gotrue/service.key 2>/dev/null || echo "")
if [ -z "$SUPABASE_ANON_KEY" ] || [ -z "$SUPABASE_SERVICE_ROLE_KEY" ]; then
echo -e "${YELLOW}⚠️ Could not read Supabase keys from .temp directory${NC}"
echo -e "${YELLOW} Using placeholder keys - update .env.worktree manually${NC}"
SUPABASE_ANON_KEY="your-anon-key"
SUPABASE_SERVICE_ROLE_KEY="your-service-role-key"
fi
# Create .env.worktree file
echo -e "${GREEN}πŸ“ Creating .env.worktree configuration${NC}"
cat > .env.worktree << EOF
# Worktree Configuration
WORKTREE_NAME=$WORKTREE_NAME
WORKTREE_SCHEMA=$WORKTREE_SCHEMA
DATABASE_SCHEMA=$WORKTREE_SCHEMA
# Database URLs with schema search path
DATABASE_URL=postgresql://postgres:postgres@localhost:54322/postgres?options=-c%20search_path%3D$WORKTREE_SCHEMA,public
DIRECT_URL=postgresql://postgres:postgres@localhost:54322/postgres?options=-c%20search_path%3D$WORKTREE_SCHEMA,public
# Supabase Configuration
NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321
NEXT_PUBLIC_SUPABASE_ANON_KEY=$SUPABASE_ANON_KEY
SUPABASE_SERVICE_ROLE_KEY=$SUPABASE_SERVICE_ROLE_KEY
SUPABASE_DB_URL=postgresql://postgres:postgres@localhost:54322/postgres?options=-c%20search_path%3D$WORKTREE_SCHEMA,public
# API Configuration
NEXT_PUBLIC_API_URL=http://localhost:8080
EOF
# Create docker-compose override for this worktree (only in worktrees)
if [ "$IS_WORKTREE" = true ]; then
echo -e "${GREEN}🐳 Creating docker-compose override configuration${NC}"
cat > docker-compose.override.yml << EOF
# Generated docker-compose override for worktree: $WORKTREE_NAME
# This file is automatically loaded by docker-compose
version: '3.8'
services:
api-dev:
environment:
- DATABASE_SCHEMA=$WORKTREE_SCHEMA
- DATABASE_URL=postgresql://postgres:[email protected]:54322/postgres?options=-c%20search_path%3D$WORKTREE_SCHEMA,public
- SUPABASE_DB_URL=postgresql://postgres:[email protected]:54322/postgres?options=-c%20search_path%3D$WORKTREE_SCHEMA,public
extra_hosts:
- "host.docker.internal:host-gateway"
web-dev:
environment:
- DATABASE_SCHEMA=$WORKTREE_SCHEMA
extra_hosts:
- "host.docker.internal:host-gateway"
EOF
echo -e "${YELLOW} Note: docker-compose will automatically use docker-compose.override.yml${NC}"
else
echo -e "${YELLOW}⚠️ Not in a worktree - set DATABASE_SCHEMA env var manually for docker-compose${NC}"
fi
# Create a migration runner script
echo -e "${GREEN}πŸ“„ Creating migration runner script${NC}"
cat > run-migrations.sh << 'EOF'
#!/bin/bash
set -e
source .env.worktree
echo "Running migrations for schema: $WORKTREE_SCHEMA"
# Run migrations with the schema-specific search path
cd packages/supabase
for migration in supabase/migrations/*.sql; do
if [ -f "$migration" ]; then
echo "Running: $(basename $migration)"
psql "$DATABASE_URL" -f "$migration"
fi
done
cd ../..
echo "βœ… Migrations completed"
EOF
chmod +x run-migrations.sh
# Create a seed data script
echo -e "${GREEN}πŸ“Š Creating seed data script${NC}"
cat > seed-worktree.sh << 'EOF'
#!/bin/bash
set -e
source .env.worktree
echo "Seeding data for schema: $WORKTREE_SCHEMA"
# Use the schema-aware seed generator
if [ -f "./scripts/generate-schema-seed.sh" ]; then
./scripts/generate-schema-seed.sh
else
echo "⚠️ Schema-aware seed script not found"
echo " Falling back to standard seed files..."
# Fallback: Run seed files with schema context
for seed_file in packages/supabase/supabase/seeds/*.sql; do
if [ -f "$seed_file" ]; then
echo "Running: $(basename $seed_file)"
psql "$DATABASE_URL" -c "SET search_path TO $WORKTREE_SCHEMA;" -f "$seed_file"
fi
done
fi
echo "βœ… Seed data loaded"
EOF
chmod +x seed-worktree.sh
# Create test runner script
echo -e "${GREEN}πŸ§ͺ Creating test runner script${NC}"
cat > test-worktree.sh << 'EOF'
#!/bin/bash
set -e
source .env.worktree
echo "Running tests for worktree: $WORKTREE_NAME (schema: $WORKTREE_SCHEMA)"
# Export environment variables for tests
export DATABASE_SCHEMA=$WORKTREE_SCHEMA
export DATABASE_URL=$DATABASE_URL
# Run different test suites
case "${1:-all}" in
integration)
echo "Running integration tests..."
cd apps/web && pnpm test:integration
;;
e2e)
echo "Running e2e tests..."
cd apps/web && pnpm test:e2e
;;
unit)
echo "Running unit tests..."
pnpm test
;;
all)
echo "Running all tests..."
pnpm test
cd apps/web && pnpm test:integration
;;
*)
echo "Usage: ./test-worktree.sh [integration|e2e|unit|all]"
exit 1
;;
esac
EOF
chmod +x test-worktree.sh
# Create convenience scripts
echo -e "${GREEN}πŸ”§ Creating convenience scripts${NC}"
# Docker compose runner that uses environment variables
cat > dev-worktree.sh << 'EOF'
#!/bin/bash
source .env.worktree
# docker-compose automatically uses docker-compose.override.yml if it exists
docker-compose --profile dev up "$@"
EOF
chmod +x dev-worktree.sh
# Create a cleanup script for when done with worktree
cat > cleanup-worktree.sh << 'EOF'
#!/bin/bash
set -e
source .env.worktree
echo "Cleaning up worktree: $WORKTREE_NAME (schema: $WORKTREE_SCHEMA)"
# Stop any running containers
docker-compose down 2>/dev/null || true
# Drop the schema (with CASCADE to remove all objects)
read -p "Drop schema $WORKTREE_SCHEMA? This will delete all data! (y/N): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
psql postgresql://postgres:postgres@localhost:54322/postgres -c "DROP SCHEMA IF EXISTS $WORKTREE_SCHEMA CASCADE;"
echo "βœ… Schema dropped"
fi
# Remove generated files
rm -f .env.worktree docker-compose.override.yml run-migrations.sh seed-worktree.sh test-worktree.sh dev-worktree.sh cleanup-worktree.sh
echo "βœ… Cleanup complete"
EOF
chmod +x cleanup-worktree.sh
echo -e "${GREEN}βœ… Worktree setup complete!${NC}"
echo ""
echo -e "Next steps:"
echo -e "1. Run migrations: ${YELLOW}./run-migrations.sh${NC}"
echo -e "2. Seed data: ${YELLOW}./seed-worktree.sh${NC}"
echo -e "3. Start development:"
echo -e " - Docker: ${YELLOW}./dev-worktree.sh${NC} (or just ${YELLOW}docker-compose --profile dev up${NC})"
echo -e " - Without Docker: ${YELLOW}source .env.worktree && pnpm dev${NC}"
echo -e "4. Run tests: ${YELLOW}./test-worktree.sh [integration|e2e|unit|all]${NC}"
echo -e "5. When done: ${YELLOW}./cleanup-worktree.sh${NC}"
echo ""
echo -e "Schema: ${GREEN}$WORKTREE_SCHEMA${NC}"
echo -e "Database: ${GREEN}postgresql://localhost:54322${NC}"
# Add gitignore entries if not already present
if ! grep -q ".env.worktree" .gitignore 2>/dev/null; then
echo -e "\n# Worktree specific files" >> .gitignore
echo ".env.worktree" >> .gitignore
echo "docker-compose.override.yml" >> .gitignore
echo "run-migrations.sh" >> .gitignore
echo "seed-worktree.sh" >> .gitignore
echo "test-worktree.sh" >> .gitignore
echo "dev-worktree.sh" >> .gitignore
echo "cleanup-worktree.sh" >> .gitignore
echo -e "${GREEN}πŸ“ Added worktree files to .gitignore${NC}"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment