@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/infra-runner.ts
|
|
4
|
-
import * as
|
|
5
|
-
import * as
|
|
4
|
+
import * as fs12 from "fs";
|
|
5
|
+
import * as path13 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(path14) {
|
|
284
|
+
if (!path14.includes("/branches/")) return null;
|
|
285
|
+
const leaf = path14.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";
|
|
@@ -511,16 +511,16 @@ function isAllSchemas(schema) {
|
|
|
511
511
|
}
|
|
512
512
|
function buildSchemaQuery(schema) {
|
|
513
513
|
const cols = "c.table_schema, c.table_name, c.column_name, c.data_type";
|
|
514
|
-
const
|
|
514
|
+
const join12 = "FROM information_schema.columns c JOIN pg_tables t ON c.table_name = t.tablename AND c.table_schema = t.schemaname ";
|
|
515
515
|
if (isAllSchemas(schema)) {
|
|
516
516
|
return {
|
|
517
|
-
text: `SELECT ${cols} ` +
|
|
517
|
+
text: `SELECT ${cols} ` + join12 + `WHERE ${SYSTEM_SCHEMA_FILTER} ORDER BY c.table_schema, c.table_name, c.ordinal_position`,
|
|
518
518
|
values: []
|
|
519
519
|
};
|
|
520
520
|
}
|
|
521
521
|
const one = (schema ?? "").trim() || "public";
|
|
522
522
|
return {
|
|
523
|
-
text: `SELECT ${cols} ` +
|
|
523
|
+
text: `SELECT ${cols} ` + join12 + "WHERE c.table_schema = $1 ORDER BY c.table_name, c.ordinal_position",
|
|
524
524
|
values: [one]
|
|
525
525
|
};
|
|
526
526
|
}
|
|
@@ -684,25 +684,87 @@ function dbcli3(args) {
|
|
|
684
684
|
}
|
|
685
685
|
|
|
686
686
|
// scripts/lakebase/schema-migrate.ts
|
|
687
|
-
import * as
|
|
688
|
-
import * as
|
|
687
|
+
import * as fs11 from "fs";
|
|
688
|
+
import * as path12 from "path";
|
|
689
|
+
|
|
690
|
+
// scripts/lakebase/dotnet-project.ts
|
|
691
|
+
import * as fs4 from "fs";
|
|
692
|
+
import * as path3 from "path";
|
|
693
|
+
var EF_DESIGN_PACKAGE = "Microsoft.EntityFrameworkCore.Design";
|
|
694
|
+
var DOTNET_FLYWAY_DIR = path3.join("db", "migrations");
|
|
695
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set(["bin", "obj", "node_modules", ".git", "client", "tests", "test"]);
|
|
696
|
+
function safeReaddir(dir) {
|
|
697
|
+
try {
|
|
698
|
+
return fs4.readdirSync(dir, { withFileTypes: true });
|
|
699
|
+
} catch {
|
|
700
|
+
return [];
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
function hasDotnetMarker(dir) {
|
|
704
|
+
return safeReaddir(dir).some(
|
|
705
|
+
(e) => e.isFile() && /\.(sln|slnx|csproj)$/i.test(e.name)
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
function findCsprojFiles(projectDir) {
|
|
709
|
+
const found = [];
|
|
710
|
+
const walk = (dir, depth) => {
|
|
711
|
+
for (const e of safeReaddir(dir)) {
|
|
712
|
+
const full = path3.join(dir, e.name);
|
|
713
|
+
if (e.isFile() && e.name.toLowerCase().endsWith(".csproj")) {
|
|
714
|
+
found.push(full);
|
|
715
|
+
} else if (e.isDirectory() && depth < 2 && !SKIP_DIRS.has(e.name.toLowerCase()) && !e.name.startsWith(".")) {
|
|
716
|
+
walk(full, depth + 1);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
};
|
|
720
|
+
walk(projectDir, 0);
|
|
721
|
+
return found.sort();
|
|
722
|
+
}
|
|
723
|
+
function findEfCoreProject(projectDir) {
|
|
724
|
+
for (const csproj of findCsprojFiles(projectDir)) {
|
|
725
|
+
let content = "";
|
|
726
|
+
try {
|
|
727
|
+
content = fs4.readFileSync(csproj, "utf-8");
|
|
728
|
+
} catch {
|
|
729
|
+
continue;
|
|
730
|
+
}
|
|
731
|
+
if (content.includes(EF_DESIGN_PACKAGE)) {
|
|
732
|
+
const dir = path3.dirname(csproj);
|
|
733
|
+
return { csproj, projectDir: dir, migrationsDir: path3.join(dir, "Migrations") };
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
return null;
|
|
737
|
+
}
|
|
738
|
+
function isDotnetProject(projectDir) {
|
|
739
|
+
return hasDotnetMarker(projectDir);
|
|
740
|
+
}
|
|
741
|
+
function isDotnetFlywayProject(projectDir) {
|
|
742
|
+
return isDotnetProject(projectDir) && fs4.existsSync(path3.join(projectDir, DOTNET_FLYWAY_DIR));
|
|
743
|
+
}
|
|
744
|
+
var MAVEN_FLYWAY_DIR = path3.join("src", "main", "resources", "db", "migration");
|
|
745
|
+
function flywayMigrationsDir(projectDir) {
|
|
746
|
+
const maven = path3.join(projectDir, MAVEN_FLYWAY_DIR);
|
|
747
|
+
if (fs4.existsSync(maven) || fs4.existsSync(path3.join(projectDir, "pom.xml"))) return maven;
|
|
748
|
+
if (isDotnetFlywayProject(projectDir)) return path3.join(projectDir, DOTNET_FLYWAY_DIR);
|
|
749
|
+
return maven;
|
|
750
|
+
}
|
|
689
751
|
|
|
690
752
|
// scripts/lakebase/adapters/alembic-adapter.ts
|
|
691
|
-
import * as
|
|
692
|
-
import * as
|
|
753
|
+
import * as fs6 from "fs";
|
|
754
|
+
import * as path5 from "path";
|
|
693
755
|
|
|
694
756
|
// scripts/lakebase/schema-migrate-runners/alembic.ts
|
|
695
757
|
import { spawn } from "child_process";
|
|
696
|
-
import * as
|
|
697
|
-
import * as
|
|
758
|
+
import * as fs5 from "fs";
|
|
759
|
+
import * as path4 from "path";
|
|
698
760
|
function resolveAlembicBin(projectDir) {
|
|
699
761
|
const candidates = [
|
|
700
|
-
|
|
701
|
-
|
|
762
|
+
path4.join(projectDir, ".venv", "bin", "alembic"),
|
|
763
|
+
path4.join(projectDir, "venv", "bin", "alembic")
|
|
702
764
|
];
|
|
703
765
|
for (const candidate of candidates) {
|
|
704
766
|
try {
|
|
705
|
-
if (
|
|
767
|
+
if (fs5.existsSync(candidate)) return candidate;
|
|
706
768
|
} catch {
|
|
707
769
|
}
|
|
708
770
|
}
|
|
@@ -712,7 +774,7 @@ function spawnAlembic(projectDir, args, dsn) {
|
|
|
712
774
|
return new Promise((resolve, reject) => {
|
|
713
775
|
const bin = resolveAlembicBin(projectDir);
|
|
714
776
|
const env = { ...process.env };
|
|
715
|
-
env.PYTHONPATH = [projectDir, process.env.PYTHONPATH].filter(Boolean).join(
|
|
777
|
+
env.PYTHONPATH = [projectDir, process.env.PYTHONPATH].filter(Boolean).join(path4.delimiter);
|
|
716
778
|
if (dsn) env.DATABASE_URL = dsn;
|
|
717
779
|
const child = spawn(bin, args, {
|
|
718
780
|
cwd: projectDir,
|
|
@@ -760,10 +822,10 @@ async function createAlembicRevision(opts) {
|
|
|
760
822
|
const m = stdout.match(/Generating\s+(\S+\.py)/);
|
|
761
823
|
if (m) return m[1].trim();
|
|
762
824
|
for (const rel of ["migrations/versions", "alembic/versions"]) {
|
|
763
|
-
const dir =
|
|
764
|
-
if (!
|
|
765
|
-
const hit =
|
|
766
|
-
if (hit) return
|
|
825
|
+
const dir = path4.join(opts.projectDir, rel);
|
|
826
|
+
if (!fs5.existsSync(dir)) continue;
|
|
827
|
+
const hit = fs5.readdirSync(dir).find((f) => f.startsWith(`${opts.revId}_`) && f.endsWith(".py"));
|
|
828
|
+
if (hit) return path4.join(dir, hit);
|
|
767
829
|
}
|
|
768
830
|
throw new SchemaMigrationError(
|
|
769
831
|
`alembic revision succeeded but the created file could not be located.
|
|
@@ -897,15 +959,15 @@ async function buildDsn(args) {
|
|
|
897
959
|
}
|
|
898
960
|
function findVersionsDir(projectDir) {
|
|
899
961
|
const candidates = [
|
|
900
|
-
|
|
901
|
-
|
|
962
|
+
path5.join(projectDir, "migrations", "versions"),
|
|
963
|
+
path5.join(projectDir, "alembic", "versions")
|
|
902
964
|
];
|
|
903
|
-
return candidates.find((p) =>
|
|
965
|
+
return candidates.find((p) => fs6.existsSync(p));
|
|
904
966
|
}
|
|
905
967
|
function listAlembicFiles(projectDir) {
|
|
906
968
|
const dir = findVersionsDir(projectDir);
|
|
907
969
|
if (!dir) return [];
|
|
908
|
-
const files =
|
|
970
|
+
const files = fs6.readdirSync(dir).filter((f) => f.endsWith(".py") && !f.startsWith("__"));
|
|
909
971
|
return files.map((filename) => {
|
|
910
972
|
const stem = filename.replace(/\.py$/, "");
|
|
911
973
|
const sep = stem.indexOf("_");
|
|
@@ -930,9 +992,9 @@ var AlembicAdapter = {
|
|
|
930
992
|
* here. Callers can still force-select via project.yaml#migration_tool.
|
|
931
993
|
*/
|
|
932
994
|
detect(projectDir) {
|
|
933
|
-
if (
|
|
934
|
-
if (
|
|
935
|
-
if (
|
|
995
|
+
if (fs6.existsSync(path5.join(projectDir, "alembic.ini"))) return true;
|
|
996
|
+
if (fs6.existsSync(path5.join(projectDir, "migrations", "env.py"))) return true;
|
|
997
|
+
if (fs6.existsSync(path5.join(projectDir, "alembic", "env.py"))) return true;
|
|
936
998
|
return false;
|
|
937
999
|
},
|
|
938
1000
|
async apply(args) {
|
|
@@ -1038,7 +1100,7 @@ var AlembicAdapter = {
|
|
|
1038
1100
|
autogenerate: !!args.autogenerate,
|
|
1039
1101
|
dsn
|
|
1040
1102
|
});
|
|
1041
|
-
return { status: "ok", version: revId, filename:
|
|
1103
|
+
return { status: "ok", version: revId, filename: path5.basename(created), path: created };
|
|
1042
1104
|
} catch (err) {
|
|
1043
1105
|
return {
|
|
1044
1106
|
status: "error",
|
|
@@ -1055,7 +1117,7 @@ var AlembicAdapter = {
|
|
|
1055
1117
|
if (heads.length <= 1) return { status: "noop", headsBefore: heads };
|
|
1056
1118
|
if (args.dryRun) return { status: "ok", headsBefore: heads };
|
|
1057
1119
|
const created = await mergeAlembicHeads(args.projectDir, args.message ?? "merge heads");
|
|
1058
|
-
const mergeRevision =
|
|
1120
|
+
const mergeRevision = path5.basename(created).replace(/\.py$/, "").split("_")[0];
|
|
1059
1121
|
return { status: "ok", headsBefore: heads, mergeRevision, path: created };
|
|
1060
1122
|
} catch (err) {
|
|
1061
1123
|
return {
|
|
@@ -1069,12 +1131,12 @@ var AlembicAdapter = {
|
|
|
1069
1131
|
registerSchemaMigrationAdapter(AlembicAdapter);
|
|
1070
1132
|
|
|
1071
1133
|
// scripts/lakebase/adapters/flyway-adapter.ts
|
|
1072
|
-
import * as
|
|
1073
|
-
import * as
|
|
1134
|
+
import * as fs7 from "fs";
|
|
1135
|
+
import * as path7 from "path";
|
|
1074
1136
|
|
|
1075
1137
|
// scripts/lakebase/schema-migrate-runners/flyway.ts
|
|
1076
1138
|
import { spawn as spawn2 } from "child_process";
|
|
1077
|
-
import * as
|
|
1139
|
+
import * as path6 from "path";
|
|
1078
1140
|
function dsnToFlywayEnv(dsn) {
|
|
1079
1141
|
const u = new URL(dsn);
|
|
1080
1142
|
const user = decodeURIComponent(u.username);
|
|
@@ -1084,7 +1146,7 @@ function dsnToFlywayEnv(dsn) {
|
|
|
1084
1146
|
return { url, user, password };
|
|
1085
1147
|
}
|
|
1086
1148
|
function migrationsLocation(projectDir) {
|
|
1087
|
-
return `filesystem:${
|
|
1149
|
+
return `filesystem:${flywayMigrationsDir(projectDir)}`;
|
|
1088
1150
|
}
|
|
1089
1151
|
function runFlyway(ctx, args) {
|
|
1090
1152
|
const { url, user, password } = dsnToFlywayEnv(ctx.dsn);
|
|
@@ -1185,7 +1247,7 @@ async function statusFlyway(ctx) {
|
|
|
1185
1247
|
if (state === "SUCCESS" || state === "BASELINE") {
|
|
1186
1248
|
current = m.version;
|
|
1187
1249
|
} else if (state === "PENDING") {
|
|
1188
|
-
const filename = m.filepath ?
|
|
1250
|
+
const filename = m.filepath ? path6.basename(m.filepath) : `V${m.version}__migration.sql`;
|
|
1189
1251
|
pending.push({
|
|
1190
1252
|
version: m.version,
|
|
1191
1253
|
filename,
|
|
@@ -1208,9 +1270,9 @@ async function buildDsn2(args) {
|
|
|
1208
1270
|
return result.url;
|
|
1209
1271
|
}
|
|
1210
1272
|
function listFlywayFiles(projectDir) {
|
|
1211
|
-
const dir =
|
|
1212
|
-
if (!
|
|
1213
|
-
const files =
|
|
1273
|
+
const dir = flywayMigrationsDir(projectDir);
|
|
1274
|
+
if (!fs7.existsSync(dir)) return [];
|
|
1275
|
+
const files = fs7.readdirSync(dir).filter((f) => /^V\d+(\.\d+)*__.+\.sql$/.test(f));
|
|
1214
1276
|
return files.map((filename) => {
|
|
1215
1277
|
const m = filename.match(/^V(\d+(?:\.\d+)*)__(.+)\.sql$/);
|
|
1216
1278
|
const version = m[1];
|
|
@@ -1231,9 +1293,9 @@ function versionCompare(a, b) {
|
|
|
1231
1293
|
}
|
|
1232
1294
|
var FlywayAdapter = {
|
|
1233
1295
|
id: "flyway",
|
|
1234
|
-
languages: ["java", "kotlin"],
|
|
1296
|
+
languages: ["java", "kotlin", "dotnet"],
|
|
1235
1297
|
detect(projectDir) {
|
|
1236
|
-
return
|
|
1298
|
+
return fs7.existsSync(path7.join(projectDir, "pom.xml")) || isDotnetFlywayProject(projectDir);
|
|
1237
1299
|
},
|
|
1238
1300
|
async apply(args) {
|
|
1239
1301
|
const dsn = await buildDsn2(args);
|
|
@@ -1291,14 +1353,14 @@ var FlywayAdapter = {
|
|
|
1291
1353
|
// optional-protocol shape makes this additive.
|
|
1292
1354
|
async newMigration(args) {
|
|
1293
1355
|
try {
|
|
1294
|
-
const dir =
|
|
1295
|
-
|
|
1356
|
+
const dir = flywayMigrationsDir(args.projectDir);
|
|
1357
|
+
fs7.mkdirSync(dir, { recursive: true });
|
|
1296
1358
|
const version = migrationTimestamp();
|
|
1297
1359
|
const slug = migrationSlug2(args.slug);
|
|
1298
1360
|
const filename = `V${version}__${slug}.sql`;
|
|
1299
|
-
const full =
|
|
1300
|
-
if (
|
|
1301
|
-
|
|
1361
|
+
const full = path7.join(dir, filename);
|
|
1362
|
+
if (fs7.existsSync(full)) throw new Error(`${filename} already exists`);
|
|
1363
|
+
fs7.writeFileSync(
|
|
1302
1364
|
full,
|
|
1303
1365
|
`-- V${version}: ${args.slug}
|
|
1304
1366
|
-- Flyway migration (write your DDL/DML below).
|
|
@@ -1320,18 +1382,18 @@ var FlywayAdapter = {
|
|
|
1320
1382
|
registerSchemaMigrationAdapter(FlywayAdapter);
|
|
1321
1383
|
|
|
1322
1384
|
// scripts/lakebase/adapters/knex-adapter.ts
|
|
1323
|
-
import * as
|
|
1324
|
-
import * as
|
|
1385
|
+
import * as fs9 from "fs";
|
|
1386
|
+
import * as path9 from "path";
|
|
1325
1387
|
|
|
1326
1388
|
// scripts/lakebase/schema-migrate-runners/knex.ts
|
|
1327
1389
|
import { spawn as spawn3 } from "child_process";
|
|
1328
|
-
import * as
|
|
1329
|
-
import * as
|
|
1390
|
+
import * as fs8 from "fs";
|
|
1391
|
+
import * as path8 from "path";
|
|
1330
1392
|
var KNEXFILE_VARIANTS = ["knexfile.js", "knexfile.ts", "knexfile.mjs", "knexfile.cjs"];
|
|
1331
1393
|
function findKnexfile(projectDir) {
|
|
1332
1394
|
for (const name of KNEXFILE_VARIANTS) {
|
|
1333
|
-
const p =
|
|
1334
|
-
if (
|
|
1395
|
+
const p = path8.join(projectDir, name);
|
|
1396
|
+
if (fs8.existsSync(p)) return p;
|
|
1335
1397
|
}
|
|
1336
1398
|
return void 0;
|
|
1337
1399
|
}
|
|
@@ -1483,9 +1545,9 @@ async function buildDsn3(args) {
|
|
|
1483
1545
|
}
|
|
1484
1546
|
var KNEXFILE_VARIANTS2 = ["knexfile.js", "knexfile.ts", "knexfile.mjs", "knexfile.cjs"];
|
|
1485
1547
|
function listKnexFiles(projectDir) {
|
|
1486
|
-
const dir =
|
|
1487
|
-
if (!
|
|
1488
|
-
const files =
|
|
1548
|
+
const dir = path9.join(projectDir, "migrations");
|
|
1549
|
+
if (!fs9.existsSync(dir)) return [];
|
|
1550
|
+
const files = fs9.readdirSync(dir).filter((f) => (f.endsWith(".js") || f.endsWith(".ts")) && !f.startsWith("."));
|
|
1489
1551
|
return files.map((filename) => {
|
|
1490
1552
|
const stem = filename.replace(/\.(js|ts)$/, "");
|
|
1491
1553
|
const m = stem.match(/^(\d{14})_(.+)$/);
|
|
@@ -1505,7 +1567,7 @@ var KnexAdapter = {
|
|
|
1505
1567
|
* project.yaml#migration_tool.
|
|
1506
1568
|
*/
|
|
1507
1569
|
detect(projectDir) {
|
|
1508
|
-
return KNEXFILE_VARIANTS2.some((name) =>
|
|
1570
|
+
return KNEXFILE_VARIANTS2.some((name) => fs9.existsSync(path9.join(projectDir, name)));
|
|
1509
1571
|
},
|
|
1510
1572
|
async apply(args) {
|
|
1511
1573
|
const dsn = await buildDsn3(args);
|
|
@@ -1578,9 +1640,9 @@ var KnexAdapter = {
|
|
|
1578
1640
|
async newMigration(args) {
|
|
1579
1641
|
try {
|
|
1580
1642
|
const created = await createKnexMigration({ projectDir: args.projectDir, slug: migrationSlug2(args.slug) });
|
|
1581
|
-
const stem =
|
|
1643
|
+
const stem = path9.basename(created).replace(/\.(js|ts)$/, "");
|
|
1582
1644
|
const version = stem.match(/^(\d{14})_/)?.[1] ?? stem;
|
|
1583
|
-
return { status: "ok", version, filename:
|
|
1645
|
+
return { status: "ok", version, filename: path9.basename(created), path: created };
|
|
1584
1646
|
} catch (err) {
|
|
1585
1647
|
return {
|
|
1586
1648
|
status: "error",
|
|
@@ -1594,6 +1656,360 @@ var KnexAdapter = {
|
|
|
1594
1656
|
};
|
|
1595
1657
|
registerSchemaMigrationAdapter(KnexAdapter);
|
|
1596
1658
|
|
|
1659
|
+
// scripts/lakebase/adapters/efcore-adapter.ts
|
|
1660
|
+
import * as path11 from "path";
|
|
1661
|
+
|
|
1662
|
+
// scripts/lakebase/schema-migrate-runners/efcore.ts
|
|
1663
|
+
import { spawn as spawn4 } from "child_process";
|
|
1664
|
+
import * as fs10 from "fs";
|
|
1665
|
+
import * as path10 from "path";
|
|
1666
|
+
import { Client as Client3 } from "pg";
|
|
1667
|
+
var MIGRATION_ID = /^(\d{14})_(.+)$/;
|
|
1668
|
+
function efVersion(id) {
|
|
1669
|
+
return MIGRATION_ID.exec(id)?.[1] ?? id;
|
|
1670
|
+
}
|
|
1671
|
+
function efDescription(id) {
|
|
1672
|
+
const name = MIGRATION_ID.exec(id)?.[2] ?? id;
|
|
1673
|
+
return name.replace(/_/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2");
|
|
1674
|
+
}
|
|
1675
|
+
function dsnToNpgsql(dsn) {
|
|
1676
|
+
const u = new URL(dsn);
|
|
1677
|
+
const parts = [
|
|
1678
|
+
`Host=${u.hostname}`,
|
|
1679
|
+
`Port=${u.port || "5432"}`,
|
|
1680
|
+
`Database=${decodeURIComponent(u.pathname.replace(/^\//, "")) || "databricks_postgres"}`
|
|
1681
|
+
];
|
|
1682
|
+
if (u.username) parts.push(`Username=${decodeURIComponent(u.username)}`);
|
|
1683
|
+
if (u.password) parts.push(`Password=${decodeURIComponent(u.password)}`);
|
|
1684
|
+
const sslmode = (u.searchParams.get("sslmode") ?? "require").toLowerCase();
|
|
1685
|
+
const ssl = {
|
|
1686
|
+
disable: "Disable",
|
|
1687
|
+
allow: "Allow",
|
|
1688
|
+
prefer: "Prefer",
|
|
1689
|
+
require: "Require",
|
|
1690
|
+
"verify-ca": "VerifyCA",
|
|
1691
|
+
"verify-full": "VerifyFull"
|
|
1692
|
+
};
|
|
1693
|
+
parts.push(`SSL Mode=${ssl[sslmode] ?? "Require"}`);
|
|
1694
|
+
const app = u.searchParams.get("application_name");
|
|
1695
|
+
if (app) parts.push(`Application Name=${app}`);
|
|
1696
|
+
return parts.map((p) => p.replace(/;/g, "")).join(";");
|
|
1697
|
+
}
|
|
1698
|
+
function parseEfJson(stdout) {
|
|
1699
|
+
const body = stdout.split(/\r?\n/).filter((l) => l.startsWith("data:")).map((l) => l.slice("data:".length)).join("\n").trim();
|
|
1700
|
+
if (!body) {
|
|
1701
|
+
throw new SchemaMigrationError(`dotnet ef returned no JSON payload: ${stdout.slice(0, 400)}`);
|
|
1702
|
+
}
|
|
1703
|
+
try {
|
|
1704
|
+
return JSON.parse(body);
|
|
1705
|
+
} catch (err) {
|
|
1706
|
+
throw new SchemaMigrationError(
|
|
1707
|
+
`dotnet ef JSON parse failed: ${err instanceof Error ? err.message : String(err)}.
|
|
1708
|
+
Body (first 400 chars): ${body.slice(0, 400)}`
|
|
1709
|
+
);
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
function requireProject(projectDir) {
|
|
1713
|
+
const project = findEfCoreProject(projectDir);
|
|
1714
|
+
if (!project) {
|
|
1715
|
+
throw new SchemaMigrationError(
|
|
1716
|
+
`No EF Core project found under ${projectDir}. Expected a *.csproj that references ${EF_DESIGN_PACKAGE}.`
|
|
1717
|
+
);
|
|
1718
|
+
}
|
|
1719
|
+
return project;
|
|
1720
|
+
}
|
|
1721
|
+
function spawnDotnet(projectDir, args, env) {
|
|
1722
|
+
return new Promise((resolve, reject) => {
|
|
1723
|
+
const child = spawn4("dotnet", args, {
|
|
1724
|
+
cwd: projectDir,
|
|
1725
|
+
env: { ...process.env, DOTNET_NOLOGO: "1", DOTNET_CLI_TELEMETRY_OPTOUT: "1", ...env },
|
|
1726
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
1727
|
+
});
|
|
1728
|
+
let stdout = "";
|
|
1729
|
+
let stderr = "";
|
|
1730
|
+
child.stdout.on("data", (chunk) => {
|
|
1731
|
+
stdout += chunk.toString("utf8");
|
|
1732
|
+
});
|
|
1733
|
+
child.stderr.on("data", (chunk) => {
|
|
1734
|
+
stderr += chunk.toString("utf8");
|
|
1735
|
+
});
|
|
1736
|
+
child.on("error", (err) => {
|
|
1737
|
+
reject(
|
|
1738
|
+
new SchemaMigrationError(
|
|
1739
|
+
`Could not spawn dotnet. Is the .NET SDK installed and on PATH? ${err.message}`,
|
|
1740
|
+
err
|
|
1741
|
+
)
|
|
1742
|
+
);
|
|
1743
|
+
});
|
|
1744
|
+
child.on("close", (code) => {
|
|
1745
|
+
if (code === 0) {
|
|
1746
|
+
resolve({ stdout, stderr });
|
|
1747
|
+
} else {
|
|
1748
|
+
reject(
|
|
1749
|
+
new SchemaMigrationError(
|
|
1750
|
+
`dotnet ${args.join(" ")} exited with code ${code}.
|
|
1751
|
+
stdout: ${stdout}
|
|
1752
|
+
stderr: ${stderr}`
|
|
1753
|
+
)
|
|
1754
|
+
);
|
|
1755
|
+
}
|
|
1756
|
+
});
|
|
1757
|
+
});
|
|
1758
|
+
}
|
|
1759
|
+
var restored = /* @__PURE__ */ new Set();
|
|
1760
|
+
async function ensureRestored(projectDir, csproj) {
|
|
1761
|
+
if (restored.has(projectDir)) return;
|
|
1762
|
+
if (fs10.existsSync(path10.join(projectDir, ".config", "dotnet-tools.json"))) {
|
|
1763
|
+
await spawnDotnet(projectDir, ["tool", "restore"], {});
|
|
1764
|
+
}
|
|
1765
|
+
await spawnDotnet(projectDir, ["restore", csproj], {});
|
|
1766
|
+
restored.add(projectDir);
|
|
1767
|
+
}
|
|
1768
|
+
async function runEf(projectDir, args, dsn, opts = {}) {
|
|
1769
|
+
const project = requireProject(projectDir);
|
|
1770
|
+
const csproj = path10.relative(projectDir, project.csproj) || project.csproj;
|
|
1771
|
+
await ensureRestored(projectDir, csproj);
|
|
1772
|
+
const full = ["ef", ...args, "--project", csproj, "--prefix-output"];
|
|
1773
|
+
if (opts.noBuild) full.push("--no-build");
|
|
1774
|
+
const env = {};
|
|
1775
|
+
if (dsn) {
|
|
1776
|
+
env.DATABASE_URL = dsn;
|
|
1777
|
+
if (process.env.LAKEBASE_EFCORE_CONNECTION_ARG === "1") full.push("--connection", dsnToNpgsql(dsn));
|
|
1778
|
+
}
|
|
1779
|
+
return spawnDotnet(projectDir, full, env);
|
|
1780
|
+
}
|
|
1781
|
+
async function listEfMigrations(ctx, opts = {}) {
|
|
1782
|
+
const { stdout, stderr } = await runEf(ctx.projectDir, ["migrations", "list", "--json"], ctx.dsn, opts);
|
|
1783
|
+
const entries = parseEfJson(stdout);
|
|
1784
|
+
if (ctx.dsn && entries.some((e) => e.applied === null)) {
|
|
1785
|
+
const warning = stdout.split(/\r?\n/).filter((l) => /^(warn|error):/.test(l)).join("\n");
|
|
1786
|
+
throw new SchemaMigrationError(
|
|
1787
|
+
`dotnet ef could not read migration state from the database (check connectivity and credentials).
|
|
1788
|
+
${warning || stderr}`.trim()
|
|
1789
|
+
);
|
|
1790
|
+
}
|
|
1791
|
+
return entries;
|
|
1792
|
+
}
|
|
1793
|
+
async function readEfHistory(dsn) {
|
|
1794
|
+
const client = new Client3({ connectionString: dsn, application_name: connectionApplicationName() });
|
|
1795
|
+
await client.connect();
|
|
1796
|
+
try {
|
|
1797
|
+
const exists = await client.query(`SELECT to_regclass('"__EFMigrationsHistory"') IS NOT NULL AS present`);
|
|
1798
|
+
if (!exists.rows[0]?.present) return null;
|
|
1799
|
+
const r = await client.query(`SELECT "MigrationId" FROM "__EFMigrationsHistory" ORDER BY "MigrationId"`);
|
|
1800
|
+
return r.rows.map((row) => row.MigrationId);
|
|
1801
|
+
} finally {
|
|
1802
|
+
await client.end();
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
function toApplied(entries) {
|
|
1806
|
+
return entries.map((e) => ({ version: efVersion(e.id), description: efDescription(e.id) }));
|
|
1807
|
+
}
|
|
1808
|
+
async function statusEfcore(ctx) {
|
|
1809
|
+
const entries = await listEfMigrations(ctx);
|
|
1810
|
+
const history = await (ctx.readHistory ?? readEfHistory)(ctx.dsn);
|
|
1811
|
+
const current = history && history.length > 0 ? efVersion(history[history.length - 1]) : void 0;
|
|
1812
|
+
const pending = entries.filter((e) => e.applied !== true).map((e) => ({ version: efVersion(e.id), filename: `${e.id}.cs`, description: efDescription(e.id) }));
|
|
1813
|
+
return { current, pending, tool: "efcore" };
|
|
1814
|
+
}
|
|
1815
|
+
async function applyEfcore(ctx) {
|
|
1816
|
+
const before = new Set((await listEfMigrations(ctx)).filter((e) => e.applied).map((e) => e.id));
|
|
1817
|
+
await runEf(ctx.projectDir, ["database", "update"], ctx.dsn, { noBuild: true });
|
|
1818
|
+
const after = await listEfMigrations(ctx, { noBuild: true });
|
|
1819
|
+
const applied = toApplied(after.filter((e) => e.applied && !before.has(e.id)));
|
|
1820
|
+
return { applied, alreadyAtLatest: applied.length === 0, tool: "efcore" };
|
|
1821
|
+
}
|
|
1822
|
+
function resolveEfTarget(target, applied) {
|
|
1823
|
+
const t = target.trim();
|
|
1824
|
+
if (t === "0") return "0";
|
|
1825
|
+
const rel = /^-(\d+)$/.exec(t);
|
|
1826
|
+
if (rel) {
|
|
1827
|
+
const steps = Number(rel[1]);
|
|
1828
|
+
if (steps < 1) throw new SchemaMigrationError(`Invalid rollback step: ${target}`);
|
|
1829
|
+
const index = applied.length - 1 - steps;
|
|
1830
|
+
if (index < -1) {
|
|
1831
|
+
throw new SchemaMigrationError(`Cannot roll back ${steps} migration(s); only ${applied.length} applied.`);
|
|
1832
|
+
}
|
|
1833
|
+
return index === -1 ? "0" : applied[index].id;
|
|
1834
|
+
}
|
|
1835
|
+
const match = applied.find((e) => e.id === t || efVersion(e.id) === t || e.name === t);
|
|
1836
|
+
if (!match) throw new SchemaMigrationError(`Rollback target '${target}' is not an applied migration.`);
|
|
1837
|
+
return match.id;
|
|
1838
|
+
}
|
|
1839
|
+
async function rollbackEfcore(ctx) {
|
|
1840
|
+
const beforeEntries = (await listEfMigrations(ctx)).filter((e) => e.applied);
|
|
1841
|
+
const targetId = resolveEfTarget(ctx.target, beforeEntries);
|
|
1842
|
+
await runEf(ctx.projectDir, ["database", "update", targetId], ctx.dsn, { noBuild: true });
|
|
1843
|
+
const stillApplied = new Set((await listEfMigrations(ctx, { noBuild: true })).filter((e) => e.applied).map((e) => e.id));
|
|
1844
|
+
return { rolledBack: toApplied(beforeEntries.filter((e) => !stillApplied.has(e.id)).reverse()), tool: "efcore" };
|
|
1845
|
+
}
|
|
1846
|
+
function efMigrationName(slug) {
|
|
1847
|
+
const name = slug.split(/[^A-Za-z0-9]+/).filter(Boolean).map((w) => w[0].toUpperCase() + w.slice(1)).join("");
|
|
1848
|
+
if (!name) throw new SchemaMigrationError(`Cannot derive a migration name from '${slug}'.`);
|
|
1849
|
+
return /^[0-9]/.test(name) ? `M${name}` : name;
|
|
1850
|
+
}
|
|
1851
|
+
async function createEfcoreMigration(opts) {
|
|
1852
|
+
const project = requireProject(opts.projectDir);
|
|
1853
|
+
const name = efMigrationName(opts.slug);
|
|
1854
|
+
const existing = new Set(fs10.existsSync(project.migrationsDir) ? fs10.readdirSync(project.migrationsDir) : []);
|
|
1855
|
+
await runEf(opts.projectDir, ["migrations", "add", name]);
|
|
1856
|
+
const created = fs10.readdirSync(project.migrationsDir).filter((f) => !existing.has(f) && MIGRATION_ID.test(f.replace(/\.cs$/, "")) && !f.endsWith(".Designer.cs"));
|
|
1857
|
+
if (created.length !== 1) {
|
|
1858
|
+
throw new SchemaMigrationError(`Expected one new migration file for ${name}, found: ${created.join(", ") || "none"}`);
|
|
1859
|
+
}
|
|
1860
|
+
return { id: created[0].replace(/\.cs$/, ""), path: path10.join(project.migrationsDir, created[0]) };
|
|
1861
|
+
}
|
|
1862
|
+
function localEfMigrationIds(projectDir) {
|
|
1863
|
+
const project = findEfCoreProject(projectDir);
|
|
1864
|
+
if (!project || !fs10.existsSync(project.migrationsDir)) return [];
|
|
1865
|
+
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();
|
|
1866
|
+
}
|
|
1867
|
+
function efProductVersion(project) {
|
|
1868
|
+
try {
|
|
1869
|
+
const csproj = fs10.readFileSync(project.csproj, "utf-8");
|
|
1870
|
+
const m = new RegExp(`${EF_DESIGN_PACKAGE.replace(/\./g, "\\.")}"\\s+Version="([^"]+)"`).exec(csproj);
|
|
1871
|
+
if (m) return m[1];
|
|
1872
|
+
} catch {
|
|
1873
|
+
}
|
|
1874
|
+
return "unknown";
|
|
1875
|
+
}
|
|
1876
|
+
async function stampEfcore(ctx) {
|
|
1877
|
+
const project = requireProject(ctx.projectDir);
|
|
1878
|
+
const ids = localEfMigrationIds(ctx.projectDir);
|
|
1879
|
+
if (ids.length === 0) throw new SchemaMigrationError("No local EF Core migrations to stamp.");
|
|
1880
|
+
const rev = ctx.revision.trim();
|
|
1881
|
+
const end = rev === "head" ? ids.length - 1 : ids.findIndex((id) => id === rev || efVersion(id) === rev);
|
|
1882
|
+
if (end < 0) throw new SchemaMigrationError(`Stamp revision '${ctx.revision}' is not a local migration.`);
|
|
1883
|
+
const toStamp = ids.slice(0, end + 1);
|
|
1884
|
+
const productVersion = efProductVersion(project);
|
|
1885
|
+
const client = new Client3({ connectionString: ctx.dsn, application_name: connectionApplicationName() });
|
|
1886
|
+
await client.connect();
|
|
1887
|
+
try {
|
|
1888
|
+
await client.query("BEGIN");
|
|
1889
|
+
await client.query(
|
|
1890
|
+
`CREATE TABLE IF NOT EXISTS "__EFMigrationsHistory" (
|
|
1891
|
+
"MigrationId" character varying(150) NOT NULL,
|
|
1892
|
+
"ProductVersion" character varying(32) NOT NULL,
|
|
1893
|
+
CONSTRAINT "PK___EFMigrationsHistory" PRIMARY KEY ("MigrationId"))`
|
|
1894
|
+
);
|
|
1895
|
+
await client.query(`DELETE FROM "__EFMigrationsHistory" WHERE NOT ("MigrationId" = ANY($1::text[]))`, [toStamp]);
|
|
1896
|
+
for (const id of toStamp) {
|
|
1897
|
+
await client.query(
|
|
1898
|
+
`INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") VALUES ($1, $2)
|
|
1899
|
+
ON CONFLICT ("MigrationId") DO NOTHING`,
|
|
1900
|
+
[id, productVersion]
|
|
1901
|
+
);
|
|
1902
|
+
}
|
|
1903
|
+
await client.query("COMMIT");
|
|
1904
|
+
} catch (err) {
|
|
1905
|
+
await client.query("ROLLBACK").catch(() => void 0);
|
|
1906
|
+
throw err;
|
|
1907
|
+
} finally {
|
|
1908
|
+
await client.end();
|
|
1909
|
+
}
|
|
1910
|
+
return efVersion(toStamp[toStamp.length - 1]);
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
// scripts/lakebase/adapters/efcore-adapter.ts
|
|
1914
|
+
async function buildDsn4(args) {
|
|
1915
|
+
const result = await getConnection({
|
|
1916
|
+
output: "dsn",
|
|
1917
|
+
instance: args.instance,
|
|
1918
|
+
branch: args.branch,
|
|
1919
|
+
database: args.database,
|
|
1920
|
+
endpointName: args.endpointName
|
|
1921
|
+
});
|
|
1922
|
+
return result.url;
|
|
1923
|
+
}
|
|
1924
|
+
function errMsg(err) {
|
|
1925
|
+
return err instanceof Error ? err.message : String(err);
|
|
1926
|
+
}
|
|
1927
|
+
function listEfCoreFiles(projectDir) {
|
|
1928
|
+
return localEfMigrationIds(projectDir).map((id) => ({
|
|
1929
|
+
version: efVersion(id),
|
|
1930
|
+
filename: `${id}.cs`,
|
|
1931
|
+
description: efDescription(id),
|
|
1932
|
+
type: "CSharp",
|
|
1933
|
+
tool: "efcore"
|
|
1934
|
+
}));
|
|
1935
|
+
}
|
|
1936
|
+
var EfCoreAdapter = {
|
|
1937
|
+
id: "efcore",
|
|
1938
|
+
languages: ["dotnet"],
|
|
1939
|
+
/** A .NET project (root marker) with a *.csproj that references Microsoft.EntityFrameworkCore.Design. */
|
|
1940
|
+
detect(projectDir) {
|
|
1941
|
+
return isDotnetProject(projectDir) && findEfCoreProject(projectDir) !== null;
|
|
1942
|
+
},
|
|
1943
|
+
async apply(args) {
|
|
1944
|
+
try {
|
|
1945
|
+
const dsn = await buildDsn4(args);
|
|
1946
|
+
const r = await applyEfcore({ projectDir: args.projectDir, dsn });
|
|
1947
|
+
return {
|
|
1948
|
+
applied_migrations: r.applied,
|
|
1949
|
+
status: r.alreadyAtLatest ? "noop" : "ok",
|
|
1950
|
+
tool_specific: { alreadyAtLatest: r.alreadyAtLatest, tool: r.tool }
|
|
1951
|
+
};
|
|
1952
|
+
} catch (err) {
|
|
1953
|
+
return { applied_migrations: [], status: "error", error: errMsg(err) };
|
|
1954
|
+
}
|
|
1955
|
+
},
|
|
1956
|
+
async rollback(args) {
|
|
1957
|
+
try {
|
|
1958
|
+
const dsn = await buildDsn4(args);
|
|
1959
|
+
const r = await rollbackEfcore({ projectDir: args.projectDir, dsn, target: args.target });
|
|
1960
|
+
return {
|
|
1961
|
+
rolled_back: r.rolledBack,
|
|
1962
|
+
status: r.rolledBack.length === 0 ? "noop" : "ok",
|
|
1963
|
+
tool_specific: { tool: r.tool }
|
|
1964
|
+
};
|
|
1965
|
+
} catch (err) {
|
|
1966
|
+
return { rolled_back: [], status: "error", error: errMsg(err) };
|
|
1967
|
+
}
|
|
1968
|
+
},
|
|
1969
|
+
async stamp(args) {
|
|
1970
|
+
try {
|
|
1971
|
+
const dsn = await buildDsn4(args);
|
|
1972
|
+
const stamped = await stampEfcore({ projectDir: args.projectDir, dsn, revision: args.revision });
|
|
1973
|
+
return { status: "ok", stamped_revision: stamped, tool_specific: { tool: "efcore" } };
|
|
1974
|
+
} catch (err) {
|
|
1975
|
+
return { status: "error", stamped_revision: null, error: errMsg(err) };
|
|
1976
|
+
}
|
|
1977
|
+
},
|
|
1978
|
+
async status(args) {
|
|
1979
|
+
try {
|
|
1980
|
+
const dsn = await buildDsn4(args);
|
|
1981
|
+
const r = await statusEfcore({ projectDir: args.projectDir, dsn });
|
|
1982
|
+
return {
|
|
1983
|
+
applied_version: r.current ?? null,
|
|
1984
|
+
pending: r.pending,
|
|
1985
|
+
applied: [],
|
|
1986
|
+
status: "ok",
|
|
1987
|
+
tool_specific: { tool: r.tool }
|
|
1988
|
+
};
|
|
1989
|
+
} catch (err) {
|
|
1990
|
+
return { applied_version: null, pending: [], applied: [], status: "error", error: errMsg(err) };
|
|
1991
|
+
}
|
|
1992
|
+
},
|
|
1993
|
+
async list(args) {
|
|
1994
|
+
return { files: listEfCoreFiles(args.projectDir) };
|
|
1995
|
+
},
|
|
1996
|
+
// baseline: use stamp (EF has no separate baseline concept).
|
|
1997
|
+
async newMigration(args) {
|
|
1998
|
+
try {
|
|
1999
|
+
const created = await createEfcoreMigration({ projectDir: args.projectDir, slug: args.slug });
|
|
2000
|
+
return {
|
|
2001
|
+
status: "ok",
|
|
2002
|
+
version: efVersion(created.id),
|
|
2003
|
+
filename: path11.basename(created.path),
|
|
2004
|
+
path: created.path
|
|
2005
|
+
};
|
|
2006
|
+
} catch (err) {
|
|
2007
|
+
return { status: "error", version: "", filename: "", path: "", error: errMsg(err) };
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
};
|
|
2011
|
+
registerSchemaMigrationAdapter(EfCoreAdapter);
|
|
2012
|
+
|
|
1597
2013
|
// scripts/lakebase/schema-migrate.ts
|
|
1598
2014
|
var SchemaMigrationError = class extends Error {
|
|
1599
2015
|
constructor(message, cause) {
|
|
@@ -1603,8 +2019,11 @@ var SchemaMigrationError = class extends Error {
|
|
|
1603
2019
|
}
|
|
1604
2020
|
cause;
|
|
1605
2021
|
};
|
|
1606
|
-
function toolForLanguage(language) {
|
|
2022
|
+
function toolForLanguage(language, projectDir) {
|
|
1607
2023
|
switch (language) {
|
|
2024
|
+
case "dotnet":
|
|
2025
|
+
if (projectDir && isDotnetFlywayProject(projectDir)) return "flyway";
|
|
2026
|
+
return "efcore";
|
|
1608
2027
|
case "java":
|
|
1609
2028
|
case "kotlin":
|
|
1610
2029
|
return "flyway";
|
|
@@ -1615,7 +2034,7 @@ function toolForLanguage(language) {
|
|
|
1615
2034
|
}
|
|
1616
2035
|
}
|
|
1617
2036
|
function adapterFor(projectDir, language) {
|
|
1618
|
-
const override = language ? toolForLanguage(language) : void 0;
|
|
2037
|
+
const override = language ? toolForLanguage(language, projectDir) : void 0;
|
|
1619
2038
|
return resolveSchemaMigrationAdapter(projectDir, override);
|
|
1620
2039
|
}
|
|
1621
2040
|
async function schemaMigrationStatus(args) {
|
|
@@ -1688,8 +2107,8 @@ async function runInfraSuite(args) {
|
|
|
1688
2107
|
duration_ms: Date.now() - start
|
|
1689
2108
|
};
|
|
1690
2109
|
if (args.junitOutput) {
|
|
1691
|
-
|
|
1692
|
-
|
|
2110
|
+
fs12.mkdirSync(path13.dirname(args.junitOutput), { recursive: true });
|
|
2111
|
+
fs12.writeFileSync(args.junitOutput, formatJUnit(result), "utf8");
|
|
1693
2112
|
}
|
|
1694
2113
|
return result;
|
|
1695
2114
|
}
|