@databricks-solutions/lakebase-scm-utils 0.2.51 → 0.3.1
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.
- package/README.md +10 -4
- package/dist/scripts/index.cjs +2813 -2244
- package/dist/scripts/index.d.cts +1 -1
- package/dist/scripts/index.d.ts +1 -1
- package/dist/scripts/index.js +857 -298
- package/dist/scripts/lakebase/ci-resolve-branch.cli.cjs +2460 -2030
- package/dist/scripts/lakebase/ci-resolve-branch.cli.js +508 -81
- package/dist/scripts/lakebase/collapse-heads.cli.cjs +2429 -2007
- package/dist/scripts/lakebase/collapse-heads.cli.js +477 -58
- package/dist/scripts/lakebase/detect-language.cli.cjs +2441 -2016
- package/dist/scripts/lakebase/detect-language.cli.js +489 -67
- package/dist/scripts/lakebase/doctor.cli.cjs +2484 -2040
- package/dist/scripts/lakebase/doctor.cli.js +532 -91
- package/dist/scripts/lakebase/get-connection.cli.cjs +2 -2
- package/dist/scripts/lakebase/get-connection.cli.js +2 -2
- package/dist/scripts/lakebase/index.cjs +2810 -2205
- package/dist/scripts/lakebase/index.d.cts +59 -10
- package/dist/scripts/lakebase/index.d.ts +59 -10
- package/dist/scripts/lakebase/index.js +851 -256
- package/dist/scripts/lakebase/infra-runner.cli.cjs +2435 -2013
- package/dist/scripts/lakebase/infra-runner.cli.js +483 -64
- package/dist/scripts/lakebase/new-migration.cli.cjs +2429 -2007
- package/dist/scripts/lakebase/new-migration.cli.js +477 -58
- package/dist/scripts/lakebase/schema-diff.cli.cjs +2 -2
- package/dist/scripts/lakebase/schema-diff.cli.js +2 -2
- package/dist/scripts/lakebase/schema-migrate.cli.cjs +2461 -2029
- package/dist/scripts/lakebase/schema-migrate.cli.js +509 -80
- package/dist/scripts/lakebase/scm-claim-feature.cli.cjs +2460 -2030
- package/dist/scripts/lakebase/scm-claim-feature.cli.js +508 -81
- package/dist/scripts/lakebase/scm-doctor.cli.cjs +2486 -2056
- package/dist/scripts/lakebase/scm-doctor.cli.js +537 -107
- package/dist/scripts/lakebase/scm-merge.cli.cjs +2443 -2021
- package/dist/scripts/lakebase/scm-merge.cli.js +494 -72
- package/dist/scripts/lakebase/scm-reconcile-tier.cli.cjs +2464 -2034
- package/dist/scripts/lakebase/scm-reconcile-tier.cli.js +511 -84
- package/dist/scripts/util/index.cjs +2 -2
- package/dist/scripts/util/index.js +2 -2
- package/package.json +1 -1
- package/scripts/lakebase/adapters/efcore-adapter.ts +158 -0
- package/scripts/lakebase/adapters/flyway-adapter.ts +8 -5
- package/scripts/lakebase/client-lockfile.ts +102 -0
- package/scripts/lakebase/collapse-heads.cli.ts +1 -1
- package/scripts/lakebase/create-project.ts +25 -2
- package/scripts/lakebase/detect-language.cli.ts +2 -2
- package/scripts/lakebase/doctor.ts +29 -0
- package/scripts/lakebase/dotnet-project.ts +112 -0
- package/scripts/lakebase/index.ts +1 -0
- package/scripts/lakebase/migration-layout.ts +43 -2
- package/scripts/lakebase/new-migration.cli.ts +1 -1
- package/scripts/lakebase/scaffold-language.ts +23 -2
- package/scripts/lakebase/scaffold.ts +4 -1
- package/scripts/lakebase/schema-migrate-runners/efcore.ts +399 -0
- package/scripts/lakebase/schema-migrate-runners/flyway.ts +6 -4
- package/scripts/lakebase/schema-migrate.cli.ts +3 -1
- package/scripts/lakebase/schema-migrate.ts +24 -10
- package/templates/project/common/.github/workflows/merge.yml +13 -1
- package/templates/project/common/.github/workflows/pr.yml +25 -5
- package/templates/project/common/scripts/flyway-migrate.sh +54 -1
- package/templates/project/common/scripts/prepare-schema-diff.sh +5 -0
- package/templates/project/common/scripts/run-dev.sh +10 -1
- package/templates/project/common/scripts/run-tests.sh +6 -1
- package/templates/project/dotnet/.gitignore.extra +8 -0
- package/templates/project/dotnet/App.sln +56 -0
- package/templates/project/dotnet/Makefile +13 -0
- package/templates/project/dotnet/global.json +6 -0
- package/templates/project/dotnet/src/App/Data/AppDbContext.cs +12 -0
- package/templates/project/dotnet/src/App/Lakebase/DotEnv.cs +48 -0
- package/templates/project/dotnet/src/App/Lakebase/LakebaseConnection.cs +225 -0
- package/templates/project/dotnet/src/App/Program.cs +34 -0
- package/templates/project/dotnet/src/App/appsettings.json +9 -0
- package/templates/project/dotnet/tests/App.Tests/App.Tests.csproj +25 -0
- package/templates/project/dotnet/tests/App.Tests/HealthTests.cs +18 -0
- package/templates/project/dotnet/tests/App.Tests/OpenApiTests.cs +23 -0
- package/templates/project/dotnet-efcore/.config/dotnet-tools.json +13 -0
- package/templates/project/dotnet-efcore/src/App/App.csproj +22 -0
- package/templates/project/dotnet-efcore/src/App/Data/DesignTimeDbContextFactory.cs +24 -0
- package/templates/project/dotnet-efcore/src/App/Migrations/README.md +23 -0
- package/templates/project/dotnet-flyway/db/migrations/README.md +23 -0
- package/templates/project/dotnet-flyway/src/App/App.csproj +17 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// scripts/lakebase/schema-migrate.ts
|
|
4
|
-
import * as
|
|
5
|
-
import * as
|
|
4
|
+
import * as fs11 from "fs";
|
|
5
|
+
import * as path12 from "path";
|
|
6
6
|
|
|
7
7
|
// scripts/lakebase/databricks-cli.ts
|
|
8
8
|
import { execFile, execFileSync as execFileSync2 } from "child_process";
|
|
@@ -280,9 +280,9 @@ function asBranchUid(s) {
|
|
|
280
280
|
}
|
|
281
281
|
return s;
|
|
282
282
|
}
|
|
283
|
-
function branchNameFromResourcePath(
|
|
284
|
-
if (!
|
|
285
|
-
const leaf =
|
|
283
|
+
function branchNameFromResourcePath(path13) {
|
|
284
|
+
if (!path13.includes("/branches/")) return null;
|
|
285
|
+
const leaf = path13.split("/branches/").pop();
|
|
286
286
|
if (!leaf) return null;
|
|
287
287
|
try {
|
|
288
288
|
return asBranchName(leaf);
|
|
@@ -393,8 +393,8 @@ var PKG_NAME = "@databricks-solutions/lakebase-scm-utils";
|
|
|
393
393
|
var cached;
|
|
394
394
|
function substrateSelfVersion() {
|
|
395
395
|
if (cached !== void 0) return cached;
|
|
396
|
-
if ("0.
|
|
397
|
-
cached = "0.
|
|
396
|
+
if ("0.3.1".length > 0) {
|
|
397
|
+
cached = "0.3.1";
|
|
398
398
|
return cached;
|
|
399
399
|
}
|
|
400
400
|
cached = "unknown";
|
|
@@ -502,22 +502,84 @@ function dbcli2(args) {
|
|
|
502
502
|
return runDatabricksSync(args, { timeout: KIT_TIMEOUTS.cliDefault });
|
|
503
503
|
}
|
|
504
504
|
|
|
505
|
+
// scripts/lakebase/dotnet-project.ts
|
|
506
|
+
import * as fs4 from "fs";
|
|
507
|
+
import * as path3 from "path";
|
|
508
|
+
var EF_DESIGN_PACKAGE = "Microsoft.EntityFrameworkCore.Design";
|
|
509
|
+
var DOTNET_FLYWAY_DIR = path3.join("db", "migrations");
|
|
510
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set(["bin", "obj", "node_modules", ".git", "client", "tests", "test"]);
|
|
511
|
+
function safeReaddir(dir) {
|
|
512
|
+
try {
|
|
513
|
+
return fs4.readdirSync(dir, { withFileTypes: true });
|
|
514
|
+
} catch {
|
|
515
|
+
return [];
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
function hasDotnetMarker(dir) {
|
|
519
|
+
return safeReaddir(dir).some(
|
|
520
|
+
(e) => e.isFile() && /\.(sln|slnx|csproj)$/i.test(e.name)
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
function findCsprojFiles(projectDir) {
|
|
524
|
+
const found = [];
|
|
525
|
+
const walk = (dir, depth) => {
|
|
526
|
+
for (const e of safeReaddir(dir)) {
|
|
527
|
+
const full = path3.join(dir, e.name);
|
|
528
|
+
if (e.isFile() && e.name.toLowerCase().endsWith(".csproj")) {
|
|
529
|
+
found.push(full);
|
|
530
|
+
} else if (e.isDirectory() && depth < 2 && !SKIP_DIRS.has(e.name.toLowerCase()) && !e.name.startsWith(".")) {
|
|
531
|
+
walk(full, depth + 1);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
walk(projectDir, 0);
|
|
536
|
+
return found.sort();
|
|
537
|
+
}
|
|
538
|
+
function findEfCoreProject(projectDir) {
|
|
539
|
+
for (const csproj of findCsprojFiles(projectDir)) {
|
|
540
|
+
let content = "";
|
|
541
|
+
try {
|
|
542
|
+
content = fs4.readFileSync(csproj, "utf-8");
|
|
543
|
+
} catch {
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
if (content.includes(EF_DESIGN_PACKAGE)) {
|
|
547
|
+
const dir = path3.dirname(csproj);
|
|
548
|
+
return { csproj, projectDir: dir, migrationsDir: path3.join(dir, "Migrations") };
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
return null;
|
|
552
|
+
}
|
|
553
|
+
function isDotnetProject(projectDir) {
|
|
554
|
+
return hasDotnetMarker(projectDir);
|
|
555
|
+
}
|
|
556
|
+
function isDotnetFlywayProject(projectDir) {
|
|
557
|
+
return isDotnetProject(projectDir) && fs4.existsSync(path3.join(projectDir, DOTNET_FLYWAY_DIR));
|
|
558
|
+
}
|
|
559
|
+
var MAVEN_FLYWAY_DIR = path3.join("src", "main", "resources", "db", "migration");
|
|
560
|
+
function flywayMigrationsDir(projectDir) {
|
|
561
|
+
const maven = path3.join(projectDir, MAVEN_FLYWAY_DIR);
|
|
562
|
+
if (fs4.existsSync(maven) || fs4.existsSync(path3.join(projectDir, "pom.xml"))) return maven;
|
|
563
|
+
if (isDotnetFlywayProject(projectDir)) return path3.join(projectDir, DOTNET_FLYWAY_DIR);
|
|
564
|
+
return maven;
|
|
565
|
+
}
|
|
566
|
+
|
|
505
567
|
// scripts/lakebase/adapters/alembic-adapter.ts
|
|
506
|
-
import * as
|
|
507
|
-
import * as
|
|
568
|
+
import * as fs6 from "fs";
|
|
569
|
+
import * as path5 from "path";
|
|
508
570
|
|
|
509
571
|
// scripts/lakebase/schema-migrate-runners/alembic.ts
|
|
510
572
|
import { spawn } from "child_process";
|
|
511
|
-
import * as
|
|
512
|
-
import * as
|
|
573
|
+
import * as fs5 from "fs";
|
|
574
|
+
import * as path4 from "path";
|
|
513
575
|
function resolveAlembicBin(projectDir) {
|
|
514
576
|
const candidates = [
|
|
515
|
-
|
|
516
|
-
|
|
577
|
+
path4.join(projectDir, ".venv", "bin", "alembic"),
|
|
578
|
+
path4.join(projectDir, "venv", "bin", "alembic")
|
|
517
579
|
];
|
|
518
580
|
for (const candidate of candidates) {
|
|
519
581
|
try {
|
|
520
|
-
if (
|
|
582
|
+
if (fs5.existsSync(candidate)) return candidate;
|
|
521
583
|
} catch {
|
|
522
584
|
}
|
|
523
585
|
}
|
|
@@ -527,7 +589,7 @@ function spawnAlembic(projectDir, args, dsn) {
|
|
|
527
589
|
return new Promise((resolve, reject) => {
|
|
528
590
|
const bin = resolveAlembicBin(projectDir);
|
|
529
591
|
const env = { ...process.env };
|
|
530
|
-
env.PYTHONPATH = [projectDir, process.env.PYTHONPATH].filter(Boolean).join(
|
|
592
|
+
env.PYTHONPATH = [projectDir, process.env.PYTHONPATH].filter(Boolean).join(path4.delimiter);
|
|
531
593
|
if (dsn) env.DATABASE_URL = dsn;
|
|
532
594
|
const child = spawn(bin, args, {
|
|
533
595
|
cwd: projectDir,
|
|
@@ -575,10 +637,10 @@ async function createAlembicRevision(opts) {
|
|
|
575
637
|
const m = stdout.match(/Generating\s+(\S+\.py)/);
|
|
576
638
|
if (m) return m[1].trim();
|
|
577
639
|
for (const rel of ["migrations/versions", "alembic/versions"]) {
|
|
578
|
-
const dir =
|
|
579
|
-
if (!
|
|
580
|
-
const hit =
|
|
581
|
-
if (hit) return
|
|
640
|
+
const dir = path4.join(opts.projectDir, rel);
|
|
641
|
+
if (!fs5.existsSync(dir)) continue;
|
|
642
|
+
const hit = fs5.readdirSync(dir).find((f) => f.startsWith(`${opts.revId}_`) && f.endsWith(".py"));
|
|
643
|
+
if (hit) return path4.join(dir, hit);
|
|
582
644
|
}
|
|
583
645
|
throw new SchemaMigrationError(
|
|
584
646
|
`alembic revision succeeded but the created file could not be located.
|
|
@@ -712,15 +774,15 @@ async function buildDsn(args) {
|
|
|
712
774
|
}
|
|
713
775
|
function findVersionsDir(projectDir) {
|
|
714
776
|
const candidates = [
|
|
715
|
-
|
|
716
|
-
|
|
777
|
+
path5.join(projectDir, "migrations", "versions"),
|
|
778
|
+
path5.join(projectDir, "alembic", "versions")
|
|
717
779
|
];
|
|
718
|
-
return candidates.find((p) =>
|
|
780
|
+
return candidates.find((p) => fs6.existsSync(p));
|
|
719
781
|
}
|
|
720
782
|
function listAlembicFiles(projectDir) {
|
|
721
783
|
const dir = findVersionsDir(projectDir);
|
|
722
784
|
if (!dir) return [];
|
|
723
|
-
const files =
|
|
785
|
+
const files = fs6.readdirSync(dir).filter((f) => f.endsWith(".py") && !f.startsWith("__"));
|
|
724
786
|
return files.map((filename) => {
|
|
725
787
|
const stem = filename.replace(/\.py$/, "");
|
|
726
788
|
const sep = stem.indexOf("_");
|
|
@@ -745,9 +807,9 @@ var AlembicAdapter = {
|
|
|
745
807
|
* here. Callers can still force-select via project.yaml#migration_tool.
|
|
746
808
|
*/
|
|
747
809
|
detect(projectDir) {
|
|
748
|
-
if (
|
|
749
|
-
if (
|
|
750
|
-
if (
|
|
810
|
+
if (fs6.existsSync(path5.join(projectDir, "alembic.ini"))) return true;
|
|
811
|
+
if (fs6.existsSync(path5.join(projectDir, "migrations", "env.py"))) return true;
|
|
812
|
+
if (fs6.existsSync(path5.join(projectDir, "alembic", "env.py"))) return true;
|
|
751
813
|
return false;
|
|
752
814
|
},
|
|
753
815
|
async apply(args) {
|
|
@@ -853,7 +915,7 @@ var AlembicAdapter = {
|
|
|
853
915
|
autogenerate: !!args.autogenerate,
|
|
854
916
|
dsn
|
|
855
917
|
});
|
|
856
|
-
return { status: "ok", version: revId, filename:
|
|
918
|
+
return { status: "ok", version: revId, filename: path5.basename(created), path: created };
|
|
857
919
|
} catch (err) {
|
|
858
920
|
return {
|
|
859
921
|
status: "error",
|
|
@@ -870,7 +932,7 @@ var AlembicAdapter = {
|
|
|
870
932
|
if (heads.length <= 1) return { status: "noop", headsBefore: heads };
|
|
871
933
|
if (args.dryRun) return { status: "ok", headsBefore: heads };
|
|
872
934
|
const created = await mergeAlembicHeads(args.projectDir, args.message ?? "merge heads");
|
|
873
|
-
const mergeRevision =
|
|
935
|
+
const mergeRevision = path5.basename(created).replace(/\.py$/, "").split("_")[0];
|
|
874
936
|
return { status: "ok", headsBefore: heads, mergeRevision, path: created };
|
|
875
937
|
} catch (err) {
|
|
876
938
|
return {
|
|
@@ -884,12 +946,12 @@ var AlembicAdapter = {
|
|
|
884
946
|
registerSchemaMigrationAdapter(AlembicAdapter);
|
|
885
947
|
|
|
886
948
|
// scripts/lakebase/adapters/flyway-adapter.ts
|
|
887
|
-
import * as
|
|
888
|
-
import * as
|
|
949
|
+
import * as fs7 from "fs";
|
|
950
|
+
import * as path7 from "path";
|
|
889
951
|
|
|
890
952
|
// scripts/lakebase/schema-migrate-runners/flyway.ts
|
|
891
953
|
import { spawn as spawn2 } from "child_process";
|
|
892
|
-
import * as
|
|
954
|
+
import * as path6 from "path";
|
|
893
955
|
function dsnToFlywayEnv(dsn) {
|
|
894
956
|
const u = new URL(dsn);
|
|
895
957
|
const user = decodeURIComponent(u.username);
|
|
@@ -899,7 +961,7 @@ function dsnToFlywayEnv(dsn) {
|
|
|
899
961
|
return { url, user, password };
|
|
900
962
|
}
|
|
901
963
|
function migrationsLocation(projectDir) {
|
|
902
|
-
return `filesystem:${
|
|
964
|
+
return `filesystem:${flywayMigrationsDir(projectDir)}`;
|
|
903
965
|
}
|
|
904
966
|
function runFlyway(ctx, args) {
|
|
905
967
|
const { url, user, password } = dsnToFlywayEnv(ctx.dsn);
|
|
@@ -1000,7 +1062,7 @@ async function statusFlyway(ctx) {
|
|
|
1000
1062
|
if (state === "SUCCESS" || state === "BASELINE") {
|
|
1001
1063
|
current = m.version;
|
|
1002
1064
|
} else if (state === "PENDING") {
|
|
1003
|
-
const filename = m.filepath ?
|
|
1065
|
+
const filename = m.filepath ? path6.basename(m.filepath) : `V${m.version}__migration.sql`;
|
|
1004
1066
|
pending.push({
|
|
1005
1067
|
version: m.version,
|
|
1006
1068
|
filename,
|
|
@@ -1023,9 +1085,9 @@ async function buildDsn2(args) {
|
|
|
1023
1085
|
return result.url;
|
|
1024
1086
|
}
|
|
1025
1087
|
function listFlywayFiles(projectDir) {
|
|
1026
|
-
const dir =
|
|
1027
|
-
if (!
|
|
1028
|
-
const files =
|
|
1088
|
+
const dir = flywayMigrationsDir(projectDir);
|
|
1089
|
+
if (!fs7.existsSync(dir)) return [];
|
|
1090
|
+
const files = fs7.readdirSync(dir).filter((f) => /^V\d+(\.\d+)*__.+\.sql$/.test(f));
|
|
1029
1091
|
return files.map((filename) => {
|
|
1030
1092
|
const m = filename.match(/^V(\d+(?:\.\d+)*)__(.+)\.sql$/);
|
|
1031
1093
|
const version = m[1];
|
|
@@ -1046,9 +1108,9 @@ function versionCompare(a, b) {
|
|
|
1046
1108
|
}
|
|
1047
1109
|
var FlywayAdapter = {
|
|
1048
1110
|
id: "flyway",
|
|
1049
|
-
languages: ["java", "kotlin"],
|
|
1111
|
+
languages: ["java", "kotlin", "dotnet"],
|
|
1050
1112
|
detect(projectDir) {
|
|
1051
|
-
return
|
|
1113
|
+
return fs7.existsSync(path7.join(projectDir, "pom.xml")) || isDotnetFlywayProject(projectDir);
|
|
1052
1114
|
},
|
|
1053
1115
|
async apply(args) {
|
|
1054
1116
|
const dsn = await buildDsn2(args);
|
|
@@ -1106,14 +1168,14 @@ var FlywayAdapter = {
|
|
|
1106
1168
|
// optional-protocol shape makes this additive.
|
|
1107
1169
|
async newMigration(args) {
|
|
1108
1170
|
try {
|
|
1109
|
-
const dir =
|
|
1110
|
-
|
|
1171
|
+
const dir = flywayMigrationsDir(args.projectDir);
|
|
1172
|
+
fs7.mkdirSync(dir, { recursive: true });
|
|
1111
1173
|
const version = migrationTimestamp();
|
|
1112
1174
|
const slug = migrationSlug2(args.slug);
|
|
1113
1175
|
const filename = `V${version}__${slug}.sql`;
|
|
1114
|
-
const full =
|
|
1115
|
-
if (
|
|
1116
|
-
|
|
1176
|
+
const full = path7.join(dir, filename);
|
|
1177
|
+
if (fs7.existsSync(full)) throw new Error(`${filename} already exists`);
|
|
1178
|
+
fs7.writeFileSync(
|
|
1117
1179
|
full,
|
|
1118
1180
|
`-- V${version}: ${args.slug}
|
|
1119
1181
|
-- Flyway migration (write your DDL/DML below).
|
|
@@ -1135,18 +1197,18 @@ var FlywayAdapter = {
|
|
|
1135
1197
|
registerSchemaMigrationAdapter(FlywayAdapter);
|
|
1136
1198
|
|
|
1137
1199
|
// scripts/lakebase/adapters/knex-adapter.ts
|
|
1138
|
-
import * as
|
|
1139
|
-
import * as
|
|
1200
|
+
import * as fs9 from "fs";
|
|
1201
|
+
import * as path9 from "path";
|
|
1140
1202
|
|
|
1141
1203
|
// scripts/lakebase/schema-migrate-runners/knex.ts
|
|
1142
1204
|
import { spawn as spawn3 } from "child_process";
|
|
1143
|
-
import * as
|
|
1144
|
-
import * as
|
|
1205
|
+
import * as fs8 from "fs";
|
|
1206
|
+
import * as path8 from "path";
|
|
1145
1207
|
var KNEXFILE_VARIANTS = ["knexfile.js", "knexfile.ts", "knexfile.mjs", "knexfile.cjs"];
|
|
1146
1208
|
function findKnexfile(projectDir) {
|
|
1147
1209
|
for (const name of KNEXFILE_VARIANTS) {
|
|
1148
|
-
const p =
|
|
1149
|
-
if (
|
|
1210
|
+
const p = path8.join(projectDir, name);
|
|
1211
|
+
if (fs8.existsSync(p)) return p;
|
|
1150
1212
|
}
|
|
1151
1213
|
return void 0;
|
|
1152
1214
|
}
|
|
@@ -1298,9 +1360,9 @@ async function buildDsn3(args) {
|
|
|
1298
1360
|
}
|
|
1299
1361
|
var KNEXFILE_VARIANTS2 = ["knexfile.js", "knexfile.ts", "knexfile.mjs", "knexfile.cjs"];
|
|
1300
1362
|
function listKnexFiles(projectDir) {
|
|
1301
|
-
const dir =
|
|
1302
|
-
if (!
|
|
1303
|
-
const files =
|
|
1363
|
+
const dir = path9.join(projectDir, "migrations");
|
|
1364
|
+
if (!fs9.existsSync(dir)) return [];
|
|
1365
|
+
const files = fs9.readdirSync(dir).filter((f) => (f.endsWith(".js") || f.endsWith(".ts")) && !f.startsWith("."));
|
|
1304
1366
|
return files.map((filename) => {
|
|
1305
1367
|
const stem = filename.replace(/\.(js|ts)$/, "");
|
|
1306
1368
|
const m = stem.match(/^(\d{14})_(.+)$/);
|
|
@@ -1320,7 +1382,7 @@ var KnexAdapter = {
|
|
|
1320
1382
|
* project.yaml#migration_tool.
|
|
1321
1383
|
*/
|
|
1322
1384
|
detect(projectDir) {
|
|
1323
|
-
return KNEXFILE_VARIANTS2.some((name) =>
|
|
1385
|
+
return KNEXFILE_VARIANTS2.some((name) => fs9.existsSync(path9.join(projectDir, name)));
|
|
1324
1386
|
},
|
|
1325
1387
|
async apply(args) {
|
|
1326
1388
|
const dsn = await buildDsn3(args);
|
|
@@ -1393,9 +1455,9 @@ var KnexAdapter = {
|
|
|
1393
1455
|
async newMigration(args) {
|
|
1394
1456
|
try {
|
|
1395
1457
|
const created = await createKnexMigration({ projectDir: args.projectDir, slug: migrationSlug2(args.slug) });
|
|
1396
|
-
const stem =
|
|
1458
|
+
const stem = path9.basename(created).replace(/\.(js|ts)$/, "");
|
|
1397
1459
|
const version = stem.match(/^(\d{14})_/)?.[1] ?? stem;
|
|
1398
|
-
return { status: "ok", version, filename:
|
|
1460
|
+
return { status: "ok", version, filename: path9.basename(created), path: created };
|
|
1399
1461
|
} catch (err) {
|
|
1400
1462
|
return {
|
|
1401
1463
|
status: "error",
|
|
@@ -1409,6 +1471,360 @@ var KnexAdapter = {
|
|
|
1409
1471
|
};
|
|
1410
1472
|
registerSchemaMigrationAdapter(KnexAdapter);
|
|
1411
1473
|
|
|
1474
|
+
// scripts/lakebase/adapters/efcore-adapter.ts
|
|
1475
|
+
import * as path11 from "path";
|
|
1476
|
+
|
|
1477
|
+
// scripts/lakebase/schema-migrate-runners/efcore.ts
|
|
1478
|
+
import { spawn as spawn4 } from "child_process";
|
|
1479
|
+
import * as fs10 from "fs";
|
|
1480
|
+
import * as path10 from "path";
|
|
1481
|
+
import { Client as Client2 } from "pg";
|
|
1482
|
+
var MIGRATION_ID = /^(\d{14})_(.+)$/;
|
|
1483
|
+
function efVersion(id) {
|
|
1484
|
+
return MIGRATION_ID.exec(id)?.[1] ?? id;
|
|
1485
|
+
}
|
|
1486
|
+
function efDescription(id) {
|
|
1487
|
+
const name = MIGRATION_ID.exec(id)?.[2] ?? id;
|
|
1488
|
+
return name.replace(/_/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2");
|
|
1489
|
+
}
|
|
1490
|
+
function dsnToNpgsql(dsn) {
|
|
1491
|
+
const u = new URL(dsn);
|
|
1492
|
+
const parts = [
|
|
1493
|
+
`Host=${u.hostname}`,
|
|
1494
|
+
`Port=${u.port || "5432"}`,
|
|
1495
|
+
`Database=${decodeURIComponent(u.pathname.replace(/^\//, "")) || "databricks_postgres"}`
|
|
1496
|
+
];
|
|
1497
|
+
if (u.username) parts.push(`Username=${decodeURIComponent(u.username)}`);
|
|
1498
|
+
if (u.password) parts.push(`Password=${decodeURIComponent(u.password)}`);
|
|
1499
|
+
const sslmode = (u.searchParams.get("sslmode") ?? "require").toLowerCase();
|
|
1500
|
+
const ssl = {
|
|
1501
|
+
disable: "Disable",
|
|
1502
|
+
allow: "Allow",
|
|
1503
|
+
prefer: "Prefer",
|
|
1504
|
+
require: "Require",
|
|
1505
|
+
"verify-ca": "VerifyCA",
|
|
1506
|
+
"verify-full": "VerifyFull"
|
|
1507
|
+
};
|
|
1508
|
+
parts.push(`SSL Mode=${ssl[sslmode] ?? "Require"}`);
|
|
1509
|
+
const app = u.searchParams.get("application_name");
|
|
1510
|
+
if (app) parts.push(`Application Name=${app}`);
|
|
1511
|
+
return parts.map((p) => p.replace(/;/g, "")).join(";");
|
|
1512
|
+
}
|
|
1513
|
+
function parseEfJson(stdout) {
|
|
1514
|
+
const body = stdout.split(/\r?\n/).filter((l) => l.startsWith("data:")).map((l) => l.slice("data:".length)).join("\n").trim();
|
|
1515
|
+
if (!body) {
|
|
1516
|
+
throw new SchemaMigrationError(`dotnet ef returned no JSON payload: ${stdout.slice(0, 400)}`);
|
|
1517
|
+
}
|
|
1518
|
+
try {
|
|
1519
|
+
return JSON.parse(body);
|
|
1520
|
+
} catch (err) {
|
|
1521
|
+
throw new SchemaMigrationError(
|
|
1522
|
+
`dotnet ef JSON parse failed: ${err instanceof Error ? err.message : String(err)}.
|
|
1523
|
+
Body (first 400 chars): ${body.slice(0, 400)}`
|
|
1524
|
+
);
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
function requireProject(projectDir) {
|
|
1528
|
+
const project = findEfCoreProject(projectDir);
|
|
1529
|
+
if (!project) {
|
|
1530
|
+
throw new SchemaMigrationError(
|
|
1531
|
+
`No EF Core project found under ${projectDir}. Expected a *.csproj that references ${EF_DESIGN_PACKAGE}.`
|
|
1532
|
+
);
|
|
1533
|
+
}
|
|
1534
|
+
return project;
|
|
1535
|
+
}
|
|
1536
|
+
function spawnDotnet(projectDir, args, env) {
|
|
1537
|
+
return new Promise((resolve, reject) => {
|
|
1538
|
+
const child = spawn4("dotnet", args, {
|
|
1539
|
+
cwd: projectDir,
|
|
1540
|
+
env: { ...process.env, DOTNET_NOLOGO: "1", DOTNET_CLI_TELEMETRY_OPTOUT: "1", ...env },
|
|
1541
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
1542
|
+
});
|
|
1543
|
+
let stdout = "";
|
|
1544
|
+
let stderr = "";
|
|
1545
|
+
child.stdout.on("data", (chunk) => {
|
|
1546
|
+
stdout += chunk.toString("utf8");
|
|
1547
|
+
});
|
|
1548
|
+
child.stderr.on("data", (chunk) => {
|
|
1549
|
+
stderr += chunk.toString("utf8");
|
|
1550
|
+
});
|
|
1551
|
+
child.on("error", (err) => {
|
|
1552
|
+
reject(
|
|
1553
|
+
new SchemaMigrationError(
|
|
1554
|
+
`Could not spawn dotnet. Is the .NET SDK installed and on PATH? ${err.message}`,
|
|
1555
|
+
err
|
|
1556
|
+
)
|
|
1557
|
+
);
|
|
1558
|
+
});
|
|
1559
|
+
child.on("close", (code) => {
|
|
1560
|
+
if (code === 0) {
|
|
1561
|
+
resolve({ stdout, stderr });
|
|
1562
|
+
} else {
|
|
1563
|
+
reject(
|
|
1564
|
+
new SchemaMigrationError(
|
|
1565
|
+
`dotnet ${args.join(" ")} exited with code ${code}.
|
|
1566
|
+
stdout: ${stdout}
|
|
1567
|
+
stderr: ${stderr}`
|
|
1568
|
+
)
|
|
1569
|
+
);
|
|
1570
|
+
}
|
|
1571
|
+
});
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
1574
|
+
var restored = /* @__PURE__ */ new Set();
|
|
1575
|
+
async function ensureRestored(projectDir, csproj) {
|
|
1576
|
+
if (restored.has(projectDir)) return;
|
|
1577
|
+
if (fs10.existsSync(path10.join(projectDir, ".config", "dotnet-tools.json"))) {
|
|
1578
|
+
await spawnDotnet(projectDir, ["tool", "restore"], {});
|
|
1579
|
+
}
|
|
1580
|
+
await spawnDotnet(projectDir, ["restore", csproj], {});
|
|
1581
|
+
restored.add(projectDir);
|
|
1582
|
+
}
|
|
1583
|
+
async function runEf(projectDir, args, dsn, opts = {}) {
|
|
1584
|
+
const project = requireProject(projectDir);
|
|
1585
|
+
const csproj = path10.relative(projectDir, project.csproj) || project.csproj;
|
|
1586
|
+
await ensureRestored(projectDir, csproj);
|
|
1587
|
+
const full = ["ef", ...args, "--project", csproj, "--prefix-output"];
|
|
1588
|
+
if (opts.noBuild) full.push("--no-build");
|
|
1589
|
+
const env = {};
|
|
1590
|
+
if (dsn) {
|
|
1591
|
+
env.DATABASE_URL = dsn;
|
|
1592
|
+
if (process.env.LAKEBASE_EFCORE_CONNECTION_ARG === "1") full.push("--connection", dsnToNpgsql(dsn));
|
|
1593
|
+
}
|
|
1594
|
+
return spawnDotnet(projectDir, full, env);
|
|
1595
|
+
}
|
|
1596
|
+
async function listEfMigrations(ctx, opts = {}) {
|
|
1597
|
+
const { stdout, stderr } = await runEf(ctx.projectDir, ["migrations", "list", "--json"], ctx.dsn, opts);
|
|
1598
|
+
const entries = parseEfJson(stdout);
|
|
1599
|
+
if (ctx.dsn && entries.some((e) => e.applied === null)) {
|
|
1600
|
+
const warning = stdout.split(/\r?\n/).filter((l) => /^(warn|error):/.test(l)).join("\n");
|
|
1601
|
+
throw new SchemaMigrationError(
|
|
1602
|
+
`dotnet ef could not read migration state from the database (check connectivity and credentials).
|
|
1603
|
+
${warning || stderr}`.trim()
|
|
1604
|
+
);
|
|
1605
|
+
}
|
|
1606
|
+
return entries;
|
|
1607
|
+
}
|
|
1608
|
+
async function readEfHistory(dsn) {
|
|
1609
|
+
const client = new Client2({ connectionString: dsn, application_name: connectionApplicationName() });
|
|
1610
|
+
await client.connect();
|
|
1611
|
+
try {
|
|
1612
|
+
const exists = await client.query(`SELECT to_regclass('"__EFMigrationsHistory"') IS NOT NULL AS present`);
|
|
1613
|
+
if (!exists.rows[0]?.present) return null;
|
|
1614
|
+
const r = await client.query(`SELECT "MigrationId" FROM "__EFMigrationsHistory" ORDER BY "MigrationId"`);
|
|
1615
|
+
return r.rows.map((row) => row.MigrationId);
|
|
1616
|
+
} finally {
|
|
1617
|
+
await client.end();
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
function toApplied(entries) {
|
|
1621
|
+
return entries.map((e) => ({ version: efVersion(e.id), description: efDescription(e.id) }));
|
|
1622
|
+
}
|
|
1623
|
+
async function statusEfcore(ctx) {
|
|
1624
|
+
const entries = await listEfMigrations(ctx);
|
|
1625
|
+
const history = await (ctx.readHistory ?? readEfHistory)(ctx.dsn);
|
|
1626
|
+
const current = history && history.length > 0 ? efVersion(history[history.length - 1]) : void 0;
|
|
1627
|
+
const pending = entries.filter((e) => e.applied !== true).map((e) => ({ version: efVersion(e.id), filename: `${e.id}.cs`, description: efDescription(e.id) }));
|
|
1628
|
+
return { current, pending, tool: "efcore" };
|
|
1629
|
+
}
|
|
1630
|
+
async function applyEfcore(ctx) {
|
|
1631
|
+
const before = new Set((await listEfMigrations(ctx)).filter((e) => e.applied).map((e) => e.id));
|
|
1632
|
+
await runEf(ctx.projectDir, ["database", "update"], ctx.dsn, { noBuild: true });
|
|
1633
|
+
const after = await listEfMigrations(ctx, { noBuild: true });
|
|
1634
|
+
const applied = toApplied(after.filter((e) => e.applied && !before.has(e.id)));
|
|
1635
|
+
return { applied, alreadyAtLatest: applied.length === 0, tool: "efcore" };
|
|
1636
|
+
}
|
|
1637
|
+
function resolveEfTarget(target, applied) {
|
|
1638
|
+
const t = target.trim();
|
|
1639
|
+
if (t === "0") return "0";
|
|
1640
|
+
const rel = /^-(\d+)$/.exec(t);
|
|
1641
|
+
if (rel) {
|
|
1642
|
+
const steps = Number(rel[1]);
|
|
1643
|
+
if (steps < 1) throw new SchemaMigrationError(`Invalid rollback step: ${target}`);
|
|
1644
|
+
const index = applied.length - 1 - steps;
|
|
1645
|
+
if (index < -1) {
|
|
1646
|
+
throw new SchemaMigrationError(`Cannot roll back ${steps} migration(s); only ${applied.length} applied.`);
|
|
1647
|
+
}
|
|
1648
|
+
return index === -1 ? "0" : applied[index].id;
|
|
1649
|
+
}
|
|
1650
|
+
const match = applied.find((e) => e.id === t || efVersion(e.id) === t || e.name === t);
|
|
1651
|
+
if (!match) throw new SchemaMigrationError(`Rollback target '${target}' is not an applied migration.`);
|
|
1652
|
+
return match.id;
|
|
1653
|
+
}
|
|
1654
|
+
async function rollbackEfcore(ctx) {
|
|
1655
|
+
const beforeEntries = (await listEfMigrations(ctx)).filter((e) => e.applied);
|
|
1656
|
+
const targetId = resolveEfTarget(ctx.target, beforeEntries);
|
|
1657
|
+
await runEf(ctx.projectDir, ["database", "update", targetId], ctx.dsn, { noBuild: true });
|
|
1658
|
+
const stillApplied = new Set((await listEfMigrations(ctx, { noBuild: true })).filter((e) => e.applied).map((e) => e.id));
|
|
1659
|
+
return { rolledBack: toApplied(beforeEntries.filter((e) => !stillApplied.has(e.id)).reverse()), tool: "efcore" };
|
|
1660
|
+
}
|
|
1661
|
+
function efMigrationName(slug) {
|
|
1662
|
+
const name = slug.split(/[^A-Za-z0-9]+/).filter(Boolean).map((w) => w[0].toUpperCase() + w.slice(1)).join("");
|
|
1663
|
+
if (!name) throw new SchemaMigrationError(`Cannot derive a migration name from '${slug}'.`);
|
|
1664
|
+
return /^[0-9]/.test(name) ? `M${name}` : name;
|
|
1665
|
+
}
|
|
1666
|
+
async function createEfcoreMigration(opts) {
|
|
1667
|
+
const project = requireProject(opts.projectDir);
|
|
1668
|
+
const name = efMigrationName(opts.slug);
|
|
1669
|
+
const existing = new Set(fs10.existsSync(project.migrationsDir) ? fs10.readdirSync(project.migrationsDir) : []);
|
|
1670
|
+
await runEf(opts.projectDir, ["migrations", "add", name]);
|
|
1671
|
+
const created = fs10.readdirSync(project.migrationsDir).filter((f) => !existing.has(f) && MIGRATION_ID.test(f.replace(/\.cs$/, "")) && !f.endsWith(".Designer.cs"));
|
|
1672
|
+
if (created.length !== 1) {
|
|
1673
|
+
throw new SchemaMigrationError(`Expected one new migration file for ${name}, found: ${created.join(", ") || "none"}`);
|
|
1674
|
+
}
|
|
1675
|
+
return { id: created[0].replace(/\.cs$/, ""), path: path10.join(project.migrationsDir, created[0]) };
|
|
1676
|
+
}
|
|
1677
|
+
function localEfMigrationIds(projectDir) {
|
|
1678
|
+
const project = findEfCoreProject(projectDir);
|
|
1679
|
+
if (!project || !fs10.existsSync(project.migrationsDir)) return [];
|
|
1680
|
+
return fs10.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();
|
|
1681
|
+
}
|
|
1682
|
+
function efProductVersion(project) {
|
|
1683
|
+
try {
|
|
1684
|
+
const csproj = fs10.readFileSync(project.csproj, "utf-8");
|
|
1685
|
+
const m = new RegExp(`${EF_DESIGN_PACKAGE.replace(/\./g, "\\.")}"\\s+Version="([^"]+)"`).exec(csproj);
|
|
1686
|
+
if (m) return m[1];
|
|
1687
|
+
} catch {
|
|
1688
|
+
}
|
|
1689
|
+
return "unknown";
|
|
1690
|
+
}
|
|
1691
|
+
async function stampEfcore(ctx) {
|
|
1692
|
+
const project = requireProject(ctx.projectDir);
|
|
1693
|
+
const ids = localEfMigrationIds(ctx.projectDir);
|
|
1694
|
+
if (ids.length === 0) throw new SchemaMigrationError("No local EF Core migrations to stamp.");
|
|
1695
|
+
const rev = ctx.revision.trim();
|
|
1696
|
+
const end = rev === "head" ? ids.length - 1 : ids.findIndex((id) => id === rev || efVersion(id) === rev);
|
|
1697
|
+
if (end < 0) throw new SchemaMigrationError(`Stamp revision '${ctx.revision}' is not a local migration.`);
|
|
1698
|
+
const toStamp = ids.slice(0, end + 1);
|
|
1699
|
+
const productVersion = efProductVersion(project);
|
|
1700
|
+
const client = new Client2({ connectionString: ctx.dsn, application_name: connectionApplicationName() });
|
|
1701
|
+
await client.connect();
|
|
1702
|
+
try {
|
|
1703
|
+
await client.query("BEGIN");
|
|
1704
|
+
await client.query(
|
|
1705
|
+
`CREATE TABLE IF NOT EXISTS "__EFMigrationsHistory" (
|
|
1706
|
+
"MigrationId" character varying(150) NOT NULL,
|
|
1707
|
+
"ProductVersion" character varying(32) NOT NULL,
|
|
1708
|
+
CONSTRAINT "PK___EFMigrationsHistory" PRIMARY KEY ("MigrationId"))`
|
|
1709
|
+
);
|
|
1710
|
+
await client.query(`DELETE FROM "__EFMigrationsHistory" WHERE NOT ("MigrationId" = ANY($1::text[]))`, [toStamp]);
|
|
1711
|
+
for (const id of toStamp) {
|
|
1712
|
+
await client.query(
|
|
1713
|
+
`INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") VALUES ($1, $2)
|
|
1714
|
+
ON CONFLICT ("MigrationId") DO NOTHING`,
|
|
1715
|
+
[id, productVersion]
|
|
1716
|
+
);
|
|
1717
|
+
}
|
|
1718
|
+
await client.query("COMMIT");
|
|
1719
|
+
} catch (err) {
|
|
1720
|
+
await client.query("ROLLBACK").catch(() => void 0);
|
|
1721
|
+
throw err;
|
|
1722
|
+
} finally {
|
|
1723
|
+
await client.end();
|
|
1724
|
+
}
|
|
1725
|
+
return efVersion(toStamp[toStamp.length - 1]);
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
// scripts/lakebase/adapters/efcore-adapter.ts
|
|
1729
|
+
async function buildDsn4(args) {
|
|
1730
|
+
const result = await getConnection({
|
|
1731
|
+
output: "dsn",
|
|
1732
|
+
instance: args.instance,
|
|
1733
|
+
branch: args.branch,
|
|
1734
|
+
database: args.database,
|
|
1735
|
+
endpointName: args.endpointName
|
|
1736
|
+
});
|
|
1737
|
+
return result.url;
|
|
1738
|
+
}
|
|
1739
|
+
function errMsg(err) {
|
|
1740
|
+
return err instanceof Error ? err.message : String(err);
|
|
1741
|
+
}
|
|
1742
|
+
function listEfCoreFiles(projectDir) {
|
|
1743
|
+
return localEfMigrationIds(projectDir).map((id) => ({
|
|
1744
|
+
version: efVersion(id),
|
|
1745
|
+
filename: `${id}.cs`,
|
|
1746
|
+
description: efDescription(id),
|
|
1747
|
+
type: "CSharp",
|
|
1748
|
+
tool: "efcore"
|
|
1749
|
+
}));
|
|
1750
|
+
}
|
|
1751
|
+
var EfCoreAdapter = {
|
|
1752
|
+
id: "efcore",
|
|
1753
|
+
languages: ["dotnet"],
|
|
1754
|
+
/** A .NET project (root marker) with a *.csproj that references Microsoft.EntityFrameworkCore.Design. */
|
|
1755
|
+
detect(projectDir) {
|
|
1756
|
+
return isDotnetProject(projectDir) && findEfCoreProject(projectDir) !== null;
|
|
1757
|
+
},
|
|
1758
|
+
async apply(args) {
|
|
1759
|
+
try {
|
|
1760
|
+
const dsn = await buildDsn4(args);
|
|
1761
|
+
const r = await applyEfcore({ projectDir: args.projectDir, dsn });
|
|
1762
|
+
return {
|
|
1763
|
+
applied_migrations: r.applied,
|
|
1764
|
+
status: r.alreadyAtLatest ? "noop" : "ok",
|
|
1765
|
+
tool_specific: { alreadyAtLatest: r.alreadyAtLatest, tool: r.tool }
|
|
1766
|
+
};
|
|
1767
|
+
} catch (err) {
|
|
1768
|
+
return { applied_migrations: [], status: "error", error: errMsg(err) };
|
|
1769
|
+
}
|
|
1770
|
+
},
|
|
1771
|
+
async rollback(args) {
|
|
1772
|
+
try {
|
|
1773
|
+
const dsn = await buildDsn4(args);
|
|
1774
|
+
const r = await rollbackEfcore({ projectDir: args.projectDir, dsn, target: args.target });
|
|
1775
|
+
return {
|
|
1776
|
+
rolled_back: r.rolledBack,
|
|
1777
|
+
status: r.rolledBack.length === 0 ? "noop" : "ok",
|
|
1778
|
+
tool_specific: { tool: r.tool }
|
|
1779
|
+
};
|
|
1780
|
+
} catch (err) {
|
|
1781
|
+
return { rolled_back: [], status: "error", error: errMsg(err) };
|
|
1782
|
+
}
|
|
1783
|
+
},
|
|
1784
|
+
async stamp(args) {
|
|
1785
|
+
try {
|
|
1786
|
+
const dsn = await buildDsn4(args);
|
|
1787
|
+
const stamped = await stampEfcore({ projectDir: args.projectDir, dsn, revision: args.revision });
|
|
1788
|
+
return { status: "ok", stamped_revision: stamped, tool_specific: { tool: "efcore" } };
|
|
1789
|
+
} catch (err) {
|
|
1790
|
+
return { status: "error", stamped_revision: null, error: errMsg(err) };
|
|
1791
|
+
}
|
|
1792
|
+
},
|
|
1793
|
+
async status(args) {
|
|
1794
|
+
try {
|
|
1795
|
+
const dsn = await buildDsn4(args);
|
|
1796
|
+
const r = await statusEfcore({ projectDir: args.projectDir, dsn });
|
|
1797
|
+
return {
|
|
1798
|
+
applied_version: r.current ?? null,
|
|
1799
|
+
pending: r.pending,
|
|
1800
|
+
applied: [],
|
|
1801
|
+
status: "ok",
|
|
1802
|
+
tool_specific: { tool: r.tool }
|
|
1803
|
+
};
|
|
1804
|
+
} catch (err) {
|
|
1805
|
+
return { applied_version: null, pending: [], applied: [], status: "error", error: errMsg(err) };
|
|
1806
|
+
}
|
|
1807
|
+
},
|
|
1808
|
+
async list(args) {
|
|
1809
|
+
return { files: listEfCoreFiles(args.projectDir) };
|
|
1810
|
+
},
|
|
1811
|
+
// baseline: use stamp (EF has no separate baseline concept).
|
|
1812
|
+
async newMigration(args) {
|
|
1813
|
+
try {
|
|
1814
|
+
const created = await createEfcoreMigration({ projectDir: args.projectDir, slug: args.slug });
|
|
1815
|
+
return {
|
|
1816
|
+
status: "ok",
|
|
1817
|
+
version: efVersion(created.id),
|
|
1818
|
+
filename: path11.basename(created.path),
|
|
1819
|
+
path: created.path
|
|
1820
|
+
};
|
|
1821
|
+
} catch (err) {
|
|
1822
|
+
return { status: "error", version: "", filename: "", path: "", error: errMsg(err) };
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
};
|
|
1826
|
+
registerSchemaMigrationAdapter(EfCoreAdapter);
|
|
1827
|
+
|
|
1412
1828
|
// scripts/lakebase/schema-migrate.ts
|
|
1413
1829
|
var SchemaMigrationError = class extends Error {
|
|
1414
1830
|
constructor(message, cause) {
|
|
@@ -1418,8 +1834,11 @@ var SchemaMigrationError = class extends Error {
|
|
|
1418
1834
|
}
|
|
1419
1835
|
cause;
|
|
1420
1836
|
};
|
|
1421
|
-
function toolForLanguage(language) {
|
|
1837
|
+
function toolForLanguage(language, projectDir) {
|
|
1422
1838
|
switch (language) {
|
|
1839
|
+
case "dotnet":
|
|
1840
|
+
if (projectDir && isDotnetFlywayProject(projectDir)) return "flyway";
|
|
1841
|
+
return "efcore";
|
|
1423
1842
|
case "java":
|
|
1424
1843
|
case "kotlin":
|
|
1425
1844
|
return "flyway";
|
|
@@ -1430,7 +1849,7 @@ function toolForLanguage(language) {
|
|
|
1430
1849
|
}
|
|
1431
1850
|
}
|
|
1432
1851
|
function adapterFor(projectDir, language) {
|
|
1433
|
-
const override = language ? toolForLanguage(language) : void 0;
|
|
1852
|
+
const override = language ? toolForLanguage(language, projectDir) : void 0;
|
|
1434
1853
|
return resolveSchemaMigrationAdapter(projectDir, override);
|
|
1435
1854
|
}
|
|
1436
1855
|
function migrationTimestamp(now = /* @__PURE__ */ new Date()) {
|
|
@@ -1494,7 +1913,7 @@ Usage:
|
|
|
1494
1913
|
|
|
1495
1914
|
Flags:
|
|
1496
1915
|
--project-dir <dir> Project root (default: cwd)
|
|
1497
|
-
--language <lang> java | kotlin | python | nodejs (default: auto-detect)
|
|
1916
|
+
--language <lang> java | kotlin | python | nodejs | dotnet (default: auto-detect)
|
|
1498
1917
|
--message, -m <txt> Message for the merge revision (Alembic; default: "merge heads")
|
|
1499
1918
|
--dry-run Report the heads without creating a merge revision
|
|
1500
1919
|
--pretty Pretty-print JSON output
|