Created
June 4, 2025 16:56
-
-
Save jamesaphoenix/b1717507a3cc914086e2a40316736d80 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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}" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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}" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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