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. }