@databricks-solutions/lakebase-scm-utils 0.2.44 → 0.2.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -4
- package/dist/scripts/index.cjs +651 -605
- package/dist/scripts/index.d.cts +1 -1
- package/dist/scripts/index.d.ts +1 -1
- package/dist/scripts/index.js +650 -605
- package/dist/scripts/lakebase/ci-resolve-branch.cli.cjs +12 -3
- package/dist/scripts/lakebase/ci-resolve-branch.cli.js +12 -3
- package/dist/scripts/lakebase/collapse-heads.cli.cjs +12 -3
- package/dist/scripts/lakebase/collapse-heads.cli.js +12 -3
- package/dist/scripts/lakebase/detect-language.cli.cjs +12 -3
- package/dist/scripts/lakebase/detect-language.cli.js +12 -3
- package/dist/scripts/lakebase/doctor.cli.cjs +13 -4
- package/dist/scripts/lakebase/doctor.cli.js +13 -4
- package/dist/scripts/lakebase/get-connection.cli.cjs +12 -3
- package/dist/scripts/lakebase/get-connection.cli.js +12 -3
- package/dist/scripts/lakebase/index.cjs +472 -395
- package/dist/scripts/lakebase/index.d.cts +33 -12
- package/dist/scripts/lakebase/index.d.ts +33 -12
- package/dist/scripts/lakebase/index.js +471 -395
- package/dist/scripts/lakebase/infra-runner.cli.cjs +12 -3
- package/dist/scripts/lakebase/infra-runner.cli.js +12 -3
- package/dist/scripts/lakebase/new-migration.cli.cjs +12 -3
- package/dist/scripts/lakebase/new-migration.cli.js +12 -3
- package/dist/scripts/lakebase/schema-diff.cli.cjs +12 -3
- package/dist/scripts/lakebase/schema-diff.cli.js +12 -3
- package/dist/scripts/lakebase/schema-migrate.cli.cjs +12 -3
- package/dist/scripts/lakebase/schema-migrate.cli.js +12 -3
- package/dist/scripts/lakebase/scm-claim-feature.cli.cjs +12 -3
- package/dist/scripts/lakebase/scm-claim-feature.cli.js +12 -3
- package/dist/scripts/lakebase/scm-doctor.cli.cjs +12 -3
- package/dist/scripts/lakebase/scm-doctor.cli.js +12 -3
- package/dist/scripts/lakebase/scm-merge.cli.cjs +2294 -2034
- package/dist/scripts/lakebase/scm-merge.cli.js +349 -89
- package/dist/scripts/lakebase/scm-reconcile-tier.cli.cjs +12 -3
- package/dist/scripts/lakebase/scm-reconcile-tier.cli.js +12 -3
- package/dist/scripts/util/index.cjs +2 -2
- package/dist/scripts/util/index.js +2 -2
- package/package.json +1 -1
- package/scripts/lakebase/constants.ts +23 -5
- package/scripts/lakebase/doctor.ts +1 -1
- package/scripts/lakebase/get-connection.ts +17 -3
- package/scripts/lakebase/scm-merge.ts +80 -1
- package/skills/lakebase-scm-workflows/README.md +2 -1
- package/templates/project/nodejs/package.json +3 -1
- package/templates/project/nodejs/src/index.js +8 -0
- package/templates/project/nodejs/src/openapi.js +20 -0
- package/templates/project/nodejs/src/routes/health.js +18 -0
- package/templates/project/nodejs/tests/app.test.js +16 -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,
|
|
@@ -81576,6 +81577,7 @@ var RUNTIME_ARTIFACT_IGNORE = [
|
|
|
81576
81577
|
var DEFAULT_ENDPOINT = "primary";
|
|
81577
81578
|
var CONSORT_APPLICATION_NAME = "consort";
|
|
81578
81579
|
var SCM_UTILS_APPLICATION_NAME = "scm-utils";
|
|
81580
|
+
var SCM_CLIENT_ENV = "LAKEBASE_SCM_CLIENT";
|
|
81579
81581
|
var CONSORT_VERSION_ENV = "CONSORT_VERSION";
|
|
81580
81582
|
|
|
81581
81583
|
// scripts/lakebase/self-version.ts
|
|
@@ -81587,8 +81589,8 @@ var PKG_NAME = "@databricks-solutions/lakebase-scm-utils";
|
|
|
81587
81589
|
var cached;
|
|
81588
81590
|
function substrateSelfVersion() {
|
|
81589
81591
|
if (cached !== void 0) return cached;
|
|
81590
|
-
if ("0.2.
|
|
81591
|
-
cached = "0.2.
|
|
81592
|
+
if ("0.2.46".length > 0) {
|
|
81593
|
+
cached = "0.2.46";
|
|
81592
81594
|
return cached;
|
|
81593
81595
|
}
|
|
81594
81596
|
cached = "unknown";
|
|
@@ -81616,9 +81618,17 @@ function substrateSelfVersion() {
|
|
|
81616
81618
|
}
|
|
81617
81619
|
|
|
81618
81620
|
// scripts/lakebase/get-connection.ts
|
|
81621
|
+
function sanitizeApplicationName(raw) {
|
|
81622
|
+
const cleaned = raw.replace(/[^\x21-\x7e]+/g, "-").replace(/^-+|-+$/g, "");
|
|
81623
|
+
if (!cleaned) return `${SCM_UTILS_APPLICATION_NAME}/${substrateSelfVersion()}`;
|
|
81624
|
+
return cleaned.length > 63 ? cleaned.slice(0, 63) : cleaned;
|
|
81625
|
+
}
|
|
81619
81626
|
function connectionApplicationName() {
|
|
81627
|
+
const client = process.env[SCM_CLIENT_ENV]?.trim();
|
|
81628
|
+
if (client) return sanitizeApplicationName(client);
|
|
81620
81629
|
const consortVersion = process.env[CONSORT_VERSION_ENV]?.trim();
|
|
81621
|
-
|
|
81630
|
+
if (consortVersion) return `${CONSORT_APPLICATION_NAME}/${consortVersion}`;
|
|
81631
|
+
return `${SCM_UTILS_APPLICATION_NAME}/${substrateSelfVersion()}`;
|
|
81622
81632
|
}
|
|
81623
81633
|
async function getConnection(args) {
|
|
81624
81634
|
const endpointName = args.endpointName ?? DEFAULT_ENDPOINT;
|
|
@@ -81816,6 +81826,14 @@ async function getCredential(args) {
|
|
|
81816
81826
|
|
|
81817
81827
|
// scripts/git/status.ts
|
|
81818
81828
|
init_cjs_shims();
|
|
81829
|
+
async function hasUpstream(args) {
|
|
81830
|
+
try {
|
|
81831
|
+
await exec2("git rev-parse --abbrev-ref @{u}", { cwd: args.cwd });
|
|
81832
|
+
return true;
|
|
81833
|
+
} catch {
|
|
81834
|
+
return false;
|
|
81835
|
+
}
|
|
81836
|
+
}
|
|
81819
81837
|
async function getAheadBehind(args) {
|
|
81820
81838
|
const { cwd } = args;
|
|
81821
81839
|
try {
|
|
@@ -94919,117 +94937,460 @@ function pickRunUrl(pr) {
|
|
|
94919
94937
|
|
|
94920
94938
|
// scripts/lakebase/scm-merge.ts
|
|
94921
94939
|
init_cjs_shims();
|
|
94922
|
-
|
|
94923
|
-
|
|
94940
|
+
|
|
94941
|
+
// scripts/git/sync.ts
|
|
94942
|
+
init_cjs_shims();
|
|
94943
|
+
async function currentBranchName(cwd) {
|
|
94924
94944
|
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).`;
|
|
94945
|
+
return await exec2("git rev-parse --abbrev-ref HEAD", { cwd });
|
|
94946
|
+
} catch {
|
|
94947
|
+
return "";
|
|
94934
94948
|
}
|
|
94935
94949
|
}
|
|
94936
|
-
|
|
94937
|
-
|
|
94938
|
-
|
|
94939
|
-
|
|
94940
|
-
|
|
94950
|
+
async function pushCurrentBranchForPr(args) {
|
|
94951
|
+
const remote = args.remote ?? "origin";
|
|
94952
|
+
const branch = await currentBranchName(args.cwd);
|
|
94953
|
+
if (!branch) throw new Error("No current branch");
|
|
94954
|
+
const upstreamSet = await hasUpstream({ cwd: args.cwd });
|
|
94955
|
+
if (!upstreamSet) {
|
|
94956
|
+
await exec2(`git push -u ${remote} ${shq(branch)}`, {
|
|
94957
|
+
cwd: args.cwd
|
|
94958
|
+
});
|
|
94959
|
+
} else {
|
|
94960
|
+
await exec2("git push", { cwd: args.cwd });
|
|
94941
94961
|
}
|
|
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
94962
|
}
|
|
94959
|
-
|
|
94960
|
-
|
|
94961
|
-
|
|
94962
|
-
|
|
94963
|
-
|
|
94964
|
-
|
|
94963
|
+
|
|
94964
|
+
// scripts/lakebase/workflow-drift.ts
|
|
94965
|
+
init_cjs_shims();
|
|
94966
|
+
var fs30 = __toESM(require("fs"), 1);
|
|
94967
|
+
var path28 = __toESM(require("path"), 1);
|
|
94968
|
+
function findKitTemplatesDir(start) {
|
|
94969
|
+
let dir = start;
|
|
94970
|
+
for (let i2 = 0; i2 < 6; i2++) {
|
|
94971
|
+
const candidate = path28.join(
|
|
94972
|
+
dir,
|
|
94973
|
+
"templates",
|
|
94974
|
+
"project",
|
|
94975
|
+
"common",
|
|
94976
|
+
".github",
|
|
94977
|
+
"workflows"
|
|
94965
94978
|
);
|
|
94979
|
+
if (fs30.existsSync(candidate)) return candidate;
|
|
94980
|
+
const parent = path28.dirname(dir);
|
|
94981
|
+
if (parent === dir) break;
|
|
94982
|
+
dir = parent;
|
|
94966
94983
|
}
|
|
94967
|
-
|
|
94968
|
-
|
|
94969
|
-
|
|
94970
|
-
|
|
94971
|
-
|
|
94984
|
+
throw new Error(
|
|
94985
|
+
`Could not locate templates/project/common/.github/workflows/ relative to ${start}. Pass explicit kitDir.`
|
|
94986
|
+
);
|
|
94987
|
+
}
|
|
94988
|
+
function unifiedDiff(name, projectContent, templateContent) {
|
|
94989
|
+
if (projectContent === templateContent) return "";
|
|
94990
|
+
const a = projectContent.split("\n");
|
|
94991
|
+
const b = templateContent.split("\n");
|
|
94992
|
+
const out = [`--- project/${name}`, `+++ template/${name}`];
|
|
94993
|
+
const max = Math.max(a.length, b.length);
|
|
94994
|
+
for (let i2 = 0; i2 < max; i2++) {
|
|
94995
|
+
const av = a[i2];
|
|
94996
|
+
const bv = b[i2];
|
|
94997
|
+
if (av === bv) continue;
|
|
94998
|
+
if (av !== void 0) out.push(`-${i2 + 1}: ${av}`);
|
|
94999
|
+
if (bv !== void 0) out.push(`+${i2 + 1}: ${bv}`);
|
|
94972
95000
|
}
|
|
94973
|
-
|
|
94974
|
-
|
|
94975
|
-
|
|
94976
|
-
|
|
94977
|
-
|
|
95001
|
+
return out.join("\n");
|
|
95002
|
+
}
|
|
95003
|
+
function detectWorkflowDrift(args) {
|
|
95004
|
+
const projectWorkflowsDir = path28.join(
|
|
95005
|
+
args.projectDir,
|
|
95006
|
+
".github",
|
|
95007
|
+
"workflows"
|
|
95008
|
+
);
|
|
95009
|
+
const here = path28.dirname(new URL(importMetaUrl).pathname);
|
|
95010
|
+
const kitWorkflowsDir = args.kitDir ? path28.join(
|
|
95011
|
+
args.kitDir,
|
|
95012
|
+
"templates",
|
|
95013
|
+
"project",
|
|
95014
|
+
"common",
|
|
95015
|
+
".github",
|
|
95016
|
+
"workflows"
|
|
95017
|
+
) : findKitTemplatesDir(here);
|
|
95018
|
+
const templateFiles = fs30.existsSync(kitWorkflowsDir) ? fs30.readdirSync(kitWorkflowsDir).filter((f3) => f3.endsWith(".yml")) : [];
|
|
95019
|
+
const projectFiles = fs30.existsSync(projectWorkflowsDir) ? fs30.readdirSync(projectWorkflowsDir).filter((f3) => f3.endsWith(".yml")) : [];
|
|
95020
|
+
const version = readKitVersion(kitWorkflowsDir);
|
|
95021
|
+
const seen = /* @__PURE__ */ new Set();
|
|
95022
|
+
const files = [];
|
|
95023
|
+
for (const name of templateFiles) {
|
|
95024
|
+
seen.add(name);
|
|
95025
|
+
const projectPath2 = path28.join(projectWorkflowsDir, name);
|
|
95026
|
+
const templatePath = path28.join(kitWorkflowsDir, name);
|
|
95027
|
+
if (!fs30.existsSync(projectPath2)) {
|
|
95028
|
+
files.push({ name, status: "missing" });
|
|
95029
|
+
continue;
|
|
95030
|
+
}
|
|
95031
|
+
const projectContent = fs30.readFileSync(projectPath2, "utf8");
|
|
95032
|
+
const templateContent = applyPlaceholders(fs30.readFileSync(templatePath, "utf8"), version);
|
|
95033
|
+
if (projectContent === templateContent) {
|
|
95034
|
+
files.push({ name, status: "unchanged" });
|
|
95035
|
+
} else {
|
|
95036
|
+
files.push({
|
|
95037
|
+
name,
|
|
95038
|
+
status: "drifted",
|
|
95039
|
+
diff: unifiedDiff(name, projectContent, templateContent)
|
|
95040
|
+
});
|
|
95041
|
+
}
|
|
94978
95042
|
}
|
|
94979
|
-
|
|
94980
|
-
|
|
94981
|
-
|
|
94982
|
-
"bad-precondition"
|
|
94983
|
-
);
|
|
95043
|
+
for (const name of projectFiles) {
|
|
95044
|
+
if (seen.has(name)) continue;
|
|
95045
|
+
files.push({ name, status: "extra" });
|
|
94984
95046
|
}
|
|
94985
|
-
const
|
|
94986
|
-
|
|
94987
|
-
|
|
94988
|
-
|
|
94989
|
-
|
|
94990
|
-
|
|
95047
|
+
const order = {
|
|
95048
|
+
drifted: 0,
|
|
95049
|
+
missing: 1,
|
|
95050
|
+
extra: 2,
|
|
95051
|
+
unchanged: 3
|
|
95052
|
+
};
|
|
95053
|
+
files.sort((a, b) => order[a.status] - order[b.status] || a.name.localeCompare(b.name));
|
|
95054
|
+
const hasDrift = files.some((f3) => f3.status === "drifted" || f3.status === "missing");
|
|
95055
|
+
return {
|
|
95056
|
+
overall: hasDrift ? "drift" : "ok",
|
|
95057
|
+
files
|
|
95058
|
+
};
|
|
95059
|
+
}
|
|
95060
|
+
function readKitVersion(kitWorkflowsDir) {
|
|
95061
|
+
let dir = kitWorkflowsDir;
|
|
95062
|
+
for (let i2 = 0; i2 < 5; i2++) {
|
|
95063
|
+
dir = path28.dirname(dir);
|
|
94991
95064
|
}
|
|
94992
|
-
|
|
94993
|
-
|
|
94994
|
-
|
|
94995
|
-
|
|
94996
|
-
|
|
95065
|
+
try {
|
|
95066
|
+
const raw = fs30.readFileSync(path28.join(dir, "package.json"), "utf-8");
|
|
95067
|
+
const pkg = JSON.parse(raw);
|
|
95068
|
+
return typeof pkg.version === "string" ? pkg.version : "unknown";
|
|
95069
|
+
} catch {
|
|
95070
|
+
return "unknown";
|
|
95071
|
+
}
|
|
95072
|
+
}
|
|
95073
|
+
function applyPlaceholders(content, version) {
|
|
95074
|
+
return content.replace(/\{\{LAKEBASE_SCM_UTILS_VERSION\}\}/g, version);
|
|
95075
|
+
}
|
|
95076
|
+
function applyCommandPlaceholders(content, version) {
|
|
95077
|
+
return content.replace(/\$\{KIT_VERSION_AT_SCAFFOLD\}/g, version);
|
|
95078
|
+
}
|
|
95079
|
+
var COMMAND_HOOK_FILE_PATTERN = /\.(pre|post)-hook\.md$/;
|
|
95080
|
+
function findKitCommandsDir(start) {
|
|
95081
|
+
let dir = start;
|
|
95082
|
+
for (let i2 = 0; i2 < 6; i2++) {
|
|
95083
|
+
const candidate = path28.join(
|
|
95084
|
+
dir,
|
|
95085
|
+
"templates",
|
|
95086
|
+
"project",
|
|
95087
|
+
"common",
|
|
95088
|
+
".claude",
|
|
95089
|
+
"commands"
|
|
94997
95090
|
);
|
|
95091
|
+
if (fs30.existsSync(candidate)) return candidate;
|
|
95092
|
+
const parent = path28.dirname(dir);
|
|
95093
|
+
if (parent === dir) break;
|
|
95094
|
+
dir = parent;
|
|
94998
95095
|
}
|
|
94999
|
-
|
|
95000
|
-
|
|
95001
|
-
|
|
95002
|
-
|
|
95003
|
-
|
|
95004
|
-
|
|
95005
|
-
|
|
95006
|
-
|
|
95007
|
-
|
|
95008
|
-
|
|
95009
|
-
|
|
95096
|
+
throw new Error(
|
|
95097
|
+
`Could not locate templates/project/common/.claude/commands/ relative to ${start}. Pass explicit kitDir.`
|
|
95098
|
+
);
|
|
95099
|
+
}
|
|
95100
|
+
function parsePinnedVersion(content) {
|
|
95101
|
+
const m2 = content.match(/^\s*[*_>`\s]*pinned\s+to\s*:\s*[`*_]*([^\s`*_]+)[`*_]*\s*$/im);
|
|
95102
|
+
return m2 ? m2[1] : void 0;
|
|
95103
|
+
}
|
|
95104
|
+
function detectCommandDrift(args) {
|
|
95105
|
+
const projectCommandsDir = path28.join(args.projectDir, ".claude", "commands");
|
|
95106
|
+
const here = path28.dirname(new URL(importMetaUrl).pathname);
|
|
95107
|
+
const kitCommandsDir = args.kitDir ? path28.join(args.kitDir, "templates", "project", "common", ".claude", "commands") : findKitCommandsDir(here);
|
|
95108
|
+
const kitVersion = readKitVersionFromCommandsDir(kitCommandsDir);
|
|
95109
|
+
const templateFiles = fs30.existsSync(kitCommandsDir) ? fs30.readdirSync(kitCommandsDir).filter((f3) => f3.endsWith(".md") && !COMMAND_HOOK_FILE_PATTERN.test(f3)) : [];
|
|
95110
|
+
const projectFiles = fs30.existsSync(projectCommandsDir) ? fs30.readdirSync(projectCommandsDir).filter((f3) => f3.endsWith(".md") && !COMMAND_HOOK_FILE_PATTERN.test(f3)) : [];
|
|
95111
|
+
const seen = /* @__PURE__ */ new Set();
|
|
95112
|
+
const files = [];
|
|
95113
|
+
for (const name of templateFiles) {
|
|
95114
|
+
seen.add(name);
|
|
95115
|
+
const projectPath2 = path28.join(projectCommandsDir, name);
|
|
95116
|
+
const templatePath = path28.join(kitCommandsDir, name);
|
|
95117
|
+
const templateRaw = fs30.readFileSync(templatePath, "utf8");
|
|
95118
|
+
if (!fs30.existsSync(projectPath2)) {
|
|
95119
|
+
files.push({ name, status: "missing", kit_version: kitVersion });
|
|
95120
|
+
continue;
|
|
95121
|
+
}
|
|
95122
|
+
const projectContent = fs30.readFileSync(projectPath2, "utf8");
|
|
95123
|
+
const pinned = parsePinnedVersion(projectContent);
|
|
95124
|
+
const versionForCompare = pinned ?? kitVersion;
|
|
95125
|
+
const templateContent = applyCommandPlaceholders(templateRaw, versionForCompare);
|
|
95126
|
+
if (projectContent === templateContent) {
|
|
95127
|
+
files.push({
|
|
95128
|
+
name,
|
|
95129
|
+
status: "unchanged",
|
|
95130
|
+
pinned_version: pinned,
|
|
95131
|
+
kit_version: kitVersion
|
|
95132
|
+
});
|
|
95133
|
+
} else {
|
|
95134
|
+
files.push({
|
|
95135
|
+
name,
|
|
95136
|
+
status: "drifted",
|
|
95137
|
+
pinned_version: pinned,
|
|
95138
|
+
kit_version: kitVersion,
|
|
95139
|
+
diff: unifiedDiff(name, projectContent, templateContent)
|
|
95140
|
+
});
|
|
95010
95141
|
}
|
|
95011
|
-
authVerified = true;
|
|
95012
95142
|
}
|
|
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
|
-
);
|
|
95143
|
+
for (const name of projectFiles) {
|
|
95144
|
+
if (seen.has(name)) continue;
|
|
95145
|
+
files.push({ name, status: "extra", kit_version: kitVersion });
|
|
95026
95146
|
}
|
|
95027
|
-
const
|
|
95028
|
-
|
|
95029
|
-
|
|
95030
|
-
|
|
95031
|
-
|
|
95032
|
-
|
|
95147
|
+
const order = {
|
|
95148
|
+
drifted: 0,
|
|
95149
|
+
missing: 1,
|
|
95150
|
+
extra: 2,
|
|
95151
|
+
unchanged: 3
|
|
95152
|
+
};
|
|
95153
|
+
files.sort((a, b) => order[a.status] - order[b.status] || a.name.localeCompare(b.name));
|
|
95154
|
+
const hasDrift = files.some((f3) => f3.status === "drifted" || f3.status === "missing");
|
|
95155
|
+
return { overall: hasDrift ? "drift" : "ok", files };
|
|
95156
|
+
}
|
|
95157
|
+
function readKitVersionFromCommandsDir(kitCommandsDir) {
|
|
95158
|
+
let dir = kitCommandsDir;
|
|
95159
|
+
for (let i2 = 0; i2 < 5; i2++) {
|
|
95160
|
+
dir = path28.dirname(dir);
|
|
95161
|
+
}
|
|
95162
|
+
try {
|
|
95163
|
+
const raw = fs30.readFileSync(path28.join(dir, "package.json"), "utf-8");
|
|
95164
|
+
const pkg = JSON.parse(raw);
|
|
95165
|
+
return typeof pkg.version === "string" ? pkg.version : "unknown";
|
|
95166
|
+
} catch {
|
|
95167
|
+
return "unknown";
|
|
95168
|
+
}
|
|
95169
|
+
}
|
|
95170
|
+
function detectScaffoldedDrift(args) {
|
|
95171
|
+
const workflows = detectWorkflowDrift(args);
|
|
95172
|
+
const commands = detectCommandDrift(args);
|
|
95173
|
+
return {
|
|
95174
|
+
overall: workflows.overall === "drift" || commands.overall === "drift" ? "drift" : "ok",
|
|
95175
|
+
workflows,
|
|
95176
|
+
commands
|
|
95177
|
+
};
|
|
95178
|
+
}
|
|
95179
|
+
function updateWorkflows(args) {
|
|
95180
|
+
const projectWorkflowsDir = path28.join(
|
|
95181
|
+
args.projectDir,
|
|
95182
|
+
".github",
|
|
95183
|
+
"workflows"
|
|
95184
|
+
);
|
|
95185
|
+
const here = path28.dirname(new URL(importMetaUrl).pathname);
|
|
95186
|
+
const kitWorkflowsDir = args.kitDir ? path28.join(
|
|
95187
|
+
args.kitDir,
|
|
95188
|
+
"templates",
|
|
95189
|
+
"project",
|
|
95190
|
+
"common",
|
|
95191
|
+
".github",
|
|
95192
|
+
"workflows"
|
|
95193
|
+
) : findKitTemplatesDir(here);
|
|
95194
|
+
const substitute = args.substitute !== false;
|
|
95195
|
+
const dryRun = args.dryRun === true;
|
|
95196
|
+
const pruneExtras = args.pruneExtras === true;
|
|
95197
|
+
const templateFiles = fs30.existsSync(kitWorkflowsDir) ? fs30.readdirSync(kitWorkflowsDir).filter((f3) => f3.endsWith(".yml")) : [];
|
|
95198
|
+
const projectFiles = fs30.existsSync(projectWorkflowsDir) ? fs30.readdirSync(projectWorkflowsDir).filter((f3) => f3.endsWith(".yml")) : [];
|
|
95199
|
+
if (!dryRun && templateFiles.length > 0 && !fs30.existsSync(projectWorkflowsDir)) {
|
|
95200
|
+
fs30.mkdirSync(projectWorkflowsDir, { recursive: true });
|
|
95201
|
+
}
|
|
95202
|
+
const version = substitute ? readKitVersion(kitWorkflowsDir) : "";
|
|
95203
|
+
const seen = /* @__PURE__ */ new Set();
|
|
95204
|
+
const files = [];
|
|
95205
|
+
for (const name of templateFiles) {
|
|
95206
|
+
seen.add(name);
|
|
95207
|
+
const projectPath2 = path28.join(projectWorkflowsDir, name);
|
|
95208
|
+
const templatePath = path28.join(kitWorkflowsDir, name);
|
|
95209
|
+
const templateRaw = fs30.readFileSync(templatePath, "utf-8");
|
|
95210
|
+
const desired = substitute ? applyPlaceholders(templateRaw, version) : templateRaw;
|
|
95211
|
+
const existed = fs30.existsSync(projectPath2);
|
|
95212
|
+
const current = existed ? fs30.readFileSync(projectPath2, "utf-8") : "";
|
|
95213
|
+
let outcome;
|
|
95214
|
+
if (!existed) {
|
|
95215
|
+
outcome = "added";
|
|
95216
|
+
} else if (current === desired) {
|
|
95217
|
+
outcome = "unchanged";
|
|
95218
|
+
} else {
|
|
95219
|
+
outcome = "updated";
|
|
95220
|
+
}
|
|
95221
|
+
if (!dryRun && outcome !== "unchanged") {
|
|
95222
|
+
fs30.writeFileSync(projectPath2, desired);
|
|
95223
|
+
}
|
|
95224
|
+
files.push({ name, outcome });
|
|
95225
|
+
}
|
|
95226
|
+
if (pruneExtras) {
|
|
95227
|
+
for (const name of projectFiles) {
|
|
95228
|
+
if (seen.has(name)) continue;
|
|
95229
|
+
const projectPath2 = path28.join(projectWorkflowsDir, name);
|
|
95230
|
+
if (!dryRun) {
|
|
95231
|
+
fs30.unlinkSync(projectPath2);
|
|
95232
|
+
}
|
|
95233
|
+
files.push({ name, outcome: "removed" });
|
|
95234
|
+
}
|
|
95235
|
+
}
|
|
95236
|
+
const order = {
|
|
95237
|
+
added: 0,
|
|
95238
|
+
updated: 1,
|
|
95239
|
+
removed: 2,
|
|
95240
|
+
unchanged: 3
|
|
95241
|
+
};
|
|
95242
|
+
files.sort((a, b) => order[a.outcome] - order[b.outcome] || a.name.localeCompare(b.name));
|
|
95243
|
+
const changed = files.some((f3) => f3.outcome !== "unchanged");
|
|
95244
|
+
return { files, changed };
|
|
95245
|
+
}
|
|
95246
|
+
|
|
95247
|
+
// scripts/lakebase/scm-merge.ts
|
|
95248
|
+
async function reconcileTierToOrigin(args) {
|
|
95249
|
+
const { cwd, tier } = args;
|
|
95250
|
+
try {
|
|
95251
|
+
const head = await getCurrentBranch({ cwd });
|
|
95252
|
+
if (head === tier) {
|
|
95253
|
+
await exec2(`git pull --ff-only`, { cwd, timeout: 3e4 });
|
|
95254
|
+
} else {
|
|
95255
|
+
await exec2(`git fetch origin ${shellEscape2(`${tier}:${tier}`)}`, { cwd, timeout: 3e4 });
|
|
95256
|
+
}
|
|
95257
|
+
return null;
|
|
95258
|
+
} catch (err) {
|
|
95259
|
+
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).`;
|
|
95260
|
+
}
|
|
95261
|
+
}
|
|
95262
|
+
var ScmMergeError = class extends Error {
|
|
95263
|
+
constructor(message, code) {
|
|
95264
|
+
super(message);
|
|
95265
|
+
this.code = code;
|
|
95266
|
+
this.name = "ScmMergeError";
|
|
95267
|
+
}
|
|
95268
|
+
code;
|
|
95269
|
+
};
|
|
95270
|
+
var DEFAULT_MIGRATE_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
95271
|
+
var DEFAULT_MIGRATE_POLL_MS = 30 * 1e3;
|
|
95272
|
+
function defaultMigratePredicate(run, mergedAt) {
|
|
95273
|
+
if (!run.createdAt) return false;
|
|
95274
|
+
const created = Date.parse(run.createdAt);
|
|
95275
|
+
if (!Number.isFinite(created)) return false;
|
|
95276
|
+
if (run.event && run.event !== "push") return false;
|
|
95277
|
+
return created >= mergedAt.getTime() - 5e3;
|
|
95278
|
+
}
|
|
95279
|
+
function shaMigratePredicate(mergeCommitSha) {
|
|
95280
|
+
return (run) => {
|
|
95281
|
+
if (run.event && run.event !== "push") return false;
|
|
95282
|
+
return !!run.headSha && run.headSha === mergeCommitSha;
|
|
95283
|
+
};
|
|
95284
|
+
}
|
|
95285
|
+
async function defaultRefreshPromoteWorkflows(projectDir) {
|
|
95286
|
+
const drift = detectWorkflowDrift({ projectDir });
|
|
95287
|
+
const touched = drift.files.filter((f3) => f3.status === "drifted").map((f3) => f3.name);
|
|
95288
|
+
if (touched.length === 0) return { refreshed: false };
|
|
95289
|
+
const result = updateWorkflows({ projectDir });
|
|
95290
|
+
if (!result.changed) return { refreshed: false };
|
|
95291
|
+
await exec2(`git add ${shellEscape2(".github/workflows")}`, { cwd: projectDir, timeout: 1e4 });
|
|
95292
|
+
const staged = await exec2("git diff --cached --name-only -- .github/workflows", {
|
|
95293
|
+
cwd: projectDir,
|
|
95294
|
+
timeout: 1e4
|
|
95295
|
+
});
|
|
95296
|
+
if (!staged.trim()) return { refreshed: false };
|
|
95297
|
+
await exec2(
|
|
95298
|
+
`git commit -m ${shellEscape2(
|
|
95299
|
+
"chore(ci): refresh scaffolded workflows to pinned substrate (promote uses apply-tier)"
|
|
95300
|
+
)} -- .github/workflows`,
|
|
95301
|
+
{ cwd: projectDir, timeout: 15e3 }
|
|
95302
|
+
);
|
|
95303
|
+
await pushCurrentBranchForPr({ cwd: projectDir });
|
|
95304
|
+
return { refreshed: true, detail: `refreshed + pushed ${touched.join(", ") || "workflow(s)"} onto the PR head` };
|
|
95305
|
+
}
|
|
95306
|
+
async function mergeFeature(args) {
|
|
95307
|
+
const current = readWorkflowState(args.projectDir);
|
|
95308
|
+
if (!current) {
|
|
95309
|
+
throw new ScmMergeError(
|
|
95310
|
+
"No SCM workflow state. wait-ci first.",
|
|
95311
|
+
"no-state-file"
|
|
95312
|
+
);
|
|
95313
|
+
}
|
|
95314
|
+
if (current.state !== "ci-green") {
|
|
95315
|
+
throw new ScmMergeError(
|
|
95316
|
+
`merge refuses state "${current.state}". Allowed predecessor: ci-green.`,
|
|
95317
|
+
"bad-precondition"
|
|
95318
|
+
);
|
|
95319
|
+
}
|
|
95320
|
+
if (!current.pr_url) {
|
|
95321
|
+
throw new ScmMergeError(
|
|
95322
|
+
"ci-green row is missing pr_url; cannot resolve the PR to merge.",
|
|
95323
|
+
"no-pr-url"
|
|
95324
|
+
);
|
|
95325
|
+
}
|
|
95326
|
+
if (!current.branch || !current.parent_branch) {
|
|
95327
|
+
throw new ScmMergeError(
|
|
95328
|
+
"ci-green row missing branch / parent_branch; refusing to merge.",
|
|
95329
|
+
"bad-precondition"
|
|
95330
|
+
);
|
|
95331
|
+
}
|
|
95332
|
+
const ownerRepo = await getOwnerRepo(args.projectDir);
|
|
95333
|
+
if (!ownerRepo) {
|
|
95334
|
+
throw new ScmMergeError(
|
|
95335
|
+
"No GitHub remote found at origin.",
|
|
95336
|
+
"no-github-remote"
|
|
95337
|
+
);
|
|
95338
|
+
}
|
|
95339
|
+
const pullNumber = extractPullNumber(current.pr_url);
|
|
95340
|
+
if (!pullNumber) {
|
|
95341
|
+
throw new ScmMergeError(
|
|
95342
|
+
`Could not extract PR number from URL: ${current.pr_url}`,
|
|
95343
|
+
"bad-pr-url"
|
|
95344
|
+
);
|
|
95345
|
+
}
|
|
95346
|
+
const instance = args.instance ?? current.project_id;
|
|
95347
|
+
const gitBase = await resolveGitBase(current.parent_branch, args.projectDir);
|
|
95348
|
+
const wantMigrate = args.waitMigrate !== false;
|
|
95349
|
+
let authVerified = false;
|
|
95350
|
+
if (wantMigrate && args.verifyMigrateAuth) {
|
|
95351
|
+
const probe = await args.verifyMigrateAuth();
|
|
95352
|
+
if (!probe.ok) {
|
|
95353
|
+
throw new ScmMergeError(
|
|
95354
|
+
`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.`,
|
|
95355
|
+
"migrate-auth"
|
|
95356
|
+
);
|
|
95357
|
+
}
|
|
95358
|
+
authVerified = true;
|
|
95359
|
+
}
|
|
95360
|
+
const preflightNotes = [];
|
|
95361
|
+
{
|
|
95362
|
+
const refresh = args.refreshWorkflows ?? (() => defaultRefreshPromoteWorkflows(args.projectDir));
|
|
95363
|
+
try {
|
|
95364
|
+
const r2 = await refresh();
|
|
95365
|
+
if (r2.refreshed) {
|
|
95366
|
+
preflightNotes.push(`Refreshed stale CI workflows before merge${r2.detail ? ` (${r2.detail})` : ""}.`);
|
|
95367
|
+
}
|
|
95368
|
+
} catch (err) {
|
|
95369
|
+
preflightNotes.push(
|
|
95370
|
+
`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').`
|
|
95371
|
+
);
|
|
95372
|
+
}
|
|
95373
|
+
}
|
|
95374
|
+
let paired;
|
|
95375
|
+
try {
|
|
95376
|
+
paired = await mergePairedPullRequest({
|
|
95377
|
+
ownerRepo,
|
|
95378
|
+
pullNumber,
|
|
95379
|
+
lakebaseInstance: instance,
|
|
95380
|
+
method: args.method ?? "squash"
|
|
95381
|
+
});
|
|
95382
|
+
} catch (err) {
|
|
95383
|
+
throw new ScmMergeError(
|
|
95384
|
+
`mergePairedPullRequest failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
95385
|
+
"merge-failed"
|
|
95386
|
+
);
|
|
95387
|
+
}
|
|
95388
|
+
const warnings = [...preflightNotes, ...paired.warnings];
|
|
95389
|
+
let localBranchDeleted = false;
|
|
95390
|
+
let headAfter = current.branch;
|
|
95391
|
+
if (!args.skipLocalCleanup) {
|
|
95392
|
+
const switchTo = args.switchTo ?? gitBase;
|
|
95393
|
+
const head = await getCurrentBranch({ cwd: args.projectDir });
|
|
95033
95394
|
if (head === current.branch) {
|
|
95034
95395
|
try {
|
|
95035
95396
|
await exec2(`git checkout ${shellEscape2(switchTo)}`, {
|
|
@@ -95221,7 +95582,7 @@ init_cjs_shims();
|
|
|
95221
95582
|
|
|
95222
95583
|
// scripts/git/branches.ts
|
|
95223
95584
|
init_cjs_shims();
|
|
95224
|
-
async function
|
|
95585
|
+
async function currentBranchName2(cwd) {
|
|
95225
95586
|
try {
|
|
95226
95587
|
return await exec2("git rev-parse --abbrev-ref HEAD", { cwd });
|
|
95227
95588
|
} catch {
|
|
@@ -95240,7 +95601,7 @@ async function listLocalBranches(args) {
|
|
|
95240
95601
|
return [];
|
|
95241
95602
|
}
|
|
95242
95603
|
if (!raw) return [];
|
|
95243
|
-
const current = await
|
|
95604
|
+
const current = await currentBranchName2(cwd);
|
|
95244
95605
|
return raw.split("\n").filter(Boolean).map((line) => {
|
|
95245
95606
|
const [name, tracking, trackInfo] = line.split("|");
|
|
95246
95607
|
let ahead = 0;
|
|
@@ -95405,15 +95766,15 @@ function parentForTopology(t2, defaultLeaf) {
|
|
|
95405
95766
|
|
|
95406
95767
|
// scripts/lakebase/scm-doctor.ts
|
|
95407
95768
|
init_cjs_shims();
|
|
95408
|
-
var
|
|
95409
|
-
var
|
|
95769
|
+
var fs31 = __toESM(require("fs"), 1);
|
|
95770
|
+
var path29 = __toESM(require("path"), 1);
|
|
95410
95771
|
var FEATURE_PREFIX = "feature/";
|
|
95411
95772
|
var TIER_LEAFS2 = DEFAULT_PROTECTED_TIER_NAMES;
|
|
95412
95773
|
function readEnv(projectDir) {
|
|
95413
|
-
const envPath =
|
|
95774
|
+
const envPath = path29.join(projectDir, ".env");
|
|
95414
95775
|
const out = /* @__PURE__ */ new Map();
|
|
95415
|
-
if (!
|
|
95416
|
-
const lines =
|
|
95776
|
+
if (!fs31.existsSync(envPath)) return out;
|
|
95777
|
+
const lines = fs31.readFileSync(envPath, "utf8").split("\n");
|
|
95417
95778
|
for (const line of lines) {
|
|
95418
95779
|
const m2 = line.match(/^\s*([A-Z0-9_]+)\s*=\s*(.+?)\s*$/);
|
|
95419
95780
|
if (m2) out.set(m2[1], m2[2].replace(/^["']|["']$/g, ""));
|
|
@@ -95468,9 +95829,9 @@ async function runDoctor(args, deps = {}) {
|
|
|
95468
95829
|
}
|
|
95469
95830
|
for (const wf of ["pr.yml", "merge.yml"]) {
|
|
95470
95831
|
try {
|
|
95471
|
-
const p =
|
|
95472
|
-
if (!
|
|
95473
|
-
const body =
|
|
95832
|
+
const p = path29.join(projectDir, ".github", "workflows", wf);
|
|
95833
|
+
if (!fs31.existsSync(p)) continue;
|
|
95834
|
+
const body = fs31.readFileSync(p, "utf8");
|
|
95474
95835
|
if (/lakebase-scm-utils#v\d/.test(body)) {
|
|
95475
95836
|
findings.push({
|
|
95476
95837
|
id: "ci-workflow-substrate-pin",
|
|
@@ -95685,7 +96046,7 @@ async function fixFinding(args) {
|
|
|
95685
96046
|
);
|
|
95686
96047
|
}
|
|
95687
96048
|
const sanitized = sanitizeBranchName(branch);
|
|
95688
|
-
const envFile =
|
|
96049
|
+
const envFile = path29.join(args.projectDir, ".env");
|
|
95689
96050
|
updateEnvConnection({
|
|
95690
96051
|
envPath: envFile,
|
|
95691
96052
|
projectId: readEnvVar(envFile, "LAKEBASE_PROJECT_ID") ?? "",
|
|
@@ -95790,291 +96151,6 @@ init_cjs_shims();
|
|
|
95790
96151
|
var fs32 = __toESM(require("fs"), 1);
|
|
95791
96152
|
var path30 = __toESM(require("path"), 1);
|
|
95792
96153
|
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
96154
|
function readEnvFile(projectDir) {
|
|
96079
96155
|
const envPath = path30.join(projectDir, ".env");
|
|
96080
96156
|
if (!fs32.existsSync(envPath)) return {};
|
|
@@ -96507,7 +96583,7 @@ function checkWorkflowDrift(projectDir) {
|
|
|
96507
96583
|
status: "warn",
|
|
96508
96584
|
message: `Scaffolded workflows drift from kit: ${drifted} drifted, ${missing} missing`,
|
|
96509
96585
|
detail: { files: report.files.map((f3) => ({ name: f3.name, status: f3.status })) },
|
|
96510
|
-
hint: "
|
|
96586
|
+
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
96587
|
};
|
|
96512
96588
|
} catch (err) {
|
|
96513
96589
|
return {
|
|
@@ -96816,6 +96892,7 @@ function isUcMissingError(msg) {
|
|
|
96816
96892
|
createUatPairedBranch,
|
|
96817
96893
|
cutBackup,
|
|
96818
96894
|
databricksAuthPrereqMessage,
|
|
96895
|
+
defaultRefreshPromoteWorkflows,
|
|
96819
96896
|
deleteAppEndpoint,
|
|
96820
96897
|
deleteBranch,
|
|
96821
96898
|
deleteLakebaseProject,
|