@databricks-solutions/lakebase-scm-utils 0.2.51 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/dist/scripts/index.cjs +2632 -2120
  2. package/dist/scripts/index.d.cts +1 -1
  3. package/dist/scripts/index.d.ts +1 -1
  4. package/dist/scripts/index.js +676 -174
  5. package/dist/scripts/lakebase/ci-resolve-branch.cli.cjs +2460 -2030
  6. package/dist/scripts/lakebase/ci-resolve-branch.cli.js +508 -81
  7. package/dist/scripts/lakebase/collapse-heads.cli.cjs +2429 -2007
  8. package/dist/scripts/lakebase/collapse-heads.cli.js +477 -58
  9. package/dist/scripts/lakebase/detect-language.cli.cjs +2441 -2016
  10. package/dist/scripts/lakebase/detect-language.cli.js +489 -67
  11. package/dist/scripts/lakebase/doctor.cli.cjs +2484 -2040
  12. package/dist/scripts/lakebase/doctor.cli.js +532 -91
  13. package/dist/scripts/lakebase/get-connection.cli.cjs +2 -2
  14. package/dist/scripts/lakebase/get-connection.cli.js +2 -2
  15. package/dist/scripts/lakebase/index.cjs +2629 -2117
  16. package/dist/scripts/lakebase/index.d.cts +59 -10
  17. package/dist/scripts/lakebase/index.d.ts +59 -10
  18. package/dist/scripts/lakebase/index.js +670 -168
  19. package/dist/scripts/lakebase/infra-runner.cli.cjs +2435 -2013
  20. package/dist/scripts/lakebase/infra-runner.cli.js +483 -64
  21. package/dist/scripts/lakebase/new-migration.cli.cjs +2429 -2007
  22. package/dist/scripts/lakebase/new-migration.cli.js +477 -58
  23. package/dist/scripts/lakebase/schema-diff.cli.cjs +2 -2
  24. package/dist/scripts/lakebase/schema-diff.cli.js +2 -2
  25. package/dist/scripts/lakebase/schema-migrate.cli.cjs +2461 -2029
  26. package/dist/scripts/lakebase/schema-migrate.cli.js +509 -80
  27. package/dist/scripts/lakebase/scm-claim-feature.cli.cjs +2460 -2030
  28. package/dist/scripts/lakebase/scm-claim-feature.cli.js +508 -81
  29. package/dist/scripts/lakebase/scm-doctor.cli.cjs +2486 -2056
  30. package/dist/scripts/lakebase/scm-doctor.cli.js +537 -107
  31. package/dist/scripts/lakebase/scm-merge.cli.cjs +2443 -2021
  32. package/dist/scripts/lakebase/scm-merge.cli.js +494 -72
  33. package/dist/scripts/lakebase/scm-reconcile-tier.cli.cjs +2464 -2034
  34. package/dist/scripts/lakebase/scm-reconcile-tier.cli.js +511 -84
  35. package/dist/scripts/util/index.cjs +2 -2
  36. package/dist/scripts/util/index.js +2 -2
  37. package/package.json +1 -1
  38. package/scripts/lakebase/adapters/efcore-adapter.ts +158 -0
  39. package/scripts/lakebase/adapters/flyway-adapter.ts +8 -5
  40. package/scripts/lakebase/collapse-heads.cli.ts +1 -1
  41. package/scripts/lakebase/create-project.ts +6 -2
  42. package/scripts/lakebase/detect-language.cli.ts +2 -2
  43. package/scripts/lakebase/doctor.ts +29 -0
  44. package/scripts/lakebase/dotnet-project.ts +112 -0
  45. package/scripts/lakebase/index.ts +1 -0
  46. package/scripts/lakebase/migration-layout.ts +43 -2
  47. package/scripts/lakebase/new-migration.cli.ts +1 -1
  48. package/scripts/lakebase/scaffold-language.ts +23 -2
  49. package/scripts/lakebase/scaffold.ts +4 -1
  50. package/scripts/lakebase/schema-migrate-runners/efcore.ts +399 -0
  51. package/scripts/lakebase/schema-migrate-runners/flyway.ts +6 -4
  52. package/scripts/lakebase/schema-migrate.cli.ts +3 -1
  53. package/scripts/lakebase/schema-migrate.ts +24 -10
  54. package/templates/project/common/.github/workflows/merge.yml +13 -1
  55. package/templates/project/common/.github/workflows/pr.yml +25 -5
  56. package/templates/project/common/scripts/flyway-migrate.sh +54 -1
  57. package/templates/project/common/scripts/prepare-schema-diff.sh +5 -0
  58. package/templates/project/common/scripts/run-dev.sh +10 -1
  59. package/templates/project/common/scripts/run-tests.sh +6 -1
  60. package/templates/project/dotnet/.gitignore.extra +8 -0
  61. package/templates/project/dotnet/App.sln +56 -0
  62. package/templates/project/dotnet/Makefile +13 -0
  63. package/templates/project/dotnet/global.json +6 -0
  64. package/templates/project/dotnet/src/App/Data/AppDbContext.cs +12 -0
  65. package/templates/project/dotnet/src/App/Lakebase/DotEnv.cs +48 -0
  66. package/templates/project/dotnet/src/App/Lakebase/LakebaseConnection.cs +225 -0
  67. package/templates/project/dotnet/src/App/Program.cs +34 -0
  68. package/templates/project/dotnet/src/App/appsettings.json +9 -0
  69. package/templates/project/dotnet/tests/App.Tests/App.Tests.csproj +25 -0
  70. package/templates/project/dotnet/tests/App.Tests/HealthTests.cs +18 -0
  71. package/templates/project/dotnet/tests/App.Tests/OpenApiTests.cs +23 -0
  72. package/templates/project/dotnet-efcore/.config/dotnet-tools.json +13 -0
  73. package/templates/project/dotnet-efcore/src/App/App.csproj +22 -0
  74. package/templates/project/dotnet-efcore/src/App/Data/DesignTimeDbContextFactory.cs +24 -0
  75. package/templates/project/dotnet-efcore/src/App/Migrations/README.md +23 -0
  76. package/templates/project/dotnet-flyway/db/migrations/README.md +23 -0
  77. package/templates/project/dotnet-flyway/src/App/App.csproj +17 -0
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // scripts/lakebase/ci-resolve-branch.cli.ts
4
- import * as fs11 from "fs";
4
+ import * as fs13 from "fs";
5
5
 
6
6
  // scripts/lakebase/databricks-cli.ts
7
7
  import { execFile, execFileSync as execFileSync2 } from "child_process";
@@ -275,9 +275,9 @@ function asBranchUid(s) {
275
275
  }
276
276
  return s;
277
277
  }
278
- function branchNameFromResourcePath(path11) {
279
- if (!path11.includes("/branches/")) return null;
280
- const leaf = path11.split("/branches/").pop();
278
+ function branchNameFromResourcePath(path14) {
279
+ if (!path14.includes("/branches/")) return null;
280
+ const leaf = path14.split("/branches/").pop();
281
281
  if (!leaf) return null;
282
282
  try {
283
283
  return asBranchName(leaf);
@@ -697,8 +697,8 @@ function dbcli4(args, host) {
697
697
  }
698
698
 
699
699
  // scripts/lakebase/schema-migrate.ts
700
- import * as fs10 from "fs";
701
- import * as path10 from "path";
700
+ import * as fs12 from "fs";
701
+ import * as path13 from "path";
702
702
 
703
703
  // scripts/lakebase/get-connection.ts
704
704
  import { createLakebasePool } from "@databricks/lakebase";
@@ -721,8 +721,8 @@ var PKG_NAME = "@databricks-solutions/lakebase-scm-utils";
721
721
  var cached;
722
722
  function substrateSelfVersion() {
723
723
  if (cached !== void 0) return cached;
724
- if ("0.2.51".length > 0) {
725
- cached = "0.2.51";
724
+ if ("0.3.0".length > 0) {
725
+ cached = "0.3.0";
726
726
  return cached;
727
727
  }
728
728
  cached = "unknown";
@@ -831,23 +831,88 @@ function dbcli5(args) {
831
831
  }
832
832
 
833
833
  // scripts/lakebase/migration-layout.ts
834
+ import * as fs5 from "fs";
835
+ import * as path4 from "path";
836
+
837
+ // scripts/lakebase/dotnet-project.ts
834
838
  import * as fs4 from "fs";
835
839
  import * as path3 from "path";
840
+ var EF_DESIGN_PACKAGE = "Microsoft.EntityFrameworkCore.Design";
841
+ var DOTNET_FLYWAY_DIR = path3.join("db", "migrations");
842
+ var SKIP_DIRS = /* @__PURE__ */ new Set(["bin", "obj", "node_modules", ".git", "client", "tests", "test"]);
843
+ function safeReaddir(dir) {
844
+ try {
845
+ return fs4.readdirSync(dir, { withFileTypes: true });
846
+ } catch {
847
+ return [];
848
+ }
849
+ }
850
+ function hasDotnetMarker(dir) {
851
+ return safeReaddir(dir).some(
852
+ (e) => e.isFile() && /\.(sln|slnx|csproj)$/i.test(e.name)
853
+ );
854
+ }
855
+ function findCsprojFiles(projectDir) {
856
+ const found = [];
857
+ const walk = (dir, depth) => {
858
+ for (const e of safeReaddir(dir)) {
859
+ const full = path3.join(dir, e.name);
860
+ if (e.isFile() && e.name.toLowerCase().endsWith(".csproj")) {
861
+ found.push(full);
862
+ } else if (e.isDirectory() && depth < 2 && !SKIP_DIRS.has(e.name.toLowerCase()) && !e.name.startsWith(".")) {
863
+ walk(full, depth + 1);
864
+ }
865
+ }
866
+ };
867
+ walk(projectDir, 0);
868
+ return found.sort();
869
+ }
870
+ function findEfCoreProject(projectDir) {
871
+ for (const csproj of findCsprojFiles(projectDir)) {
872
+ let content = "";
873
+ try {
874
+ content = fs4.readFileSync(csproj, "utf-8");
875
+ } catch {
876
+ continue;
877
+ }
878
+ if (content.includes(EF_DESIGN_PACKAGE)) {
879
+ const dir = path3.dirname(csproj);
880
+ return { csproj, projectDir: dir, migrationsDir: path3.join(dir, "Migrations") };
881
+ }
882
+ }
883
+ return null;
884
+ }
885
+ function isDotnetProject(projectDir) {
886
+ return hasDotnetMarker(projectDir);
887
+ }
888
+ function isDotnetFlywayProject(projectDir) {
889
+ return isDotnetProject(projectDir) && fs4.existsSync(path3.join(projectDir, DOTNET_FLYWAY_DIR));
890
+ }
891
+ var MAVEN_FLYWAY_DIR = path3.join("src", "main", "resources", "db", "migration");
892
+ function flywayMigrationsDir(projectDir) {
893
+ const maven = path3.join(projectDir, MAVEN_FLYWAY_DIR);
894
+ if (fs4.existsSync(maven) || fs4.existsSync(path3.join(projectDir, "pom.xml"))) return maven;
895
+ if (isDotnetFlywayProject(projectDir)) return path3.join(projectDir, DOTNET_FLYWAY_DIR);
896
+ return maven;
897
+ }
898
+
899
+ // scripts/lakebase/migration-layout.ts
836
900
  var MIGRATION_LANGUAGES = [
837
901
  "java",
838
902
  "kotlin",
839
903
  "python",
840
904
  "nodejs",
905
+ "dotnet",
841
906
  "unknown"
842
907
  ];
843
908
  function detectLanguageAt(dir) {
844
- if (fs4.existsSync(path3.join(dir, "pom.xml"))) {
845
- const kotlinDir = path3.join(dir, "src", "main", "kotlin");
846
- if (fs4.existsSync(kotlinDir)) {
909
+ if (fs5.existsSync(path4.join(dir, "pom.xml"))) {
910
+ const kotlinDir = path4.join(dir, "src", "main", "kotlin");
911
+ if (fs5.existsSync(kotlinDir)) {
847
912
  return "kotlin";
848
913
  }
849
914
  try {
850
- const pom = fs4.readFileSync(path3.join(dir, "pom.xml"), "utf-8");
915
+ const pom = fs5.readFileSync(path4.join(dir, "pom.xml"), "utf-8");
851
916
  if (pom.includes("kotlin-maven-plugin")) {
852
917
  return "kotlin";
853
918
  }
@@ -855,10 +920,13 @@ function detectLanguageAt(dir) {
855
920
  }
856
921
  return "java";
857
922
  }
858
- if (fs4.existsSync(path3.join(dir, "pyproject.toml")) || fs4.existsSync(path3.join(dir, "requirements.txt")) || fs4.existsSync(path3.join(dir, "alembic.ini"))) {
923
+ if (fs5.existsSync(path4.join(dir, "pyproject.toml")) || fs5.existsSync(path4.join(dir, "requirements.txt")) || fs5.existsSync(path4.join(dir, "alembic.ini"))) {
859
924
  return "python";
860
925
  }
861
- if (fs4.existsSync(path3.join(dir, "package.json"))) {
926
+ if (hasDotnetMarker(dir)) {
927
+ return "dotnet";
928
+ }
929
+ if (fs5.existsSync(path4.join(dir, "package.json"))) {
862
930
  return "nodejs";
863
931
  }
864
932
  return "unknown";
@@ -879,14 +947,14 @@ function resolveMigrationLanguage(projectDir, configuredMigrationPath, override)
879
947
  if (!rel) {
880
948
  return "unknown";
881
949
  }
882
- const rootResolved = path3.resolve(projectDir);
883
- let dir = path3.resolve(projectDir, rel);
884
- while (dir === rootResolved || dir.startsWith(rootResolved + path3.sep)) {
950
+ const rootResolved = path4.resolve(projectDir);
951
+ let dir = path4.resolve(projectDir, rel);
952
+ while (dir === rootResolved || dir.startsWith(rootResolved + path4.sep)) {
885
953
  const lang = detectLanguageAt(dir);
886
954
  if (lang !== "unknown") {
887
955
  return lang;
888
956
  }
889
- const parent = path3.dirname(dir);
957
+ const parent = path4.dirname(dir);
890
958
  if (parent === dir) {
891
959
  break;
892
960
  }
@@ -896,21 +964,21 @@ function resolveMigrationLanguage(projectDir, configuredMigrationPath, override)
896
964
  }
897
965
 
898
966
  // scripts/lakebase/adapters/alembic-adapter.ts
899
- import * as fs6 from "fs";
900
- import * as path5 from "path";
967
+ import * as fs7 from "fs";
968
+ import * as path6 from "path";
901
969
 
902
970
  // scripts/lakebase/schema-migrate-runners/alembic.ts
903
971
  import { spawn } from "child_process";
904
- import * as fs5 from "fs";
905
- import * as path4 from "path";
972
+ import * as fs6 from "fs";
973
+ import * as path5 from "path";
906
974
  function resolveAlembicBin(projectDir) {
907
975
  const candidates = [
908
- path4.join(projectDir, ".venv", "bin", "alembic"),
909
- path4.join(projectDir, "venv", "bin", "alembic")
976
+ path5.join(projectDir, ".venv", "bin", "alembic"),
977
+ path5.join(projectDir, "venv", "bin", "alembic")
910
978
  ];
911
979
  for (const candidate of candidates) {
912
980
  try {
913
- if (fs5.existsSync(candidate)) return candidate;
981
+ if (fs6.existsSync(candidate)) return candidate;
914
982
  } catch {
915
983
  }
916
984
  }
@@ -920,7 +988,7 @@ function spawnAlembic(projectDir, args, dsn) {
920
988
  return new Promise((resolve2, reject) => {
921
989
  const bin = resolveAlembicBin(projectDir);
922
990
  const env = { ...process.env };
923
- env.PYTHONPATH = [projectDir, process.env.PYTHONPATH].filter(Boolean).join(path4.delimiter);
991
+ env.PYTHONPATH = [projectDir, process.env.PYTHONPATH].filter(Boolean).join(path5.delimiter);
924
992
  if (dsn) env.DATABASE_URL = dsn;
925
993
  const child = spawn(bin, args, {
926
994
  cwd: projectDir,
@@ -968,10 +1036,10 @@ async function createAlembicRevision(opts) {
968
1036
  const m = stdout.match(/Generating\s+(\S+\.py)/);
969
1037
  if (m) return m[1].trim();
970
1038
  for (const rel of ["migrations/versions", "alembic/versions"]) {
971
- const dir = path4.join(opts.projectDir, rel);
972
- if (!fs5.existsSync(dir)) continue;
973
- const hit = fs5.readdirSync(dir).find((f) => f.startsWith(`${opts.revId}_`) && f.endsWith(".py"));
974
- if (hit) return path4.join(dir, hit);
1039
+ const dir = path5.join(opts.projectDir, rel);
1040
+ if (!fs6.existsSync(dir)) continue;
1041
+ const hit = fs6.readdirSync(dir).find((f) => f.startsWith(`${opts.revId}_`) && f.endsWith(".py"));
1042
+ if (hit) return path5.join(dir, hit);
975
1043
  }
976
1044
  throw new SchemaMigrationError(
977
1045
  `alembic revision succeeded but the created file could not be located.
@@ -1105,15 +1173,15 @@ async function buildDsn(args) {
1105
1173
  }
1106
1174
  function findVersionsDir(projectDir) {
1107
1175
  const candidates = [
1108
- path5.join(projectDir, "migrations", "versions"),
1109
- path5.join(projectDir, "alembic", "versions")
1176
+ path6.join(projectDir, "migrations", "versions"),
1177
+ path6.join(projectDir, "alembic", "versions")
1110
1178
  ];
1111
- return candidates.find((p) => fs6.existsSync(p));
1179
+ return candidates.find((p) => fs7.existsSync(p));
1112
1180
  }
1113
1181
  function listAlembicFiles(projectDir) {
1114
1182
  const dir = findVersionsDir(projectDir);
1115
1183
  if (!dir) return [];
1116
- const files = fs6.readdirSync(dir).filter((f) => f.endsWith(".py") && !f.startsWith("__"));
1184
+ const files = fs7.readdirSync(dir).filter((f) => f.endsWith(".py") && !f.startsWith("__"));
1117
1185
  return files.map((filename) => {
1118
1186
  const stem = filename.replace(/\.py$/, "");
1119
1187
  const sep2 = stem.indexOf("_");
@@ -1138,9 +1206,9 @@ var AlembicAdapter = {
1138
1206
  * here. Callers can still force-select via project.yaml#migration_tool.
1139
1207
  */
1140
1208
  detect(projectDir) {
1141
- if (fs6.existsSync(path5.join(projectDir, "alembic.ini"))) return true;
1142
- if (fs6.existsSync(path5.join(projectDir, "migrations", "env.py"))) return true;
1143
- if (fs6.existsSync(path5.join(projectDir, "alembic", "env.py"))) return true;
1209
+ if (fs7.existsSync(path6.join(projectDir, "alembic.ini"))) return true;
1210
+ if (fs7.existsSync(path6.join(projectDir, "migrations", "env.py"))) return true;
1211
+ if (fs7.existsSync(path6.join(projectDir, "alembic", "env.py"))) return true;
1144
1212
  return false;
1145
1213
  },
1146
1214
  async apply(args) {
@@ -1246,7 +1314,7 @@ var AlembicAdapter = {
1246
1314
  autogenerate: !!args.autogenerate,
1247
1315
  dsn
1248
1316
  });
1249
- return { status: "ok", version: revId, filename: path5.basename(created), path: created };
1317
+ return { status: "ok", version: revId, filename: path6.basename(created), path: created };
1250
1318
  } catch (err) {
1251
1319
  return {
1252
1320
  status: "error",
@@ -1263,7 +1331,7 @@ var AlembicAdapter = {
1263
1331
  if (heads.length <= 1) return { status: "noop", headsBefore: heads };
1264
1332
  if (args.dryRun) return { status: "ok", headsBefore: heads };
1265
1333
  const created = await mergeAlembicHeads(args.projectDir, args.message ?? "merge heads");
1266
- const mergeRevision = path5.basename(created).replace(/\.py$/, "").split("_")[0];
1334
+ const mergeRevision = path6.basename(created).replace(/\.py$/, "").split("_")[0];
1267
1335
  return { status: "ok", headsBefore: heads, mergeRevision, path: created };
1268
1336
  } catch (err) {
1269
1337
  return {
@@ -1277,12 +1345,12 @@ var AlembicAdapter = {
1277
1345
  registerSchemaMigrationAdapter(AlembicAdapter);
1278
1346
 
1279
1347
  // scripts/lakebase/adapters/flyway-adapter.ts
1280
- import * as fs7 from "fs";
1281
- import * as path7 from "path";
1348
+ import * as fs8 from "fs";
1349
+ import * as path8 from "path";
1282
1350
 
1283
1351
  // scripts/lakebase/schema-migrate-runners/flyway.ts
1284
1352
  import { spawn as spawn2 } from "child_process";
1285
- import * as path6 from "path";
1353
+ import * as path7 from "path";
1286
1354
  function dsnToFlywayEnv(dsn) {
1287
1355
  const u = new URL(dsn);
1288
1356
  const user = decodeURIComponent(u.username);
@@ -1292,7 +1360,7 @@ function dsnToFlywayEnv(dsn) {
1292
1360
  return { url, user, password };
1293
1361
  }
1294
1362
  function migrationsLocation(projectDir) {
1295
- return `filesystem:${path6.join(projectDir, "src", "main", "resources", "db", "migration")}`;
1363
+ return `filesystem:${flywayMigrationsDir(projectDir)}`;
1296
1364
  }
1297
1365
  function runFlyway(ctx, args) {
1298
1366
  const { url, user, password } = dsnToFlywayEnv(ctx.dsn);
@@ -1393,7 +1461,7 @@ async function statusFlyway(ctx) {
1393
1461
  if (state === "SUCCESS" || state === "BASELINE") {
1394
1462
  current = m.version;
1395
1463
  } else if (state === "PENDING") {
1396
- const filename = m.filepath ? path6.basename(m.filepath) : `V${m.version}__migration.sql`;
1464
+ const filename = m.filepath ? path7.basename(m.filepath) : `V${m.version}__migration.sql`;
1397
1465
  pending.push({
1398
1466
  version: m.version,
1399
1467
  filename,
@@ -1416,9 +1484,9 @@ async function buildDsn2(args) {
1416
1484
  return result.url;
1417
1485
  }
1418
1486
  function listFlywayFiles(projectDir) {
1419
- const dir = path7.join(projectDir, "src", "main", "resources", "db", "migration");
1420
- if (!fs7.existsSync(dir)) return [];
1421
- const files = fs7.readdirSync(dir).filter((f) => /^V\d+(\.\d+)*__.+\.sql$/.test(f));
1487
+ const dir = flywayMigrationsDir(projectDir);
1488
+ if (!fs8.existsSync(dir)) return [];
1489
+ const files = fs8.readdirSync(dir).filter((f) => /^V\d+(\.\d+)*__.+\.sql$/.test(f));
1422
1490
  return files.map((filename) => {
1423
1491
  const m = filename.match(/^V(\d+(?:\.\d+)*)__(.+)\.sql$/);
1424
1492
  const version = m[1];
@@ -1439,9 +1507,9 @@ function versionCompare(a, b) {
1439
1507
  }
1440
1508
  var FlywayAdapter = {
1441
1509
  id: "flyway",
1442
- languages: ["java", "kotlin"],
1510
+ languages: ["java", "kotlin", "dotnet"],
1443
1511
  detect(projectDir) {
1444
- return fs7.existsSync(path7.join(projectDir, "pom.xml"));
1512
+ return fs8.existsSync(path8.join(projectDir, "pom.xml")) || isDotnetFlywayProject(projectDir);
1445
1513
  },
1446
1514
  async apply(args) {
1447
1515
  const dsn = await buildDsn2(args);
@@ -1499,14 +1567,14 @@ var FlywayAdapter = {
1499
1567
  // optional-protocol shape makes this additive.
1500
1568
  async newMigration(args) {
1501
1569
  try {
1502
- const dir = path7.join(args.projectDir, "src", "main", "resources", "db", "migration");
1503
- fs7.mkdirSync(dir, { recursive: true });
1570
+ const dir = flywayMigrationsDir(args.projectDir);
1571
+ fs8.mkdirSync(dir, { recursive: true });
1504
1572
  const version = migrationTimestamp();
1505
1573
  const slug = migrationSlug2(args.slug);
1506
1574
  const filename = `V${version}__${slug}.sql`;
1507
- const full = path7.join(dir, filename);
1508
- if (fs7.existsSync(full)) throw new Error(`${filename} already exists`);
1509
- fs7.writeFileSync(
1575
+ const full = path8.join(dir, filename);
1576
+ if (fs8.existsSync(full)) throw new Error(`${filename} already exists`);
1577
+ fs8.writeFileSync(
1510
1578
  full,
1511
1579
  `-- V${version}: ${args.slug}
1512
1580
  -- Flyway migration (write your DDL/DML below).
@@ -1528,18 +1596,18 @@ var FlywayAdapter = {
1528
1596
  registerSchemaMigrationAdapter(FlywayAdapter);
1529
1597
 
1530
1598
  // scripts/lakebase/adapters/knex-adapter.ts
1531
- import * as fs9 from "fs";
1532
- import * as path9 from "path";
1599
+ import * as fs10 from "fs";
1600
+ import * as path10 from "path";
1533
1601
 
1534
1602
  // scripts/lakebase/schema-migrate-runners/knex.ts
1535
1603
  import { spawn as spawn3 } from "child_process";
1536
- import * as fs8 from "fs";
1537
- import * as path8 from "path";
1604
+ import * as fs9 from "fs";
1605
+ import * as path9 from "path";
1538
1606
  var KNEXFILE_VARIANTS = ["knexfile.js", "knexfile.ts", "knexfile.mjs", "knexfile.cjs"];
1539
1607
  function findKnexfile(projectDir) {
1540
1608
  for (const name of KNEXFILE_VARIANTS) {
1541
- const p = path8.join(projectDir, name);
1542
- if (fs8.existsSync(p)) return p;
1609
+ const p = path9.join(projectDir, name);
1610
+ if (fs9.existsSync(p)) return p;
1543
1611
  }
1544
1612
  return void 0;
1545
1613
  }
@@ -1691,9 +1759,9 @@ async function buildDsn3(args) {
1691
1759
  }
1692
1760
  var KNEXFILE_VARIANTS2 = ["knexfile.js", "knexfile.ts", "knexfile.mjs", "knexfile.cjs"];
1693
1761
  function listKnexFiles(projectDir) {
1694
- const dir = path9.join(projectDir, "migrations");
1695
- if (!fs9.existsSync(dir)) return [];
1696
- const files = fs9.readdirSync(dir).filter((f) => (f.endsWith(".js") || f.endsWith(".ts")) && !f.startsWith("."));
1762
+ const dir = path10.join(projectDir, "migrations");
1763
+ if (!fs10.existsSync(dir)) return [];
1764
+ const files = fs10.readdirSync(dir).filter((f) => (f.endsWith(".js") || f.endsWith(".ts")) && !f.startsWith("."));
1697
1765
  return files.map((filename) => {
1698
1766
  const stem = filename.replace(/\.(js|ts)$/, "");
1699
1767
  const m = stem.match(/^(\d{14})_(.+)$/);
@@ -1713,7 +1781,7 @@ var KnexAdapter = {
1713
1781
  * project.yaml#migration_tool.
1714
1782
  */
1715
1783
  detect(projectDir) {
1716
- return KNEXFILE_VARIANTS2.some((name) => fs9.existsSync(path9.join(projectDir, name)));
1784
+ return KNEXFILE_VARIANTS2.some((name) => fs10.existsSync(path10.join(projectDir, name)));
1717
1785
  },
1718
1786
  async apply(args) {
1719
1787
  const dsn = await buildDsn3(args);
@@ -1786,9 +1854,9 @@ var KnexAdapter = {
1786
1854
  async newMigration(args) {
1787
1855
  try {
1788
1856
  const created = await createKnexMigration({ projectDir: args.projectDir, slug: migrationSlug2(args.slug) });
1789
- const stem = path9.basename(created).replace(/\.(js|ts)$/, "");
1857
+ const stem = path10.basename(created).replace(/\.(js|ts)$/, "");
1790
1858
  const version = stem.match(/^(\d{14})_/)?.[1] ?? stem;
1791
- return { status: "ok", version, filename: path9.basename(created), path: created };
1859
+ return { status: "ok", version, filename: path10.basename(created), path: created };
1792
1860
  } catch (err) {
1793
1861
  return {
1794
1862
  status: "error",
@@ -1802,6 +1870,360 @@ var KnexAdapter = {
1802
1870
  };
1803
1871
  registerSchemaMigrationAdapter(KnexAdapter);
1804
1872
 
1873
+ // scripts/lakebase/adapters/efcore-adapter.ts
1874
+ import * as path12 from "path";
1875
+
1876
+ // scripts/lakebase/schema-migrate-runners/efcore.ts
1877
+ import { spawn as spawn4 } from "child_process";
1878
+ import * as fs11 from "fs";
1879
+ import * as path11 from "path";
1880
+ import { Client as Client2 } from "pg";
1881
+ var MIGRATION_ID = /^(\d{14})_(.+)$/;
1882
+ function efVersion(id) {
1883
+ return MIGRATION_ID.exec(id)?.[1] ?? id;
1884
+ }
1885
+ function efDescription(id) {
1886
+ const name = MIGRATION_ID.exec(id)?.[2] ?? id;
1887
+ return name.replace(/_/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2");
1888
+ }
1889
+ function dsnToNpgsql(dsn) {
1890
+ const u = new URL(dsn);
1891
+ const parts = [
1892
+ `Host=${u.hostname}`,
1893
+ `Port=${u.port || "5432"}`,
1894
+ `Database=${decodeURIComponent(u.pathname.replace(/^\//, "")) || "databricks_postgres"}`
1895
+ ];
1896
+ if (u.username) parts.push(`Username=${decodeURIComponent(u.username)}`);
1897
+ if (u.password) parts.push(`Password=${decodeURIComponent(u.password)}`);
1898
+ const sslmode = (u.searchParams.get("sslmode") ?? "require").toLowerCase();
1899
+ const ssl = {
1900
+ disable: "Disable",
1901
+ allow: "Allow",
1902
+ prefer: "Prefer",
1903
+ require: "Require",
1904
+ "verify-ca": "VerifyCA",
1905
+ "verify-full": "VerifyFull"
1906
+ };
1907
+ parts.push(`SSL Mode=${ssl[sslmode] ?? "Require"}`);
1908
+ const app = u.searchParams.get("application_name");
1909
+ if (app) parts.push(`Application Name=${app}`);
1910
+ return parts.map((p) => p.replace(/;/g, "")).join(";");
1911
+ }
1912
+ function parseEfJson(stdout) {
1913
+ const body = stdout.split(/\r?\n/).filter((l) => l.startsWith("data:")).map((l) => l.slice("data:".length)).join("\n").trim();
1914
+ if (!body) {
1915
+ throw new SchemaMigrationError(`dotnet ef returned no JSON payload: ${stdout.slice(0, 400)}`);
1916
+ }
1917
+ try {
1918
+ return JSON.parse(body);
1919
+ } catch (err) {
1920
+ throw new SchemaMigrationError(
1921
+ `dotnet ef JSON parse failed: ${err instanceof Error ? err.message : String(err)}.
1922
+ Body (first 400 chars): ${body.slice(0, 400)}`
1923
+ );
1924
+ }
1925
+ }
1926
+ function requireProject(projectDir) {
1927
+ const project = findEfCoreProject(projectDir);
1928
+ if (!project) {
1929
+ throw new SchemaMigrationError(
1930
+ `No EF Core project found under ${projectDir}. Expected a *.csproj that references ${EF_DESIGN_PACKAGE}.`
1931
+ );
1932
+ }
1933
+ return project;
1934
+ }
1935
+ function spawnDotnet(projectDir, args, env) {
1936
+ return new Promise((resolve2, reject) => {
1937
+ const child = spawn4("dotnet", args, {
1938
+ cwd: projectDir,
1939
+ env: { ...process.env, DOTNET_NOLOGO: "1", DOTNET_CLI_TELEMETRY_OPTOUT: "1", ...env },
1940
+ stdio: ["ignore", "pipe", "pipe"]
1941
+ });
1942
+ let stdout = "";
1943
+ let stderr = "";
1944
+ child.stdout.on("data", (chunk) => {
1945
+ stdout += chunk.toString("utf8");
1946
+ });
1947
+ child.stderr.on("data", (chunk) => {
1948
+ stderr += chunk.toString("utf8");
1949
+ });
1950
+ child.on("error", (err) => {
1951
+ reject(
1952
+ new SchemaMigrationError(
1953
+ `Could not spawn dotnet. Is the .NET SDK installed and on PATH? ${err.message}`,
1954
+ err
1955
+ )
1956
+ );
1957
+ });
1958
+ child.on("close", (code) => {
1959
+ if (code === 0) {
1960
+ resolve2({ stdout, stderr });
1961
+ } else {
1962
+ reject(
1963
+ new SchemaMigrationError(
1964
+ `dotnet ${args.join(" ")} exited with code ${code}.
1965
+ stdout: ${stdout}
1966
+ stderr: ${stderr}`
1967
+ )
1968
+ );
1969
+ }
1970
+ });
1971
+ });
1972
+ }
1973
+ var restored = /* @__PURE__ */ new Set();
1974
+ async function ensureRestored(projectDir, csproj) {
1975
+ if (restored.has(projectDir)) return;
1976
+ if (fs11.existsSync(path11.join(projectDir, ".config", "dotnet-tools.json"))) {
1977
+ await spawnDotnet(projectDir, ["tool", "restore"], {});
1978
+ }
1979
+ await spawnDotnet(projectDir, ["restore", csproj], {});
1980
+ restored.add(projectDir);
1981
+ }
1982
+ async function runEf(projectDir, args, dsn, opts = {}) {
1983
+ const project = requireProject(projectDir);
1984
+ const csproj = path11.relative(projectDir, project.csproj) || project.csproj;
1985
+ await ensureRestored(projectDir, csproj);
1986
+ const full = ["ef", ...args, "--project", csproj, "--prefix-output"];
1987
+ if (opts.noBuild) full.push("--no-build");
1988
+ const env = {};
1989
+ if (dsn) {
1990
+ env.DATABASE_URL = dsn;
1991
+ if (process.env.LAKEBASE_EFCORE_CONNECTION_ARG === "1") full.push("--connection", dsnToNpgsql(dsn));
1992
+ }
1993
+ return spawnDotnet(projectDir, full, env);
1994
+ }
1995
+ async function listEfMigrations(ctx, opts = {}) {
1996
+ const { stdout, stderr } = await runEf(ctx.projectDir, ["migrations", "list", "--json"], ctx.dsn, opts);
1997
+ const entries = parseEfJson(stdout);
1998
+ if (ctx.dsn && entries.some((e) => e.applied === null)) {
1999
+ const warning = stdout.split(/\r?\n/).filter((l) => /^(warn|error):/.test(l)).join("\n");
2000
+ throw new SchemaMigrationError(
2001
+ `dotnet ef could not read migration state from the database (check connectivity and credentials).
2002
+ ${warning || stderr}`.trim()
2003
+ );
2004
+ }
2005
+ return entries;
2006
+ }
2007
+ async function readEfHistory(dsn) {
2008
+ const client = new Client2({ connectionString: dsn, application_name: connectionApplicationName() });
2009
+ await client.connect();
2010
+ try {
2011
+ const exists = await client.query(`SELECT to_regclass('"__EFMigrationsHistory"') IS NOT NULL AS present`);
2012
+ if (!exists.rows[0]?.present) return null;
2013
+ const r = await client.query(`SELECT "MigrationId" FROM "__EFMigrationsHistory" ORDER BY "MigrationId"`);
2014
+ return r.rows.map((row) => row.MigrationId);
2015
+ } finally {
2016
+ await client.end();
2017
+ }
2018
+ }
2019
+ function toApplied(entries) {
2020
+ return entries.map((e) => ({ version: efVersion(e.id), description: efDescription(e.id) }));
2021
+ }
2022
+ async function statusEfcore(ctx) {
2023
+ const entries = await listEfMigrations(ctx);
2024
+ const history = await (ctx.readHistory ?? readEfHistory)(ctx.dsn);
2025
+ const current = history && history.length > 0 ? efVersion(history[history.length - 1]) : void 0;
2026
+ const pending = entries.filter((e) => e.applied !== true).map((e) => ({ version: efVersion(e.id), filename: `${e.id}.cs`, description: efDescription(e.id) }));
2027
+ return { current, pending, tool: "efcore" };
2028
+ }
2029
+ async function applyEfcore(ctx) {
2030
+ const before = new Set((await listEfMigrations(ctx)).filter((e) => e.applied).map((e) => e.id));
2031
+ await runEf(ctx.projectDir, ["database", "update"], ctx.dsn, { noBuild: true });
2032
+ const after = await listEfMigrations(ctx, { noBuild: true });
2033
+ const applied = toApplied(after.filter((e) => e.applied && !before.has(e.id)));
2034
+ return { applied, alreadyAtLatest: applied.length === 0, tool: "efcore" };
2035
+ }
2036
+ function resolveEfTarget(target, applied) {
2037
+ const t = target.trim();
2038
+ if (t === "0") return "0";
2039
+ const rel = /^-(\d+)$/.exec(t);
2040
+ if (rel) {
2041
+ const steps = Number(rel[1]);
2042
+ if (steps < 1) throw new SchemaMigrationError(`Invalid rollback step: ${target}`);
2043
+ const index = applied.length - 1 - steps;
2044
+ if (index < -1) {
2045
+ throw new SchemaMigrationError(`Cannot roll back ${steps} migration(s); only ${applied.length} applied.`);
2046
+ }
2047
+ return index === -1 ? "0" : applied[index].id;
2048
+ }
2049
+ const match = applied.find((e) => e.id === t || efVersion(e.id) === t || e.name === t);
2050
+ if (!match) throw new SchemaMigrationError(`Rollback target '${target}' is not an applied migration.`);
2051
+ return match.id;
2052
+ }
2053
+ async function rollbackEfcore(ctx) {
2054
+ const beforeEntries = (await listEfMigrations(ctx)).filter((e) => e.applied);
2055
+ const targetId = resolveEfTarget(ctx.target, beforeEntries);
2056
+ await runEf(ctx.projectDir, ["database", "update", targetId], ctx.dsn, { noBuild: true });
2057
+ const stillApplied = new Set((await listEfMigrations(ctx, { noBuild: true })).filter((e) => e.applied).map((e) => e.id));
2058
+ return { rolledBack: toApplied(beforeEntries.filter((e) => !stillApplied.has(e.id)).reverse()), tool: "efcore" };
2059
+ }
2060
+ function efMigrationName(slug) {
2061
+ const name = slug.split(/[^A-Za-z0-9]+/).filter(Boolean).map((w) => w[0].toUpperCase() + w.slice(1)).join("");
2062
+ if (!name) throw new SchemaMigrationError(`Cannot derive a migration name from '${slug}'.`);
2063
+ return /^[0-9]/.test(name) ? `M${name}` : name;
2064
+ }
2065
+ async function createEfcoreMigration(opts) {
2066
+ const project = requireProject(opts.projectDir);
2067
+ const name = efMigrationName(opts.slug);
2068
+ const existing = new Set(fs11.existsSync(project.migrationsDir) ? fs11.readdirSync(project.migrationsDir) : []);
2069
+ await runEf(opts.projectDir, ["migrations", "add", name]);
2070
+ const created = fs11.readdirSync(project.migrationsDir).filter((f) => !existing.has(f) && MIGRATION_ID.test(f.replace(/\.cs$/, "")) && !f.endsWith(".Designer.cs"));
2071
+ if (created.length !== 1) {
2072
+ throw new SchemaMigrationError(`Expected one new migration file for ${name}, found: ${created.join(", ") || "none"}`);
2073
+ }
2074
+ return { id: created[0].replace(/\.cs$/, ""), path: path11.join(project.migrationsDir, created[0]) };
2075
+ }
2076
+ function localEfMigrationIds(projectDir) {
2077
+ const project = findEfCoreProject(projectDir);
2078
+ if (!project || !fs11.existsSync(project.migrationsDir)) return [];
2079
+ return fs11.readdirSync(project.migrationsDir).filter((f) => f.endsWith(".cs") && !f.endsWith(".Designer.cs") && MIGRATION_ID.test(f.slice(0, -3))).map((f) => f.slice(0, -3)).sort();
2080
+ }
2081
+ function efProductVersion(project) {
2082
+ try {
2083
+ const csproj = fs11.readFileSync(project.csproj, "utf-8");
2084
+ const m = new RegExp(`${EF_DESIGN_PACKAGE.replace(/\./g, "\\.")}"\\s+Version="([^"]+)"`).exec(csproj);
2085
+ if (m) return m[1];
2086
+ } catch {
2087
+ }
2088
+ return "unknown";
2089
+ }
2090
+ async function stampEfcore(ctx) {
2091
+ const project = requireProject(ctx.projectDir);
2092
+ const ids = localEfMigrationIds(ctx.projectDir);
2093
+ if (ids.length === 0) throw new SchemaMigrationError("No local EF Core migrations to stamp.");
2094
+ const rev = ctx.revision.trim();
2095
+ const end = rev === "head" ? ids.length - 1 : ids.findIndex((id) => id === rev || efVersion(id) === rev);
2096
+ if (end < 0) throw new SchemaMigrationError(`Stamp revision '${ctx.revision}' is not a local migration.`);
2097
+ const toStamp = ids.slice(0, end + 1);
2098
+ const productVersion = efProductVersion(project);
2099
+ const client = new Client2({ connectionString: ctx.dsn, application_name: connectionApplicationName() });
2100
+ await client.connect();
2101
+ try {
2102
+ await client.query("BEGIN");
2103
+ await client.query(
2104
+ `CREATE TABLE IF NOT EXISTS "__EFMigrationsHistory" (
2105
+ "MigrationId" character varying(150) NOT NULL,
2106
+ "ProductVersion" character varying(32) NOT NULL,
2107
+ CONSTRAINT "PK___EFMigrationsHistory" PRIMARY KEY ("MigrationId"))`
2108
+ );
2109
+ await client.query(`DELETE FROM "__EFMigrationsHistory" WHERE NOT ("MigrationId" = ANY($1::text[]))`, [toStamp]);
2110
+ for (const id of toStamp) {
2111
+ await client.query(
2112
+ `INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") VALUES ($1, $2)
2113
+ ON CONFLICT ("MigrationId") DO NOTHING`,
2114
+ [id, productVersion]
2115
+ );
2116
+ }
2117
+ await client.query("COMMIT");
2118
+ } catch (err) {
2119
+ await client.query("ROLLBACK").catch(() => void 0);
2120
+ throw err;
2121
+ } finally {
2122
+ await client.end();
2123
+ }
2124
+ return efVersion(toStamp[toStamp.length - 1]);
2125
+ }
2126
+
2127
+ // scripts/lakebase/adapters/efcore-adapter.ts
2128
+ async function buildDsn4(args) {
2129
+ const result = await getConnection({
2130
+ output: "dsn",
2131
+ instance: args.instance,
2132
+ branch: args.branch,
2133
+ database: args.database,
2134
+ endpointName: args.endpointName
2135
+ });
2136
+ return result.url;
2137
+ }
2138
+ function errMsg(err) {
2139
+ return err instanceof Error ? err.message : String(err);
2140
+ }
2141
+ function listEfCoreFiles(projectDir) {
2142
+ return localEfMigrationIds(projectDir).map((id) => ({
2143
+ version: efVersion(id),
2144
+ filename: `${id}.cs`,
2145
+ description: efDescription(id),
2146
+ type: "CSharp",
2147
+ tool: "efcore"
2148
+ }));
2149
+ }
2150
+ var EfCoreAdapter = {
2151
+ id: "efcore",
2152
+ languages: ["dotnet"],
2153
+ /** A .NET project (root marker) with a *.csproj that references Microsoft.EntityFrameworkCore.Design. */
2154
+ detect(projectDir) {
2155
+ return isDotnetProject(projectDir) && findEfCoreProject(projectDir) !== null;
2156
+ },
2157
+ async apply(args) {
2158
+ try {
2159
+ const dsn = await buildDsn4(args);
2160
+ const r = await applyEfcore({ projectDir: args.projectDir, dsn });
2161
+ return {
2162
+ applied_migrations: r.applied,
2163
+ status: r.alreadyAtLatest ? "noop" : "ok",
2164
+ tool_specific: { alreadyAtLatest: r.alreadyAtLatest, tool: r.tool }
2165
+ };
2166
+ } catch (err) {
2167
+ return { applied_migrations: [], status: "error", error: errMsg(err) };
2168
+ }
2169
+ },
2170
+ async rollback(args) {
2171
+ try {
2172
+ const dsn = await buildDsn4(args);
2173
+ const r = await rollbackEfcore({ projectDir: args.projectDir, dsn, target: args.target });
2174
+ return {
2175
+ rolled_back: r.rolledBack,
2176
+ status: r.rolledBack.length === 0 ? "noop" : "ok",
2177
+ tool_specific: { tool: r.tool }
2178
+ };
2179
+ } catch (err) {
2180
+ return { rolled_back: [], status: "error", error: errMsg(err) };
2181
+ }
2182
+ },
2183
+ async stamp(args) {
2184
+ try {
2185
+ const dsn = await buildDsn4(args);
2186
+ const stamped = await stampEfcore({ projectDir: args.projectDir, dsn, revision: args.revision });
2187
+ return { status: "ok", stamped_revision: stamped, tool_specific: { tool: "efcore" } };
2188
+ } catch (err) {
2189
+ return { status: "error", stamped_revision: null, error: errMsg(err) };
2190
+ }
2191
+ },
2192
+ async status(args) {
2193
+ try {
2194
+ const dsn = await buildDsn4(args);
2195
+ const r = await statusEfcore({ projectDir: args.projectDir, dsn });
2196
+ return {
2197
+ applied_version: r.current ?? null,
2198
+ pending: r.pending,
2199
+ applied: [],
2200
+ status: "ok",
2201
+ tool_specific: { tool: r.tool }
2202
+ };
2203
+ } catch (err) {
2204
+ return { applied_version: null, pending: [], applied: [], status: "error", error: errMsg(err) };
2205
+ }
2206
+ },
2207
+ async list(args) {
2208
+ return { files: listEfCoreFiles(args.projectDir) };
2209
+ },
2210
+ // baseline: use stamp (EF has no separate baseline concept).
2211
+ async newMigration(args) {
2212
+ try {
2213
+ const created = await createEfcoreMigration({ projectDir: args.projectDir, slug: args.slug });
2214
+ return {
2215
+ status: "ok",
2216
+ version: efVersion(created.id),
2217
+ filename: path12.basename(created.path),
2218
+ path: created.path
2219
+ };
2220
+ } catch (err) {
2221
+ return { status: "error", version: "", filename: "", path: "", error: errMsg(err) };
2222
+ }
2223
+ }
2224
+ };
2225
+ registerSchemaMigrationAdapter(EfCoreAdapter);
2226
+
1805
2227
  // scripts/lakebase/schema-migrate.ts
1806
2228
  var SchemaMigrationError = class extends Error {
1807
2229
  constructor(message, cause) {
@@ -1815,13 +2237,16 @@ function detectLanguage(projectDir) {
1815
2237
  const lang = resolveMigrationLanguage(projectDir);
1816
2238
  if (lang === "unknown") {
1817
2239
  throw new SchemaMigrationError(
1818
- `Could not detect project language in ${projectDir}. Expected one of: pom.xml (java/kotlin), pyproject.toml or alembic.ini (python), package.json (nodejs). Pass {language} explicitly to override.`
2240
+ `Could not detect project language in ${projectDir}. Expected one of: pom.xml (java/kotlin), pyproject.toml or alembic.ini (python), package.json (nodejs), *.sln or *.csproj (dotnet). Pass {language} explicitly to override.`
1819
2241
  );
1820
2242
  }
1821
2243
  return lang;
1822
2244
  }
1823
- function toolForLanguage(language) {
2245
+ function toolForLanguage(language, projectDir) {
1824
2246
  switch (language) {
2247
+ case "dotnet":
2248
+ if (projectDir && isDotnetFlywayProject(projectDir)) return "flyway";
2249
+ return "efcore";
1825
2250
  case "java":
1826
2251
  case "kotlin":
1827
2252
  return "flyway";
@@ -1834,8 +2259,10 @@ function toolForLanguage(language) {
1834
2259
  function listSchemaMigrations(args = {}) {
1835
2260
  const projectDir = args.projectDir ?? process.cwd();
1836
2261
  const language = args.language ?? detectLanguage(projectDir);
1837
- const tool = toolForLanguage(language);
2262
+ const tool = toolForLanguage(language, projectDir);
1838
2263
  switch (tool) {
2264
+ case "efcore":
2265
+ return listEfCoreFiles(projectDir);
1839
2266
  case "flyway":
1840
2267
  return listFlywayMigrations(projectDir);
1841
2268
  case "alembic":
@@ -1845,9 +2272,9 @@ function listSchemaMigrations(args = {}) {
1845
2272
  }
1846
2273
  }
1847
2274
  function listFlywayMigrations(projectDir) {
1848
- const dir = path10.join(projectDir, "src", "main", "resources", "db", "migration");
1849
- if (!fs10.existsSync(dir)) return [];
1850
- const files = fs10.readdirSync(dir).filter((f) => /^V\d+(\.\d+)*__.+\.sql$/.test(f));
2275
+ const dir = flywayMigrationsDir(projectDir);
2276
+ if (!fs12.existsSync(dir)) return [];
2277
+ const files = fs12.readdirSync(dir).filter((f) => /^V\d+(\.\d+)*__.+\.sql$/.test(f));
1851
2278
  return files.map((filename) => {
1852
2279
  const m = filename.match(/^V(\d+(?:\.\d+)*)__(.+)\.sql$/);
1853
2280
  const version = m[1];
@@ -1857,12 +2284,12 @@ function listFlywayMigrations(projectDir) {
1857
2284
  }
1858
2285
  function listAlembicMigrations(projectDir) {
1859
2286
  const candidates = [
1860
- path10.join(projectDir, "migrations", "versions"),
1861
- path10.join(projectDir, "alembic", "versions")
2287
+ path13.join(projectDir, "migrations", "versions"),
2288
+ path13.join(projectDir, "alembic", "versions")
1862
2289
  ];
1863
- const dir = candidates.find((p) => fs10.existsSync(p));
2290
+ const dir = candidates.find((p) => fs12.existsSync(p));
1864
2291
  if (!dir) return [];
1865
- const files = fs10.readdirSync(dir).filter((f) => f.endsWith(".py") && !f.startsWith("__"));
2292
+ const files = fs12.readdirSync(dir).filter((f) => f.endsWith(".py") && !f.startsWith("__"));
1866
2293
  return files.map((filename) => {
1867
2294
  const stem = filename.replace(/\.py$/, "");
1868
2295
  const sep2 = stem.indexOf("_");
@@ -1872,9 +2299,9 @@ function listAlembicMigrations(projectDir) {
1872
2299
  }).sort((a, b) => a.filename.localeCompare(b.filename));
1873
2300
  }
1874
2301
  function listKnexMigrations(projectDir) {
1875
- const dir = path10.join(projectDir, "migrations");
1876
- if (!fs10.existsSync(dir)) return [];
1877
- const files = fs10.readdirSync(dir).filter((f) => (f.endsWith(".js") || f.endsWith(".ts")) && !f.startsWith("."));
2302
+ const dir = path13.join(projectDir, "migrations");
2303
+ if (!fs12.existsSync(dir)) return [];
2304
+ const files = fs12.readdirSync(dir).filter((f) => (f.endsWith(".js") || f.endsWith(".ts")) && !f.startsWith("."));
1878
2305
  return files.map((filename) => {
1879
2306
  const stem = filename.replace(/\.(js|ts)$/, "");
1880
2307
  const m = stem.match(/^(\d{14})_(.+)$/);
@@ -1896,7 +2323,7 @@ function versionCompare2(a, b) {
1896
2323
  return 0;
1897
2324
  }
1898
2325
  function adapterFor(projectDir, language) {
1899
- const override = language ? toolForLanguage(language) : void 0;
2326
+ const override = language ? toolForLanguage(language, projectDir) : void 0;
1900
2327
  return resolveSchemaMigrationAdapter(projectDir, override);
1901
2328
  }
1902
2329
  function dbRevisionOrphaned(appliedRevision, localRevisionIds) {
@@ -2369,7 +2796,7 @@ async function main() {
2369
2796
  return 2;
2370
2797
  }
2371
2798
  const ghEnvLines = emitGithubEnvScalar("LAKEBASE_BRANCH_NAME", result.lakebaseName) + emitGithubEnvScalar("LAKEBASE_BRANCH_PATH", result.branchPath) + emitGithubEnvScalar("LAKEBASE_BRANCH_STATUS", result.status) + emitGithubEnvScalar("LAKEBASE_BRANCH_SOURCE", result.source) + emitGithubEnvScalar("LAKEBASE_HOST", result.host) + emitGithubEnvScalar("LAKEBASE_USERNAME", result.email) + emitGithubEnvHeredoc("LAKEBASE_PASSWORD", result.token) + emitGithubEnvScalar("DATABASE_URL", result.databaseUrl) + emitGithubEnvScalar("JDBC_URL", result.jdbcUrl) + emitGithubEnvScalar("DB_USERNAME", result.email) + emitGithubEnvHeredoc("DB_PASSWORD", result.token) + emitGithubEnvScalar("SPRING_DATASOURCE_URL", result.jdbcUrl) + emitGithubEnvScalar("SPRING_DATASOURCE_USERNAME", result.email) + emitGithubEnvHeredoc("SPRING_DATASOURCE_PASSWORD", result.token);
2372
- fs11.appendFileSync(ghEnvFile, ghEnvLines, { encoding: "utf8" });
2799
+ fs13.appendFileSync(ghEnvFile, ghEnvLines, { encoding: "utf8" });
2373
2800
  process.stdout.write(emitEvalLine("LAKEBASE_BRANCH_NAME", result.lakebaseName));
2374
2801
  process.stdout.write(emitEvalLine("LAKEBASE_BRANCH_PATH", result.branchPath));
2375
2802
  process.stdout.write(emitEvalLine("LAKEBASE_BRANCH_STATUS", result.status));