@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
|
@@ -1457,7 +1457,7 @@ var require_light = __commonJS({
|
|
|
1457
1457
|
|
|
1458
1458
|
// scripts/lakebase/scm-merge.cli.ts
|
|
1459
1459
|
init_esm_shims();
|
|
1460
|
-
import * as
|
|
1460
|
+
import * as path17 from "path";
|
|
1461
1461
|
|
|
1462
1462
|
// scripts/util/cli-entry.ts
|
|
1463
1463
|
init_esm_shims();
|
|
@@ -2549,7 +2549,7 @@ paginateRest.VERSION = VERSION5;
|
|
|
2549
2549
|
|
|
2550
2550
|
// node_modules/@octokit/plugin-paginate-graphql/dist-bundle/index.js
|
|
2551
2551
|
init_esm_shims();
|
|
2552
|
-
var generateMessage = (
|
|
2552
|
+
var generateMessage = (path18, cursorValue) => `The cursor at "${path18.join(
|
|
2553
2553
|
","
|
|
2554
2554
|
)}" did not change its value "${cursorValue}" after a page transition. Please make sure your that your query is set up correctly.`;
|
|
2555
2555
|
var MissingCursorChange = class extends Error {
|
|
@@ -2590,9 +2590,9 @@ function findPaginatedResourcePath(responseData) {
|
|
|
2590
2590
|
}
|
|
2591
2591
|
return paginatedResourcePath;
|
|
2592
2592
|
}
|
|
2593
|
-
var deepFindPathToProperty = (object, searchProp,
|
|
2593
|
+
var deepFindPathToProperty = (object, searchProp, path18 = []) => {
|
|
2594
2594
|
for (const key of Object.keys(object)) {
|
|
2595
|
-
const currentPath = [...
|
|
2595
|
+
const currentPath = [...path18, key];
|
|
2596
2596
|
const currentValue = object[key];
|
|
2597
2597
|
if (isObject(currentValue)) {
|
|
2598
2598
|
if (currentValue.hasOwnProperty(searchProp)) {
|
|
@@ -2610,12 +2610,12 @@ var deepFindPathToProperty = (object, searchProp, path15 = []) => {
|
|
|
2610
2610
|
}
|
|
2611
2611
|
return [];
|
|
2612
2612
|
};
|
|
2613
|
-
var get = (object,
|
|
2614
|
-
return
|
|
2613
|
+
var get = (object, path18) => {
|
|
2614
|
+
return path18.reduce((current, nextProperty) => current[nextProperty], object);
|
|
2615
2615
|
};
|
|
2616
|
-
var set = (object,
|
|
2617
|
-
const lastProperty =
|
|
2618
|
-
const parentPath = [...
|
|
2616
|
+
var set = (object, path18, mutator) => {
|
|
2617
|
+
const lastProperty = path18[path18.length - 1];
|
|
2618
|
+
const parentPath = [...path18].slice(0, -1);
|
|
2619
2619
|
const parent = get(object, parentPath);
|
|
2620
2620
|
if (typeof mutator === "function") {
|
|
2621
2621
|
parent[lastProperty] = mutator(parent[lastProperty]);
|
|
@@ -2667,22 +2667,22 @@ var mergeResponses = (response1, response2) => {
|
|
|
2667
2667
|
if (Object.keys(response1).length === 0) {
|
|
2668
2668
|
return Object.assign(response1, response2);
|
|
2669
2669
|
}
|
|
2670
|
-
const
|
|
2671
|
-
const nodesPath = [...
|
|
2670
|
+
const path18 = findPaginatedResourcePath(response1);
|
|
2671
|
+
const nodesPath = [...path18, "nodes"];
|
|
2672
2672
|
const newNodes = get(response2, nodesPath);
|
|
2673
2673
|
if (newNodes) {
|
|
2674
2674
|
set(response1, nodesPath, (values) => {
|
|
2675
2675
|
return [...values, ...newNodes];
|
|
2676
2676
|
});
|
|
2677
2677
|
}
|
|
2678
|
-
const edgesPath = [...
|
|
2678
|
+
const edgesPath = [...path18, "edges"];
|
|
2679
2679
|
const newEdges = get(response2, edgesPath);
|
|
2680
2680
|
if (newEdges) {
|
|
2681
2681
|
set(response1, edgesPath, (values) => {
|
|
2682
2682
|
return [...values, ...newEdges];
|
|
2683
2683
|
});
|
|
2684
2684
|
}
|
|
2685
|
-
const pageInfoPath = [...
|
|
2685
|
+
const pageInfoPath = [...path18, "pageInfo"];
|
|
2686
2686
|
set(response1, pageInfoPath, get(response2, pageInfoPath));
|
|
2687
2687
|
return response1;
|
|
2688
2688
|
};
|
|
@@ -5103,7 +5103,7 @@ var triggers_notification_paths_default = [
|
|
|
5103
5103
|
];
|
|
5104
5104
|
function routeMatcher(paths) {
|
|
5105
5105
|
const regexes = paths.map(
|
|
5106
|
-
(
|
|
5106
|
+
(path18) => path18.split("/").map((c) => c.startsWith("{") ? "(?:.+?)" : c).join("/")
|
|
5107
5107
|
);
|
|
5108
5108
|
const regex2 = `^(?:${regexes.map((r) => `(?:${r})`).join("|")})[^/]*$`;
|
|
5109
5109
|
return new RegExp(regex2, "i");
|
|
@@ -8378,9 +8378,9 @@ function asBranchUid(s) {
|
|
|
8378
8378
|
}
|
|
8379
8379
|
return s;
|
|
8380
8380
|
}
|
|
8381
|
-
function branchNameFromResourcePath(
|
|
8382
|
-
if (!
|
|
8383
|
-
const leaf =
|
|
8381
|
+
function branchNameFromResourcePath(path18) {
|
|
8382
|
+
if (!path18.includes("/branches/")) return null;
|
|
8383
|
+
const leaf = path18.split("/branches/").pop();
|
|
8384
8384
|
if (!leaf) return null;
|
|
8385
8385
|
try {
|
|
8386
8386
|
return asBranchName(leaf);
|
|
@@ -9951,8 +9951,8 @@ function shellEscape(s) {
|
|
|
9951
9951
|
|
|
9952
9952
|
// scripts/lakebase/schema-migrate.ts
|
|
9953
9953
|
init_esm_shims();
|
|
9954
|
-
import * as
|
|
9955
|
-
import * as
|
|
9954
|
+
import * as fs13 from "fs";
|
|
9955
|
+
import * as path16 from "path";
|
|
9956
9956
|
|
|
9957
9957
|
// scripts/lakebase/get-connection.ts
|
|
9958
9958
|
init_esm_shims();
|
|
@@ -9978,8 +9978,8 @@ var PKG_NAME = "@databricks-solutions/lakebase-scm-utils";
|
|
|
9978
9978
|
var cached;
|
|
9979
9979
|
function substrateSelfVersion() {
|
|
9980
9980
|
if (cached !== void 0) return cached;
|
|
9981
|
-
if ("0.
|
|
9982
|
-
cached = "0.
|
|
9981
|
+
if ("0.3.1".length > 0) {
|
|
9982
|
+
cached = "0.3.1";
|
|
9983
9983
|
return cached;
|
|
9984
9984
|
}
|
|
9985
9985
|
cached = "unknown";
|
|
@@ -10090,24 +10090,87 @@ function dbcli3(args) {
|
|
|
10090
10090
|
// scripts/lakebase/migration-layout.ts
|
|
10091
10091
|
init_esm_shims();
|
|
10092
10092
|
|
|
10093
|
+
// scripts/lakebase/dotnet-project.ts
|
|
10094
|
+
init_esm_shims();
|
|
10095
|
+
import * as fs6 from "fs";
|
|
10096
|
+
import * as path7 from "path";
|
|
10097
|
+
var EF_DESIGN_PACKAGE = "Microsoft.EntityFrameworkCore.Design";
|
|
10098
|
+
var DOTNET_FLYWAY_DIR = path7.join("db", "migrations");
|
|
10099
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set(["bin", "obj", "node_modules", ".git", "client", "tests", "test"]);
|
|
10100
|
+
function safeReaddir(dir) {
|
|
10101
|
+
try {
|
|
10102
|
+
return fs6.readdirSync(dir, { withFileTypes: true });
|
|
10103
|
+
} catch {
|
|
10104
|
+
return [];
|
|
10105
|
+
}
|
|
10106
|
+
}
|
|
10107
|
+
function hasDotnetMarker(dir) {
|
|
10108
|
+
return safeReaddir(dir).some(
|
|
10109
|
+
(e) => e.isFile() && /\.(sln|slnx|csproj)$/i.test(e.name)
|
|
10110
|
+
);
|
|
10111
|
+
}
|
|
10112
|
+
function findCsprojFiles(projectDir) {
|
|
10113
|
+
const found = [];
|
|
10114
|
+
const walk = (dir, depth) => {
|
|
10115
|
+
for (const e of safeReaddir(dir)) {
|
|
10116
|
+
const full = path7.join(dir, e.name);
|
|
10117
|
+
if (e.isFile() && e.name.toLowerCase().endsWith(".csproj")) {
|
|
10118
|
+
found.push(full);
|
|
10119
|
+
} else if (e.isDirectory() && depth < 2 && !SKIP_DIRS.has(e.name.toLowerCase()) && !e.name.startsWith(".")) {
|
|
10120
|
+
walk(full, depth + 1);
|
|
10121
|
+
}
|
|
10122
|
+
}
|
|
10123
|
+
};
|
|
10124
|
+
walk(projectDir, 0);
|
|
10125
|
+
return found.sort();
|
|
10126
|
+
}
|
|
10127
|
+
function findEfCoreProject(projectDir) {
|
|
10128
|
+
for (const csproj of findCsprojFiles(projectDir)) {
|
|
10129
|
+
let content = "";
|
|
10130
|
+
try {
|
|
10131
|
+
content = fs6.readFileSync(csproj, "utf-8");
|
|
10132
|
+
} catch {
|
|
10133
|
+
continue;
|
|
10134
|
+
}
|
|
10135
|
+
if (content.includes(EF_DESIGN_PACKAGE)) {
|
|
10136
|
+
const dir = path7.dirname(csproj);
|
|
10137
|
+
return { csproj, projectDir: dir, migrationsDir: path7.join(dir, "Migrations") };
|
|
10138
|
+
}
|
|
10139
|
+
}
|
|
10140
|
+
return null;
|
|
10141
|
+
}
|
|
10142
|
+
function isDotnetProject(projectDir) {
|
|
10143
|
+
return hasDotnetMarker(projectDir);
|
|
10144
|
+
}
|
|
10145
|
+
function isDotnetFlywayProject(projectDir) {
|
|
10146
|
+
return isDotnetProject(projectDir) && fs6.existsSync(path7.join(projectDir, DOTNET_FLYWAY_DIR));
|
|
10147
|
+
}
|
|
10148
|
+
var MAVEN_FLYWAY_DIR = path7.join("src", "main", "resources", "db", "migration");
|
|
10149
|
+
function flywayMigrationsDir(projectDir) {
|
|
10150
|
+
const maven = path7.join(projectDir, MAVEN_FLYWAY_DIR);
|
|
10151
|
+
if (fs6.existsSync(maven) || fs6.existsSync(path7.join(projectDir, "pom.xml"))) return maven;
|
|
10152
|
+
if (isDotnetFlywayProject(projectDir)) return path7.join(projectDir, DOTNET_FLYWAY_DIR);
|
|
10153
|
+
return maven;
|
|
10154
|
+
}
|
|
10155
|
+
|
|
10093
10156
|
// scripts/lakebase/adapters/alembic-adapter.ts
|
|
10094
10157
|
init_esm_shims();
|
|
10095
|
-
import * as
|
|
10096
|
-
import * as
|
|
10158
|
+
import * as fs8 from "fs";
|
|
10159
|
+
import * as path9 from "path";
|
|
10097
10160
|
|
|
10098
10161
|
// scripts/lakebase/schema-migrate-runners/alembic.ts
|
|
10099
10162
|
init_esm_shims();
|
|
10100
10163
|
import { spawn } from "child_process";
|
|
10101
|
-
import * as
|
|
10102
|
-
import * as
|
|
10164
|
+
import * as fs7 from "fs";
|
|
10165
|
+
import * as path8 from "path";
|
|
10103
10166
|
function resolveAlembicBin(projectDir) {
|
|
10104
10167
|
const candidates = [
|
|
10105
|
-
|
|
10106
|
-
|
|
10168
|
+
path8.join(projectDir, ".venv", "bin", "alembic"),
|
|
10169
|
+
path8.join(projectDir, "venv", "bin", "alembic")
|
|
10107
10170
|
];
|
|
10108
10171
|
for (const candidate of candidates) {
|
|
10109
10172
|
try {
|
|
10110
|
-
if (
|
|
10173
|
+
if (fs7.existsSync(candidate)) return candidate;
|
|
10111
10174
|
} catch {
|
|
10112
10175
|
}
|
|
10113
10176
|
}
|
|
@@ -10117,7 +10180,7 @@ function spawnAlembic(projectDir, args, dsn) {
|
|
|
10117
10180
|
return new Promise((resolve2, reject) => {
|
|
10118
10181
|
const bin = resolveAlembicBin(projectDir);
|
|
10119
10182
|
const env = { ...process.env };
|
|
10120
|
-
env.PYTHONPATH = [projectDir, process.env.PYTHONPATH].filter(Boolean).join(
|
|
10183
|
+
env.PYTHONPATH = [projectDir, process.env.PYTHONPATH].filter(Boolean).join(path8.delimiter);
|
|
10121
10184
|
if (dsn) env.DATABASE_URL = dsn;
|
|
10122
10185
|
const child = spawn(bin, args, {
|
|
10123
10186
|
cwd: projectDir,
|
|
@@ -10165,10 +10228,10 @@ async function createAlembicRevision(opts) {
|
|
|
10165
10228
|
const m = stdout.match(/Generating\s+(\S+\.py)/);
|
|
10166
10229
|
if (m) return m[1].trim();
|
|
10167
10230
|
for (const rel of ["migrations/versions", "alembic/versions"]) {
|
|
10168
|
-
const dir =
|
|
10169
|
-
if (!
|
|
10170
|
-
const hit =
|
|
10171
|
-
if (hit) return
|
|
10231
|
+
const dir = path8.join(opts.projectDir, rel);
|
|
10232
|
+
if (!fs7.existsSync(dir)) continue;
|
|
10233
|
+
const hit = fs7.readdirSync(dir).find((f) => f.startsWith(`${opts.revId}_`) && f.endsWith(".py"));
|
|
10234
|
+
if (hit) return path8.join(dir, hit);
|
|
10172
10235
|
}
|
|
10173
10236
|
throw new SchemaMigrationError(
|
|
10174
10237
|
`alembic revision succeeded but the created file could not be located.
|
|
@@ -10303,15 +10366,15 @@ async function buildDsn(args) {
|
|
|
10303
10366
|
}
|
|
10304
10367
|
function findVersionsDir(projectDir) {
|
|
10305
10368
|
const candidates = [
|
|
10306
|
-
|
|
10307
|
-
|
|
10369
|
+
path9.join(projectDir, "migrations", "versions"),
|
|
10370
|
+
path9.join(projectDir, "alembic", "versions")
|
|
10308
10371
|
];
|
|
10309
|
-
return candidates.find((p) =>
|
|
10372
|
+
return candidates.find((p) => fs8.existsSync(p));
|
|
10310
10373
|
}
|
|
10311
10374
|
function listAlembicFiles(projectDir) {
|
|
10312
10375
|
const dir = findVersionsDir(projectDir);
|
|
10313
10376
|
if (!dir) return [];
|
|
10314
|
-
const files =
|
|
10377
|
+
const files = fs8.readdirSync(dir).filter((f) => f.endsWith(".py") && !f.startsWith("__"));
|
|
10315
10378
|
return files.map((filename) => {
|
|
10316
10379
|
const stem = filename.replace(/\.py$/, "");
|
|
10317
10380
|
const sep2 = stem.indexOf("_");
|
|
@@ -10336,9 +10399,9 @@ var AlembicAdapter = {
|
|
|
10336
10399
|
* here. Callers can still force-select via project.yaml#migration_tool.
|
|
10337
10400
|
*/
|
|
10338
10401
|
detect(projectDir) {
|
|
10339
|
-
if (
|
|
10340
|
-
if (
|
|
10341
|
-
if (
|
|
10402
|
+
if (fs8.existsSync(path9.join(projectDir, "alembic.ini"))) return true;
|
|
10403
|
+
if (fs8.existsSync(path9.join(projectDir, "migrations", "env.py"))) return true;
|
|
10404
|
+
if (fs8.existsSync(path9.join(projectDir, "alembic", "env.py"))) return true;
|
|
10342
10405
|
return false;
|
|
10343
10406
|
},
|
|
10344
10407
|
async apply(args) {
|
|
@@ -10444,7 +10507,7 @@ var AlembicAdapter = {
|
|
|
10444
10507
|
autogenerate: !!args.autogenerate,
|
|
10445
10508
|
dsn
|
|
10446
10509
|
});
|
|
10447
|
-
return { status: "ok", version: revId, filename:
|
|
10510
|
+
return { status: "ok", version: revId, filename: path9.basename(created), path: created };
|
|
10448
10511
|
} catch (err) {
|
|
10449
10512
|
return {
|
|
10450
10513
|
status: "error",
|
|
@@ -10461,7 +10524,7 @@ var AlembicAdapter = {
|
|
|
10461
10524
|
if (heads.length <= 1) return { status: "noop", headsBefore: heads };
|
|
10462
10525
|
if (args.dryRun) return { status: "ok", headsBefore: heads };
|
|
10463
10526
|
const created = await mergeAlembicHeads(args.projectDir, args.message ?? "merge heads");
|
|
10464
|
-
const mergeRevision =
|
|
10527
|
+
const mergeRevision = path9.basename(created).replace(/\.py$/, "").split("_")[0];
|
|
10465
10528
|
return { status: "ok", headsBefore: heads, mergeRevision, path: created };
|
|
10466
10529
|
} catch (err) {
|
|
10467
10530
|
return {
|
|
@@ -10476,13 +10539,13 @@ registerSchemaMigrationAdapter(AlembicAdapter);
|
|
|
10476
10539
|
|
|
10477
10540
|
// scripts/lakebase/adapters/flyway-adapter.ts
|
|
10478
10541
|
init_esm_shims();
|
|
10479
|
-
import * as
|
|
10480
|
-
import * as
|
|
10542
|
+
import * as fs9 from "fs";
|
|
10543
|
+
import * as path11 from "path";
|
|
10481
10544
|
|
|
10482
10545
|
// scripts/lakebase/schema-migrate-runners/flyway.ts
|
|
10483
10546
|
init_esm_shims();
|
|
10484
10547
|
import { spawn as spawn2 } from "child_process";
|
|
10485
|
-
import * as
|
|
10548
|
+
import * as path10 from "path";
|
|
10486
10549
|
function dsnToFlywayEnv(dsn) {
|
|
10487
10550
|
const u = new URL(dsn);
|
|
10488
10551
|
const user = decodeURIComponent(u.username);
|
|
@@ -10492,7 +10555,7 @@ function dsnToFlywayEnv(dsn) {
|
|
|
10492
10555
|
return { url, user, password };
|
|
10493
10556
|
}
|
|
10494
10557
|
function migrationsLocation(projectDir) {
|
|
10495
|
-
return `filesystem:${
|
|
10558
|
+
return `filesystem:${flywayMigrationsDir(projectDir)}`;
|
|
10496
10559
|
}
|
|
10497
10560
|
function runFlyway(ctx, args) {
|
|
10498
10561
|
const { url, user, password } = dsnToFlywayEnv(ctx.dsn);
|
|
@@ -10593,7 +10656,7 @@ async function statusFlyway(ctx) {
|
|
|
10593
10656
|
if (state === "SUCCESS" || state === "BASELINE") {
|
|
10594
10657
|
current = m.version;
|
|
10595
10658
|
} else if (state === "PENDING") {
|
|
10596
|
-
const filename = m.filepath ?
|
|
10659
|
+
const filename = m.filepath ? path10.basename(m.filepath) : `V${m.version}__migration.sql`;
|
|
10597
10660
|
pending.push({
|
|
10598
10661
|
version: m.version,
|
|
10599
10662
|
filename,
|
|
@@ -10616,9 +10679,9 @@ async function buildDsn2(args) {
|
|
|
10616
10679
|
return result.url;
|
|
10617
10680
|
}
|
|
10618
10681
|
function listFlywayFiles(projectDir) {
|
|
10619
|
-
const dir =
|
|
10620
|
-
if (!
|
|
10621
|
-
const files =
|
|
10682
|
+
const dir = flywayMigrationsDir(projectDir);
|
|
10683
|
+
if (!fs9.existsSync(dir)) return [];
|
|
10684
|
+
const files = fs9.readdirSync(dir).filter((f) => /^V\d+(\.\d+)*__.+\.sql$/.test(f));
|
|
10622
10685
|
return files.map((filename) => {
|
|
10623
10686
|
const m = filename.match(/^V(\d+(?:\.\d+)*)__(.+)\.sql$/);
|
|
10624
10687
|
const version = m[1];
|
|
@@ -10639,9 +10702,9 @@ function versionCompare(a, b) {
|
|
|
10639
10702
|
}
|
|
10640
10703
|
var FlywayAdapter = {
|
|
10641
10704
|
id: "flyway",
|
|
10642
|
-
languages: ["java", "kotlin"],
|
|
10705
|
+
languages: ["java", "kotlin", "dotnet"],
|
|
10643
10706
|
detect(projectDir) {
|
|
10644
|
-
return
|
|
10707
|
+
return fs9.existsSync(path11.join(projectDir, "pom.xml")) || isDotnetFlywayProject(projectDir);
|
|
10645
10708
|
},
|
|
10646
10709
|
async apply(args) {
|
|
10647
10710
|
const dsn = await buildDsn2(args);
|
|
@@ -10699,14 +10762,14 @@ var FlywayAdapter = {
|
|
|
10699
10762
|
// optional-protocol shape makes this additive.
|
|
10700
10763
|
async newMigration(args) {
|
|
10701
10764
|
try {
|
|
10702
|
-
const dir =
|
|
10703
|
-
|
|
10765
|
+
const dir = flywayMigrationsDir(args.projectDir);
|
|
10766
|
+
fs9.mkdirSync(dir, { recursive: true });
|
|
10704
10767
|
const version = migrationTimestamp();
|
|
10705
10768
|
const slug = migrationSlug2(args.slug);
|
|
10706
10769
|
const filename = `V${version}__${slug}.sql`;
|
|
10707
|
-
const full =
|
|
10708
|
-
if (
|
|
10709
|
-
|
|
10770
|
+
const full = path11.join(dir, filename);
|
|
10771
|
+
if (fs9.existsSync(full)) throw new Error(`${filename} already exists`);
|
|
10772
|
+
fs9.writeFileSync(
|
|
10710
10773
|
full,
|
|
10711
10774
|
`-- V${version}: ${args.slug}
|
|
10712
10775
|
-- Flyway migration (write your DDL/DML below).
|
|
@@ -10729,19 +10792,19 @@ registerSchemaMigrationAdapter(FlywayAdapter);
|
|
|
10729
10792
|
|
|
10730
10793
|
// scripts/lakebase/adapters/knex-adapter.ts
|
|
10731
10794
|
init_esm_shims();
|
|
10732
|
-
import * as
|
|
10733
|
-
import * as
|
|
10795
|
+
import * as fs11 from "fs";
|
|
10796
|
+
import * as path13 from "path";
|
|
10734
10797
|
|
|
10735
10798
|
// scripts/lakebase/schema-migrate-runners/knex.ts
|
|
10736
10799
|
init_esm_shims();
|
|
10737
10800
|
import { spawn as spawn3 } from "child_process";
|
|
10738
|
-
import * as
|
|
10739
|
-
import * as
|
|
10801
|
+
import * as fs10 from "fs";
|
|
10802
|
+
import * as path12 from "path";
|
|
10740
10803
|
var KNEXFILE_VARIANTS = ["knexfile.js", "knexfile.ts", "knexfile.mjs", "knexfile.cjs"];
|
|
10741
10804
|
function findKnexfile(projectDir) {
|
|
10742
10805
|
for (const name of KNEXFILE_VARIANTS) {
|
|
10743
|
-
const p =
|
|
10744
|
-
if (
|
|
10806
|
+
const p = path12.join(projectDir, name);
|
|
10807
|
+
if (fs10.existsSync(p)) return p;
|
|
10745
10808
|
}
|
|
10746
10809
|
return void 0;
|
|
10747
10810
|
}
|
|
@@ -10893,9 +10956,9 @@ async function buildDsn3(args) {
|
|
|
10893
10956
|
}
|
|
10894
10957
|
var KNEXFILE_VARIANTS2 = ["knexfile.js", "knexfile.ts", "knexfile.mjs", "knexfile.cjs"];
|
|
10895
10958
|
function listKnexFiles(projectDir) {
|
|
10896
|
-
const dir =
|
|
10897
|
-
if (!
|
|
10898
|
-
const files =
|
|
10959
|
+
const dir = path13.join(projectDir, "migrations");
|
|
10960
|
+
if (!fs11.existsSync(dir)) return [];
|
|
10961
|
+
const files = fs11.readdirSync(dir).filter((f) => (f.endsWith(".js") || f.endsWith(".ts")) && !f.startsWith("."));
|
|
10899
10962
|
return files.map((filename) => {
|
|
10900
10963
|
const stem = filename.replace(/\.(js|ts)$/, "");
|
|
10901
10964
|
const m = stem.match(/^(\d{14})_(.+)$/);
|
|
@@ -10915,7 +10978,7 @@ var KnexAdapter = {
|
|
|
10915
10978
|
* project.yaml#migration_tool.
|
|
10916
10979
|
*/
|
|
10917
10980
|
detect(projectDir) {
|
|
10918
|
-
return KNEXFILE_VARIANTS2.some((name) =>
|
|
10981
|
+
return KNEXFILE_VARIANTS2.some((name) => fs11.existsSync(path13.join(projectDir, name)));
|
|
10919
10982
|
},
|
|
10920
10983
|
async apply(args) {
|
|
10921
10984
|
const dsn = await buildDsn3(args);
|
|
@@ -10988,9 +11051,9 @@ var KnexAdapter = {
|
|
|
10988
11051
|
async newMigration(args) {
|
|
10989
11052
|
try {
|
|
10990
11053
|
const created = await createKnexMigration({ projectDir: args.projectDir, slug: migrationSlug2(args.slug) });
|
|
10991
|
-
const stem =
|
|
11054
|
+
const stem = path13.basename(created).replace(/\.(js|ts)$/, "");
|
|
10992
11055
|
const version = stem.match(/^(\d{14})_/)?.[1] ?? stem;
|
|
10993
|
-
return { status: "ok", version, filename:
|
|
11056
|
+
return { status: "ok", version, filename: path13.basename(created), path: created };
|
|
10994
11057
|
} catch (err) {
|
|
10995
11058
|
return {
|
|
10996
11059
|
status: "error",
|
|
@@ -11004,6 +11067,362 @@ var KnexAdapter = {
|
|
|
11004
11067
|
};
|
|
11005
11068
|
registerSchemaMigrationAdapter(KnexAdapter);
|
|
11006
11069
|
|
|
11070
|
+
// scripts/lakebase/adapters/efcore-adapter.ts
|
|
11071
|
+
init_esm_shims();
|
|
11072
|
+
import * as path15 from "path";
|
|
11073
|
+
|
|
11074
|
+
// scripts/lakebase/schema-migrate-runners/efcore.ts
|
|
11075
|
+
init_esm_shims();
|
|
11076
|
+
import { spawn as spawn4 } from "child_process";
|
|
11077
|
+
import * as fs12 from "fs";
|
|
11078
|
+
import * as path14 from "path";
|
|
11079
|
+
import { Client as Client2 } from "pg";
|
|
11080
|
+
var MIGRATION_ID = /^(\d{14})_(.+)$/;
|
|
11081
|
+
function efVersion(id) {
|
|
11082
|
+
return MIGRATION_ID.exec(id)?.[1] ?? id;
|
|
11083
|
+
}
|
|
11084
|
+
function efDescription(id) {
|
|
11085
|
+
const name = MIGRATION_ID.exec(id)?.[2] ?? id;
|
|
11086
|
+
return name.replace(/_/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2");
|
|
11087
|
+
}
|
|
11088
|
+
function dsnToNpgsql(dsn) {
|
|
11089
|
+
const u = new URL(dsn);
|
|
11090
|
+
const parts = [
|
|
11091
|
+
`Host=${u.hostname}`,
|
|
11092
|
+
`Port=${u.port || "5432"}`,
|
|
11093
|
+
`Database=${decodeURIComponent(u.pathname.replace(/^\//, "")) || "databricks_postgres"}`
|
|
11094
|
+
];
|
|
11095
|
+
if (u.username) parts.push(`Username=${decodeURIComponent(u.username)}`);
|
|
11096
|
+
if (u.password) parts.push(`Password=${decodeURIComponent(u.password)}`);
|
|
11097
|
+
const sslmode = (u.searchParams.get("sslmode") ?? "require").toLowerCase();
|
|
11098
|
+
const ssl = {
|
|
11099
|
+
disable: "Disable",
|
|
11100
|
+
allow: "Allow",
|
|
11101
|
+
prefer: "Prefer",
|
|
11102
|
+
require: "Require",
|
|
11103
|
+
"verify-ca": "VerifyCA",
|
|
11104
|
+
"verify-full": "VerifyFull"
|
|
11105
|
+
};
|
|
11106
|
+
parts.push(`SSL Mode=${ssl[sslmode] ?? "Require"}`);
|
|
11107
|
+
const app = u.searchParams.get("application_name");
|
|
11108
|
+
if (app) parts.push(`Application Name=${app}`);
|
|
11109
|
+
return parts.map((p) => p.replace(/;/g, "")).join(";");
|
|
11110
|
+
}
|
|
11111
|
+
function parseEfJson(stdout) {
|
|
11112
|
+
const body = stdout.split(/\r?\n/).filter((l) => l.startsWith("data:")).map((l) => l.slice("data:".length)).join("\n").trim();
|
|
11113
|
+
if (!body) {
|
|
11114
|
+
throw new SchemaMigrationError(`dotnet ef returned no JSON payload: ${stdout.slice(0, 400)}`);
|
|
11115
|
+
}
|
|
11116
|
+
try {
|
|
11117
|
+
return JSON.parse(body);
|
|
11118
|
+
} catch (err) {
|
|
11119
|
+
throw new SchemaMigrationError(
|
|
11120
|
+
`dotnet ef JSON parse failed: ${err instanceof Error ? err.message : String(err)}.
|
|
11121
|
+
Body (first 400 chars): ${body.slice(0, 400)}`
|
|
11122
|
+
);
|
|
11123
|
+
}
|
|
11124
|
+
}
|
|
11125
|
+
function requireProject(projectDir) {
|
|
11126
|
+
const project = findEfCoreProject(projectDir);
|
|
11127
|
+
if (!project) {
|
|
11128
|
+
throw new SchemaMigrationError(
|
|
11129
|
+
`No EF Core project found under ${projectDir}. Expected a *.csproj that references ${EF_DESIGN_PACKAGE}.`
|
|
11130
|
+
);
|
|
11131
|
+
}
|
|
11132
|
+
return project;
|
|
11133
|
+
}
|
|
11134
|
+
function spawnDotnet(projectDir, args, env) {
|
|
11135
|
+
return new Promise((resolve2, reject) => {
|
|
11136
|
+
const child = spawn4("dotnet", args, {
|
|
11137
|
+
cwd: projectDir,
|
|
11138
|
+
env: { ...process.env, DOTNET_NOLOGO: "1", DOTNET_CLI_TELEMETRY_OPTOUT: "1", ...env },
|
|
11139
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
11140
|
+
});
|
|
11141
|
+
let stdout = "";
|
|
11142
|
+
let stderr = "";
|
|
11143
|
+
child.stdout.on("data", (chunk) => {
|
|
11144
|
+
stdout += chunk.toString("utf8");
|
|
11145
|
+
});
|
|
11146
|
+
child.stderr.on("data", (chunk) => {
|
|
11147
|
+
stderr += chunk.toString("utf8");
|
|
11148
|
+
});
|
|
11149
|
+
child.on("error", (err) => {
|
|
11150
|
+
reject(
|
|
11151
|
+
new SchemaMigrationError(
|
|
11152
|
+
`Could not spawn dotnet. Is the .NET SDK installed and on PATH? ${err.message}`,
|
|
11153
|
+
err
|
|
11154
|
+
)
|
|
11155
|
+
);
|
|
11156
|
+
});
|
|
11157
|
+
child.on("close", (code) => {
|
|
11158
|
+
if (code === 0) {
|
|
11159
|
+
resolve2({ stdout, stderr });
|
|
11160
|
+
} else {
|
|
11161
|
+
reject(
|
|
11162
|
+
new SchemaMigrationError(
|
|
11163
|
+
`dotnet ${args.join(" ")} exited with code ${code}.
|
|
11164
|
+
stdout: ${stdout}
|
|
11165
|
+
stderr: ${stderr}`
|
|
11166
|
+
)
|
|
11167
|
+
);
|
|
11168
|
+
}
|
|
11169
|
+
});
|
|
11170
|
+
});
|
|
11171
|
+
}
|
|
11172
|
+
var restored = /* @__PURE__ */ new Set();
|
|
11173
|
+
async function ensureRestored(projectDir, csproj) {
|
|
11174
|
+
if (restored.has(projectDir)) return;
|
|
11175
|
+
if (fs12.existsSync(path14.join(projectDir, ".config", "dotnet-tools.json"))) {
|
|
11176
|
+
await spawnDotnet(projectDir, ["tool", "restore"], {});
|
|
11177
|
+
}
|
|
11178
|
+
await spawnDotnet(projectDir, ["restore", csproj], {});
|
|
11179
|
+
restored.add(projectDir);
|
|
11180
|
+
}
|
|
11181
|
+
async function runEf(projectDir, args, dsn, opts = {}) {
|
|
11182
|
+
const project = requireProject(projectDir);
|
|
11183
|
+
const csproj = path14.relative(projectDir, project.csproj) || project.csproj;
|
|
11184
|
+
await ensureRestored(projectDir, csproj);
|
|
11185
|
+
const full = ["ef", ...args, "--project", csproj, "--prefix-output"];
|
|
11186
|
+
if (opts.noBuild) full.push("--no-build");
|
|
11187
|
+
const env = {};
|
|
11188
|
+
if (dsn) {
|
|
11189
|
+
env.DATABASE_URL = dsn;
|
|
11190
|
+
if (process.env.LAKEBASE_EFCORE_CONNECTION_ARG === "1") full.push("--connection", dsnToNpgsql(dsn));
|
|
11191
|
+
}
|
|
11192
|
+
return spawnDotnet(projectDir, full, env);
|
|
11193
|
+
}
|
|
11194
|
+
async function listEfMigrations(ctx, opts = {}) {
|
|
11195
|
+
const { stdout, stderr } = await runEf(ctx.projectDir, ["migrations", "list", "--json"], ctx.dsn, opts);
|
|
11196
|
+
const entries = parseEfJson(stdout);
|
|
11197
|
+
if (ctx.dsn && entries.some((e) => e.applied === null)) {
|
|
11198
|
+
const warning = stdout.split(/\r?\n/).filter((l) => /^(warn|error):/.test(l)).join("\n");
|
|
11199
|
+
throw new SchemaMigrationError(
|
|
11200
|
+
`dotnet ef could not read migration state from the database (check connectivity and credentials).
|
|
11201
|
+
${warning || stderr}`.trim()
|
|
11202
|
+
);
|
|
11203
|
+
}
|
|
11204
|
+
return entries;
|
|
11205
|
+
}
|
|
11206
|
+
async function readEfHistory(dsn) {
|
|
11207
|
+
const client = new Client2({ connectionString: dsn, application_name: connectionApplicationName() });
|
|
11208
|
+
await client.connect();
|
|
11209
|
+
try {
|
|
11210
|
+
const exists = await client.query(`SELECT to_regclass('"__EFMigrationsHistory"') IS NOT NULL AS present`);
|
|
11211
|
+
if (!exists.rows[0]?.present) return null;
|
|
11212
|
+
const r = await client.query(`SELECT "MigrationId" FROM "__EFMigrationsHistory" ORDER BY "MigrationId"`);
|
|
11213
|
+
return r.rows.map((row) => row.MigrationId);
|
|
11214
|
+
} finally {
|
|
11215
|
+
await client.end();
|
|
11216
|
+
}
|
|
11217
|
+
}
|
|
11218
|
+
function toApplied(entries) {
|
|
11219
|
+
return entries.map((e) => ({ version: efVersion(e.id), description: efDescription(e.id) }));
|
|
11220
|
+
}
|
|
11221
|
+
async function statusEfcore(ctx) {
|
|
11222
|
+
const entries = await listEfMigrations(ctx);
|
|
11223
|
+
const history = await (ctx.readHistory ?? readEfHistory)(ctx.dsn);
|
|
11224
|
+
const current = history && history.length > 0 ? efVersion(history[history.length - 1]) : void 0;
|
|
11225
|
+
const pending = entries.filter((e) => e.applied !== true).map((e) => ({ version: efVersion(e.id), filename: `${e.id}.cs`, description: efDescription(e.id) }));
|
|
11226
|
+
return { current, pending, tool: "efcore" };
|
|
11227
|
+
}
|
|
11228
|
+
async function applyEfcore(ctx) {
|
|
11229
|
+
const before = new Set((await listEfMigrations(ctx)).filter((e) => e.applied).map((e) => e.id));
|
|
11230
|
+
await runEf(ctx.projectDir, ["database", "update"], ctx.dsn, { noBuild: true });
|
|
11231
|
+
const after = await listEfMigrations(ctx, { noBuild: true });
|
|
11232
|
+
const applied = toApplied(after.filter((e) => e.applied && !before.has(e.id)));
|
|
11233
|
+
return { applied, alreadyAtLatest: applied.length === 0, tool: "efcore" };
|
|
11234
|
+
}
|
|
11235
|
+
function resolveEfTarget(target, applied) {
|
|
11236
|
+
const t = target.trim();
|
|
11237
|
+
if (t === "0") return "0";
|
|
11238
|
+
const rel = /^-(\d+)$/.exec(t);
|
|
11239
|
+
if (rel) {
|
|
11240
|
+
const steps = Number(rel[1]);
|
|
11241
|
+
if (steps < 1) throw new SchemaMigrationError(`Invalid rollback step: ${target}`);
|
|
11242
|
+
const index = applied.length - 1 - steps;
|
|
11243
|
+
if (index < -1) {
|
|
11244
|
+
throw new SchemaMigrationError(`Cannot roll back ${steps} migration(s); only ${applied.length} applied.`);
|
|
11245
|
+
}
|
|
11246
|
+
return index === -1 ? "0" : applied[index].id;
|
|
11247
|
+
}
|
|
11248
|
+
const match = applied.find((e) => e.id === t || efVersion(e.id) === t || e.name === t);
|
|
11249
|
+
if (!match) throw new SchemaMigrationError(`Rollback target '${target}' is not an applied migration.`);
|
|
11250
|
+
return match.id;
|
|
11251
|
+
}
|
|
11252
|
+
async function rollbackEfcore(ctx) {
|
|
11253
|
+
const beforeEntries = (await listEfMigrations(ctx)).filter((e) => e.applied);
|
|
11254
|
+
const targetId = resolveEfTarget(ctx.target, beforeEntries);
|
|
11255
|
+
await runEf(ctx.projectDir, ["database", "update", targetId], ctx.dsn, { noBuild: true });
|
|
11256
|
+
const stillApplied = new Set((await listEfMigrations(ctx, { noBuild: true })).filter((e) => e.applied).map((e) => e.id));
|
|
11257
|
+
return { rolledBack: toApplied(beforeEntries.filter((e) => !stillApplied.has(e.id)).reverse()), tool: "efcore" };
|
|
11258
|
+
}
|
|
11259
|
+
function efMigrationName(slug) {
|
|
11260
|
+
const name = slug.split(/[^A-Za-z0-9]+/).filter(Boolean).map((w) => w[0].toUpperCase() + w.slice(1)).join("");
|
|
11261
|
+
if (!name) throw new SchemaMigrationError(`Cannot derive a migration name from '${slug}'.`);
|
|
11262
|
+
return /^[0-9]/.test(name) ? `M${name}` : name;
|
|
11263
|
+
}
|
|
11264
|
+
async function createEfcoreMigration(opts) {
|
|
11265
|
+
const project = requireProject(opts.projectDir);
|
|
11266
|
+
const name = efMigrationName(opts.slug);
|
|
11267
|
+
const existing = new Set(fs12.existsSync(project.migrationsDir) ? fs12.readdirSync(project.migrationsDir) : []);
|
|
11268
|
+
await runEf(opts.projectDir, ["migrations", "add", name]);
|
|
11269
|
+
const created = fs12.readdirSync(project.migrationsDir).filter((f) => !existing.has(f) && MIGRATION_ID.test(f.replace(/\.cs$/, "")) && !f.endsWith(".Designer.cs"));
|
|
11270
|
+
if (created.length !== 1) {
|
|
11271
|
+
throw new SchemaMigrationError(`Expected one new migration file for ${name}, found: ${created.join(", ") || "none"}`);
|
|
11272
|
+
}
|
|
11273
|
+
return { id: created[0].replace(/\.cs$/, ""), path: path14.join(project.migrationsDir, created[0]) };
|
|
11274
|
+
}
|
|
11275
|
+
function localEfMigrationIds(projectDir) {
|
|
11276
|
+
const project = findEfCoreProject(projectDir);
|
|
11277
|
+
if (!project || !fs12.existsSync(project.migrationsDir)) return [];
|
|
11278
|
+
return fs12.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();
|
|
11279
|
+
}
|
|
11280
|
+
function efProductVersion(project) {
|
|
11281
|
+
try {
|
|
11282
|
+
const csproj = fs12.readFileSync(project.csproj, "utf-8");
|
|
11283
|
+
const m = new RegExp(`${EF_DESIGN_PACKAGE.replace(/\./g, "\\.")}"\\s+Version="([^"]+)"`).exec(csproj);
|
|
11284
|
+
if (m) return m[1];
|
|
11285
|
+
} catch {
|
|
11286
|
+
}
|
|
11287
|
+
return "unknown";
|
|
11288
|
+
}
|
|
11289
|
+
async function stampEfcore(ctx) {
|
|
11290
|
+
const project = requireProject(ctx.projectDir);
|
|
11291
|
+
const ids = localEfMigrationIds(ctx.projectDir);
|
|
11292
|
+
if (ids.length === 0) throw new SchemaMigrationError("No local EF Core migrations to stamp.");
|
|
11293
|
+
const rev = ctx.revision.trim();
|
|
11294
|
+
const end = rev === "head" ? ids.length - 1 : ids.findIndex((id) => id === rev || efVersion(id) === rev);
|
|
11295
|
+
if (end < 0) throw new SchemaMigrationError(`Stamp revision '${ctx.revision}' is not a local migration.`);
|
|
11296
|
+
const toStamp = ids.slice(0, end + 1);
|
|
11297
|
+
const productVersion = efProductVersion(project);
|
|
11298
|
+
const client = new Client2({ connectionString: ctx.dsn, application_name: connectionApplicationName() });
|
|
11299
|
+
await client.connect();
|
|
11300
|
+
try {
|
|
11301
|
+
await client.query("BEGIN");
|
|
11302
|
+
await client.query(
|
|
11303
|
+
`CREATE TABLE IF NOT EXISTS "__EFMigrationsHistory" (
|
|
11304
|
+
"MigrationId" character varying(150) NOT NULL,
|
|
11305
|
+
"ProductVersion" character varying(32) NOT NULL,
|
|
11306
|
+
CONSTRAINT "PK___EFMigrationsHistory" PRIMARY KEY ("MigrationId"))`
|
|
11307
|
+
);
|
|
11308
|
+
await client.query(`DELETE FROM "__EFMigrationsHistory" WHERE NOT ("MigrationId" = ANY($1::text[]))`, [toStamp]);
|
|
11309
|
+
for (const id of toStamp) {
|
|
11310
|
+
await client.query(
|
|
11311
|
+
`INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") VALUES ($1, $2)
|
|
11312
|
+
ON CONFLICT ("MigrationId") DO NOTHING`,
|
|
11313
|
+
[id, productVersion]
|
|
11314
|
+
);
|
|
11315
|
+
}
|
|
11316
|
+
await client.query("COMMIT");
|
|
11317
|
+
} catch (err) {
|
|
11318
|
+
await client.query("ROLLBACK").catch(() => void 0);
|
|
11319
|
+
throw err;
|
|
11320
|
+
} finally {
|
|
11321
|
+
await client.end();
|
|
11322
|
+
}
|
|
11323
|
+
return efVersion(toStamp[toStamp.length - 1]);
|
|
11324
|
+
}
|
|
11325
|
+
|
|
11326
|
+
// scripts/lakebase/adapters/efcore-adapter.ts
|
|
11327
|
+
async function buildDsn4(args) {
|
|
11328
|
+
const result = await getConnection({
|
|
11329
|
+
output: "dsn",
|
|
11330
|
+
instance: args.instance,
|
|
11331
|
+
branch: args.branch,
|
|
11332
|
+
database: args.database,
|
|
11333
|
+
endpointName: args.endpointName
|
|
11334
|
+
});
|
|
11335
|
+
return result.url;
|
|
11336
|
+
}
|
|
11337
|
+
function errMsg(err) {
|
|
11338
|
+
return err instanceof Error ? err.message : String(err);
|
|
11339
|
+
}
|
|
11340
|
+
function listEfCoreFiles(projectDir) {
|
|
11341
|
+
return localEfMigrationIds(projectDir).map((id) => ({
|
|
11342
|
+
version: efVersion(id),
|
|
11343
|
+
filename: `${id}.cs`,
|
|
11344
|
+
description: efDescription(id),
|
|
11345
|
+
type: "CSharp",
|
|
11346
|
+
tool: "efcore"
|
|
11347
|
+
}));
|
|
11348
|
+
}
|
|
11349
|
+
var EfCoreAdapter = {
|
|
11350
|
+
id: "efcore",
|
|
11351
|
+
languages: ["dotnet"],
|
|
11352
|
+
/** A .NET project (root marker) with a *.csproj that references Microsoft.EntityFrameworkCore.Design. */
|
|
11353
|
+
detect(projectDir) {
|
|
11354
|
+
return isDotnetProject(projectDir) && findEfCoreProject(projectDir) !== null;
|
|
11355
|
+
},
|
|
11356
|
+
async apply(args) {
|
|
11357
|
+
try {
|
|
11358
|
+
const dsn = await buildDsn4(args);
|
|
11359
|
+
const r = await applyEfcore({ projectDir: args.projectDir, dsn });
|
|
11360
|
+
return {
|
|
11361
|
+
applied_migrations: r.applied,
|
|
11362
|
+
status: r.alreadyAtLatest ? "noop" : "ok",
|
|
11363
|
+
tool_specific: { alreadyAtLatest: r.alreadyAtLatest, tool: r.tool }
|
|
11364
|
+
};
|
|
11365
|
+
} catch (err) {
|
|
11366
|
+
return { applied_migrations: [], status: "error", error: errMsg(err) };
|
|
11367
|
+
}
|
|
11368
|
+
},
|
|
11369
|
+
async rollback(args) {
|
|
11370
|
+
try {
|
|
11371
|
+
const dsn = await buildDsn4(args);
|
|
11372
|
+
const r = await rollbackEfcore({ projectDir: args.projectDir, dsn, target: args.target });
|
|
11373
|
+
return {
|
|
11374
|
+
rolled_back: r.rolledBack,
|
|
11375
|
+
status: r.rolledBack.length === 0 ? "noop" : "ok",
|
|
11376
|
+
tool_specific: { tool: r.tool }
|
|
11377
|
+
};
|
|
11378
|
+
} catch (err) {
|
|
11379
|
+
return { rolled_back: [], status: "error", error: errMsg(err) };
|
|
11380
|
+
}
|
|
11381
|
+
},
|
|
11382
|
+
async stamp(args) {
|
|
11383
|
+
try {
|
|
11384
|
+
const dsn = await buildDsn4(args);
|
|
11385
|
+
const stamped = await stampEfcore({ projectDir: args.projectDir, dsn, revision: args.revision });
|
|
11386
|
+
return { status: "ok", stamped_revision: stamped, tool_specific: { tool: "efcore" } };
|
|
11387
|
+
} catch (err) {
|
|
11388
|
+
return { status: "error", stamped_revision: null, error: errMsg(err) };
|
|
11389
|
+
}
|
|
11390
|
+
},
|
|
11391
|
+
async status(args) {
|
|
11392
|
+
try {
|
|
11393
|
+
const dsn = await buildDsn4(args);
|
|
11394
|
+
const r = await statusEfcore({ projectDir: args.projectDir, dsn });
|
|
11395
|
+
return {
|
|
11396
|
+
applied_version: r.current ?? null,
|
|
11397
|
+
pending: r.pending,
|
|
11398
|
+
applied: [],
|
|
11399
|
+
status: "ok",
|
|
11400
|
+
tool_specific: { tool: r.tool }
|
|
11401
|
+
};
|
|
11402
|
+
} catch (err) {
|
|
11403
|
+
return { applied_version: null, pending: [], applied: [], status: "error", error: errMsg(err) };
|
|
11404
|
+
}
|
|
11405
|
+
},
|
|
11406
|
+
async list(args) {
|
|
11407
|
+
return { files: listEfCoreFiles(args.projectDir) };
|
|
11408
|
+
},
|
|
11409
|
+
// baseline: use stamp (EF has no separate baseline concept).
|
|
11410
|
+
async newMigration(args) {
|
|
11411
|
+
try {
|
|
11412
|
+
const created = await createEfcoreMigration({ projectDir: args.projectDir, slug: args.slug });
|
|
11413
|
+
return {
|
|
11414
|
+
status: "ok",
|
|
11415
|
+
version: efVersion(created.id),
|
|
11416
|
+
filename: path15.basename(created.path),
|
|
11417
|
+
path: created.path
|
|
11418
|
+
};
|
|
11419
|
+
} catch (err) {
|
|
11420
|
+
return { status: "error", version: "", filename: "", path: "", error: errMsg(err) };
|
|
11421
|
+
}
|
|
11422
|
+
}
|
|
11423
|
+
};
|
|
11424
|
+
registerSchemaMigrationAdapter(EfCoreAdapter);
|
|
11425
|
+
|
|
11007
11426
|
// scripts/lakebase/schema-migrate.ts
|
|
11008
11427
|
var SchemaMigrationError = class extends Error {
|
|
11009
11428
|
constructor(message, cause) {
|
|
@@ -11013,8 +11432,11 @@ var SchemaMigrationError = class extends Error {
|
|
|
11013
11432
|
}
|
|
11014
11433
|
cause;
|
|
11015
11434
|
};
|
|
11016
|
-
function toolForLanguage(language) {
|
|
11435
|
+
function toolForLanguage(language, projectDir) {
|
|
11017
11436
|
switch (language) {
|
|
11437
|
+
case "dotnet":
|
|
11438
|
+
if (projectDir && isDotnetFlywayProject(projectDir)) return "flyway";
|
|
11439
|
+
return "efcore";
|
|
11018
11440
|
case "java":
|
|
11019
11441
|
case "kotlin":
|
|
11020
11442
|
return "flyway";
|
|
@@ -11025,7 +11447,7 @@ function toolForLanguage(language) {
|
|
|
11025
11447
|
}
|
|
11026
11448
|
}
|
|
11027
11449
|
function adapterFor(projectDir, language) {
|
|
11028
|
-
const override = language ? toolForLanguage(language) : void 0;
|
|
11450
|
+
const override = language ? toolForLanguage(language, projectDir) : void 0;
|
|
11029
11451
|
return resolveSchemaMigrationAdapter(projectDir, override);
|
|
11030
11452
|
}
|
|
11031
11453
|
var TierMigrationRefusedError = class extends Error {
|
|
@@ -11283,7 +11705,7 @@ async function runScmMergeCli(argv) {
|
|
|
11283
11705
|
`);
|
|
11284
11706
|
return 0;
|
|
11285
11707
|
}
|
|
11286
|
-
const projectDir =
|
|
11708
|
+
const projectDir = path17.resolve(args.projectDir ?? process.cwd());
|
|
11287
11709
|
const waitMigrate = args.noWaitMigrate ? false : true;
|
|
11288
11710
|
const verifyMigrateAuth = waitMigrate && !args.noVerifyMigrateAuth ? async () => {
|
|
11289
11711
|
try {
|