migrate.ts 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. #!/usr/bin/env tsx
  2. import Database from 'better-sqlite3';
  3. import fs from 'fs';
  4. import path from 'path';
  5. import { MigrationRunner } from '../apps/service/src/migration-runner';
  6. const args = process.argv.slice(2);
  7. const command = args[0];
  8. if (!command) {
  9. console.log('Usage: migrate <command>');
  10. console.log('Commands:');
  11. console.log(' up - Apply all pending migrations');
  12. console.log(' create <name> - Create a new migration file');
  13. console.log(' status - Show migration status');
  14. process.exit(1);
  15. }
  16. // Find project root
  17. let projectRoot = process.cwd();
  18. while (projectRoot !== path.dirname(projectRoot)) {
  19. if (fs.existsSync(path.join(projectRoot, 'package.json'))) {
  20. try {
  21. const pkg = JSON.parse(
  22. fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf-8'),
  23. );
  24. if (pkg.name === 'watch-finished-turbo') {
  25. break;
  26. }
  27. } catch (e) {
  28. // ignore
  29. }
  30. }
  31. projectRoot = path.dirname(projectRoot);
  32. }
  33. const dbPath = path.resolve(projectRoot, 'data/database.db');
  34. const migrationsDir = path.resolve(projectRoot, 'data/migrations');
  35. // Ensure directories exist
  36. if (!fs.existsSync(path.dirname(dbPath))) {
  37. fs.mkdirSync(path.dirname(dbPath), { recursive: true });
  38. }
  39. if (!fs.existsSync(migrationsDir)) {
  40. fs.mkdirSync(migrationsDir, { recursive: true });
  41. }
  42. const db = new Database(dbPath);
  43. const runner = new MigrationRunner(db, migrationsDir);
  44. try {
  45. switch (command) {
  46. case 'up':
  47. runner.applyPendingMigrations();
  48. break;
  49. case 'create':
  50. const name = args[1];
  51. if (!name) {
  52. console.error('Migration name is required');
  53. process.exit(1);
  54. }
  55. runner.createMigration(name);
  56. break;
  57. case 'status':
  58. runner.init();
  59. const applied = runner.getAppliedMigrations();
  60. const available = runner.getAvailableMigrations();
  61. const pending = available.filter(m => !applied.includes(m));
  62. console.log('Migration Status:');
  63. console.log(`Applied: ${applied.length}`);
  64. console.log(`Available: ${available.length}`);
  65. console.log(`Pending: ${pending.length}`);
  66. if (pending.length > 0) {
  67. console.log('\nPending migrations:');
  68. pending.forEach(m => console.log(` - ${m}`));
  69. }
  70. break;
  71. default:
  72. console.error(`Unknown command: ${command}`);
  73. process.exit(1);
  74. }
  75. } finally {
  76. db.close();
  77. }