@databricks-solutions/lakebase-scm-utils 0.2.43 → 0.2.45
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/github/index.cjs +21 -4
- package/dist/scripts/github/index.js +21 -4
- package/dist/scripts/github/pr.cli.cjs +21 -4
- package/dist/scripts/github/pr.cli.js +21 -4
- package/dist/scripts/index.cjs +1224 -1157
- package/dist/scripts/index.d.cts +1 -1
- package/dist/scripts/index.d.ts +1 -1
- package/dist/scripts/index.js +1223 -1157
- package/dist/scripts/lakebase/branch.cli.cjs +21 -4
- package/dist/scripts/lakebase/branch.cli.js +21 -4
- package/dist/scripts/lakebase/ci-app-endpoint.cli.cjs +21 -4
- package/dist/scripts/lakebase/ci-app-endpoint.cli.js +21 -4
- package/dist/scripts/lakebase/ci-resolve-branch.cli.cjs +23 -6
- package/dist/scripts/lakebase/ci-resolve-branch.cli.js +23 -6
- package/dist/scripts/lakebase/collapse-heads.cli.cjs +23 -6
- package/dist/scripts/lakebase/collapse-heads.cli.js +23 -6
- package/dist/scripts/lakebase/cut-backup.cli.cjs +21 -4
- package/dist/scripts/lakebase/cut-backup.cli.js +21 -4
- package/dist/scripts/lakebase/cut-tier.cli.cjs +21 -4
- package/dist/scripts/lakebase/cut-tier.cli.js +21 -4
- package/dist/scripts/lakebase/detect-language.cli.cjs +23 -6
- package/dist/scripts/lakebase/detect-language.cli.js +23 -6
- package/dist/scripts/lakebase/doctor.cli.cjs +24 -7
- package/dist/scripts/lakebase/doctor.cli.js +24 -7
- package/dist/scripts/lakebase/get-connection.cli.cjs +23 -6
- package/dist/scripts/lakebase/get-connection.cli.js +23 -6
- package/dist/scripts/lakebase/index.cjs +489 -391
- package/dist/scripts/lakebase/index.d.cts +33 -1
- package/dist/scripts/lakebase/index.d.ts +33 -1
- package/dist/scripts/lakebase/index.js +488 -391
- package/dist/scripts/lakebase/infra-runner.cli.cjs +23 -6
- package/dist/scripts/lakebase/infra-runner.cli.js +23 -6
- package/dist/scripts/lakebase/new-migration.cli.cjs +23 -6
- package/dist/scripts/lakebase/new-migration.cli.js +23 -6
- package/dist/scripts/lakebase/schema-diff.cli.cjs +23 -6
- package/dist/scripts/lakebase/schema-diff.cli.js +23 -6
- package/dist/scripts/lakebase/schema-migrate.cli.cjs +23 -6
- package/dist/scripts/lakebase/schema-migrate.cli.js +23 -6
- package/dist/scripts/lakebase/scm-abandon-feature.cli.cjs +21 -4
- package/dist/scripts/lakebase/scm-abandon-feature.cli.js +21 -4
- package/dist/scripts/lakebase/scm-adopt-state.cli.cjs +21 -4
- package/dist/scripts/lakebase/scm-adopt-state.cli.js +21 -4
- package/dist/scripts/lakebase/scm-claim-feature.cli.cjs +23 -6
- package/dist/scripts/lakebase/scm-claim-feature.cli.js +23 -6
- package/dist/scripts/lakebase/scm-cleanup.cli.cjs +21 -4
- package/dist/scripts/lakebase/scm-cleanup.cli.js +21 -4
- package/dist/scripts/lakebase/scm-doctor.cli.cjs +23 -6
- package/dist/scripts/lakebase/scm-doctor.cli.js +23 -6
- package/dist/scripts/lakebase/scm-merge.cli.cjs +2305 -2037
- package/dist/scripts/lakebase/scm-merge.cli.js +360 -92
- package/dist/scripts/lakebase/scm-reconcile-tier.cli.cjs +23 -6
- package/dist/scripts/lakebase/scm-reconcile-tier.cli.js +23 -6
- package/dist/scripts/lakebase/scm-recover-orphans.cli.cjs +21 -4
- package/dist/scripts/lakebase/scm-recover-orphans.cli.js +21 -4
- package/dist/scripts/util/index.cjs +23 -6
- package/dist/scripts/util/index.js +23 -6
- package/package.json +1 -1
- package/scripts/lakebase/databricks-cli.ts +44 -4
- package/scripts/lakebase/doctor.ts +1 -1
- package/scripts/lakebase/runner-setup.ts +18 -2
- package/scripts/lakebase/scm-merge.ts +80 -1
- package/scripts/lakebase/secret-auth.ts +9 -2
- package/templates/project/common/scripts/run-tests.sh +10 -0
|
@@ -78993,6 +78993,7 @@ __export(lakebase_exports, {
|
|
|
78993
78993
|
createUatPairedBranch: () => createUatPairedBranch,
|
|
78994
78994
|
cutBackup: () => cutBackup,
|
|
78995
78995
|
databricksAuthPrereqMessage: () => databricksAuthPrereqMessage,
|
|
78996
|
+
defaultRefreshPromoteWorkflows: () => defaultRefreshPromoteWorkflows,
|
|
78996
78997
|
deleteAppEndpoint: () => deleteAppEndpoint,
|
|
78997
78998
|
deleteBranch: () => deleteBranch,
|
|
78998
78999
|
deleteLakebaseProject: () => deleteLakebaseProject,
|
|
@@ -79485,16 +79486,33 @@ stdout: ${stdout}` : killed || signal ? `
|
|
|
79485
79486
|
}
|
|
79486
79487
|
async function runDatabricks(args, opts = {}) {
|
|
79487
79488
|
const { argv, env, profile } = buildInvocation(args, opts);
|
|
79489
|
+
const timeout = opts.timeout ?? KIT_TIMEOUTS.cliDefault;
|
|
79488
79490
|
try {
|
|
79489
|
-
|
|
79490
|
-
env,
|
|
79491
|
-
|
|
79492
|
-
});
|
|
79491
|
+
if (opts.input !== void 0) {
|
|
79492
|
+
return await execDatabricksWithStdin(argv, opts.input, env, timeout);
|
|
79493
|
+
}
|
|
79494
|
+
const { stdout } = await execFileP("databricks", argv, { env, timeout });
|
|
79493
79495
|
return stdout.toString();
|
|
79494
79496
|
} catch (err) {
|
|
79495
79497
|
throw classifyDatabricksError(err, argv, profile);
|
|
79496
79498
|
}
|
|
79497
79499
|
}
|
|
79500
|
+
function execDatabricksWithStdin(argv, input, env, timeout) {
|
|
79501
|
+
return new Promise((resolve2, reject) => {
|
|
79502
|
+
const child = (0, import_node_child_process2.execFile)("databricks", argv, { env, timeout }, (err, stdout, stderr) => {
|
|
79503
|
+
if (err) {
|
|
79504
|
+
err.stdout = String(stdout ?? "");
|
|
79505
|
+
err.stderr = String(stderr ?? "");
|
|
79506
|
+
reject(err);
|
|
79507
|
+
return;
|
|
79508
|
+
}
|
|
79509
|
+
resolve2(String(stdout ?? ""));
|
|
79510
|
+
});
|
|
79511
|
+
child.stdin?.on("error", () => {
|
|
79512
|
+
});
|
|
79513
|
+
child.stdin?.end(input);
|
|
79514
|
+
});
|
|
79515
|
+
}
|
|
79498
79516
|
function runDatabricksSync(args, opts = {}) {
|
|
79499
79517
|
const { argv, env, profile } = buildInvocation(args, opts);
|
|
79500
79518
|
try {
|
|
@@ -81570,8 +81588,8 @@ var PKG_NAME = "@databricks-solutions/lakebase-scm-utils";
|
|
|
81570
81588
|
var cached;
|
|
81571
81589
|
function substrateSelfVersion() {
|
|
81572
81590
|
if (cached !== void 0) return cached;
|
|
81573
|
-
if ("0.2.
|
|
81574
|
-
cached = "0.2.
|
|
81591
|
+
if ("0.2.45".length > 0) {
|
|
81592
|
+
cached = "0.2.45";
|
|
81575
81593
|
return cached;
|
|
81576
81594
|
}
|
|
81577
81595
|
cached = "unknown";
|
|
@@ -81799,6 +81817,14 @@ async function getCredential(args) {
|
|
|
81799
81817
|
|
|
81800
81818
|
// scripts/git/status.ts
|
|
81801
81819
|
init_cjs_shims();
|
|
81820
|
+
async function hasUpstream(args) {
|
|
81821
|
+
try {
|
|
81822
|
+
await exec2("git rev-parse --abbrev-ref @{u}", { cwd: args.cwd });
|
|
81823
|
+
return true;
|
|
81824
|
+
} catch {
|
|
81825
|
+
return false;
|
|
81826
|
+
}
|
|
81827
|
+
}
|
|
81802
81828
|
async function getAheadBehind(args) {
|
|
81803
81829
|
const { cwd } = args;
|
|
81804
81830
|
try {
|
|
@@ -91830,8 +91856,20 @@ async function setupRunner(args) {
|
|
|
91830
91856
|
if (needsConfig) {
|
|
91831
91857
|
report("Registering runner with GitHub...");
|
|
91832
91858
|
const regToken = await createRegistrationToken(args.fullRepoName);
|
|
91833
|
-
cp4.
|
|
91834
|
-
|
|
91859
|
+
cp4.execFileSync(
|
|
91860
|
+
"./config.sh",
|
|
91861
|
+
[
|
|
91862
|
+
"--url",
|
|
91863
|
+
`https://github.com/${args.fullRepoName}`,
|
|
91864
|
+
"--token",
|
|
91865
|
+
regToken,
|
|
91866
|
+
"--name",
|
|
91867
|
+
name,
|
|
91868
|
+
"--labels",
|
|
91869
|
+
"self-hosted",
|
|
91870
|
+
"--unattended",
|
|
91871
|
+
"--replace"
|
|
91872
|
+
],
|
|
91835
91873
|
{ cwd: dir, timeout: KIT_TIMEOUTS.cliLong }
|
|
91836
91874
|
);
|
|
91837
91875
|
}
|
|
@@ -94890,104 +94928,447 @@ function pickRunUrl(pr) {
|
|
|
94890
94928
|
|
|
94891
94929
|
// scripts/lakebase/scm-merge.ts
|
|
94892
94930
|
init_cjs_shims();
|
|
94893
|
-
|
|
94894
|
-
|
|
94931
|
+
|
|
94932
|
+
// scripts/git/sync.ts
|
|
94933
|
+
init_cjs_shims();
|
|
94934
|
+
async function currentBranchName(cwd) {
|
|
94895
94935
|
try {
|
|
94896
|
-
|
|
94897
|
-
|
|
94898
|
-
|
|
94899
|
-
} else {
|
|
94900
|
-
await exec2(`git fetch origin ${shellEscape2(`${tier}:${tier}`)}`, { cwd, timeout: 3e4 });
|
|
94901
|
-
}
|
|
94902
|
-
return null;
|
|
94903
|
-
} catch (err) {
|
|
94904
|
-
return `local reconcile of ${tier} to origin/${tier} failed: ${err instanceof Error ? err.message : String(err)}. Reconcile it by hand (git checkout ${tier} && git pull --ff-only).`;
|
|
94936
|
+
return await exec2("git rev-parse --abbrev-ref HEAD", { cwd });
|
|
94937
|
+
} catch {
|
|
94938
|
+
return "";
|
|
94905
94939
|
}
|
|
94906
94940
|
}
|
|
94907
|
-
|
|
94908
|
-
|
|
94909
|
-
|
|
94910
|
-
|
|
94911
|
-
|
|
94941
|
+
async function pushCurrentBranchForPr(args) {
|
|
94942
|
+
const remote = args.remote ?? "origin";
|
|
94943
|
+
const branch = await currentBranchName(args.cwd);
|
|
94944
|
+
if (!branch) throw new Error("No current branch");
|
|
94945
|
+
const upstreamSet = await hasUpstream({ cwd: args.cwd });
|
|
94946
|
+
if (!upstreamSet) {
|
|
94947
|
+
await exec2(`git push -u ${remote} ${shq(branch)}`, {
|
|
94948
|
+
cwd: args.cwd
|
|
94949
|
+
});
|
|
94950
|
+
} else {
|
|
94951
|
+
await exec2("git push", { cwd: args.cwd });
|
|
94912
94952
|
}
|
|
94913
|
-
code;
|
|
94914
|
-
};
|
|
94915
|
-
var DEFAULT_MIGRATE_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
94916
|
-
var DEFAULT_MIGRATE_POLL_MS = 30 * 1e3;
|
|
94917
|
-
function defaultMigratePredicate(run, mergedAt) {
|
|
94918
|
-
if (!run.createdAt) return false;
|
|
94919
|
-
const created = Date.parse(run.createdAt);
|
|
94920
|
-
if (!Number.isFinite(created)) return false;
|
|
94921
|
-
if (run.event && run.event !== "push") return false;
|
|
94922
|
-
return created >= mergedAt.getTime() - 5e3;
|
|
94923
|
-
}
|
|
94924
|
-
function shaMigratePredicate(mergeCommitSha) {
|
|
94925
|
-
return (run) => {
|
|
94926
|
-
if (run.event && run.event !== "push") return false;
|
|
94927
|
-
return !!run.headSha && run.headSha === mergeCommitSha;
|
|
94928
|
-
};
|
|
94929
94953
|
}
|
|
94930
|
-
|
|
94931
|
-
|
|
94932
|
-
|
|
94933
|
-
|
|
94934
|
-
|
|
94935
|
-
|
|
94954
|
+
|
|
94955
|
+
// scripts/lakebase/workflow-drift.ts
|
|
94956
|
+
init_cjs_shims();
|
|
94957
|
+
var fs30 = __toESM(require("fs"), 1);
|
|
94958
|
+
var path28 = __toESM(require("path"), 1);
|
|
94959
|
+
function findKitTemplatesDir(start) {
|
|
94960
|
+
let dir = start;
|
|
94961
|
+
for (let i2 = 0; i2 < 6; i2++) {
|
|
94962
|
+
const candidate = path28.join(
|
|
94963
|
+
dir,
|
|
94964
|
+
"templates",
|
|
94965
|
+
"project",
|
|
94966
|
+
"common",
|
|
94967
|
+
".github",
|
|
94968
|
+
"workflows"
|
|
94936
94969
|
);
|
|
94970
|
+
if (fs30.existsSync(candidate)) return candidate;
|
|
94971
|
+
const parent = path28.dirname(dir);
|
|
94972
|
+
if (parent === dir) break;
|
|
94973
|
+
dir = parent;
|
|
94937
94974
|
}
|
|
94938
|
-
|
|
94939
|
-
|
|
94940
|
-
|
|
94941
|
-
|
|
94942
|
-
|
|
94975
|
+
throw new Error(
|
|
94976
|
+
`Could not locate templates/project/common/.github/workflows/ relative to ${start}. Pass explicit kitDir.`
|
|
94977
|
+
);
|
|
94978
|
+
}
|
|
94979
|
+
function unifiedDiff(name, projectContent, templateContent) {
|
|
94980
|
+
if (projectContent === templateContent) return "";
|
|
94981
|
+
const a = projectContent.split("\n");
|
|
94982
|
+
const b = templateContent.split("\n");
|
|
94983
|
+
const out = [`--- project/${name}`, `+++ template/${name}`];
|
|
94984
|
+
const max = Math.max(a.length, b.length);
|
|
94985
|
+
for (let i2 = 0; i2 < max; i2++) {
|
|
94986
|
+
const av = a[i2];
|
|
94987
|
+
const bv = b[i2];
|
|
94988
|
+
if (av === bv) continue;
|
|
94989
|
+
if (av !== void 0) out.push(`-${i2 + 1}: ${av}`);
|
|
94990
|
+
if (bv !== void 0) out.push(`+${i2 + 1}: ${bv}`);
|
|
94943
94991
|
}
|
|
94944
|
-
|
|
94945
|
-
|
|
94946
|
-
|
|
94947
|
-
|
|
94948
|
-
|
|
94992
|
+
return out.join("\n");
|
|
94993
|
+
}
|
|
94994
|
+
function detectWorkflowDrift(args) {
|
|
94995
|
+
const projectWorkflowsDir = path28.join(
|
|
94996
|
+
args.projectDir,
|
|
94997
|
+
".github",
|
|
94998
|
+
"workflows"
|
|
94999
|
+
);
|
|
95000
|
+
const here = path28.dirname(new URL(importMetaUrl).pathname);
|
|
95001
|
+
const kitWorkflowsDir = args.kitDir ? path28.join(
|
|
95002
|
+
args.kitDir,
|
|
95003
|
+
"templates",
|
|
95004
|
+
"project",
|
|
95005
|
+
"common",
|
|
95006
|
+
".github",
|
|
95007
|
+
"workflows"
|
|
95008
|
+
) : findKitTemplatesDir(here);
|
|
95009
|
+
const templateFiles = fs30.existsSync(kitWorkflowsDir) ? fs30.readdirSync(kitWorkflowsDir).filter((f3) => f3.endsWith(".yml")) : [];
|
|
95010
|
+
const projectFiles = fs30.existsSync(projectWorkflowsDir) ? fs30.readdirSync(projectWorkflowsDir).filter((f3) => f3.endsWith(".yml")) : [];
|
|
95011
|
+
const version = readKitVersion(kitWorkflowsDir);
|
|
95012
|
+
const seen = /* @__PURE__ */ new Set();
|
|
95013
|
+
const files = [];
|
|
95014
|
+
for (const name of templateFiles) {
|
|
95015
|
+
seen.add(name);
|
|
95016
|
+
const projectPath2 = path28.join(projectWorkflowsDir, name);
|
|
95017
|
+
const templatePath = path28.join(kitWorkflowsDir, name);
|
|
95018
|
+
if (!fs30.existsSync(projectPath2)) {
|
|
95019
|
+
files.push({ name, status: "missing" });
|
|
95020
|
+
continue;
|
|
95021
|
+
}
|
|
95022
|
+
const projectContent = fs30.readFileSync(projectPath2, "utf8");
|
|
95023
|
+
const templateContent = applyPlaceholders(fs30.readFileSync(templatePath, "utf8"), version);
|
|
95024
|
+
if (projectContent === templateContent) {
|
|
95025
|
+
files.push({ name, status: "unchanged" });
|
|
95026
|
+
} else {
|
|
95027
|
+
files.push({
|
|
95028
|
+
name,
|
|
95029
|
+
status: "drifted",
|
|
95030
|
+
diff: unifiedDiff(name, projectContent, templateContent)
|
|
95031
|
+
});
|
|
95032
|
+
}
|
|
94949
95033
|
}
|
|
94950
|
-
|
|
94951
|
-
|
|
94952
|
-
|
|
94953
|
-
"bad-precondition"
|
|
94954
|
-
);
|
|
95034
|
+
for (const name of projectFiles) {
|
|
95035
|
+
if (seen.has(name)) continue;
|
|
95036
|
+
files.push({ name, status: "extra" });
|
|
94955
95037
|
}
|
|
94956
|
-
const
|
|
94957
|
-
|
|
94958
|
-
|
|
94959
|
-
|
|
94960
|
-
|
|
94961
|
-
|
|
95038
|
+
const order = {
|
|
95039
|
+
drifted: 0,
|
|
95040
|
+
missing: 1,
|
|
95041
|
+
extra: 2,
|
|
95042
|
+
unchanged: 3
|
|
95043
|
+
};
|
|
95044
|
+
files.sort((a, b) => order[a.status] - order[b.status] || a.name.localeCompare(b.name));
|
|
95045
|
+
const hasDrift = files.some((f3) => f3.status === "drifted" || f3.status === "missing");
|
|
95046
|
+
return {
|
|
95047
|
+
overall: hasDrift ? "drift" : "ok",
|
|
95048
|
+
files
|
|
95049
|
+
};
|
|
95050
|
+
}
|
|
95051
|
+
function readKitVersion(kitWorkflowsDir) {
|
|
95052
|
+
let dir = kitWorkflowsDir;
|
|
95053
|
+
for (let i2 = 0; i2 < 5; i2++) {
|
|
95054
|
+
dir = path28.dirname(dir);
|
|
94962
95055
|
}
|
|
94963
|
-
|
|
94964
|
-
|
|
94965
|
-
|
|
94966
|
-
|
|
94967
|
-
|
|
95056
|
+
try {
|
|
95057
|
+
const raw = fs30.readFileSync(path28.join(dir, "package.json"), "utf-8");
|
|
95058
|
+
const pkg = JSON.parse(raw);
|
|
95059
|
+
return typeof pkg.version === "string" ? pkg.version : "unknown";
|
|
95060
|
+
} catch {
|
|
95061
|
+
return "unknown";
|
|
95062
|
+
}
|
|
95063
|
+
}
|
|
95064
|
+
function applyPlaceholders(content, version) {
|
|
95065
|
+
return content.replace(/\{\{LAKEBASE_SCM_UTILS_VERSION\}\}/g, version);
|
|
95066
|
+
}
|
|
95067
|
+
function applyCommandPlaceholders(content, version) {
|
|
95068
|
+
return content.replace(/\$\{KIT_VERSION_AT_SCAFFOLD\}/g, version);
|
|
95069
|
+
}
|
|
95070
|
+
var COMMAND_HOOK_FILE_PATTERN = /\.(pre|post)-hook\.md$/;
|
|
95071
|
+
function findKitCommandsDir(start) {
|
|
95072
|
+
let dir = start;
|
|
95073
|
+
for (let i2 = 0; i2 < 6; i2++) {
|
|
95074
|
+
const candidate = path28.join(
|
|
95075
|
+
dir,
|
|
95076
|
+
"templates",
|
|
95077
|
+
"project",
|
|
95078
|
+
"common",
|
|
95079
|
+
".claude",
|
|
95080
|
+
"commands"
|
|
94968
95081
|
);
|
|
95082
|
+
if (fs30.existsSync(candidate)) return candidate;
|
|
95083
|
+
const parent = path28.dirname(dir);
|
|
95084
|
+
if (parent === dir) break;
|
|
95085
|
+
dir = parent;
|
|
94969
95086
|
}
|
|
94970
|
-
|
|
94971
|
-
|
|
94972
|
-
|
|
94973
|
-
|
|
94974
|
-
|
|
94975
|
-
|
|
94976
|
-
|
|
94977
|
-
|
|
94978
|
-
|
|
94979
|
-
|
|
94980
|
-
|
|
95087
|
+
throw new Error(
|
|
95088
|
+
`Could not locate templates/project/common/.claude/commands/ relative to ${start}. Pass explicit kitDir.`
|
|
95089
|
+
);
|
|
95090
|
+
}
|
|
95091
|
+
function parsePinnedVersion(content) {
|
|
95092
|
+
const m2 = content.match(/^\s*[*_>`\s]*pinned\s+to\s*:\s*[`*_]*([^\s`*_]+)[`*_]*\s*$/im);
|
|
95093
|
+
return m2 ? m2[1] : void 0;
|
|
95094
|
+
}
|
|
95095
|
+
function detectCommandDrift(args) {
|
|
95096
|
+
const projectCommandsDir = path28.join(args.projectDir, ".claude", "commands");
|
|
95097
|
+
const here = path28.dirname(new URL(importMetaUrl).pathname);
|
|
95098
|
+
const kitCommandsDir = args.kitDir ? path28.join(args.kitDir, "templates", "project", "common", ".claude", "commands") : findKitCommandsDir(here);
|
|
95099
|
+
const kitVersion = readKitVersionFromCommandsDir(kitCommandsDir);
|
|
95100
|
+
const templateFiles = fs30.existsSync(kitCommandsDir) ? fs30.readdirSync(kitCommandsDir).filter((f3) => f3.endsWith(".md") && !COMMAND_HOOK_FILE_PATTERN.test(f3)) : [];
|
|
95101
|
+
const projectFiles = fs30.existsSync(projectCommandsDir) ? fs30.readdirSync(projectCommandsDir).filter((f3) => f3.endsWith(".md") && !COMMAND_HOOK_FILE_PATTERN.test(f3)) : [];
|
|
95102
|
+
const seen = /* @__PURE__ */ new Set();
|
|
95103
|
+
const files = [];
|
|
95104
|
+
for (const name of templateFiles) {
|
|
95105
|
+
seen.add(name);
|
|
95106
|
+
const projectPath2 = path28.join(projectCommandsDir, name);
|
|
95107
|
+
const templatePath = path28.join(kitCommandsDir, name);
|
|
95108
|
+
const templateRaw = fs30.readFileSync(templatePath, "utf8");
|
|
95109
|
+
if (!fs30.existsSync(projectPath2)) {
|
|
95110
|
+
files.push({ name, status: "missing", kit_version: kitVersion });
|
|
95111
|
+
continue;
|
|
95112
|
+
}
|
|
95113
|
+
const projectContent = fs30.readFileSync(projectPath2, "utf8");
|
|
95114
|
+
const pinned = parsePinnedVersion(projectContent);
|
|
95115
|
+
const versionForCompare = pinned ?? kitVersion;
|
|
95116
|
+
const templateContent = applyCommandPlaceholders(templateRaw, versionForCompare);
|
|
95117
|
+
if (projectContent === templateContent) {
|
|
95118
|
+
files.push({
|
|
95119
|
+
name,
|
|
95120
|
+
status: "unchanged",
|
|
95121
|
+
pinned_version: pinned,
|
|
95122
|
+
kit_version: kitVersion
|
|
95123
|
+
});
|
|
95124
|
+
} else {
|
|
95125
|
+
files.push({
|
|
95126
|
+
name,
|
|
95127
|
+
status: "drifted",
|
|
95128
|
+
pinned_version: pinned,
|
|
95129
|
+
kit_version: kitVersion,
|
|
95130
|
+
diff: unifiedDiff(name, projectContent, templateContent)
|
|
95131
|
+
});
|
|
94981
95132
|
}
|
|
94982
|
-
authVerified = true;
|
|
94983
95133
|
}
|
|
94984
|
-
|
|
94985
|
-
|
|
94986
|
-
|
|
94987
|
-
|
|
94988
|
-
|
|
94989
|
-
|
|
94990
|
-
|
|
95134
|
+
for (const name of projectFiles) {
|
|
95135
|
+
if (seen.has(name)) continue;
|
|
95136
|
+
files.push({ name, status: "extra", kit_version: kitVersion });
|
|
95137
|
+
}
|
|
95138
|
+
const order = {
|
|
95139
|
+
drifted: 0,
|
|
95140
|
+
missing: 1,
|
|
95141
|
+
extra: 2,
|
|
95142
|
+
unchanged: 3
|
|
95143
|
+
};
|
|
95144
|
+
files.sort((a, b) => order[a.status] - order[b.status] || a.name.localeCompare(b.name));
|
|
95145
|
+
const hasDrift = files.some((f3) => f3.status === "drifted" || f3.status === "missing");
|
|
95146
|
+
return { overall: hasDrift ? "drift" : "ok", files };
|
|
95147
|
+
}
|
|
95148
|
+
function readKitVersionFromCommandsDir(kitCommandsDir) {
|
|
95149
|
+
let dir = kitCommandsDir;
|
|
95150
|
+
for (let i2 = 0; i2 < 5; i2++) {
|
|
95151
|
+
dir = path28.dirname(dir);
|
|
95152
|
+
}
|
|
95153
|
+
try {
|
|
95154
|
+
const raw = fs30.readFileSync(path28.join(dir, "package.json"), "utf-8");
|
|
95155
|
+
const pkg = JSON.parse(raw);
|
|
95156
|
+
return typeof pkg.version === "string" ? pkg.version : "unknown";
|
|
95157
|
+
} catch {
|
|
95158
|
+
return "unknown";
|
|
95159
|
+
}
|
|
95160
|
+
}
|
|
95161
|
+
function detectScaffoldedDrift(args) {
|
|
95162
|
+
const workflows = detectWorkflowDrift(args);
|
|
95163
|
+
const commands = detectCommandDrift(args);
|
|
95164
|
+
return {
|
|
95165
|
+
overall: workflows.overall === "drift" || commands.overall === "drift" ? "drift" : "ok",
|
|
95166
|
+
workflows,
|
|
95167
|
+
commands
|
|
95168
|
+
};
|
|
95169
|
+
}
|
|
95170
|
+
function updateWorkflows(args) {
|
|
95171
|
+
const projectWorkflowsDir = path28.join(
|
|
95172
|
+
args.projectDir,
|
|
95173
|
+
".github",
|
|
95174
|
+
"workflows"
|
|
95175
|
+
);
|
|
95176
|
+
const here = path28.dirname(new URL(importMetaUrl).pathname);
|
|
95177
|
+
const kitWorkflowsDir = args.kitDir ? path28.join(
|
|
95178
|
+
args.kitDir,
|
|
95179
|
+
"templates",
|
|
95180
|
+
"project",
|
|
95181
|
+
"common",
|
|
95182
|
+
".github",
|
|
95183
|
+
"workflows"
|
|
95184
|
+
) : findKitTemplatesDir(here);
|
|
95185
|
+
const substitute = args.substitute !== false;
|
|
95186
|
+
const dryRun = args.dryRun === true;
|
|
95187
|
+
const pruneExtras = args.pruneExtras === true;
|
|
95188
|
+
const templateFiles = fs30.existsSync(kitWorkflowsDir) ? fs30.readdirSync(kitWorkflowsDir).filter((f3) => f3.endsWith(".yml")) : [];
|
|
95189
|
+
const projectFiles = fs30.existsSync(projectWorkflowsDir) ? fs30.readdirSync(projectWorkflowsDir).filter((f3) => f3.endsWith(".yml")) : [];
|
|
95190
|
+
if (!dryRun && templateFiles.length > 0 && !fs30.existsSync(projectWorkflowsDir)) {
|
|
95191
|
+
fs30.mkdirSync(projectWorkflowsDir, { recursive: true });
|
|
95192
|
+
}
|
|
95193
|
+
const version = substitute ? readKitVersion(kitWorkflowsDir) : "";
|
|
95194
|
+
const seen = /* @__PURE__ */ new Set();
|
|
95195
|
+
const files = [];
|
|
95196
|
+
for (const name of templateFiles) {
|
|
95197
|
+
seen.add(name);
|
|
95198
|
+
const projectPath2 = path28.join(projectWorkflowsDir, name);
|
|
95199
|
+
const templatePath = path28.join(kitWorkflowsDir, name);
|
|
95200
|
+
const templateRaw = fs30.readFileSync(templatePath, "utf-8");
|
|
95201
|
+
const desired = substitute ? applyPlaceholders(templateRaw, version) : templateRaw;
|
|
95202
|
+
const existed = fs30.existsSync(projectPath2);
|
|
95203
|
+
const current = existed ? fs30.readFileSync(projectPath2, "utf-8") : "";
|
|
95204
|
+
let outcome;
|
|
95205
|
+
if (!existed) {
|
|
95206
|
+
outcome = "added";
|
|
95207
|
+
} else if (current === desired) {
|
|
95208
|
+
outcome = "unchanged";
|
|
95209
|
+
} else {
|
|
95210
|
+
outcome = "updated";
|
|
95211
|
+
}
|
|
95212
|
+
if (!dryRun && outcome !== "unchanged") {
|
|
95213
|
+
fs30.writeFileSync(projectPath2, desired);
|
|
95214
|
+
}
|
|
95215
|
+
files.push({ name, outcome });
|
|
95216
|
+
}
|
|
95217
|
+
if (pruneExtras) {
|
|
95218
|
+
for (const name of projectFiles) {
|
|
95219
|
+
if (seen.has(name)) continue;
|
|
95220
|
+
const projectPath2 = path28.join(projectWorkflowsDir, name);
|
|
95221
|
+
if (!dryRun) {
|
|
95222
|
+
fs30.unlinkSync(projectPath2);
|
|
95223
|
+
}
|
|
95224
|
+
files.push({ name, outcome: "removed" });
|
|
95225
|
+
}
|
|
95226
|
+
}
|
|
95227
|
+
const order = {
|
|
95228
|
+
added: 0,
|
|
95229
|
+
updated: 1,
|
|
95230
|
+
removed: 2,
|
|
95231
|
+
unchanged: 3
|
|
95232
|
+
};
|
|
95233
|
+
files.sort((a, b) => order[a.outcome] - order[b.outcome] || a.name.localeCompare(b.name));
|
|
95234
|
+
const changed = files.some((f3) => f3.outcome !== "unchanged");
|
|
95235
|
+
return { files, changed };
|
|
95236
|
+
}
|
|
95237
|
+
|
|
95238
|
+
// scripts/lakebase/scm-merge.ts
|
|
95239
|
+
async function reconcileTierToOrigin(args) {
|
|
95240
|
+
const { cwd, tier } = args;
|
|
95241
|
+
try {
|
|
95242
|
+
const head = await getCurrentBranch({ cwd });
|
|
95243
|
+
if (head === tier) {
|
|
95244
|
+
await exec2(`git pull --ff-only`, { cwd, timeout: 3e4 });
|
|
95245
|
+
} else {
|
|
95246
|
+
await exec2(`git fetch origin ${shellEscape2(`${tier}:${tier}`)}`, { cwd, timeout: 3e4 });
|
|
95247
|
+
}
|
|
95248
|
+
return null;
|
|
95249
|
+
} catch (err) {
|
|
95250
|
+
return `local reconcile of ${tier} to origin/${tier} failed: ${err instanceof Error ? err.message : String(err)}. Reconcile it by hand (git checkout ${tier} && git pull --ff-only).`;
|
|
95251
|
+
}
|
|
95252
|
+
}
|
|
95253
|
+
var ScmMergeError = class extends Error {
|
|
95254
|
+
constructor(message, code) {
|
|
95255
|
+
super(message);
|
|
95256
|
+
this.code = code;
|
|
95257
|
+
this.name = "ScmMergeError";
|
|
95258
|
+
}
|
|
95259
|
+
code;
|
|
95260
|
+
};
|
|
95261
|
+
var DEFAULT_MIGRATE_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
95262
|
+
var DEFAULT_MIGRATE_POLL_MS = 30 * 1e3;
|
|
95263
|
+
function defaultMigratePredicate(run, mergedAt) {
|
|
95264
|
+
if (!run.createdAt) return false;
|
|
95265
|
+
const created = Date.parse(run.createdAt);
|
|
95266
|
+
if (!Number.isFinite(created)) return false;
|
|
95267
|
+
if (run.event && run.event !== "push") return false;
|
|
95268
|
+
return created >= mergedAt.getTime() - 5e3;
|
|
95269
|
+
}
|
|
95270
|
+
function shaMigratePredicate(mergeCommitSha) {
|
|
95271
|
+
return (run) => {
|
|
95272
|
+
if (run.event && run.event !== "push") return false;
|
|
95273
|
+
return !!run.headSha && run.headSha === mergeCommitSha;
|
|
95274
|
+
};
|
|
95275
|
+
}
|
|
95276
|
+
async function defaultRefreshPromoteWorkflows(projectDir) {
|
|
95277
|
+
const drift = detectWorkflowDrift({ projectDir });
|
|
95278
|
+
const touched = drift.files.filter((f3) => f3.status === "drifted").map((f3) => f3.name);
|
|
95279
|
+
if (touched.length === 0) return { refreshed: false };
|
|
95280
|
+
const result = updateWorkflows({ projectDir });
|
|
95281
|
+
if (!result.changed) return { refreshed: false };
|
|
95282
|
+
await exec2(`git add ${shellEscape2(".github/workflows")}`, { cwd: projectDir, timeout: 1e4 });
|
|
95283
|
+
const staged = await exec2("git diff --cached --name-only -- .github/workflows", {
|
|
95284
|
+
cwd: projectDir,
|
|
95285
|
+
timeout: 1e4
|
|
95286
|
+
});
|
|
95287
|
+
if (!staged.trim()) return { refreshed: false };
|
|
95288
|
+
await exec2(
|
|
95289
|
+
`git commit -m ${shellEscape2(
|
|
95290
|
+
"chore(ci): refresh scaffolded workflows to pinned substrate (promote uses apply-tier)"
|
|
95291
|
+
)} -- .github/workflows`,
|
|
95292
|
+
{ cwd: projectDir, timeout: 15e3 }
|
|
95293
|
+
);
|
|
95294
|
+
await pushCurrentBranchForPr({ cwd: projectDir });
|
|
95295
|
+
return { refreshed: true, detail: `refreshed + pushed ${touched.join(", ") || "workflow(s)"} onto the PR head` };
|
|
95296
|
+
}
|
|
95297
|
+
async function mergeFeature(args) {
|
|
95298
|
+
const current = readWorkflowState(args.projectDir);
|
|
95299
|
+
if (!current) {
|
|
95300
|
+
throw new ScmMergeError(
|
|
95301
|
+
"No SCM workflow state. wait-ci first.",
|
|
95302
|
+
"no-state-file"
|
|
95303
|
+
);
|
|
95304
|
+
}
|
|
95305
|
+
if (current.state !== "ci-green") {
|
|
95306
|
+
throw new ScmMergeError(
|
|
95307
|
+
`merge refuses state "${current.state}". Allowed predecessor: ci-green.`,
|
|
95308
|
+
"bad-precondition"
|
|
95309
|
+
);
|
|
95310
|
+
}
|
|
95311
|
+
if (!current.pr_url) {
|
|
95312
|
+
throw new ScmMergeError(
|
|
95313
|
+
"ci-green row is missing pr_url; cannot resolve the PR to merge.",
|
|
95314
|
+
"no-pr-url"
|
|
95315
|
+
);
|
|
95316
|
+
}
|
|
95317
|
+
if (!current.branch || !current.parent_branch) {
|
|
95318
|
+
throw new ScmMergeError(
|
|
95319
|
+
"ci-green row missing branch / parent_branch; refusing to merge.",
|
|
95320
|
+
"bad-precondition"
|
|
95321
|
+
);
|
|
95322
|
+
}
|
|
95323
|
+
const ownerRepo = await getOwnerRepo(args.projectDir);
|
|
95324
|
+
if (!ownerRepo) {
|
|
95325
|
+
throw new ScmMergeError(
|
|
95326
|
+
"No GitHub remote found at origin.",
|
|
95327
|
+
"no-github-remote"
|
|
95328
|
+
);
|
|
95329
|
+
}
|
|
95330
|
+
const pullNumber = extractPullNumber(current.pr_url);
|
|
95331
|
+
if (!pullNumber) {
|
|
95332
|
+
throw new ScmMergeError(
|
|
95333
|
+
`Could not extract PR number from URL: ${current.pr_url}`,
|
|
95334
|
+
"bad-pr-url"
|
|
95335
|
+
);
|
|
95336
|
+
}
|
|
95337
|
+
const instance = args.instance ?? current.project_id;
|
|
95338
|
+
const gitBase = await resolveGitBase(current.parent_branch, args.projectDir);
|
|
95339
|
+
const wantMigrate = args.waitMigrate !== false;
|
|
95340
|
+
let authVerified = false;
|
|
95341
|
+
if (wantMigrate && args.verifyMigrateAuth) {
|
|
95342
|
+
const probe = await args.verifyMigrateAuth();
|
|
95343
|
+
if (!probe.ok) {
|
|
95344
|
+
throw new ScmMergeError(
|
|
95345
|
+
`Migrate-auth precondition failed: ${probe.detail ?? "the Databricks credential is not usable"}. Refusing to merge: the downstream migrate would promote git without applying the schema. Refresh your credential (databricks auth login), then re-run.`,
|
|
95346
|
+
"migrate-auth"
|
|
95347
|
+
);
|
|
95348
|
+
}
|
|
95349
|
+
authVerified = true;
|
|
95350
|
+
}
|
|
95351
|
+
const preflightNotes = [];
|
|
95352
|
+
{
|
|
95353
|
+
const refresh = args.refreshWorkflows ?? (() => defaultRefreshPromoteWorkflows(args.projectDir));
|
|
95354
|
+
try {
|
|
95355
|
+
const r2 = await refresh();
|
|
95356
|
+
if (r2.refreshed) {
|
|
95357
|
+
preflightNotes.push(`Refreshed stale CI workflows before merge${r2.detail ? ` (${r2.detail})` : ""}.`);
|
|
95358
|
+
}
|
|
95359
|
+
} catch (err) {
|
|
95360
|
+
preflightNotes.push(
|
|
95361
|
+
`Workflow self-heal skipped (${err instanceof Error ? err.message : String(err)}); if the promote's migrate-target fails on the tier guard, refresh .github/workflows (merge.yml must call 'lakebase-schema-migrate apply-tier').`
|
|
95362
|
+
);
|
|
95363
|
+
}
|
|
95364
|
+
}
|
|
95365
|
+
let paired;
|
|
95366
|
+
try {
|
|
95367
|
+
paired = await mergePairedPullRequest({
|
|
95368
|
+
ownerRepo,
|
|
95369
|
+
pullNumber,
|
|
95370
|
+
lakebaseInstance: instance,
|
|
95371
|
+
method: args.method ?? "squash"
|
|
94991
95372
|
});
|
|
94992
95373
|
} catch (err) {
|
|
94993
95374
|
throw new ScmMergeError(
|
|
@@ -94995,7 +95376,7 @@ async function mergeFeature(args) {
|
|
|
94995
95376
|
"merge-failed"
|
|
94996
95377
|
);
|
|
94997
95378
|
}
|
|
94998
|
-
const warnings = [...paired.warnings];
|
|
95379
|
+
const warnings = [...preflightNotes, ...paired.warnings];
|
|
94999
95380
|
let localBranchDeleted = false;
|
|
95000
95381
|
let headAfter = current.branch;
|
|
95001
95382
|
if (!args.skipLocalCleanup) {
|
|
@@ -95192,7 +95573,7 @@ init_cjs_shims();
|
|
|
95192
95573
|
|
|
95193
95574
|
// scripts/git/branches.ts
|
|
95194
95575
|
init_cjs_shims();
|
|
95195
|
-
async function
|
|
95576
|
+
async function currentBranchName2(cwd) {
|
|
95196
95577
|
try {
|
|
95197
95578
|
return await exec2("git rev-parse --abbrev-ref HEAD", { cwd });
|
|
95198
95579
|
} catch {
|
|
@@ -95211,7 +95592,7 @@ async function listLocalBranches(args) {
|
|
|
95211
95592
|
return [];
|
|
95212
95593
|
}
|
|
95213
95594
|
if (!raw) return [];
|
|
95214
|
-
const current = await
|
|
95595
|
+
const current = await currentBranchName2(cwd);
|
|
95215
95596
|
return raw.split("\n").filter(Boolean).map((line) => {
|
|
95216
95597
|
const [name, tracking, trackInfo] = line.split("|");
|
|
95217
95598
|
let ahead = 0;
|
|
@@ -95376,15 +95757,15 @@ function parentForTopology(t2, defaultLeaf) {
|
|
|
95376
95757
|
|
|
95377
95758
|
// scripts/lakebase/scm-doctor.ts
|
|
95378
95759
|
init_cjs_shims();
|
|
95379
|
-
var
|
|
95380
|
-
var
|
|
95760
|
+
var fs31 = __toESM(require("fs"), 1);
|
|
95761
|
+
var path29 = __toESM(require("path"), 1);
|
|
95381
95762
|
var FEATURE_PREFIX = "feature/";
|
|
95382
95763
|
var TIER_LEAFS2 = DEFAULT_PROTECTED_TIER_NAMES;
|
|
95383
95764
|
function readEnv(projectDir) {
|
|
95384
|
-
const envPath =
|
|
95765
|
+
const envPath = path29.join(projectDir, ".env");
|
|
95385
95766
|
const out = /* @__PURE__ */ new Map();
|
|
95386
|
-
if (!
|
|
95387
|
-
const lines =
|
|
95767
|
+
if (!fs31.existsSync(envPath)) return out;
|
|
95768
|
+
const lines = fs31.readFileSync(envPath, "utf8").split("\n");
|
|
95388
95769
|
for (const line of lines) {
|
|
95389
95770
|
const m2 = line.match(/^\s*([A-Z0-9_]+)\s*=\s*(.+?)\s*$/);
|
|
95390
95771
|
if (m2) out.set(m2[1], m2[2].replace(/^["']|["']$/g, ""));
|
|
@@ -95439,9 +95820,9 @@ async function runDoctor(args, deps = {}) {
|
|
|
95439
95820
|
}
|
|
95440
95821
|
for (const wf of ["pr.yml", "merge.yml"]) {
|
|
95441
95822
|
try {
|
|
95442
|
-
const p =
|
|
95443
|
-
if (!
|
|
95444
|
-
const body =
|
|
95823
|
+
const p = path29.join(projectDir, ".github", "workflows", wf);
|
|
95824
|
+
if (!fs31.existsSync(p)) continue;
|
|
95825
|
+
const body = fs31.readFileSync(p, "utf8");
|
|
95445
95826
|
if (/lakebase-scm-utils#v\d/.test(body)) {
|
|
95446
95827
|
findings.push({
|
|
95447
95828
|
id: "ci-workflow-substrate-pin",
|
|
@@ -95656,7 +96037,7 @@ async function fixFinding(args) {
|
|
|
95656
96037
|
);
|
|
95657
96038
|
}
|
|
95658
96039
|
const sanitized = sanitizeBranchName(branch);
|
|
95659
|
-
const envFile =
|
|
96040
|
+
const envFile = path29.join(args.projectDir, ".env");
|
|
95660
96041
|
updateEnvConnection({
|
|
95661
96042
|
envPath: envFile,
|
|
95662
96043
|
projectId: readEnvVar(envFile, "LAKEBASE_PROJECT_ID") ?? "",
|
|
@@ -95761,291 +96142,6 @@ init_cjs_shims();
|
|
|
95761
96142
|
var fs32 = __toESM(require("fs"), 1);
|
|
95762
96143
|
var path30 = __toESM(require("path"), 1);
|
|
95763
96144
|
var cp6 = __toESM(require("child_process"), 1);
|
|
95764
|
-
|
|
95765
|
-
// scripts/lakebase/workflow-drift.ts
|
|
95766
|
-
init_cjs_shims();
|
|
95767
|
-
var fs31 = __toESM(require("fs"), 1);
|
|
95768
|
-
var path29 = __toESM(require("path"), 1);
|
|
95769
|
-
function findKitTemplatesDir(start) {
|
|
95770
|
-
let dir = start;
|
|
95771
|
-
for (let i2 = 0; i2 < 6; i2++) {
|
|
95772
|
-
const candidate = path29.join(
|
|
95773
|
-
dir,
|
|
95774
|
-
"templates",
|
|
95775
|
-
"project",
|
|
95776
|
-
"common",
|
|
95777
|
-
".github",
|
|
95778
|
-
"workflows"
|
|
95779
|
-
);
|
|
95780
|
-
if (fs31.existsSync(candidate)) return candidate;
|
|
95781
|
-
const parent = path29.dirname(dir);
|
|
95782
|
-
if (parent === dir) break;
|
|
95783
|
-
dir = parent;
|
|
95784
|
-
}
|
|
95785
|
-
throw new Error(
|
|
95786
|
-
`Could not locate templates/project/common/.github/workflows/ relative to ${start}. Pass explicit kitDir.`
|
|
95787
|
-
);
|
|
95788
|
-
}
|
|
95789
|
-
function unifiedDiff(name, projectContent, templateContent) {
|
|
95790
|
-
if (projectContent === templateContent) return "";
|
|
95791
|
-
const a = projectContent.split("\n");
|
|
95792
|
-
const b = templateContent.split("\n");
|
|
95793
|
-
const out = [`--- project/${name}`, `+++ template/${name}`];
|
|
95794
|
-
const max = Math.max(a.length, b.length);
|
|
95795
|
-
for (let i2 = 0; i2 < max; i2++) {
|
|
95796
|
-
const av = a[i2];
|
|
95797
|
-
const bv = b[i2];
|
|
95798
|
-
if (av === bv) continue;
|
|
95799
|
-
if (av !== void 0) out.push(`-${i2 + 1}: ${av}`);
|
|
95800
|
-
if (bv !== void 0) out.push(`+${i2 + 1}: ${bv}`);
|
|
95801
|
-
}
|
|
95802
|
-
return out.join("\n");
|
|
95803
|
-
}
|
|
95804
|
-
function detectWorkflowDrift(args) {
|
|
95805
|
-
const projectWorkflowsDir = path29.join(
|
|
95806
|
-
args.projectDir,
|
|
95807
|
-
".github",
|
|
95808
|
-
"workflows"
|
|
95809
|
-
);
|
|
95810
|
-
const here = path29.dirname(new URL(importMetaUrl).pathname);
|
|
95811
|
-
const kitWorkflowsDir = args.kitDir ? path29.join(
|
|
95812
|
-
args.kitDir,
|
|
95813
|
-
"templates",
|
|
95814
|
-
"project",
|
|
95815
|
-
"common",
|
|
95816
|
-
".github",
|
|
95817
|
-
"workflows"
|
|
95818
|
-
) : findKitTemplatesDir(here);
|
|
95819
|
-
const templateFiles = fs31.existsSync(kitWorkflowsDir) ? fs31.readdirSync(kitWorkflowsDir).filter((f3) => f3.endsWith(".yml")) : [];
|
|
95820
|
-
const projectFiles = fs31.existsSync(projectWorkflowsDir) ? fs31.readdirSync(projectWorkflowsDir).filter((f3) => f3.endsWith(".yml")) : [];
|
|
95821
|
-
const version = readKitVersion(kitWorkflowsDir);
|
|
95822
|
-
const seen = /* @__PURE__ */ new Set();
|
|
95823
|
-
const files = [];
|
|
95824
|
-
for (const name of templateFiles) {
|
|
95825
|
-
seen.add(name);
|
|
95826
|
-
const projectPath2 = path29.join(projectWorkflowsDir, name);
|
|
95827
|
-
const templatePath = path29.join(kitWorkflowsDir, name);
|
|
95828
|
-
if (!fs31.existsSync(projectPath2)) {
|
|
95829
|
-
files.push({ name, status: "missing" });
|
|
95830
|
-
continue;
|
|
95831
|
-
}
|
|
95832
|
-
const projectContent = fs31.readFileSync(projectPath2, "utf8");
|
|
95833
|
-
const templateContent = applyPlaceholders(fs31.readFileSync(templatePath, "utf8"), version);
|
|
95834
|
-
if (projectContent === templateContent) {
|
|
95835
|
-
files.push({ name, status: "unchanged" });
|
|
95836
|
-
} else {
|
|
95837
|
-
files.push({
|
|
95838
|
-
name,
|
|
95839
|
-
status: "drifted",
|
|
95840
|
-
diff: unifiedDiff(name, projectContent, templateContent)
|
|
95841
|
-
});
|
|
95842
|
-
}
|
|
95843
|
-
}
|
|
95844
|
-
for (const name of projectFiles) {
|
|
95845
|
-
if (seen.has(name)) continue;
|
|
95846
|
-
files.push({ name, status: "extra" });
|
|
95847
|
-
}
|
|
95848
|
-
const order = {
|
|
95849
|
-
drifted: 0,
|
|
95850
|
-
missing: 1,
|
|
95851
|
-
extra: 2,
|
|
95852
|
-
unchanged: 3
|
|
95853
|
-
};
|
|
95854
|
-
files.sort((a, b) => order[a.status] - order[b.status] || a.name.localeCompare(b.name));
|
|
95855
|
-
const hasDrift = files.some((f3) => f3.status === "drifted" || f3.status === "missing");
|
|
95856
|
-
return {
|
|
95857
|
-
overall: hasDrift ? "drift" : "ok",
|
|
95858
|
-
files
|
|
95859
|
-
};
|
|
95860
|
-
}
|
|
95861
|
-
function readKitVersion(kitWorkflowsDir) {
|
|
95862
|
-
let dir = kitWorkflowsDir;
|
|
95863
|
-
for (let i2 = 0; i2 < 5; i2++) {
|
|
95864
|
-
dir = path29.dirname(dir);
|
|
95865
|
-
}
|
|
95866
|
-
try {
|
|
95867
|
-
const raw = fs31.readFileSync(path29.join(dir, "package.json"), "utf-8");
|
|
95868
|
-
const pkg = JSON.parse(raw);
|
|
95869
|
-
return typeof pkg.version === "string" ? pkg.version : "unknown";
|
|
95870
|
-
} catch {
|
|
95871
|
-
return "unknown";
|
|
95872
|
-
}
|
|
95873
|
-
}
|
|
95874
|
-
function applyPlaceholders(content, version) {
|
|
95875
|
-
return content.replace(/\{\{LAKEBASE_SCM_UTILS_VERSION\}\}/g, version);
|
|
95876
|
-
}
|
|
95877
|
-
function applyCommandPlaceholders(content, version) {
|
|
95878
|
-
return content.replace(/\$\{KIT_VERSION_AT_SCAFFOLD\}/g, version);
|
|
95879
|
-
}
|
|
95880
|
-
var COMMAND_HOOK_FILE_PATTERN = /\.(pre|post)-hook\.md$/;
|
|
95881
|
-
function findKitCommandsDir(start) {
|
|
95882
|
-
let dir = start;
|
|
95883
|
-
for (let i2 = 0; i2 < 6; i2++) {
|
|
95884
|
-
const candidate = path29.join(
|
|
95885
|
-
dir,
|
|
95886
|
-
"templates",
|
|
95887
|
-
"project",
|
|
95888
|
-
"common",
|
|
95889
|
-
".claude",
|
|
95890
|
-
"commands"
|
|
95891
|
-
);
|
|
95892
|
-
if (fs31.existsSync(candidate)) return candidate;
|
|
95893
|
-
const parent = path29.dirname(dir);
|
|
95894
|
-
if (parent === dir) break;
|
|
95895
|
-
dir = parent;
|
|
95896
|
-
}
|
|
95897
|
-
throw new Error(
|
|
95898
|
-
`Could not locate templates/project/common/.claude/commands/ relative to ${start}. Pass explicit kitDir.`
|
|
95899
|
-
);
|
|
95900
|
-
}
|
|
95901
|
-
function parsePinnedVersion(content) {
|
|
95902
|
-
const m2 = content.match(/^\s*[*_>`\s]*pinned\s+to\s*:\s*[`*_]*([^\s`*_]+)[`*_]*\s*$/im);
|
|
95903
|
-
return m2 ? m2[1] : void 0;
|
|
95904
|
-
}
|
|
95905
|
-
function detectCommandDrift(args) {
|
|
95906
|
-
const projectCommandsDir = path29.join(args.projectDir, ".claude", "commands");
|
|
95907
|
-
const here = path29.dirname(new URL(importMetaUrl).pathname);
|
|
95908
|
-
const kitCommandsDir = args.kitDir ? path29.join(args.kitDir, "templates", "project", "common", ".claude", "commands") : findKitCommandsDir(here);
|
|
95909
|
-
const kitVersion = readKitVersionFromCommandsDir(kitCommandsDir);
|
|
95910
|
-
const templateFiles = fs31.existsSync(kitCommandsDir) ? fs31.readdirSync(kitCommandsDir).filter((f3) => f3.endsWith(".md") && !COMMAND_HOOK_FILE_PATTERN.test(f3)) : [];
|
|
95911
|
-
const projectFiles = fs31.existsSync(projectCommandsDir) ? fs31.readdirSync(projectCommandsDir).filter((f3) => f3.endsWith(".md") && !COMMAND_HOOK_FILE_PATTERN.test(f3)) : [];
|
|
95912
|
-
const seen = /* @__PURE__ */ new Set();
|
|
95913
|
-
const files = [];
|
|
95914
|
-
for (const name of templateFiles) {
|
|
95915
|
-
seen.add(name);
|
|
95916
|
-
const projectPath2 = path29.join(projectCommandsDir, name);
|
|
95917
|
-
const templatePath = path29.join(kitCommandsDir, name);
|
|
95918
|
-
const templateRaw = fs31.readFileSync(templatePath, "utf8");
|
|
95919
|
-
if (!fs31.existsSync(projectPath2)) {
|
|
95920
|
-
files.push({ name, status: "missing", kit_version: kitVersion });
|
|
95921
|
-
continue;
|
|
95922
|
-
}
|
|
95923
|
-
const projectContent = fs31.readFileSync(projectPath2, "utf8");
|
|
95924
|
-
const pinned = parsePinnedVersion(projectContent);
|
|
95925
|
-
const versionForCompare = pinned ?? kitVersion;
|
|
95926
|
-
const templateContent = applyCommandPlaceholders(templateRaw, versionForCompare);
|
|
95927
|
-
if (projectContent === templateContent) {
|
|
95928
|
-
files.push({
|
|
95929
|
-
name,
|
|
95930
|
-
status: "unchanged",
|
|
95931
|
-
pinned_version: pinned,
|
|
95932
|
-
kit_version: kitVersion
|
|
95933
|
-
});
|
|
95934
|
-
} else {
|
|
95935
|
-
files.push({
|
|
95936
|
-
name,
|
|
95937
|
-
status: "drifted",
|
|
95938
|
-
pinned_version: pinned,
|
|
95939
|
-
kit_version: kitVersion,
|
|
95940
|
-
diff: unifiedDiff(name, projectContent, templateContent)
|
|
95941
|
-
});
|
|
95942
|
-
}
|
|
95943
|
-
}
|
|
95944
|
-
for (const name of projectFiles) {
|
|
95945
|
-
if (seen.has(name)) continue;
|
|
95946
|
-
files.push({ name, status: "extra", kit_version: kitVersion });
|
|
95947
|
-
}
|
|
95948
|
-
const order = {
|
|
95949
|
-
drifted: 0,
|
|
95950
|
-
missing: 1,
|
|
95951
|
-
extra: 2,
|
|
95952
|
-
unchanged: 3
|
|
95953
|
-
};
|
|
95954
|
-
files.sort((a, b) => order[a.status] - order[b.status] || a.name.localeCompare(b.name));
|
|
95955
|
-
const hasDrift = files.some((f3) => f3.status === "drifted" || f3.status === "missing");
|
|
95956
|
-
return { overall: hasDrift ? "drift" : "ok", files };
|
|
95957
|
-
}
|
|
95958
|
-
function readKitVersionFromCommandsDir(kitCommandsDir) {
|
|
95959
|
-
let dir = kitCommandsDir;
|
|
95960
|
-
for (let i2 = 0; i2 < 5; i2++) {
|
|
95961
|
-
dir = path29.dirname(dir);
|
|
95962
|
-
}
|
|
95963
|
-
try {
|
|
95964
|
-
const raw = fs31.readFileSync(path29.join(dir, "package.json"), "utf-8");
|
|
95965
|
-
const pkg = JSON.parse(raw);
|
|
95966
|
-
return typeof pkg.version === "string" ? pkg.version : "unknown";
|
|
95967
|
-
} catch {
|
|
95968
|
-
return "unknown";
|
|
95969
|
-
}
|
|
95970
|
-
}
|
|
95971
|
-
function detectScaffoldedDrift(args) {
|
|
95972
|
-
const workflows = detectWorkflowDrift(args);
|
|
95973
|
-
const commands = detectCommandDrift(args);
|
|
95974
|
-
return {
|
|
95975
|
-
overall: workflows.overall === "drift" || commands.overall === "drift" ? "drift" : "ok",
|
|
95976
|
-
workflows,
|
|
95977
|
-
commands
|
|
95978
|
-
};
|
|
95979
|
-
}
|
|
95980
|
-
function updateWorkflows(args) {
|
|
95981
|
-
const projectWorkflowsDir = path29.join(
|
|
95982
|
-
args.projectDir,
|
|
95983
|
-
".github",
|
|
95984
|
-
"workflows"
|
|
95985
|
-
);
|
|
95986
|
-
const here = path29.dirname(new URL(importMetaUrl).pathname);
|
|
95987
|
-
const kitWorkflowsDir = args.kitDir ? path29.join(
|
|
95988
|
-
args.kitDir,
|
|
95989
|
-
"templates",
|
|
95990
|
-
"project",
|
|
95991
|
-
"common",
|
|
95992
|
-
".github",
|
|
95993
|
-
"workflows"
|
|
95994
|
-
) : findKitTemplatesDir(here);
|
|
95995
|
-
const substitute = args.substitute !== false;
|
|
95996
|
-
const dryRun = args.dryRun === true;
|
|
95997
|
-
const pruneExtras = args.pruneExtras === true;
|
|
95998
|
-
const templateFiles = fs31.existsSync(kitWorkflowsDir) ? fs31.readdirSync(kitWorkflowsDir).filter((f3) => f3.endsWith(".yml")) : [];
|
|
95999
|
-
const projectFiles = fs31.existsSync(projectWorkflowsDir) ? fs31.readdirSync(projectWorkflowsDir).filter((f3) => f3.endsWith(".yml")) : [];
|
|
96000
|
-
if (!dryRun && templateFiles.length > 0 && !fs31.existsSync(projectWorkflowsDir)) {
|
|
96001
|
-
fs31.mkdirSync(projectWorkflowsDir, { recursive: true });
|
|
96002
|
-
}
|
|
96003
|
-
const version = substitute ? readKitVersion(kitWorkflowsDir) : "";
|
|
96004
|
-
const seen = /* @__PURE__ */ new Set();
|
|
96005
|
-
const files = [];
|
|
96006
|
-
for (const name of templateFiles) {
|
|
96007
|
-
seen.add(name);
|
|
96008
|
-
const projectPath2 = path29.join(projectWorkflowsDir, name);
|
|
96009
|
-
const templatePath = path29.join(kitWorkflowsDir, name);
|
|
96010
|
-
const templateRaw = fs31.readFileSync(templatePath, "utf-8");
|
|
96011
|
-
const desired = substitute ? applyPlaceholders(templateRaw, version) : templateRaw;
|
|
96012
|
-
const existed = fs31.existsSync(projectPath2);
|
|
96013
|
-
const current = existed ? fs31.readFileSync(projectPath2, "utf-8") : "";
|
|
96014
|
-
let outcome;
|
|
96015
|
-
if (!existed) {
|
|
96016
|
-
outcome = "added";
|
|
96017
|
-
} else if (current === desired) {
|
|
96018
|
-
outcome = "unchanged";
|
|
96019
|
-
} else {
|
|
96020
|
-
outcome = "updated";
|
|
96021
|
-
}
|
|
96022
|
-
if (!dryRun && outcome !== "unchanged") {
|
|
96023
|
-
fs31.writeFileSync(projectPath2, desired);
|
|
96024
|
-
}
|
|
96025
|
-
files.push({ name, outcome });
|
|
96026
|
-
}
|
|
96027
|
-
if (pruneExtras) {
|
|
96028
|
-
for (const name of projectFiles) {
|
|
96029
|
-
if (seen.has(name)) continue;
|
|
96030
|
-
const projectPath2 = path29.join(projectWorkflowsDir, name);
|
|
96031
|
-
if (!dryRun) {
|
|
96032
|
-
fs31.unlinkSync(projectPath2);
|
|
96033
|
-
}
|
|
96034
|
-
files.push({ name, outcome: "removed" });
|
|
96035
|
-
}
|
|
96036
|
-
}
|
|
96037
|
-
const order = {
|
|
96038
|
-
added: 0,
|
|
96039
|
-
updated: 1,
|
|
96040
|
-
removed: 2,
|
|
96041
|
-
unchanged: 3
|
|
96042
|
-
};
|
|
96043
|
-
files.sort((a, b) => order[a.outcome] - order[b.outcome] || a.name.localeCompare(b.name));
|
|
96044
|
-
const changed = files.some((f3) => f3.outcome !== "unchanged");
|
|
96045
|
-
return { files, changed };
|
|
96046
|
-
}
|
|
96047
|
-
|
|
96048
|
-
// scripts/lakebase/doctor.ts
|
|
96049
96145
|
function readEnvFile(projectDir) {
|
|
96050
96146
|
const envPath = path30.join(projectDir, ".env");
|
|
96051
96147
|
if (!fs32.existsSync(envPath)) return {};
|
|
@@ -96478,7 +96574,7 @@ function checkWorkflowDrift(projectDir) {
|
|
|
96478
96574
|
status: "warn",
|
|
96479
96575
|
message: `Scaffolded workflows drift from kit: ${drifted} drifted, ${missing} missing`,
|
|
96480
96576
|
detail: { files: report.files.map((f3) => ({ name: f3.name, status: f3.status })) },
|
|
96481
|
-
hint: "
|
|
96577
|
+
hint: "Refresh with updateWorkflows (or the lakebase_workflow_drift MCP tool). A stale merge.yml calling 'lakebase-schema-migrate apply' (not 'apply-tier') makes the promote's migrate-target fail on the tier guard; the scm-merge promote also self-heals drifted workflows onto the PR head before merging."
|
|
96482
96578
|
};
|
|
96483
96579
|
} catch (err) {
|
|
96484
96580
|
return {
|
|
@@ -96589,9 +96685,10 @@ async function ensureLakebaseSecretAuth(args) {
|
|
|
96589
96685
|
if (typeof pat !== "string" || !pat) {
|
|
96590
96686
|
throw new Error("databricks tokens create returned no token_value");
|
|
96591
96687
|
}
|
|
96592
|
-
await runDatabricks(["secrets", "put-secret",
|
|
96688
|
+
await runDatabricks(["secrets", "put-secret", "--json", "@/dev/stdin"], {
|
|
96593
96689
|
profile,
|
|
96594
|
-
timeout: timeoutMs
|
|
96690
|
+
timeout: timeoutMs,
|
|
96691
|
+
input: JSON.stringify({ scope: scopeName, key: keyName, string_value: pat })
|
|
96595
96692
|
});
|
|
96596
96693
|
let aclGranted = false;
|
|
96597
96694
|
if (servicePrincipalClientId) {
|
|
@@ -96786,6 +96883,7 @@ function isUcMissingError(msg) {
|
|
|
96786
96883
|
createUatPairedBranch,
|
|
96787
96884
|
cutBackup,
|
|
96788
96885
|
databricksAuthPrereqMessage,
|
|
96886
|
+
defaultRefreshPromoteWorkflows,
|
|
96789
96887
|
deleteAppEndpoint,
|
|
96790
96888
|
deleteBranch,
|
|
96791
96889
|
deleteLakebaseProject,
|