@databricks-solutions/lakebase-scm-utils 0.2.44 → 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/index.cjs +795 -758
- package/dist/scripts/index.d.cts +1 -1
- package/dist/scripts/index.d.ts +1 -1
- package/dist/scripts/index.js +794 -758
- package/dist/scripts/lakebase/ci-resolve-branch.cli.cjs +2 -2
- package/dist/scripts/lakebase/ci-resolve-branch.cli.js +2 -2
- package/dist/scripts/lakebase/collapse-heads.cli.cjs +2 -2
- package/dist/scripts/lakebase/collapse-heads.cli.js +2 -2
- package/dist/scripts/lakebase/detect-language.cli.cjs +2 -2
- package/dist/scripts/lakebase/detect-language.cli.js +2 -2
- package/dist/scripts/lakebase/doctor.cli.cjs +3 -3
- package/dist/scripts/lakebase/doctor.cli.js +3 -3
- 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 +463 -395
- package/dist/scripts/lakebase/index.d.cts +33 -1
- package/dist/scripts/lakebase/index.d.ts +33 -1
- package/dist/scripts/lakebase/index.js +462 -395
- package/dist/scripts/lakebase/infra-runner.cli.cjs +2 -2
- package/dist/scripts/lakebase/infra-runner.cli.js +2 -2
- package/dist/scripts/lakebase/new-migration.cli.cjs +2 -2
- package/dist/scripts/lakebase/new-migration.cli.js +2 -2
- 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 +2 -2
- package/dist/scripts/lakebase/schema-migrate.cli.js +2 -2
- package/dist/scripts/lakebase/scm-claim-feature.cli.cjs +2 -2
- package/dist/scripts/lakebase/scm-claim-feature.cli.js +2 -2
- package/dist/scripts/lakebase/scm-doctor.cli.cjs +2 -2
- package/dist/scripts/lakebase/scm-doctor.cli.js +2 -2
- package/dist/scripts/lakebase/scm-merge.cli.cjs +2284 -2033
- package/dist/scripts/lakebase/scm-merge.cli.js +339 -88
- package/dist/scripts/lakebase/scm-reconcile-tier.cli.cjs +2 -2
- package/dist/scripts/lakebase/scm-reconcile-tier.cli.js +2 -2
- package/dist/scripts/util/index.cjs +2 -2
- package/dist/scripts/util/index.js +2 -2
- package/package.json +1 -1
- package/scripts/lakebase/doctor.ts +1 -1
- package/scripts/lakebase/scm-merge.ts +80 -1
|
@@ -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,
|
|
@@ -81587,8 +81588,8 @@ var PKG_NAME = "@databricks-solutions/lakebase-scm-utils";
|
|
|
81587
81588
|
var cached;
|
|
81588
81589
|
function substrateSelfVersion() {
|
|
81589
81590
|
if (cached !== void 0) return cached;
|
|
81590
|
-
if ("0.2.
|
|
81591
|
-
cached = "0.2.
|
|
81591
|
+
if ("0.2.45".length > 0) {
|
|
81592
|
+
cached = "0.2.45";
|
|
81592
81593
|
return cached;
|
|
81593
81594
|
}
|
|
81594
81595
|
cached = "unknown";
|
|
@@ -81816,6 +81817,14 @@ async function getCredential(args) {
|
|
|
81816
81817
|
|
|
81817
81818
|
// scripts/git/status.ts
|
|
81818
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
|
+
}
|
|
81819
81828
|
async function getAheadBehind(args) {
|
|
81820
81829
|
const { cwd } = args;
|
|
81821
81830
|
try {
|
|
@@ -94919,118 +94928,461 @@ function pickRunUrl(pr) {
|
|
|
94919
94928
|
|
|
94920
94929
|
// scripts/lakebase/scm-merge.ts
|
|
94921
94930
|
init_cjs_shims();
|
|
94922
|
-
|
|
94923
|
-
|
|
94931
|
+
|
|
94932
|
+
// scripts/git/sync.ts
|
|
94933
|
+
init_cjs_shims();
|
|
94934
|
+
async function currentBranchName(cwd) {
|
|
94924
94935
|
try {
|
|
94925
|
-
|
|
94926
|
-
|
|
94927
|
-
|
|
94928
|
-
} else {
|
|
94929
|
-
await exec2(`git fetch origin ${shellEscape2(`${tier}:${tier}`)}`, { cwd, timeout: 3e4 });
|
|
94930
|
-
}
|
|
94931
|
-
return null;
|
|
94932
|
-
} catch (err) {
|
|
94933
|
-
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 "";
|
|
94934
94939
|
}
|
|
94935
94940
|
}
|
|
94936
|
-
|
|
94937
|
-
|
|
94938
|
-
|
|
94939
|
-
|
|
94940
|
-
|
|
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 });
|
|
94941
94952
|
}
|
|
94942
|
-
code;
|
|
94943
|
-
};
|
|
94944
|
-
var DEFAULT_MIGRATE_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
94945
|
-
var DEFAULT_MIGRATE_POLL_MS = 30 * 1e3;
|
|
94946
|
-
function defaultMigratePredicate(run, mergedAt) {
|
|
94947
|
-
if (!run.createdAt) return false;
|
|
94948
|
-
const created = Date.parse(run.createdAt);
|
|
94949
|
-
if (!Number.isFinite(created)) return false;
|
|
94950
|
-
if (run.event && run.event !== "push") return false;
|
|
94951
|
-
return created >= mergedAt.getTime() - 5e3;
|
|
94952
|
-
}
|
|
94953
|
-
function shaMigratePredicate(mergeCommitSha) {
|
|
94954
|
-
return (run) => {
|
|
94955
|
-
if (run.event && run.event !== "push") return false;
|
|
94956
|
-
return !!run.headSha && run.headSha === mergeCommitSha;
|
|
94957
|
-
};
|
|
94958
94953
|
}
|
|
94959
|
-
|
|
94960
|
-
|
|
94961
|
-
|
|
94962
|
-
|
|
94963
|
-
|
|
94964
|
-
|
|
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"
|
|
94965
94969
|
);
|
|
94970
|
+
if (fs30.existsSync(candidate)) return candidate;
|
|
94971
|
+
const parent = path28.dirname(dir);
|
|
94972
|
+
if (parent === dir) break;
|
|
94973
|
+
dir = parent;
|
|
94966
94974
|
}
|
|
94967
|
-
|
|
94968
|
-
|
|
94969
|
-
|
|
94970
|
-
|
|
94971
|
-
|
|
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}`);
|
|
94972
94991
|
}
|
|
94973
|
-
|
|
94974
|
-
|
|
94975
|
-
|
|
94976
|
-
|
|
94977
|
-
|
|
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
|
+
}
|
|
94978
95033
|
}
|
|
94979
|
-
|
|
94980
|
-
|
|
94981
|
-
|
|
94982
|
-
"bad-precondition"
|
|
94983
|
-
);
|
|
95034
|
+
for (const name of projectFiles) {
|
|
95035
|
+
if (seen.has(name)) continue;
|
|
95036
|
+
files.push({ name, status: "extra" });
|
|
94984
95037
|
}
|
|
94985
|
-
const
|
|
94986
|
-
|
|
94987
|
-
|
|
94988
|
-
|
|
94989
|
-
|
|
94990
|
-
|
|
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);
|
|
94991
95055
|
}
|
|
94992
|
-
|
|
94993
|
-
|
|
94994
|
-
|
|
94995
|
-
|
|
94996
|
-
|
|
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"
|
|
94997
95081
|
);
|
|
95082
|
+
if (fs30.existsSync(candidate)) return candidate;
|
|
95083
|
+
const parent = path28.dirname(dir);
|
|
95084
|
+
if (parent === dir) break;
|
|
95085
|
+
dir = parent;
|
|
94998
95086
|
}
|
|
94999
|
-
|
|
95000
|
-
|
|
95001
|
-
|
|
95002
|
-
|
|
95003
|
-
|
|
95004
|
-
|
|
95005
|
-
|
|
95006
|
-
|
|
95007
|
-
|
|
95008
|
-
|
|
95009
|
-
|
|
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
|
+
});
|
|
95010
95132
|
}
|
|
95011
|
-
authVerified = true;
|
|
95012
95133
|
}
|
|
95013
|
-
|
|
95014
|
-
|
|
95015
|
-
|
|
95016
|
-
ownerRepo,
|
|
95017
|
-
pullNumber,
|
|
95018
|
-
lakebaseInstance: instance,
|
|
95019
|
-
method: args.method ?? "squash"
|
|
95020
|
-
});
|
|
95021
|
-
} catch (err) {
|
|
95022
|
-
throw new ScmMergeError(
|
|
95023
|
-
`mergePairedPullRequest failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
95024
|
-
"merge-failed"
|
|
95025
|
-
);
|
|
95134
|
+
for (const name of projectFiles) {
|
|
95135
|
+
if (seen.has(name)) continue;
|
|
95136
|
+
files.push({ name, status: "extra", kit_version: kitVersion });
|
|
95026
95137
|
}
|
|
95027
|
-
const
|
|
95028
|
-
|
|
95029
|
-
|
|
95030
|
-
|
|
95031
|
-
|
|
95032
|
-
|
|
95033
|
-
|
|
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"
|
|
95372
|
+
});
|
|
95373
|
+
} catch (err) {
|
|
95374
|
+
throw new ScmMergeError(
|
|
95375
|
+
`mergePairedPullRequest failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
95376
|
+
"merge-failed"
|
|
95377
|
+
);
|
|
95378
|
+
}
|
|
95379
|
+
const warnings = [...preflightNotes, ...paired.warnings];
|
|
95380
|
+
let localBranchDeleted = false;
|
|
95381
|
+
let headAfter = current.branch;
|
|
95382
|
+
if (!args.skipLocalCleanup) {
|
|
95383
|
+
const switchTo = args.switchTo ?? gitBase;
|
|
95384
|
+
const head = await getCurrentBranch({ cwd: args.projectDir });
|
|
95385
|
+
if (head === current.branch) {
|
|
95034
95386
|
try {
|
|
95035
95387
|
await exec2(`git checkout ${shellEscape2(switchTo)}`, {
|
|
95036
95388
|
cwd: args.projectDir,
|
|
@@ -95221,7 +95573,7 @@ init_cjs_shims();
|
|
|
95221
95573
|
|
|
95222
95574
|
// scripts/git/branches.ts
|
|
95223
95575
|
init_cjs_shims();
|
|
95224
|
-
async function
|
|
95576
|
+
async function currentBranchName2(cwd) {
|
|
95225
95577
|
try {
|
|
95226
95578
|
return await exec2("git rev-parse --abbrev-ref HEAD", { cwd });
|
|
95227
95579
|
} catch {
|
|
@@ -95240,7 +95592,7 @@ async function listLocalBranches(args) {
|
|
|
95240
95592
|
return [];
|
|
95241
95593
|
}
|
|
95242
95594
|
if (!raw) return [];
|
|
95243
|
-
const current = await
|
|
95595
|
+
const current = await currentBranchName2(cwd);
|
|
95244
95596
|
return raw.split("\n").filter(Boolean).map((line) => {
|
|
95245
95597
|
const [name, tracking, trackInfo] = line.split("|");
|
|
95246
95598
|
let ahead = 0;
|
|
@@ -95405,15 +95757,15 @@ function parentForTopology(t2, defaultLeaf) {
|
|
|
95405
95757
|
|
|
95406
95758
|
// scripts/lakebase/scm-doctor.ts
|
|
95407
95759
|
init_cjs_shims();
|
|
95408
|
-
var
|
|
95409
|
-
var
|
|
95760
|
+
var fs31 = __toESM(require("fs"), 1);
|
|
95761
|
+
var path29 = __toESM(require("path"), 1);
|
|
95410
95762
|
var FEATURE_PREFIX = "feature/";
|
|
95411
95763
|
var TIER_LEAFS2 = DEFAULT_PROTECTED_TIER_NAMES;
|
|
95412
95764
|
function readEnv(projectDir) {
|
|
95413
|
-
const envPath =
|
|
95765
|
+
const envPath = path29.join(projectDir, ".env");
|
|
95414
95766
|
const out = /* @__PURE__ */ new Map();
|
|
95415
|
-
if (!
|
|
95416
|
-
const lines =
|
|
95767
|
+
if (!fs31.existsSync(envPath)) return out;
|
|
95768
|
+
const lines = fs31.readFileSync(envPath, "utf8").split("\n");
|
|
95417
95769
|
for (const line of lines) {
|
|
95418
95770
|
const m2 = line.match(/^\s*([A-Z0-9_]+)\s*=\s*(.+?)\s*$/);
|
|
95419
95771
|
if (m2) out.set(m2[1], m2[2].replace(/^["']|["']$/g, ""));
|
|
@@ -95468,9 +95820,9 @@ async function runDoctor(args, deps = {}) {
|
|
|
95468
95820
|
}
|
|
95469
95821
|
for (const wf of ["pr.yml", "merge.yml"]) {
|
|
95470
95822
|
try {
|
|
95471
|
-
const p =
|
|
95472
|
-
if (!
|
|
95473
|
-
const body =
|
|
95823
|
+
const p = path29.join(projectDir, ".github", "workflows", wf);
|
|
95824
|
+
if (!fs31.existsSync(p)) continue;
|
|
95825
|
+
const body = fs31.readFileSync(p, "utf8");
|
|
95474
95826
|
if (/lakebase-scm-utils#v\d/.test(body)) {
|
|
95475
95827
|
findings.push({
|
|
95476
95828
|
id: "ci-workflow-substrate-pin",
|
|
@@ -95685,7 +96037,7 @@ async function fixFinding(args) {
|
|
|
95685
96037
|
);
|
|
95686
96038
|
}
|
|
95687
96039
|
const sanitized = sanitizeBranchName(branch);
|
|
95688
|
-
const envFile =
|
|
96040
|
+
const envFile = path29.join(args.projectDir, ".env");
|
|
95689
96041
|
updateEnvConnection({
|
|
95690
96042
|
envPath: envFile,
|
|
95691
96043
|
projectId: readEnvVar(envFile, "LAKEBASE_PROJECT_ID") ?? "",
|
|
@@ -95790,291 +96142,6 @@ init_cjs_shims();
|
|
|
95790
96142
|
var fs32 = __toESM(require("fs"), 1);
|
|
95791
96143
|
var path30 = __toESM(require("path"), 1);
|
|
95792
96144
|
var cp6 = __toESM(require("child_process"), 1);
|
|
95793
|
-
|
|
95794
|
-
// scripts/lakebase/workflow-drift.ts
|
|
95795
|
-
init_cjs_shims();
|
|
95796
|
-
var fs31 = __toESM(require("fs"), 1);
|
|
95797
|
-
var path29 = __toESM(require("path"), 1);
|
|
95798
|
-
function findKitTemplatesDir(start) {
|
|
95799
|
-
let dir = start;
|
|
95800
|
-
for (let i2 = 0; i2 < 6; i2++) {
|
|
95801
|
-
const candidate = path29.join(
|
|
95802
|
-
dir,
|
|
95803
|
-
"templates",
|
|
95804
|
-
"project",
|
|
95805
|
-
"common",
|
|
95806
|
-
".github",
|
|
95807
|
-
"workflows"
|
|
95808
|
-
);
|
|
95809
|
-
if (fs31.existsSync(candidate)) return candidate;
|
|
95810
|
-
const parent = path29.dirname(dir);
|
|
95811
|
-
if (parent === dir) break;
|
|
95812
|
-
dir = parent;
|
|
95813
|
-
}
|
|
95814
|
-
throw new Error(
|
|
95815
|
-
`Could not locate templates/project/common/.github/workflows/ relative to ${start}. Pass explicit kitDir.`
|
|
95816
|
-
);
|
|
95817
|
-
}
|
|
95818
|
-
function unifiedDiff(name, projectContent, templateContent) {
|
|
95819
|
-
if (projectContent === templateContent) return "";
|
|
95820
|
-
const a = projectContent.split("\n");
|
|
95821
|
-
const b = templateContent.split("\n");
|
|
95822
|
-
const out = [`--- project/${name}`, `+++ template/${name}`];
|
|
95823
|
-
const max = Math.max(a.length, b.length);
|
|
95824
|
-
for (let i2 = 0; i2 < max; i2++) {
|
|
95825
|
-
const av = a[i2];
|
|
95826
|
-
const bv = b[i2];
|
|
95827
|
-
if (av === bv) continue;
|
|
95828
|
-
if (av !== void 0) out.push(`-${i2 + 1}: ${av}`);
|
|
95829
|
-
if (bv !== void 0) out.push(`+${i2 + 1}: ${bv}`);
|
|
95830
|
-
}
|
|
95831
|
-
return out.join("\n");
|
|
95832
|
-
}
|
|
95833
|
-
function detectWorkflowDrift(args) {
|
|
95834
|
-
const projectWorkflowsDir = path29.join(
|
|
95835
|
-
args.projectDir,
|
|
95836
|
-
".github",
|
|
95837
|
-
"workflows"
|
|
95838
|
-
);
|
|
95839
|
-
const here = path29.dirname(new URL(importMetaUrl).pathname);
|
|
95840
|
-
const kitWorkflowsDir = args.kitDir ? path29.join(
|
|
95841
|
-
args.kitDir,
|
|
95842
|
-
"templates",
|
|
95843
|
-
"project",
|
|
95844
|
-
"common",
|
|
95845
|
-
".github",
|
|
95846
|
-
"workflows"
|
|
95847
|
-
) : findKitTemplatesDir(here);
|
|
95848
|
-
const templateFiles = fs31.existsSync(kitWorkflowsDir) ? fs31.readdirSync(kitWorkflowsDir).filter((f3) => f3.endsWith(".yml")) : [];
|
|
95849
|
-
const projectFiles = fs31.existsSync(projectWorkflowsDir) ? fs31.readdirSync(projectWorkflowsDir).filter((f3) => f3.endsWith(".yml")) : [];
|
|
95850
|
-
const version = readKitVersion(kitWorkflowsDir);
|
|
95851
|
-
const seen = /* @__PURE__ */ new Set();
|
|
95852
|
-
const files = [];
|
|
95853
|
-
for (const name of templateFiles) {
|
|
95854
|
-
seen.add(name);
|
|
95855
|
-
const projectPath2 = path29.join(projectWorkflowsDir, name);
|
|
95856
|
-
const templatePath = path29.join(kitWorkflowsDir, name);
|
|
95857
|
-
if (!fs31.existsSync(projectPath2)) {
|
|
95858
|
-
files.push({ name, status: "missing" });
|
|
95859
|
-
continue;
|
|
95860
|
-
}
|
|
95861
|
-
const projectContent = fs31.readFileSync(projectPath2, "utf8");
|
|
95862
|
-
const templateContent = applyPlaceholders(fs31.readFileSync(templatePath, "utf8"), version);
|
|
95863
|
-
if (projectContent === templateContent) {
|
|
95864
|
-
files.push({ name, status: "unchanged" });
|
|
95865
|
-
} else {
|
|
95866
|
-
files.push({
|
|
95867
|
-
name,
|
|
95868
|
-
status: "drifted",
|
|
95869
|
-
diff: unifiedDiff(name, projectContent, templateContent)
|
|
95870
|
-
});
|
|
95871
|
-
}
|
|
95872
|
-
}
|
|
95873
|
-
for (const name of projectFiles) {
|
|
95874
|
-
if (seen.has(name)) continue;
|
|
95875
|
-
files.push({ name, status: "extra" });
|
|
95876
|
-
}
|
|
95877
|
-
const order = {
|
|
95878
|
-
drifted: 0,
|
|
95879
|
-
missing: 1,
|
|
95880
|
-
extra: 2,
|
|
95881
|
-
unchanged: 3
|
|
95882
|
-
};
|
|
95883
|
-
files.sort((a, b) => order[a.status] - order[b.status] || a.name.localeCompare(b.name));
|
|
95884
|
-
const hasDrift = files.some((f3) => f3.status === "drifted" || f3.status === "missing");
|
|
95885
|
-
return {
|
|
95886
|
-
overall: hasDrift ? "drift" : "ok",
|
|
95887
|
-
files
|
|
95888
|
-
};
|
|
95889
|
-
}
|
|
95890
|
-
function readKitVersion(kitWorkflowsDir) {
|
|
95891
|
-
let dir = kitWorkflowsDir;
|
|
95892
|
-
for (let i2 = 0; i2 < 5; i2++) {
|
|
95893
|
-
dir = path29.dirname(dir);
|
|
95894
|
-
}
|
|
95895
|
-
try {
|
|
95896
|
-
const raw = fs31.readFileSync(path29.join(dir, "package.json"), "utf-8");
|
|
95897
|
-
const pkg = JSON.parse(raw);
|
|
95898
|
-
return typeof pkg.version === "string" ? pkg.version : "unknown";
|
|
95899
|
-
} catch {
|
|
95900
|
-
return "unknown";
|
|
95901
|
-
}
|
|
95902
|
-
}
|
|
95903
|
-
function applyPlaceholders(content, version) {
|
|
95904
|
-
return content.replace(/\{\{LAKEBASE_SCM_UTILS_VERSION\}\}/g, version);
|
|
95905
|
-
}
|
|
95906
|
-
function applyCommandPlaceholders(content, version) {
|
|
95907
|
-
return content.replace(/\$\{KIT_VERSION_AT_SCAFFOLD\}/g, version);
|
|
95908
|
-
}
|
|
95909
|
-
var COMMAND_HOOK_FILE_PATTERN = /\.(pre|post)-hook\.md$/;
|
|
95910
|
-
function findKitCommandsDir(start) {
|
|
95911
|
-
let dir = start;
|
|
95912
|
-
for (let i2 = 0; i2 < 6; i2++) {
|
|
95913
|
-
const candidate = path29.join(
|
|
95914
|
-
dir,
|
|
95915
|
-
"templates",
|
|
95916
|
-
"project",
|
|
95917
|
-
"common",
|
|
95918
|
-
".claude",
|
|
95919
|
-
"commands"
|
|
95920
|
-
);
|
|
95921
|
-
if (fs31.existsSync(candidate)) return candidate;
|
|
95922
|
-
const parent = path29.dirname(dir);
|
|
95923
|
-
if (parent === dir) break;
|
|
95924
|
-
dir = parent;
|
|
95925
|
-
}
|
|
95926
|
-
throw new Error(
|
|
95927
|
-
`Could not locate templates/project/common/.claude/commands/ relative to ${start}. Pass explicit kitDir.`
|
|
95928
|
-
);
|
|
95929
|
-
}
|
|
95930
|
-
function parsePinnedVersion(content) {
|
|
95931
|
-
const m2 = content.match(/^\s*[*_>`\s]*pinned\s+to\s*:\s*[`*_]*([^\s`*_]+)[`*_]*\s*$/im);
|
|
95932
|
-
return m2 ? m2[1] : void 0;
|
|
95933
|
-
}
|
|
95934
|
-
function detectCommandDrift(args) {
|
|
95935
|
-
const projectCommandsDir = path29.join(args.projectDir, ".claude", "commands");
|
|
95936
|
-
const here = path29.dirname(new URL(importMetaUrl).pathname);
|
|
95937
|
-
const kitCommandsDir = args.kitDir ? path29.join(args.kitDir, "templates", "project", "common", ".claude", "commands") : findKitCommandsDir(here);
|
|
95938
|
-
const kitVersion = readKitVersionFromCommandsDir(kitCommandsDir);
|
|
95939
|
-
const templateFiles = fs31.existsSync(kitCommandsDir) ? fs31.readdirSync(kitCommandsDir).filter((f3) => f3.endsWith(".md") && !COMMAND_HOOK_FILE_PATTERN.test(f3)) : [];
|
|
95940
|
-
const projectFiles = fs31.existsSync(projectCommandsDir) ? fs31.readdirSync(projectCommandsDir).filter((f3) => f3.endsWith(".md") && !COMMAND_HOOK_FILE_PATTERN.test(f3)) : [];
|
|
95941
|
-
const seen = /* @__PURE__ */ new Set();
|
|
95942
|
-
const files = [];
|
|
95943
|
-
for (const name of templateFiles) {
|
|
95944
|
-
seen.add(name);
|
|
95945
|
-
const projectPath2 = path29.join(projectCommandsDir, name);
|
|
95946
|
-
const templatePath = path29.join(kitCommandsDir, name);
|
|
95947
|
-
const templateRaw = fs31.readFileSync(templatePath, "utf8");
|
|
95948
|
-
if (!fs31.existsSync(projectPath2)) {
|
|
95949
|
-
files.push({ name, status: "missing", kit_version: kitVersion });
|
|
95950
|
-
continue;
|
|
95951
|
-
}
|
|
95952
|
-
const projectContent = fs31.readFileSync(projectPath2, "utf8");
|
|
95953
|
-
const pinned = parsePinnedVersion(projectContent);
|
|
95954
|
-
const versionForCompare = pinned ?? kitVersion;
|
|
95955
|
-
const templateContent = applyCommandPlaceholders(templateRaw, versionForCompare);
|
|
95956
|
-
if (projectContent === templateContent) {
|
|
95957
|
-
files.push({
|
|
95958
|
-
name,
|
|
95959
|
-
status: "unchanged",
|
|
95960
|
-
pinned_version: pinned,
|
|
95961
|
-
kit_version: kitVersion
|
|
95962
|
-
});
|
|
95963
|
-
} else {
|
|
95964
|
-
files.push({
|
|
95965
|
-
name,
|
|
95966
|
-
status: "drifted",
|
|
95967
|
-
pinned_version: pinned,
|
|
95968
|
-
kit_version: kitVersion,
|
|
95969
|
-
diff: unifiedDiff(name, projectContent, templateContent)
|
|
95970
|
-
});
|
|
95971
|
-
}
|
|
95972
|
-
}
|
|
95973
|
-
for (const name of projectFiles) {
|
|
95974
|
-
if (seen.has(name)) continue;
|
|
95975
|
-
files.push({ name, status: "extra", kit_version: kitVersion });
|
|
95976
|
-
}
|
|
95977
|
-
const order = {
|
|
95978
|
-
drifted: 0,
|
|
95979
|
-
missing: 1,
|
|
95980
|
-
extra: 2,
|
|
95981
|
-
unchanged: 3
|
|
95982
|
-
};
|
|
95983
|
-
files.sort((a, b) => order[a.status] - order[b.status] || a.name.localeCompare(b.name));
|
|
95984
|
-
const hasDrift = files.some((f3) => f3.status === "drifted" || f3.status === "missing");
|
|
95985
|
-
return { overall: hasDrift ? "drift" : "ok", files };
|
|
95986
|
-
}
|
|
95987
|
-
function readKitVersionFromCommandsDir(kitCommandsDir) {
|
|
95988
|
-
let dir = kitCommandsDir;
|
|
95989
|
-
for (let i2 = 0; i2 < 5; i2++) {
|
|
95990
|
-
dir = path29.dirname(dir);
|
|
95991
|
-
}
|
|
95992
|
-
try {
|
|
95993
|
-
const raw = fs31.readFileSync(path29.join(dir, "package.json"), "utf-8");
|
|
95994
|
-
const pkg = JSON.parse(raw);
|
|
95995
|
-
return typeof pkg.version === "string" ? pkg.version : "unknown";
|
|
95996
|
-
} catch {
|
|
95997
|
-
return "unknown";
|
|
95998
|
-
}
|
|
95999
|
-
}
|
|
96000
|
-
function detectScaffoldedDrift(args) {
|
|
96001
|
-
const workflows = detectWorkflowDrift(args);
|
|
96002
|
-
const commands = detectCommandDrift(args);
|
|
96003
|
-
return {
|
|
96004
|
-
overall: workflows.overall === "drift" || commands.overall === "drift" ? "drift" : "ok",
|
|
96005
|
-
workflows,
|
|
96006
|
-
commands
|
|
96007
|
-
};
|
|
96008
|
-
}
|
|
96009
|
-
function updateWorkflows(args) {
|
|
96010
|
-
const projectWorkflowsDir = path29.join(
|
|
96011
|
-
args.projectDir,
|
|
96012
|
-
".github",
|
|
96013
|
-
"workflows"
|
|
96014
|
-
);
|
|
96015
|
-
const here = path29.dirname(new URL(importMetaUrl).pathname);
|
|
96016
|
-
const kitWorkflowsDir = args.kitDir ? path29.join(
|
|
96017
|
-
args.kitDir,
|
|
96018
|
-
"templates",
|
|
96019
|
-
"project",
|
|
96020
|
-
"common",
|
|
96021
|
-
".github",
|
|
96022
|
-
"workflows"
|
|
96023
|
-
) : findKitTemplatesDir(here);
|
|
96024
|
-
const substitute = args.substitute !== false;
|
|
96025
|
-
const dryRun = args.dryRun === true;
|
|
96026
|
-
const pruneExtras = args.pruneExtras === true;
|
|
96027
|
-
const templateFiles = fs31.existsSync(kitWorkflowsDir) ? fs31.readdirSync(kitWorkflowsDir).filter((f3) => f3.endsWith(".yml")) : [];
|
|
96028
|
-
const projectFiles = fs31.existsSync(projectWorkflowsDir) ? fs31.readdirSync(projectWorkflowsDir).filter((f3) => f3.endsWith(".yml")) : [];
|
|
96029
|
-
if (!dryRun && templateFiles.length > 0 && !fs31.existsSync(projectWorkflowsDir)) {
|
|
96030
|
-
fs31.mkdirSync(projectWorkflowsDir, { recursive: true });
|
|
96031
|
-
}
|
|
96032
|
-
const version = substitute ? readKitVersion(kitWorkflowsDir) : "";
|
|
96033
|
-
const seen = /* @__PURE__ */ new Set();
|
|
96034
|
-
const files = [];
|
|
96035
|
-
for (const name of templateFiles) {
|
|
96036
|
-
seen.add(name);
|
|
96037
|
-
const projectPath2 = path29.join(projectWorkflowsDir, name);
|
|
96038
|
-
const templatePath = path29.join(kitWorkflowsDir, name);
|
|
96039
|
-
const templateRaw = fs31.readFileSync(templatePath, "utf-8");
|
|
96040
|
-
const desired = substitute ? applyPlaceholders(templateRaw, version) : templateRaw;
|
|
96041
|
-
const existed = fs31.existsSync(projectPath2);
|
|
96042
|
-
const current = existed ? fs31.readFileSync(projectPath2, "utf-8") : "";
|
|
96043
|
-
let outcome;
|
|
96044
|
-
if (!existed) {
|
|
96045
|
-
outcome = "added";
|
|
96046
|
-
} else if (current === desired) {
|
|
96047
|
-
outcome = "unchanged";
|
|
96048
|
-
} else {
|
|
96049
|
-
outcome = "updated";
|
|
96050
|
-
}
|
|
96051
|
-
if (!dryRun && outcome !== "unchanged") {
|
|
96052
|
-
fs31.writeFileSync(projectPath2, desired);
|
|
96053
|
-
}
|
|
96054
|
-
files.push({ name, outcome });
|
|
96055
|
-
}
|
|
96056
|
-
if (pruneExtras) {
|
|
96057
|
-
for (const name of projectFiles) {
|
|
96058
|
-
if (seen.has(name)) continue;
|
|
96059
|
-
const projectPath2 = path29.join(projectWorkflowsDir, name);
|
|
96060
|
-
if (!dryRun) {
|
|
96061
|
-
fs31.unlinkSync(projectPath2);
|
|
96062
|
-
}
|
|
96063
|
-
files.push({ name, outcome: "removed" });
|
|
96064
|
-
}
|
|
96065
|
-
}
|
|
96066
|
-
const order = {
|
|
96067
|
-
added: 0,
|
|
96068
|
-
updated: 1,
|
|
96069
|
-
removed: 2,
|
|
96070
|
-
unchanged: 3
|
|
96071
|
-
};
|
|
96072
|
-
files.sort((a, b) => order[a.outcome] - order[b.outcome] || a.name.localeCompare(b.name));
|
|
96073
|
-
const changed = files.some((f3) => f3.outcome !== "unchanged");
|
|
96074
|
-
return { files, changed };
|
|
96075
|
-
}
|
|
96076
|
-
|
|
96077
|
-
// scripts/lakebase/doctor.ts
|
|
96078
96145
|
function readEnvFile(projectDir) {
|
|
96079
96146
|
const envPath = path30.join(projectDir, ".env");
|
|
96080
96147
|
if (!fs32.existsSync(envPath)) return {};
|
|
@@ -96507,7 +96574,7 @@ function checkWorkflowDrift(projectDir) {
|
|
|
96507
96574
|
status: "warn",
|
|
96508
96575
|
message: `Scaffolded workflows drift from kit: ${drifted} drifted, ${missing} missing`,
|
|
96509
96576
|
detail: { files: report.files.map((f3) => ({ name: f3.name, status: f3.status })) },
|
|
96510
|
-
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."
|
|
96511
96578
|
};
|
|
96512
96579
|
} catch (err) {
|
|
96513
96580
|
return {
|
|
@@ -96816,6 +96883,7 @@ function isUcMissingError(msg) {
|
|
|
96816
96883
|
createUatPairedBranch,
|
|
96817
96884
|
cutBackup,
|
|
96818
96885
|
databricksAuthPrereqMessage,
|
|
96886
|
+
defaultRefreshPromoteWorkflows,
|
|
96819
96887
|
deleteAppEndpoint,
|
|
96820
96888
|
deleteBranch,
|
|
96821
96889
|
deleteLakebaseProject,
|