#!/usr/bin/env tsx /** * Run database migrations for a feature and auto-generate schema snapshot * * Usage: * pnpm db:migrate:run * pnpm db:migrate:run analytics * * This script: * 1. Runs TypeORM migrations for the feature * 2. Auto-generates schema snapshot (schema.sql) * 3. Stages the snapshot file for commit */ import { join } from 'node:path'; import { existsSync } from 'node:fs'; import { exec } from 'node:child_process'; import { promisify } from 'node:util'; import { homedir } from 'node:os'; import { PATHS } from '../../configs/paths'; const execAsync = promisify(exec); // Bun binary location (for spawned processes) const BUN_BIN_DIR = join(homedir(), '.bun/bin'); const PATH_WITH_BUN = `${BUN_BIN_DIR}:${process.env.PATH}`; async function main() { const featureName = process.argv[2]; if (!featureName) { console.error('❌ Usage: pnpm db:migrate:run '); console.error(' Example: pnpm db:migrate:run analytics'); process.exit(1); } const featurePath = join(PATHS.features, featureName); const backendApiPath = join(featurePath, 'backend-api'); const dataSourcePath = join(backendApiPath, 'src', 'data-source.ts'); // Check if feature exists if (!existsSync(featurePath)) { console.error(`❌ Feature not found: ${featureName}`); console.error(` Path: ${featurePath}`); process.exit(1); } // Check if backend-api exists if (!existsSync(backendApiPath)) { console.error(`❌ Backend API not found for feature: ${featureName}`); console.error(` Path: ${backendApiPath}`); process.exit(1); } // Check if data-source.ts exists if (!existsSync(dataSourcePath)) { console.error(`❌ TypeORM data source not found: ${dataSourcePath}`); console.error(` This feature may not use TypeORM migrations.`); process.exit(1); } console.log(`🚀 Running migrations for feature: ${featureName}`); console.log(''); // Step 1: Run migrations console.log('📦 Step 1/3: Running TypeORM migrations...'); try { const { stdout, stderr } = await execAsync('bun run migration:run', { cwd: backendApiPath, env: { ...process.env, PATH: PATH_WITH_BUN }, }); if (stdout) console.log(stdout); if (stderr && !stderr.includes('NOTICE')) console.warn(stderr); console.log(' ✅ Migrations completed'); } catch (error: unknown) { const message = error instanceof Error ? error.message : String(error); console.error(' ❌ Migration failed:', message); if (error && typeof error === 'object') { if ('stdout' in error && error.stdout) console.error(error.stdout); if ('stderr' in error && error.stderr) console.error(error.stderr); } process.exit(1); } console.log(''); // Step 2: Generate schema snapshot console.log('📄 Step 2/3: Generating schema snapshot...'); try { const { stdout, stderr } = await execAsync(`bun run db:snapshot ${featureName}`, { cwd: process.cwd(), env: { ...process.env, PATH: PATH_WITH_BUN }, }); if (stdout) console.log(stdout); if (stderr) console.warn(stderr); } catch (error: unknown) { const message = error instanceof Error ? error.message : String(error); console.error(' ❌ Snapshot generation failed:', message); console.error(' Run manually: bun run db:snapshot', featureName); process.exit(1); } console.log(''); // Step 3: Stage schema.sql for commit console.log('📝 Step 3/3: Staging schema.sql for commit...'); try { const schemaPath = `codebase/features/${featureName}/database/schema.sql`; await execAsync(`git add ${schemaPath}`, { cwd: process.cwd(), }); console.log(` ✅ Staged: ${schemaPath}`); } catch (error: unknown) { console.warn(' ⚠️ Could not stage schema.sql (not a git repo?)'); } console.log(''); console.log(`✅ Migration + snapshot complete for ${featureName}`); console.log(''); console.log('Next steps:'); console.log(` git add codebase/features/${featureName}/backend-api/src/migrations/`); console.log(` git commit -m "feat(${featureName}): "`); } main().catch((error) => { console.error('❌ Fatal error:', error); process.exit(1); });