@databricks-solutions/lakebase-scm-utils 0.2.51 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/scripts/index.cjs +2632 -2120
- package/dist/scripts/index.d.cts +1 -1
- package/dist/scripts/index.d.ts +1 -1
- package/dist/scripts/index.js +676 -174
- 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 +2629 -2117
- package/dist/scripts/lakebase/index.d.cts +59 -10
- package/dist/scripts/lakebase/index.d.ts +59 -10
- package/dist/scripts/lakebase/index.js +670 -168
- 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/collapse-heads.cli.ts +1 -1
- package/scripts/lakebase/create-project.ts +6 -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,8 +1457,8 @@ var require_light = __commonJS({
|
|
|
1457
1457
|
|
|
1458
1458
|
// scripts/lakebase/scm-doctor.cli.ts
|
|
1459
1459
|
init_esm_shims();
|
|
1460
|
-
import * as
|
|
1461
|
-
import * as
|
|
1460
|
+
import * as fs16 from "fs";
|
|
1461
|
+
import * as path18 from "path";
|
|
1462
1462
|
|
|
1463
1463
|
// scripts/util/cli-entry.ts
|
|
1464
1464
|
init_esm_shims();
|
|
@@ -1484,8 +1484,8 @@ function isCliEntry(importMetaUrl) {
|
|
|
1484
1484
|
|
|
1485
1485
|
// scripts/lakebase/scm-doctor.ts
|
|
1486
1486
|
init_esm_shims();
|
|
1487
|
-
import * as
|
|
1488
|
-
import * as
|
|
1487
|
+
import * as fs15 from "fs";
|
|
1488
|
+
import * as path17 from "path";
|
|
1489
1489
|
|
|
1490
1490
|
// scripts/lakebase/branch-utils.ts
|
|
1491
1491
|
init_esm_shims();
|
|
@@ -1856,9 +1856,9 @@ function asBranchUid(s) {
|
|
|
1856
1856
|
}
|
|
1857
1857
|
return s;
|
|
1858
1858
|
}
|
|
1859
|
-
function branchNameFromResourcePath(
|
|
1860
|
-
if (!
|
|
1861
|
-
const leaf =
|
|
1859
|
+
function branchNameFromResourcePath(path19) {
|
|
1860
|
+
if (!path19.includes("/branches/")) return null;
|
|
1861
|
+
const leaf = path19.split("/branches/").pop();
|
|
1862
1862
|
if (!leaf) return null;
|
|
1863
1863
|
try {
|
|
1864
1864
|
return asBranchName(leaf);
|
|
@@ -3175,7 +3175,7 @@ paginateRest.VERSION = VERSION5;
|
|
|
3175
3175
|
|
|
3176
3176
|
// node_modules/@octokit/plugin-paginate-graphql/dist-bundle/index.js
|
|
3177
3177
|
init_esm_shims();
|
|
3178
|
-
var generateMessage = (
|
|
3178
|
+
var generateMessage = (path19, cursorValue) => `The cursor at "${path19.join(
|
|
3179
3179
|
","
|
|
3180
3180
|
)}" did not change its value "${cursorValue}" after a page transition. Please make sure your that your query is set up correctly.`;
|
|
3181
3181
|
var MissingCursorChange = class extends Error {
|
|
@@ -3216,9 +3216,9 @@ function findPaginatedResourcePath(responseData) {
|
|
|
3216
3216
|
}
|
|
3217
3217
|
return paginatedResourcePath;
|
|
3218
3218
|
}
|
|
3219
|
-
var deepFindPathToProperty = (object, searchProp,
|
|
3219
|
+
var deepFindPathToProperty = (object, searchProp, path19 = []) => {
|
|
3220
3220
|
for (const key of Object.keys(object)) {
|
|
3221
|
-
const currentPath = [...
|
|
3221
|
+
const currentPath = [...path19, key];
|
|
3222
3222
|
const currentValue = object[key];
|
|
3223
3223
|
if (isObject(currentValue)) {
|
|
3224
3224
|
if (currentValue.hasOwnProperty(searchProp)) {
|
|
@@ -3236,12 +3236,12 @@ var deepFindPathToProperty = (object, searchProp, path16 = []) => {
|
|
|
3236
3236
|
}
|
|
3237
3237
|
return [];
|
|
3238
3238
|
};
|
|
3239
|
-
var get = (object,
|
|
3240
|
-
return
|
|
3239
|
+
var get = (object, path19) => {
|
|
3240
|
+
return path19.reduce((current, nextProperty) => current[nextProperty], object);
|
|
3241
3241
|
};
|
|
3242
|
-
var set = (object,
|
|
3243
|
-
const lastProperty =
|
|
3244
|
-
const parentPath = [...
|
|
3242
|
+
var set = (object, path19, mutator) => {
|
|
3243
|
+
const lastProperty = path19[path19.length - 1];
|
|
3244
|
+
const parentPath = [...path19].slice(0, -1);
|
|
3245
3245
|
const parent = get(object, parentPath);
|
|
3246
3246
|
if (typeof mutator === "function") {
|
|
3247
3247
|
parent[lastProperty] = mutator(parent[lastProperty]);
|
|
@@ -3293,22 +3293,22 @@ var mergeResponses = (response1, response2) => {
|
|
|
3293
3293
|
if (Object.keys(response1).length === 0) {
|
|
3294
3294
|
return Object.assign(response1, response2);
|
|
3295
3295
|
}
|
|
3296
|
-
const
|
|
3297
|
-
const nodesPath = [...
|
|
3296
|
+
const path19 = findPaginatedResourcePath(response1);
|
|
3297
|
+
const nodesPath = [...path19, "nodes"];
|
|
3298
3298
|
const newNodes = get(response2, nodesPath);
|
|
3299
3299
|
if (newNodes) {
|
|
3300
3300
|
set(response1, nodesPath, (values) => {
|
|
3301
3301
|
return [...values, ...newNodes];
|
|
3302
3302
|
});
|
|
3303
3303
|
}
|
|
3304
|
-
const edgesPath = [...
|
|
3304
|
+
const edgesPath = [...path19, "edges"];
|
|
3305
3305
|
const newEdges = get(response2, edgesPath);
|
|
3306
3306
|
if (newEdges) {
|
|
3307
3307
|
set(response1, edgesPath, (values) => {
|
|
3308
3308
|
return [...values, ...newEdges];
|
|
3309
3309
|
});
|
|
3310
3310
|
}
|
|
3311
|
-
const pageInfoPath = [...
|
|
3311
|
+
const pageInfoPath = [...path19, "pageInfo"];
|
|
3312
3312
|
set(response1, pageInfoPath, get(response2, pageInfoPath));
|
|
3313
3313
|
return response1;
|
|
3314
3314
|
};
|
|
@@ -5729,7 +5729,7 @@ var triggers_notification_paths_default = [
|
|
|
5729
5729
|
];
|
|
5730
5730
|
function routeMatcher(paths) {
|
|
5731
5731
|
const regexes = paths.map(
|
|
5732
|
-
(
|
|
5732
|
+
(path19) => path19.split("/").map((c) => c.startsWith("{") ? "(?:.+?)" : c).join("/")
|
|
5733
5733
|
);
|
|
5734
5734
|
const regex2 = `^(?:${regexes.map((r) => `(?:${r})`).join("|")})[^/]*$`;
|
|
5735
5735
|
return new RegExp(regex2, "i");
|
|
@@ -9393,8 +9393,8 @@ var PKG_NAME = "@databricks-solutions/lakebase-scm-utils";
|
|
|
9393
9393
|
var cached;
|
|
9394
9394
|
function substrateSelfVersion() {
|
|
9395
9395
|
if (cached !== void 0) return cached;
|
|
9396
|
-
if ("0.
|
|
9397
|
-
cached = "0.
|
|
9396
|
+
if ("0.3.0".length > 0) {
|
|
9397
|
+
cached = "0.3.0";
|
|
9398
9398
|
return cached;
|
|
9399
9399
|
}
|
|
9400
9400
|
cached = "unknown";
|
|
@@ -10123,28 +10123,94 @@ async function abandonFeatureBranch(args) {
|
|
|
10123
10123
|
|
|
10124
10124
|
// scripts/lakebase/schema-migrate.ts
|
|
10125
10125
|
init_esm_shims();
|
|
10126
|
-
import * as
|
|
10127
|
-
import * as
|
|
10126
|
+
import * as fs14 from "fs";
|
|
10127
|
+
import * as path16 from "path";
|
|
10128
10128
|
|
|
10129
10129
|
// scripts/lakebase/migration-layout.ts
|
|
10130
10130
|
init_esm_shims();
|
|
10131
|
+
import * as fs7 from "fs";
|
|
10132
|
+
import * as path7 from "path";
|
|
10133
|
+
|
|
10134
|
+
// scripts/lakebase/dotnet-project.ts
|
|
10135
|
+
init_esm_shims();
|
|
10131
10136
|
import * as fs6 from "fs";
|
|
10132
10137
|
import * as path6 from "path";
|
|
10138
|
+
var EF_DESIGN_PACKAGE = "Microsoft.EntityFrameworkCore.Design";
|
|
10139
|
+
var DOTNET_FLYWAY_DIR = path6.join("db", "migrations");
|
|
10140
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set(["bin", "obj", "node_modules", ".git", "client", "tests", "test"]);
|
|
10141
|
+
function safeReaddir(dir) {
|
|
10142
|
+
try {
|
|
10143
|
+
return fs6.readdirSync(dir, { withFileTypes: true });
|
|
10144
|
+
} catch {
|
|
10145
|
+
return [];
|
|
10146
|
+
}
|
|
10147
|
+
}
|
|
10148
|
+
function hasDotnetMarker(dir) {
|
|
10149
|
+
return safeReaddir(dir).some(
|
|
10150
|
+
(e) => e.isFile() && /\.(sln|slnx|csproj)$/i.test(e.name)
|
|
10151
|
+
);
|
|
10152
|
+
}
|
|
10153
|
+
function findCsprojFiles(projectDir) {
|
|
10154
|
+
const found = [];
|
|
10155
|
+
const walk = (dir, depth) => {
|
|
10156
|
+
for (const e of safeReaddir(dir)) {
|
|
10157
|
+
const full = path6.join(dir, e.name);
|
|
10158
|
+
if (e.isFile() && e.name.toLowerCase().endsWith(".csproj")) {
|
|
10159
|
+
found.push(full);
|
|
10160
|
+
} else if (e.isDirectory() && depth < 2 && !SKIP_DIRS.has(e.name.toLowerCase()) && !e.name.startsWith(".")) {
|
|
10161
|
+
walk(full, depth + 1);
|
|
10162
|
+
}
|
|
10163
|
+
}
|
|
10164
|
+
};
|
|
10165
|
+
walk(projectDir, 0);
|
|
10166
|
+
return found.sort();
|
|
10167
|
+
}
|
|
10168
|
+
function findEfCoreProject(projectDir) {
|
|
10169
|
+
for (const csproj of findCsprojFiles(projectDir)) {
|
|
10170
|
+
let content = "";
|
|
10171
|
+
try {
|
|
10172
|
+
content = fs6.readFileSync(csproj, "utf-8");
|
|
10173
|
+
} catch {
|
|
10174
|
+
continue;
|
|
10175
|
+
}
|
|
10176
|
+
if (content.includes(EF_DESIGN_PACKAGE)) {
|
|
10177
|
+
const dir = path6.dirname(csproj);
|
|
10178
|
+
return { csproj, projectDir: dir, migrationsDir: path6.join(dir, "Migrations") };
|
|
10179
|
+
}
|
|
10180
|
+
}
|
|
10181
|
+
return null;
|
|
10182
|
+
}
|
|
10183
|
+
function isDotnetProject(projectDir) {
|
|
10184
|
+
return hasDotnetMarker(projectDir);
|
|
10185
|
+
}
|
|
10186
|
+
function isDotnetFlywayProject(projectDir) {
|
|
10187
|
+
return isDotnetProject(projectDir) && fs6.existsSync(path6.join(projectDir, DOTNET_FLYWAY_DIR));
|
|
10188
|
+
}
|
|
10189
|
+
var MAVEN_FLYWAY_DIR = path6.join("src", "main", "resources", "db", "migration");
|
|
10190
|
+
function flywayMigrationsDir(projectDir) {
|
|
10191
|
+
const maven = path6.join(projectDir, MAVEN_FLYWAY_DIR);
|
|
10192
|
+
if (fs6.existsSync(maven) || fs6.existsSync(path6.join(projectDir, "pom.xml"))) return maven;
|
|
10193
|
+
if (isDotnetFlywayProject(projectDir)) return path6.join(projectDir, DOTNET_FLYWAY_DIR);
|
|
10194
|
+
return maven;
|
|
10195
|
+
}
|
|
10196
|
+
|
|
10197
|
+
// scripts/lakebase/migration-layout.ts
|
|
10133
10198
|
var MIGRATION_LANGUAGES = [
|
|
10134
10199
|
"java",
|
|
10135
10200
|
"kotlin",
|
|
10136
10201
|
"python",
|
|
10137
10202
|
"nodejs",
|
|
10203
|
+
"dotnet",
|
|
10138
10204
|
"unknown"
|
|
10139
10205
|
];
|
|
10140
10206
|
function detectLanguageAt(dir) {
|
|
10141
|
-
if (
|
|
10142
|
-
const kotlinDir =
|
|
10143
|
-
if (
|
|
10207
|
+
if (fs7.existsSync(path7.join(dir, "pom.xml"))) {
|
|
10208
|
+
const kotlinDir = path7.join(dir, "src", "main", "kotlin");
|
|
10209
|
+
if (fs7.existsSync(kotlinDir)) {
|
|
10144
10210
|
return "kotlin";
|
|
10145
10211
|
}
|
|
10146
10212
|
try {
|
|
10147
|
-
const pom =
|
|
10213
|
+
const pom = fs7.readFileSync(path7.join(dir, "pom.xml"), "utf-8");
|
|
10148
10214
|
if (pom.includes("kotlin-maven-plugin")) {
|
|
10149
10215
|
return "kotlin";
|
|
10150
10216
|
}
|
|
@@ -10152,10 +10218,13 @@ function detectLanguageAt(dir) {
|
|
|
10152
10218
|
}
|
|
10153
10219
|
return "java";
|
|
10154
10220
|
}
|
|
10155
|
-
if (
|
|
10221
|
+
if (fs7.existsSync(path7.join(dir, "pyproject.toml")) || fs7.existsSync(path7.join(dir, "requirements.txt")) || fs7.existsSync(path7.join(dir, "alembic.ini"))) {
|
|
10156
10222
|
return "python";
|
|
10157
10223
|
}
|
|
10158
|
-
if (
|
|
10224
|
+
if (hasDotnetMarker(dir)) {
|
|
10225
|
+
return "dotnet";
|
|
10226
|
+
}
|
|
10227
|
+
if (fs7.existsSync(path7.join(dir, "package.json"))) {
|
|
10159
10228
|
return "nodejs";
|
|
10160
10229
|
}
|
|
10161
10230
|
return "unknown";
|
|
@@ -10176,14 +10245,14 @@ function resolveMigrationLanguage(projectDir, configuredMigrationPath, override)
|
|
|
10176
10245
|
if (!rel) {
|
|
10177
10246
|
return "unknown";
|
|
10178
10247
|
}
|
|
10179
|
-
const rootResolved =
|
|
10180
|
-
let dir =
|
|
10181
|
-
while (dir === rootResolved || dir.startsWith(rootResolved +
|
|
10248
|
+
const rootResolved = path7.resolve(projectDir);
|
|
10249
|
+
let dir = path7.resolve(projectDir, rel);
|
|
10250
|
+
while (dir === rootResolved || dir.startsWith(rootResolved + path7.sep)) {
|
|
10182
10251
|
const lang = detectLanguageAt(dir);
|
|
10183
10252
|
if (lang !== "unknown") {
|
|
10184
10253
|
return lang;
|
|
10185
10254
|
}
|
|
10186
|
-
const parent =
|
|
10255
|
+
const parent = path7.dirname(dir);
|
|
10187
10256
|
if (parent === dir) {
|
|
10188
10257
|
break;
|
|
10189
10258
|
}
|
|
@@ -10194,22 +10263,22 @@ function resolveMigrationLanguage(projectDir, configuredMigrationPath, override)
|
|
|
10194
10263
|
|
|
10195
10264
|
// scripts/lakebase/adapters/alembic-adapter.ts
|
|
10196
10265
|
init_esm_shims();
|
|
10197
|
-
import * as
|
|
10198
|
-
import * as
|
|
10266
|
+
import * as fs9 from "fs";
|
|
10267
|
+
import * as path9 from "path";
|
|
10199
10268
|
|
|
10200
10269
|
// scripts/lakebase/schema-migrate-runners/alembic.ts
|
|
10201
10270
|
init_esm_shims();
|
|
10202
10271
|
import { spawn } from "child_process";
|
|
10203
|
-
import * as
|
|
10204
|
-
import * as
|
|
10272
|
+
import * as fs8 from "fs";
|
|
10273
|
+
import * as path8 from "path";
|
|
10205
10274
|
function resolveAlembicBin(projectDir) {
|
|
10206
10275
|
const candidates = [
|
|
10207
|
-
|
|
10208
|
-
|
|
10276
|
+
path8.join(projectDir, ".venv", "bin", "alembic"),
|
|
10277
|
+
path8.join(projectDir, "venv", "bin", "alembic")
|
|
10209
10278
|
];
|
|
10210
10279
|
for (const candidate of candidates) {
|
|
10211
10280
|
try {
|
|
10212
|
-
if (
|
|
10281
|
+
if (fs8.existsSync(candidate)) return candidate;
|
|
10213
10282
|
} catch {
|
|
10214
10283
|
}
|
|
10215
10284
|
}
|
|
@@ -10219,7 +10288,7 @@ function spawnAlembic(projectDir, args, dsn) {
|
|
|
10219
10288
|
return new Promise((resolve3, reject) => {
|
|
10220
10289
|
const bin = resolveAlembicBin(projectDir);
|
|
10221
10290
|
const env = { ...process.env };
|
|
10222
|
-
env.PYTHONPATH = [projectDir, process.env.PYTHONPATH].filter(Boolean).join(
|
|
10291
|
+
env.PYTHONPATH = [projectDir, process.env.PYTHONPATH].filter(Boolean).join(path8.delimiter);
|
|
10223
10292
|
if (dsn) env.DATABASE_URL = dsn;
|
|
10224
10293
|
const child = spawn(bin, args, {
|
|
10225
10294
|
cwd: projectDir,
|
|
@@ -10267,10 +10336,10 @@ async function createAlembicRevision(opts) {
|
|
|
10267
10336
|
const m = stdout.match(/Generating\s+(\S+\.py)/);
|
|
10268
10337
|
if (m) return m[1].trim();
|
|
10269
10338
|
for (const rel of ["migrations/versions", "alembic/versions"]) {
|
|
10270
|
-
const dir =
|
|
10271
|
-
if (!
|
|
10272
|
-
const hit =
|
|
10273
|
-
if (hit) return
|
|
10339
|
+
const dir = path8.join(opts.projectDir, rel);
|
|
10340
|
+
if (!fs8.existsSync(dir)) continue;
|
|
10341
|
+
const hit = fs8.readdirSync(dir).find((f) => f.startsWith(`${opts.revId}_`) && f.endsWith(".py"));
|
|
10342
|
+
if (hit) return path8.join(dir, hit);
|
|
10274
10343
|
}
|
|
10275
10344
|
throw new SchemaMigrationError(
|
|
10276
10345
|
`alembic revision succeeded but the created file could not be located.
|
|
@@ -10405,15 +10474,15 @@ async function buildDsn(args) {
|
|
|
10405
10474
|
}
|
|
10406
10475
|
function findVersionsDir(projectDir) {
|
|
10407
10476
|
const candidates = [
|
|
10408
|
-
|
|
10409
|
-
|
|
10477
|
+
path9.join(projectDir, "migrations", "versions"),
|
|
10478
|
+
path9.join(projectDir, "alembic", "versions")
|
|
10410
10479
|
];
|
|
10411
|
-
return candidates.find((p) =>
|
|
10480
|
+
return candidates.find((p) => fs9.existsSync(p));
|
|
10412
10481
|
}
|
|
10413
10482
|
function listAlembicFiles(projectDir) {
|
|
10414
10483
|
const dir = findVersionsDir(projectDir);
|
|
10415
10484
|
if (!dir) return [];
|
|
10416
|
-
const files =
|
|
10485
|
+
const files = fs9.readdirSync(dir).filter((f) => f.endsWith(".py") && !f.startsWith("__"));
|
|
10417
10486
|
return files.map((filename) => {
|
|
10418
10487
|
const stem = filename.replace(/\.py$/, "");
|
|
10419
10488
|
const sep3 = stem.indexOf("_");
|
|
@@ -10438,9 +10507,9 @@ var AlembicAdapter = {
|
|
|
10438
10507
|
* here. Callers can still force-select via project.yaml#migration_tool.
|
|
10439
10508
|
*/
|
|
10440
10509
|
detect(projectDir) {
|
|
10441
|
-
if (
|
|
10442
|
-
if (
|
|
10443
|
-
if (
|
|
10510
|
+
if (fs9.existsSync(path9.join(projectDir, "alembic.ini"))) return true;
|
|
10511
|
+
if (fs9.existsSync(path9.join(projectDir, "migrations", "env.py"))) return true;
|
|
10512
|
+
if (fs9.existsSync(path9.join(projectDir, "alembic", "env.py"))) return true;
|
|
10444
10513
|
return false;
|
|
10445
10514
|
},
|
|
10446
10515
|
async apply(args) {
|
|
@@ -10546,7 +10615,7 @@ var AlembicAdapter = {
|
|
|
10546
10615
|
autogenerate: !!args.autogenerate,
|
|
10547
10616
|
dsn
|
|
10548
10617
|
});
|
|
10549
|
-
return { status: "ok", version: revId, filename:
|
|
10618
|
+
return { status: "ok", version: revId, filename: path9.basename(created), path: created };
|
|
10550
10619
|
} catch (err) {
|
|
10551
10620
|
return {
|
|
10552
10621
|
status: "error",
|
|
@@ -10563,7 +10632,7 @@ var AlembicAdapter = {
|
|
|
10563
10632
|
if (heads.length <= 1) return { status: "noop", headsBefore: heads };
|
|
10564
10633
|
if (args.dryRun) return { status: "ok", headsBefore: heads };
|
|
10565
10634
|
const created = await mergeAlembicHeads(args.projectDir, args.message ?? "merge heads");
|
|
10566
|
-
const mergeRevision =
|
|
10635
|
+
const mergeRevision = path9.basename(created).replace(/\.py$/, "").split("_")[0];
|
|
10567
10636
|
return { status: "ok", headsBefore: heads, mergeRevision, path: created };
|
|
10568
10637
|
} catch (err) {
|
|
10569
10638
|
return {
|
|
@@ -10578,13 +10647,13 @@ registerSchemaMigrationAdapter(AlembicAdapter);
|
|
|
10578
10647
|
|
|
10579
10648
|
// scripts/lakebase/adapters/flyway-adapter.ts
|
|
10580
10649
|
init_esm_shims();
|
|
10581
|
-
import * as
|
|
10582
|
-
import * as
|
|
10650
|
+
import * as fs10 from "fs";
|
|
10651
|
+
import * as path11 from "path";
|
|
10583
10652
|
|
|
10584
10653
|
// scripts/lakebase/schema-migrate-runners/flyway.ts
|
|
10585
10654
|
init_esm_shims();
|
|
10586
10655
|
import { spawn as spawn2 } from "child_process";
|
|
10587
|
-
import * as
|
|
10656
|
+
import * as path10 from "path";
|
|
10588
10657
|
function dsnToFlywayEnv(dsn) {
|
|
10589
10658
|
const u = new URL(dsn);
|
|
10590
10659
|
const user = decodeURIComponent(u.username);
|
|
@@ -10594,7 +10663,7 @@ function dsnToFlywayEnv(dsn) {
|
|
|
10594
10663
|
return { url, user, password };
|
|
10595
10664
|
}
|
|
10596
10665
|
function migrationsLocation(projectDir) {
|
|
10597
|
-
return `filesystem:${
|
|
10666
|
+
return `filesystem:${flywayMigrationsDir(projectDir)}`;
|
|
10598
10667
|
}
|
|
10599
10668
|
function runFlyway(ctx, args) {
|
|
10600
10669
|
const { url, user, password } = dsnToFlywayEnv(ctx.dsn);
|
|
@@ -10695,7 +10764,7 @@ async function statusFlyway(ctx) {
|
|
|
10695
10764
|
if (state === "SUCCESS" || state === "BASELINE") {
|
|
10696
10765
|
current = m.version;
|
|
10697
10766
|
} else if (state === "PENDING") {
|
|
10698
|
-
const filename = m.filepath ?
|
|
10767
|
+
const filename = m.filepath ? path10.basename(m.filepath) : `V${m.version}__migration.sql`;
|
|
10699
10768
|
pending.push({
|
|
10700
10769
|
version: m.version,
|
|
10701
10770
|
filename,
|
|
@@ -10718,9 +10787,9 @@ async function buildDsn2(args) {
|
|
|
10718
10787
|
return result.url;
|
|
10719
10788
|
}
|
|
10720
10789
|
function listFlywayFiles(projectDir) {
|
|
10721
|
-
const dir =
|
|
10722
|
-
if (!
|
|
10723
|
-
const files =
|
|
10790
|
+
const dir = flywayMigrationsDir(projectDir);
|
|
10791
|
+
if (!fs10.existsSync(dir)) return [];
|
|
10792
|
+
const files = fs10.readdirSync(dir).filter((f) => /^V\d+(\.\d+)*__.+\.sql$/.test(f));
|
|
10724
10793
|
return files.map((filename) => {
|
|
10725
10794
|
const m = filename.match(/^V(\d+(?:\.\d+)*)__(.+)\.sql$/);
|
|
10726
10795
|
const version = m[1];
|
|
@@ -10741,9 +10810,9 @@ function versionCompare(a, b) {
|
|
|
10741
10810
|
}
|
|
10742
10811
|
var FlywayAdapter = {
|
|
10743
10812
|
id: "flyway",
|
|
10744
|
-
languages: ["java", "kotlin"],
|
|
10813
|
+
languages: ["java", "kotlin", "dotnet"],
|
|
10745
10814
|
detect(projectDir) {
|
|
10746
|
-
return
|
|
10815
|
+
return fs10.existsSync(path11.join(projectDir, "pom.xml")) || isDotnetFlywayProject(projectDir);
|
|
10747
10816
|
},
|
|
10748
10817
|
async apply(args) {
|
|
10749
10818
|
const dsn = await buildDsn2(args);
|
|
@@ -10801,14 +10870,14 @@ var FlywayAdapter = {
|
|
|
10801
10870
|
// optional-protocol shape makes this additive.
|
|
10802
10871
|
async newMigration(args) {
|
|
10803
10872
|
try {
|
|
10804
|
-
const dir =
|
|
10805
|
-
|
|
10873
|
+
const dir = flywayMigrationsDir(args.projectDir);
|
|
10874
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
10806
10875
|
const version = migrationTimestamp();
|
|
10807
10876
|
const slug = migrationSlug2(args.slug);
|
|
10808
10877
|
const filename = `V${version}__${slug}.sql`;
|
|
10809
|
-
const full =
|
|
10810
|
-
if (
|
|
10811
|
-
|
|
10878
|
+
const full = path11.join(dir, filename);
|
|
10879
|
+
if (fs10.existsSync(full)) throw new Error(`${filename} already exists`);
|
|
10880
|
+
fs10.writeFileSync(
|
|
10812
10881
|
full,
|
|
10813
10882
|
`-- V${version}: ${args.slug}
|
|
10814
10883
|
-- Flyway migration (write your DDL/DML below).
|
|
@@ -10831,19 +10900,19 @@ registerSchemaMigrationAdapter(FlywayAdapter);
|
|
|
10831
10900
|
|
|
10832
10901
|
// scripts/lakebase/adapters/knex-adapter.ts
|
|
10833
10902
|
init_esm_shims();
|
|
10834
|
-
import * as
|
|
10835
|
-
import * as
|
|
10903
|
+
import * as fs12 from "fs";
|
|
10904
|
+
import * as path13 from "path";
|
|
10836
10905
|
|
|
10837
10906
|
// scripts/lakebase/schema-migrate-runners/knex.ts
|
|
10838
10907
|
init_esm_shims();
|
|
10839
10908
|
import { spawn as spawn3 } from "child_process";
|
|
10840
|
-
import * as
|
|
10841
|
-
import * as
|
|
10909
|
+
import * as fs11 from "fs";
|
|
10910
|
+
import * as path12 from "path";
|
|
10842
10911
|
var KNEXFILE_VARIANTS = ["knexfile.js", "knexfile.ts", "knexfile.mjs", "knexfile.cjs"];
|
|
10843
10912
|
function findKnexfile(projectDir) {
|
|
10844
10913
|
for (const name of KNEXFILE_VARIANTS) {
|
|
10845
|
-
const p =
|
|
10846
|
-
if (
|
|
10914
|
+
const p = path12.join(projectDir, name);
|
|
10915
|
+
if (fs11.existsSync(p)) return p;
|
|
10847
10916
|
}
|
|
10848
10917
|
return void 0;
|
|
10849
10918
|
}
|
|
@@ -10995,9 +11064,9 @@ async function buildDsn3(args) {
|
|
|
10995
11064
|
}
|
|
10996
11065
|
var KNEXFILE_VARIANTS2 = ["knexfile.js", "knexfile.ts", "knexfile.mjs", "knexfile.cjs"];
|
|
10997
11066
|
function listKnexFiles(projectDir) {
|
|
10998
|
-
const dir =
|
|
10999
|
-
if (!
|
|
11000
|
-
const files =
|
|
11067
|
+
const dir = path13.join(projectDir, "migrations");
|
|
11068
|
+
if (!fs12.existsSync(dir)) return [];
|
|
11069
|
+
const files = fs12.readdirSync(dir).filter((f) => (f.endsWith(".js") || f.endsWith(".ts")) && !f.startsWith("."));
|
|
11001
11070
|
return files.map((filename) => {
|
|
11002
11071
|
const stem = filename.replace(/\.(js|ts)$/, "");
|
|
11003
11072
|
const m = stem.match(/^(\d{14})_(.+)$/);
|
|
@@ -11017,7 +11086,7 @@ var KnexAdapter = {
|
|
|
11017
11086
|
* project.yaml#migration_tool.
|
|
11018
11087
|
*/
|
|
11019
11088
|
detect(projectDir) {
|
|
11020
|
-
return KNEXFILE_VARIANTS2.some((name) =>
|
|
11089
|
+
return KNEXFILE_VARIANTS2.some((name) => fs12.existsSync(path13.join(projectDir, name)));
|
|
11021
11090
|
},
|
|
11022
11091
|
async apply(args) {
|
|
11023
11092
|
const dsn = await buildDsn3(args);
|
|
@@ -11090,9 +11159,9 @@ var KnexAdapter = {
|
|
|
11090
11159
|
async newMigration(args) {
|
|
11091
11160
|
try {
|
|
11092
11161
|
const created = await createKnexMigration({ projectDir: args.projectDir, slug: migrationSlug2(args.slug) });
|
|
11093
|
-
const stem =
|
|
11162
|
+
const stem = path13.basename(created).replace(/\.(js|ts)$/, "");
|
|
11094
11163
|
const version = stem.match(/^(\d{14})_/)?.[1] ?? stem;
|
|
11095
|
-
return { status: "ok", version, filename:
|
|
11164
|
+
return { status: "ok", version, filename: path13.basename(created), path: created };
|
|
11096
11165
|
} catch (err) {
|
|
11097
11166
|
return {
|
|
11098
11167
|
status: "error",
|
|
@@ -11106,6 +11175,362 @@ var KnexAdapter = {
|
|
|
11106
11175
|
};
|
|
11107
11176
|
registerSchemaMigrationAdapter(KnexAdapter);
|
|
11108
11177
|
|
|
11178
|
+
// scripts/lakebase/adapters/efcore-adapter.ts
|
|
11179
|
+
init_esm_shims();
|
|
11180
|
+
import * as path15 from "path";
|
|
11181
|
+
|
|
11182
|
+
// scripts/lakebase/schema-migrate-runners/efcore.ts
|
|
11183
|
+
init_esm_shims();
|
|
11184
|
+
import { spawn as spawn4 } from "child_process";
|
|
11185
|
+
import * as fs13 from "fs";
|
|
11186
|
+
import * as path14 from "path";
|
|
11187
|
+
import { Client as Client2 } from "pg";
|
|
11188
|
+
var MIGRATION_ID = /^(\d{14})_(.+)$/;
|
|
11189
|
+
function efVersion(id) {
|
|
11190
|
+
return MIGRATION_ID.exec(id)?.[1] ?? id;
|
|
11191
|
+
}
|
|
11192
|
+
function efDescription(id) {
|
|
11193
|
+
const name = MIGRATION_ID.exec(id)?.[2] ?? id;
|
|
11194
|
+
return name.replace(/_/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2");
|
|
11195
|
+
}
|
|
11196
|
+
function dsnToNpgsql(dsn) {
|
|
11197
|
+
const u = new URL(dsn);
|
|
11198
|
+
const parts = [
|
|
11199
|
+
`Host=${u.hostname}`,
|
|
11200
|
+
`Port=${u.port || "5432"}`,
|
|
11201
|
+
`Database=${decodeURIComponent(u.pathname.replace(/^\//, "")) || "databricks_postgres"}`
|
|
11202
|
+
];
|
|
11203
|
+
if (u.username) parts.push(`Username=${decodeURIComponent(u.username)}`);
|
|
11204
|
+
if (u.password) parts.push(`Password=${decodeURIComponent(u.password)}`);
|
|
11205
|
+
const sslmode = (u.searchParams.get("sslmode") ?? "require").toLowerCase();
|
|
11206
|
+
const ssl = {
|
|
11207
|
+
disable: "Disable",
|
|
11208
|
+
allow: "Allow",
|
|
11209
|
+
prefer: "Prefer",
|
|
11210
|
+
require: "Require",
|
|
11211
|
+
"verify-ca": "VerifyCA",
|
|
11212
|
+
"verify-full": "VerifyFull"
|
|
11213
|
+
};
|
|
11214
|
+
parts.push(`SSL Mode=${ssl[sslmode] ?? "Require"}`);
|
|
11215
|
+
const app = u.searchParams.get("application_name");
|
|
11216
|
+
if (app) parts.push(`Application Name=${app}`);
|
|
11217
|
+
return parts.map((p) => p.replace(/;/g, "")).join(";");
|
|
11218
|
+
}
|
|
11219
|
+
function parseEfJson(stdout) {
|
|
11220
|
+
const body = stdout.split(/\r?\n/).filter((l) => l.startsWith("data:")).map((l) => l.slice("data:".length)).join("\n").trim();
|
|
11221
|
+
if (!body) {
|
|
11222
|
+
throw new SchemaMigrationError(`dotnet ef returned no JSON payload: ${stdout.slice(0, 400)}`);
|
|
11223
|
+
}
|
|
11224
|
+
try {
|
|
11225
|
+
return JSON.parse(body);
|
|
11226
|
+
} catch (err) {
|
|
11227
|
+
throw new SchemaMigrationError(
|
|
11228
|
+
`dotnet ef JSON parse failed: ${err instanceof Error ? err.message : String(err)}.
|
|
11229
|
+
Body (first 400 chars): ${body.slice(0, 400)}`
|
|
11230
|
+
);
|
|
11231
|
+
}
|
|
11232
|
+
}
|
|
11233
|
+
function requireProject(projectDir) {
|
|
11234
|
+
const project = findEfCoreProject(projectDir);
|
|
11235
|
+
if (!project) {
|
|
11236
|
+
throw new SchemaMigrationError(
|
|
11237
|
+
`No EF Core project found under ${projectDir}. Expected a *.csproj that references ${EF_DESIGN_PACKAGE}.`
|
|
11238
|
+
);
|
|
11239
|
+
}
|
|
11240
|
+
return project;
|
|
11241
|
+
}
|
|
11242
|
+
function spawnDotnet(projectDir, args, env) {
|
|
11243
|
+
return new Promise((resolve3, reject) => {
|
|
11244
|
+
const child = spawn4("dotnet", args, {
|
|
11245
|
+
cwd: projectDir,
|
|
11246
|
+
env: { ...process.env, DOTNET_NOLOGO: "1", DOTNET_CLI_TELEMETRY_OPTOUT: "1", ...env },
|
|
11247
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
11248
|
+
});
|
|
11249
|
+
let stdout = "";
|
|
11250
|
+
let stderr = "";
|
|
11251
|
+
child.stdout.on("data", (chunk) => {
|
|
11252
|
+
stdout += chunk.toString("utf8");
|
|
11253
|
+
});
|
|
11254
|
+
child.stderr.on("data", (chunk) => {
|
|
11255
|
+
stderr += chunk.toString("utf8");
|
|
11256
|
+
});
|
|
11257
|
+
child.on("error", (err) => {
|
|
11258
|
+
reject(
|
|
11259
|
+
new SchemaMigrationError(
|
|
11260
|
+
`Could not spawn dotnet. Is the .NET SDK installed and on PATH? ${err.message}`,
|
|
11261
|
+
err
|
|
11262
|
+
)
|
|
11263
|
+
);
|
|
11264
|
+
});
|
|
11265
|
+
child.on("close", (code) => {
|
|
11266
|
+
if (code === 0) {
|
|
11267
|
+
resolve3({ stdout, stderr });
|
|
11268
|
+
} else {
|
|
11269
|
+
reject(
|
|
11270
|
+
new SchemaMigrationError(
|
|
11271
|
+
`dotnet ${args.join(" ")} exited with code ${code}.
|
|
11272
|
+
stdout: ${stdout}
|
|
11273
|
+
stderr: ${stderr}`
|
|
11274
|
+
)
|
|
11275
|
+
);
|
|
11276
|
+
}
|
|
11277
|
+
});
|
|
11278
|
+
});
|
|
11279
|
+
}
|
|
11280
|
+
var restored = /* @__PURE__ */ new Set();
|
|
11281
|
+
async function ensureRestored(projectDir, csproj) {
|
|
11282
|
+
if (restored.has(projectDir)) return;
|
|
11283
|
+
if (fs13.existsSync(path14.join(projectDir, ".config", "dotnet-tools.json"))) {
|
|
11284
|
+
await spawnDotnet(projectDir, ["tool", "restore"], {});
|
|
11285
|
+
}
|
|
11286
|
+
await spawnDotnet(projectDir, ["restore", csproj], {});
|
|
11287
|
+
restored.add(projectDir);
|
|
11288
|
+
}
|
|
11289
|
+
async function runEf(projectDir, args, dsn, opts = {}) {
|
|
11290
|
+
const project = requireProject(projectDir);
|
|
11291
|
+
const csproj = path14.relative(projectDir, project.csproj) || project.csproj;
|
|
11292
|
+
await ensureRestored(projectDir, csproj);
|
|
11293
|
+
const full = ["ef", ...args, "--project", csproj, "--prefix-output"];
|
|
11294
|
+
if (opts.noBuild) full.push("--no-build");
|
|
11295
|
+
const env = {};
|
|
11296
|
+
if (dsn) {
|
|
11297
|
+
env.DATABASE_URL = dsn;
|
|
11298
|
+
if (process.env.LAKEBASE_EFCORE_CONNECTION_ARG === "1") full.push("--connection", dsnToNpgsql(dsn));
|
|
11299
|
+
}
|
|
11300
|
+
return spawnDotnet(projectDir, full, env);
|
|
11301
|
+
}
|
|
11302
|
+
async function listEfMigrations(ctx, opts = {}) {
|
|
11303
|
+
const { stdout, stderr } = await runEf(ctx.projectDir, ["migrations", "list", "--json"], ctx.dsn, opts);
|
|
11304
|
+
const entries = parseEfJson(stdout);
|
|
11305
|
+
if (ctx.dsn && entries.some((e) => e.applied === null)) {
|
|
11306
|
+
const warning = stdout.split(/\r?\n/).filter((l) => /^(warn|error):/.test(l)).join("\n");
|
|
11307
|
+
throw new SchemaMigrationError(
|
|
11308
|
+
`dotnet ef could not read migration state from the database (check connectivity and credentials).
|
|
11309
|
+
${warning || stderr}`.trim()
|
|
11310
|
+
);
|
|
11311
|
+
}
|
|
11312
|
+
return entries;
|
|
11313
|
+
}
|
|
11314
|
+
async function readEfHistory(dsn) {
|
|
11315
|
+
const client = new Client2({ connectionString: dsn, application_name: connectionApplicationName() });
|
|
11316
|
+
await client.connect();
|
|
11317
|
+
try {
|
|
11318
|
+
const exists = await client.query(`SELECT to_regclass('"__EFMigrationsHistory"') IS NOT NULL AS present`);
|
|
11319
|
+
if (!exists.rows[0]?.present) return null;
|
|
11320
|
+
const r = await client.query(`SELECT "MigrationId" FROM "__EFMigrationsHistory" ORDER BY "MigrationId"`);
|
|
11321
|
+
return r.rows.map((row) => row.MigrationId);
|
|
11322
|
+
} finally {
|
|
11323
|
+
await client.end();
|
|
11324
|
+
}
|
|
11325
|
+
}
|
|
11326
|
+
function toApplied(entries) {
|
|
11327
|
+
return entries.map((e) => ({ version: efVersion(e.id), description: efDescription(e.id) }));
|
|
11328
|
+
}
|
|
11329
|
+
async function statusEfcore(ctx) {
|
|
11330
|
+
const entries = await listEfMigrations(ctx);
|
|
11331
|
+
const history = await (ctx.readHistory ?? readEfHistory)(ctx.dsn);
|
|
11332
|
+
const current = history && history.length > 0 ? efVersion(history[history.length - 1]) : void 0;
|
|
11333
|
+
const pending = entries.filter((e) => e.applied !== true).map((e) => ({ version: efVersion(e.id), filename: `${e.id}.cs`, description: efDescription(e.id) }));
|
|
11334
|
+
return { current, pending, tool: "efcore" };
|
|
11335
|
+
}
|
|
11336
|
+
async function applyEfcore(ctx) {
|
|
11337
|
+
const before = new Set((await listEfMigrations(ctx)).filter((e) => e.applied).map((e) => e.id));
|
|
11338
|
+
await runEf(ctx.projectDir, ["database", "update"], ctx.dsn, { noBuild: true });
|
|
11339
|
+
const after = await listEfMigrations(ctx, { noBuild: true });
|
|
11340
|
+
const applied = toApplied(after.filter((e) => e.applied && !before.has(e.id)));
|
|
11341
|
+
return { applied, alreadyAtLatest: applied.length === 0, tool: "efcore" };
|
|
11342
|
+
}
|
|
11343
|
+
function resolveEfTarget(target, applied) {
|
|
11344
|
+
const t = target.trim();
|
|
11345
|
+
if (t === "0") return "0";
|
|
11346
|
+
const rel = /^-(\d+)$/.exec(t);
|
|
11347
|
+
if (rel) {
|
|
11348
|
+
const steps = Number(rel[1]);
|
|
11349
|
+
if (steps < 1) throw new SchemaMigrationError(`Invalid rollback step: ${target}`);
|
|
11350
|
+
const index = applied.length - 1 - steps;
|
|
11351
|
+
if (index < -1) {
|
|
11352
|
+
throw new SchemaMigrationError(`Cannot roll back ${steps} migration(s); only ${applied.length} applied.`);
|
|
11353
|
+
}
|
|
11354
|
+
return index === -1 ? "0" : applied[index].id;
|
|
11355
|
+
}
|
|
11356
|
+
const match = applied.find((e) => e.id === t || efVersion(e.id) === t || e.name === t);
|
|
11357
|
+
if (!match) throw new SchemaMigrationError(`Rollback target '${target}' is not an applied migration.`);
|
|
11358
|
+
return match.id;
|
|
11359
|
+
}
|
|
11360
|
+
async function rollbackEfcore(ctx) {
|
|
11361
|
+
const beforeEntries = (await listEfMigrations(ctx)).filter((e) => e.applied);
|
|
11362
|
+
const targetId = resolveEfTarget(ctx.target, beforeEntries);
|
|
11363
|
+
await runEf(ctx.projectDir, ["database", "update", targetId], ctx.dsn, { noBuild: true });
|
|
11364
|
+
const stillApplied = new Set((await listEfMigrations(ctx, { noBuild: true })).filter((e) => e.applied).map((e) => e.id));
|
|
11365
|
+
return { rolledBack: toApplied(beforeEntries.filter((e) => !stillApplied.has(e.id)).reverse()), tool: "efcore" };
|
|
11366
|
+
}
|
|
11367
|
+
function efMigrationName(slug) {
|
|
11368
|
+
const name = slug.split(/[^A-Za-z0-9]+/).filter(Boolean).map((w) => w[0].toUpperCase() + w.slice(1)).join("");
|
|
11369
|
+
if (!name) throw new SchemaMigrationError(`Cannot derive a migration name from '${slug}'.`);
|
|
11370
|
+
return /^[0-9]/.test(name) ? `M${name}` : name;
|
|
11371
|
+
}
|
|
11372
|
+
async function createEfcoreMigration(opts) {
|
|
11373
|
+
const project = requireProject(opts.projectDir);
|
|
11374
|
+
const name = efMigrationName(opts.slug);
|
|
11375
|
+
const existing = new Set(fs13.existsSync(project.migrationsDir) ? fs13.readdirSync(project.migrationsDir) : []);
|
|
11376
|
+
await runEf(opts.projectDir, ["migrations", "add", name]);
|
|
11377
|
+
const created = fs13.readdirSync(project.migrationsDir).filter((f) => !existing.has(f) && MIGRATION_ID.test(f.replace(/\.cs$/, "")) && !f.endsWith(".Designer.cs"));
|
|
11378
|
+
if (created.length !== 1) {
|
|
11379
|
+
throw new SchemaMigrationError(`Expected one new migration file for ${name}, found: ${created.join(", ") || "none"}`);
|
|
11380
|
+
}
|
|
11381
|
+
return { id: created[0].replace(/\.cs$/, ""), path: path14.join(project.migrationsDir, created[0]) };
|
|
11382
|
+
}
|
|
11383
|
+
function localEfMigrationIds(projectDir) {
|
|
11384
|
+
const project = findEfCoreProject(projectDir);
|
|
11385
|
+
if (!project || !fs13.existsSync(project.migrationsDir)) return [];
|
|
11386
|
+
return fs13.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();
|
|
11387
|
+
}
|
|
11388
|
+
function efProductVersion(project) {
|
|
11389
|
+
try {
|
|
11390
|
+
const csproj = fs13.readFileSync(project.csproj, "utf-8");
|
|
11391
|
+
const m = new RegExp(`${EF_DESIGN_PACKAGE.replace(/\./g, "\\.")}"\\s+Version="([^"]+)"`).exec(csproj);
|
|
11392
|
+
if (m) return m[1];
|
|
11393
|
+
} catch {
|
|
11394
|
+
}
|
|
11395
|
+
return "unknown";
|
|
11396
|
+
}
|
|
11397
|
+
async function stampEfcore(ctx) {
|
|
11398
|
+
const project = requireProject(ctx.projectDir);
|
|
11399
|
+
const ids = localEfMigrationIds(ctx.projectDir);
|
|
11400
|
+
if (ids.length === 0) throw new SchemaMigrationError("No local EF Core migrations to stamp.");
|
|
11401
|
+
const rev = ctx.revision.trim();
|
|
11402
|
+
const end = rev === "head" ? ids.length - 1 : ids.findIndex((id) => id === rev || efVersion(id) === rev);
|
|
11403
|
+
if (end < 0) throw new SchemaMigrationError(`Stamp revision '${ctx.revision}' is not a local migration.`);
|
|
11404
|
+
const toStamp = ids.slice(0, end + 1);
|
|
11405
|
+
const productVersion = efProductVersion(project);
|
|
11406
|
+
const client = new Client2({ connectionString: ctx.dsn, application_name: connectionApplicationName() });
|
|
11407
|
+
await client.connect();
|
|
11408
|
+
try {
|
|
11409
|
+
await client.query("BEGIN");
|
|
11410
|
+
await client.query(
|
|
11411
|
+
`CREATE TABLE IF NOT EXISTS "__EFMigrationsHistory" (
|
|
11412
|
+
"MigrationId" character varying(150) NOT NULL,
|
|
11413
|
+
"ProductVersion" character varying(32) NOT NULL,
|
|
11414
|
+
CONSTRAINT "PK___EFMigrationsHistory" PRIMARY KEY ("MigrationId"))`
|
|
11415
|
+
);
|
|
11416
|
+
await client.query(`DELETE FROM "__EFMigrationsHistory" WHERE NOT ("MigrationId" = ANY($1::text[]))`, [toStamp]);
|
|
11417
|
+
for (const id of toStamp) {
|
|
11418
|
+
await client.query(
|
|
11419
|
+
`INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") VALUES ($1, $2)
|
|
11420
|
+
ON CONFLICT ("MigrationId") DO NOTHING`,
|
|
11421
|
+
[id, productVersion]
|
|
11422
|
+
);
|
|
11423
|
+
}
|
|
11424
|
+
await client.query("COMMIT");
|
|
11425
|
+
} catch (err) {
|
|
11426
|
+
await client.query("ROLLBACK").catch(() => void 0);
|
|
11427
|
+
throw err;
|
|
11428
|
+
} finally {
|
|
11429
|
+
await client.end();
|
|
11430
|
+
}
|
|
11431
|
+
return efVersion(toStamp[toStamp.length - 1]);
|
|
11432
|
+
}
|
|
11433
|
+
|
|
11434
|
+
// scripts/lakebase/adapters/efcore-adapter.ts
|
|
11435
|
+
async function buildDsn4(args) {
|
|
11436
|
+
const result = await getConnection({
|
|
11437
|
+
output: "dsn",
|
|
11438
|
+
instance: args.instance,
|
|
11439
|
+
branch: args.branch,
|
|
11440
|
+
database: args.database,
|
|
11441
|
+
endpointName: args.endpointName
|
|
11442
|
+
});
|
|
11443
|
+
return result.url;
|
|
11444
|
+
}
|
|
11445
|
+
function errMsg(err) {
|
|
11446
|
+
return err instanceof Error ? err.message : String(err);
|
|
11447
|
+
}
|
|
11448
|
+
function listEfCoreFiles(projectDir) {
|
|
11449
|
+
return localEfMigrationIds(projectDir).map((id) => ({
|
|
11450
|
+
version: efVersion(id),
|
|
11451
|
+
filename: `${id}.cs`,
|
|
11452
|
+
description: efDescription(id),
|
|
11453
|
+
type: "CSharp",
|
|
11454
|
+
tool: "efcore"
|
|
11455
|
+
}));
|
|
11456
|
+
}
|
|
11457
|
+
var EfCoreAdapter = {
|
|
11458
|
+
id: "efcore",
|
|
11459
|
+
languages: ["dotnet"],
|
|
11460
|
+
/** A .NET project (root marker) with a *.csproj that references Microsoft.EntityFrameworkCore.Design. */
|
|
11461
|
+
detect(projectDir) {
|
|
11462
|
+
return isDotnetProject(projectDir) && findEfCoreProject(projectDir) !== null;
|
|
11463
|
+
},
|
|
11464
|
+
async apply(args) {
|
|
11465
|
+
try {
|
|
11466
|
+
const dsn = await buildDsn4(args);
|
|
11467
|
+
const r = await applyEfcore({ projectDir: args.projectDir, dsn });
|
|
11468
|
+
return {
|
|
11469
|
+
applied_migrations: r.applied,
|
|
11470
|
+
status: r.alreadyAtLatest ? "noop" : "ok",
|
|
11471
|
+
tool_specific: { alreadyAtLatest: r.alreadyAtLatest, tool: r.tool }
|
|
11472
|
+
};
|
|
11473
|
+
} catch (err) {
|
|
11474
|
+
return { applied_migrations: [], status: "error", error: errMsg(err) };
|
|
11475
|
+
}
|
|
11476
|
+
},
|
|
11477
|
+
async rollback(args) {
|
|
11478
|
+
try {
|
|
11479
|
+
const dsn = await buildDsn4(args);
|
|
11480
|
+
const r = await rollbackEfcore({ projectDir: args.projectDir, dsn, target: args.target });
|
|
11481
|
+
return {
|
|
11482
|
+
rolled_back: r.rolledBack,
|
|
11483
|
+
status: r.rolledBack.length === 0 ? "noop" : "ok",
|
|
11484
|
+
tool_specific: { tool: r.tool }
|
|
11485
|
+
};
|
|
11486
|
+
} catch (err) {
|
|
11487
|
+
return { rolled_back: [], status: "error", error: errMsg(err) };
|
|
11488
|
+
}
|
|
11489
|
+
},
|
|
11490
|
+
async stamp(args) {
|
|
11491
|
+
try {
|
|
11492
|
+
const dsn = await buildDsn4(args);
|
|
11493
|
+
const stamped = await stampEfcore({ projectDir: args.projectDir, dsn, revision: args.revision });
|
|
11494
|
+
return { status: "ok", stamped_revision: stamped, tool_specific: { tool: "efcore" } };
|
|
11495
|
+
} catch (err) {
|
|
11496
|
+
return { status: "error", stamped_revision: null, error: errMsg(err) };
|
|
11497
|
+
}
|
|
11498
|
+
},
|
|
11499
|
+
async status(args) {
|
|
11500
|
+
try {
|
|
11501
|
+
const dsn = await buildDsn4(args);
|
|
11502
|
+
const r = await statusEfcore({ projectDir: args.projectDir, dsn });
|
|
11503
|
+
return {
|
|
11504
|
+
applied_version: r.current ?? null,
|
|
11505
|
+
pending: r.pending,
|
|
11506
|
+
applied: [],
|
|
11507
|
+
status: "ok",
|
|
11508
|
+
tool_specific: { tool: r.tool }
|
|
11509
|
+
};
|
|
11510
|
+
} catch (err) {
|
|
11511
|
+
return { applied_version: null, pending: [], applied: [], status: "error", error: errMsg(err) };
|
|
11512
|
+
}
|
|
11513
|
+
},
|
|
11514
|
+
async list(args) {
|
|
11515
|
+
return { files: listEfCoreFiles(args.projectDir) };
|
|
11516
|
+
},
|
|
11517
|
+
// baseline: use stamp (EF has no separate baseline concept).
|
|
11518
|
+
async newMigration(args) {
|
|
11519
|
+
try {
|
|
11520
|
+
const created = await createEfcoreMigration({ projectDir: args.projectDir, slug: args.slug });
|
|
11521
|
+
return {
|
|
11522
|
+
status: "ok",
|
|
11523
|
+
version: efVersion(created.id),
|
|
11524
|
+
filename: path15.basename(created.path),
|
|
11525
|
+
path: created.path
|
|
11526
|
+
};
|
|
11527
|
+
} catch (err) {
|
|
11528
|
+
return { status: "error", version: "", filename: "", path: "", error: errMsg(err) };
|
|
11529
|
+
}
|
|
11530
|
+
}
|
|
11531
|
+
};
|
|
11532
|
+
registerSchemaMigrationAdapter(EfCoreAdapter);
|
|
11533
|
+
|
|
11109
11534
|
// scripts/lakebase/schema-migrate.ts
|
|
11110
11535
|
var SchemaMigrationError = class extends Error {
|
|
11111
11536
|
constructor(message, cause) {
|
|
@@ -11119,13 +11544,16 @@ function detectLanguage(projectDir) {
|
|
|
11119
11544
|
const lang = resolveMigrationLanguage(projectDir);
|
|
11120
11545
|
if (lang === "unknown") {
|
|
11121
11546
|
throw new SchemaMigrationError(
|
|
11122
|
-
`Could not detect project language in ${projectDir}. Expected one of: pom.xml (java/kotlin), pyproject.toml or alembic.ini (python), package.json (nodejs). Pass {language} explicitly to override.`
|
|
11547
|
+
`Could not detect project language in ${projectDir}. Expected one of: pom.xml (java/kotlin), pyproject.toml or alembic.ini (python), package.json (nodejs), *.sln or *.csproj (dotnet). Pass {language} explicitly to override.`
|
|
11123
11548
|
);
|
|
11124
11549
|
}
|
|
11125
11550
|
return lang;
|
|
11126
11551
|
}
|
|
11127
|
-
function toolForLanguage(language) {
|
|
11552
|
+
function toolForLanguage(language, projectDir) {
|
|
11128
11553
|
switch (language) {
|
|
11554
|
+
case "dotnet":
|
|
11555
|
+
if (projectDir && isDotnetFlywayProject(projectDir)) return "flyway";
|
|
11556
|
+
return "efcore";
|
|
11129
11557
|
case "java":
|
|
11130
11558
|
case "kotlin":
|
|
11131
11559
|
return "flyway";
|
|
@@ -11138,8 +11566,10 @@ function toolForLanguage(language) {
|
|
|
11138
11566
|
function listSchemaMigrations(args = {}) {
|
|
11139
11567
|
const projectDir = args.projectDir ?? process.cwd();
|
|
11140
11568
|
const language = args.language ?? detectLanguage(projectDir);
|
|
11141
|
-
const tool = toolForLanguage(language);
|
|
11569
|
+
const tool = toolForLanguage(language, projectDir);
|
|
11142
11570
|
switch (tool) {
|
|
11571
|
+
case "efcore":
|
|
11572
|
+
return listEfCoreFiles(projectDir);
|
|
11143
11573
|
case "flyway":
|
|
11144
11574
|
return listFlywayMigrations(projectDir);
|
|
11145
11575
|
case "alembic":
|
|
@@ -11149,9 +11579,9 @@ function listSchemaMigrations(args = {}) {
|
|
|
11149
11579
|
}
|
|
11150
11580
|
}
|
|
11151
11581
|
function listFlywayMigrations(projectDir) {
|
|
11152
|
-
const dir =
|
|
11153
|
-
if (!
|
|
11154
|
-
const files =
|
|
11582
|
+
const dir = flywayMigrationsDir(projectDir);
|
|
11583
|
+
if (!fs14.existsSync(dir)) return [];
|
|
11584
|
+
const files = fs14.readdirSync(dir).filter((f) => /^V\d+(\.\d+)*__.+\.sql$/.test(f));
|
|
11155
11585
|
return files.map((filename) => {
|
|
11156
11586
|
const m = filename.match(/^V(\d+(?:\.\d+)*)__(.+)\.sql$/);
|
|
11157
11587
|
const version = m[1];
|
|
@@ -11161,12 +11591,12 @@ function listFlywayMigrations(projectDir) {
|
|
|
11161
11591
|
}
|
|
11162
11592
|
function listAlembicMigrations(projectDir) {
|
|
11163
11593
|
const candidates = [
|
|
11164
|
-
|
|
11165
|
-
|
|
11594
|
+
path16.join(projectDir, "migrations", "versions"),
|
|
11595
|
+
path16.join(projectDir, "alembic", "versions")
|
|
11166
11596
|
];
|
|
11167
|
-
const dir = candidates.find((p) =>
|
|
11597
|
+
const dir = candidates.find((p) => fs14.existsSync(p));
|
|
11168
11598
|
if (!dir) return [];
|
|
11169
|
-
const files =
|
|
11599
|
+
const files = fs14.readdirSync(dir).filter((f) => f.endsWith(".py") && !f.startsWith("__"));
|
|
11170
11600
|
return files.map((filename) => {
|
|
11171
11601
|
const stem = filename.replace(/\.py$/, "");
|
|
11172
11602
|
const sep3 = stem.indexOf("_");
|
|
@@ -11176,9 +11606,9 @@ function listAlembicMigrations(projectDir) {
|
|
|
11176
11606
|
}).sort((a, b) => a.filename.localeCompare(b.filename));
|
|
11177
11607
|
}
|
|
11178
11608
|
function listKnexMigrations(projectDir) {
|
|
11179
|
-
const dir =
|
|
11180
|
-
if (!
|
|
11181
|
-
const files =
|
|
11609
|
+
const dir = path16.join(projectDir, "migrations");
|
|
11610
|
+
if (!fs14.existsSync(dir)) return [];
|
|
11611
|
+
const files = fs14.readdirSync(dir).filter((f) => (f.endsWith(".js") || f.endsWith(".ts")) && !f.startsWith("."));
|
|
11182
11612
|
return files.map((filename) => {
|
|
11183
11613
|
const stem = filename.replace(/\.(js|ts)$/, "");
|
|
11184
11614
|
const m = stem.match(/^(\d{14})_(.+)$/);
|
|
@@ -11200,7 +11630,7 @@ function versionCompare2(a, b) {
|
|
|
11200
11630
|
return 0;
|
|
11201
11631
|
}
|
|
11202
11632
|
function adapterFor(projectDir, language) {
|
|
11203
|
-
const override = language ? toolForLanguage(language) : void 0;
|
|
11633
|
+
const override = language ? toolForLanguage(language, projectDir) : void 0;
|
|
11204
11634
|
return resolveSchemaMigrationAdapter(projectDir, override);
|
|
11205
11635
|
}
|
|
11206
11636
|
function dbRevisionOrphaned(appliedRevision, localRevisionIds) {
|
|
@@ -11255,10 +11685,10 @@ async function collapseMigrationHeads(args) {
|
|
|
11255
11685
|
var FEATURE_PREFIX = "feature/";
|
|
11256
11686
|
var TIER_LEAFS2 = DEFAULT_PROTECTED_TIER_NAMES;
|
|
11257
11687
|
function readEnv(projectDir) {
|
|
11258
|
-
const envPath =
|
|
11688
|
+
const envPath = path17.join(projectDir, ".env");
|
|
11259
11689
|
const out = /* @__PURE__ */ new Map();
|
|
11260
|
-
if (!
|
|
11261
|
-
const lines =
|
|
11690
|
+
if (!fs15.existsSync(envPath)) return out;
|
|
11691
|
+
const lines = fs15.readFileSync(envPath, "utf8").split("\n");
|
|
11262
11692
|
for (const line of lines) {
|
|
11263
11693
|
const m = line.match(/^\s*([A-Z0-9_]+)\s*=\s*(.+?)\s*$/);
|
|
11264
11694
|
if (m) out.set(m[1], m[2].replace(/^["']|["']$/g, ""));
|
|
@@ -11313,9 +11743,9 @@ async function runDoctor(args, deps = {}) {
|
|
|
11313
11743
|
}
|
|
11314
11744
|
for (const wf of ["pr.yml", "merge.yml"]) {
|
|
11315
11745
|
try {
|
|
11316
|
-
const p =
|
|
11317
|
-
if (!
|
|
11318
|
-
const body =
|
|
11746
|
+
const p = path17.join(projectDir, ".github", "workflows", wf);
|
|
11747
|
+
if (!fs15.existsSync(p)) continue;
|
|
11748
|
+
const body = fs15.readFileSync(p, "utf8");
|
|
11319
11749
|
if (/lakebase-scm-utils#v\d/.test(body)) {
|
|
11320
11750
|
findings.push({
|
|
11321
11751
|
id: "ci-workflow-substrate-pin",
|
|
@@ -11530,7 +11960,7 @@ async function fixFinding(args) {
|
|
|
11530
11960
|
);
|
|
11531
11961
|
}
|
|
11532
11962
|
const sanitized = sanitizeBranchName(branch);
|
|
11533
|
-
const envFile =
|
|
11963
|
+
const envFile = path17.join(args.projectDir, ".env");
|
|
11534
11964
|
updateEnvConnection({
|
|
11535
11965
|
envPath: envFile,
|
|
11536
11966
|
projectId: readEnvVar(envFile, "LAKEBASE_PROJECT_ID") ?? "",
|
|
@@ -11690,9 +12120,9 @@ Exit codes (--fix mode):
|
|
|
11690
12120
|
3 = fix executed but the underlying command failed
|
|
11691
12121
|
`;
|
|
11692
12122
|
function readEnvProjectId(projectDir) {
|
|
11693
|
-
const envPath =
|
|
11694
|
-
if (!
|
|
11695
|
-
const lines =
|
|
12123
|
+
const envPath = path18.join(projectDir, ".env");
|
|
12124
|
+
if (!fs16.existsSync(envPath)) return void 0;
|
|
12125
|
+
const lines = fs16.readFileSync(envPath, "utf8").split("\n");
|
|
11696
12126
|
for (const line of lines) {
|
|
11697
12127
|
const m = line.match(/^\s*LAKEBASE_PROJECT_ID\s*=\s*(.+?)\s*$/);
|
|
11698
12128
|
if (m) return m[1].replace(/^["']|["']$/g, "");
|
|
@@ -11752,7 +12182,7 @@ async function runScmDoctorCli(argv) {
|
|
|
11752
12182
|
`);
|
|
11753
12183
|
return 0;
|
|
11754
12184
|
}
|
|
11755
|
-
const projectDir =
|
|
12185
|
+
const projectDir = path18.resolve(args.projectDir ?? process.cwd());
|
|
11756
12186
|
const instance = args.instance ?? readEnvProjectId(projectDir);
|
|
11757
12187
|
if (args.fix) {
|
|
11758
12188
|
if (!FIXABLE_FINDING_IDS.includes(args.fix)) {
|