@databricks-solutions/lakebase-scm-utils 0.2.47 → 0.2.49

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.
Files changed (52) hide show
  1. package/dist/ci-secrets-CnGrdQV-.d.cts +84 -0
  2. package/dist/ci-secrets-CnGrdQV-.d.ts +84 -0
  3. package/dist/scripts/index.cjs +2230 -2052
  4. package/dist/scripts/index.d.cts +3 -2
  5. package/dist/scripts/index.d.ts +3 -2
  6. package/dist/scripts/index.js +305 -134
  7. package/dist/scripts/lakebase/ci-resolve-branch.cli.cjs +2 -2
  8. package/dist/scripts/lakebase/ci-resolve-branch.cli.js +2 -2
  9. package/dist/scripts/lakebase/collapse-heads.cli.cjs +2 -2
  10. package/dist/scripts/lakebase/collapse-heads.cli.js +2 -2
  11. package/dist/scripts/lakebase/detect-language.cli.cjs +2 -2
  12. package/dist/scripts/lakebase/detect-language.cli.js +2 -2
  13. package/dist/scripts/lakebase/doctor.cli.cjs +2 -2
  14. package/dist/scripts/lakebase/doctor.cli.js +2 -2
  15. package/dist/scripts/lakebase/get-connection.cli.cjs +2 -2
  16. package/dist/scripts/lakebase/get-connection.cli.js +2 -2
  17. package/dist/scripts/lakebase/index.cjs +2216 -2050
  18. package/dist/scripts/lakebase/index.d.cts +37 -11
  19. package/dist/scripts/lakebase/index.d.ts +37 -11
  20. package/dist/scripts/lakebase/index.js +295 -130
  21. package/dist/scripts/lakebase/infra-runner.cli.cjs +2 -2
  22. package/dist/scripts/lakebase/infra-runner.cli.js +2 -2
  23. package/dist/scripts/lakebase/new-migration.cli.cjs +2 -2
  24. package/dist/scripts/lakebase/new-migration.cli.js +2 -2
  25. package/dist/scripts/lakebase/schema-diff.cli.cjs +2 -2
  26. package/dist/scripts/lakebase/schema-diff.cli.js +2 -2
  27. package/dist/scripts/lakebase/schema-migrate.cli.cjs +2 -2
  28. package/dist/scripts/lakebase/schema-migrate.cli.js +2 -2
  29. package/dist/scripts/lakebase/scm-claim-feature.cli.cjs +2 -2
  30. package/dist/scripts/lakebase/scm-claim-feature.cli.js +2 -2
  31. package/dist/scripts/lakebase/scm-doctor.cli.cjs +2 -2
  32. package/dist/scripts/lakebase/scm-doctor.cli.js +2 -2
  33. package/dist/scripts/lakebase/scm-merge.cli.cjs +2345 -1994
  34. package/dist/scripts/lakebase/scm-merge.cli.js +425 -74
  35. package/dist/scripts/lakebase/scm-prepare-pr.cli.cjs +479 -86
  36. package/dist/scripts/lakebase/scm-prepare-pr.cli.js +479 -86
  37. package/dist/scripts/lakebase/scm-reconcile-tier.cli.cjs +2 -2
  38. package/dist/scripts/lakebase/scm-reconcile-tier.cli.js +2 -2
  39. package/dist/scripts/lakebase/sync-ci-secrets.cli.cjs +56 -30
  40. package/dist/scripts/lakebase/sync-ci-secrets.cli.js +56 -30
  41. package/dist/scripts/util/index.cjs +142 -25
  42. package/dist/scripts/util/index.d.cts +2 -27
  43. package/dist/scripts/util/index.d.ts +2 -27
  44. package/dist/scripts/util/index.js +136 -25
  45. package/package.json +1 -1
  46. package/scripts/lakebase/create-project.ts +2 -2
  47. package/scripts/lakebase/scm-merge.ts +112 -0
  48. package/scripts/lakebase/scm-prepare-pr.ts +10 -1
  49. package/scripts/lakebase/sync-ci-secrets.cli.ts +2 -2
  50. package/scripts/util/ci-secrets.ts +212 -26
  51. package/templates/project/common/.gitignore.base +4 -6
  52. package/templates/project/common/scripts/create-token-and-sync-secrets.sh +9 -86
@@ -2581,7 +2581,7 @@ paginateRest.VERSION = VERSION5;
2581
2581
 
2582
2582
  // node_modules/@octokit/plugin-paginate-graphql/dist-bundle/index.js
2583
2583
  init_esm_shims();
2584
- var generateMessage = (path33, cursorValue) => `The cursor at "${path33.join(
2584
+ var generateMessage = (path34, cursorValue) => `The cursor at "${path34.join(
2585
2585
  ","
2586
2586
  )}" did not change its value "${cursorValue}" after a page transition. Please make sure your that your query is set up correctly.`;
2587
2587
  var MissingCursorChange = class extends Error {
@@ -2622,9 +2622,9 @@ function findPaginatedResourcePath(responseData) {
2622
2622
  }
2623
2623
  return paginatedResourcePath;
2624
2624
  }
2625
- var deepFindPathToProperty = (object, searchProp, path33 = []) => {
2625
+ var deepFindPathToProperty = (object, searchProp, path34 = []) => {
2626
2626
  for (const key of Object.keys(object)) {
2627
- const currentPath = [...path33, key];
2627
+ const currentPath = [...path34, key];
2628
2628
  const currentValue = object[key];
2629
2629
  if (isObject(currentValue)) {
2630
2630
  if (currentValue.hasOwnProperty(searchProp)) {
@@ -2642,12 +2642,12 @@ var deepFindPathToProperty = (object, searchProp, path33 = []) => {
2642
2642
  }
2643
2643
  return [];
2644
2644
  };
2645
- var get = (object, path33) => {
2646
- return path33.reduce((current, nextProperty) => current[nextProperty], object);
2645
+ var get = (object, path34) => {
2646
+ return path34.reduce((current, nextProperty) => current[nextProperty], object);
2647
2647
  };
2648
- var set = (object, path33, mutator) => {
2649
- const lastProperty = path33[path33.length - 1];
2650
- const parentPath = [...path33].slice(0, -1);
2648
+ var set = (object, path34, mutator) => {
2649
+ const lastProperty = path34[path34.length - 1];
2650
+ const parentPath = [...path34].slice(0, -1);
2651
2651
  const parent = get(object, parentPath);
2652
2652
  if (typeof mutator === "function") {
2653
2653
  parent[lastProperty] = mutator(parent[lastProperty]);
@@ -2699,22 +2699,22 @@ var mergeResponses = (response1, response2) => {
2699
2699
  if (Object.keys(response1).length === 0) {
2700
2700
  return Object.assign(response1, response2);
2701
2701
  }
2702
- const path33 = findPaginatedResourcePath(response1);
2703
- const nodesPath = [...path33, "nodes"];
2702
+ const path34 = findPaginatedResourcePath(response1);
2703
+ const nodesPath = [...path34, "nodes"];
2704
2704
  const newNodes = get(response2, nodesPath);
2705
2705
  if (newNodes) {
2706
2706
  set(response1, nodesPath, (values) => {
2707
2707
  return [...values, ...newNodes];
2708
2708
  });
2709
2709
  }
2710
- const edgesPath = [...path33, "edges"];
2710
+ const edgesPath = [...path34, "edges"];
2711
2711
  const newEdges = get(response2, edgesPath);
2712
2712
  if (newEdges) {
2713
2713
  set(response1, edgesPath, (values) => {
2714
2714
  return [...values, ...newEdges];
2715
2715
  });
2716
2716
  }
2717
- const pageInfoPath = [...path33, "pageInfo"];
2717
+ const pageInfoPath = [...path34, "pageInfo"];
2718
2718
  set(response1, pageInfoPath, get(response2, pageInfoPath));
2719
2719
  return response1;
2720
2720
  };
@@ -5135,7 +5135,7 @@ var triggers_notification_paths_default = [
5135
5135
  ];
5136
5136
  function routeMatcher(paths) {
5137
5137
  const regexes = paths.map(
5138
- (path33) => path33.split("/").map((c) => c.startsWith("{") ? "(?:.+?)" : c).join("/")
5138
+ (path34) => path34.split("/").map((c) => c.startsWith("{") ? "(?:.+?)" : c).join("/")
5139
5139
  );
5140
5140
  const regex2 = `^(?:${regexes.map((r) => `(?:${r})`).join("|")})[^/]*$`;
5141
5141
  return new RegExp(regex2, "i");
@@ -8715,9 +8715,9 @@ function asBranchUid(s) {
8715
8715
  }
8716
8716
  return s;
8717
8717
  }
8718
- function branchNameFromResourcePath(path33) {
8719
- if (!path33.includes("/branches/")) return null;
8720
- const leaf = path33.split("/branches/").pop();
8718
+ function branchNameFromResourcePath(path34) {
8719
+ if (!path34.includes("/branches/")) return null;
8720
+ const leaf = path34.split("/branches/").pop();
8721
8721
  if (!leaf) return null;
8722
8722
  try {
8723
8723
  return asBranchName(leaf);
@@ -9722,8 +9722,8 @@ var PKG_NAME = "@databricks-solutions/lakebase-scm-utils";
9722
9722
  var cached;
9723
9723
  function substrateSelfVersion() {
9724
9724
  if (cached !== void 0) return cached;
9725
- if ("0.2.47".length > 0) {
9726
- cached = "0.2.47";
9725
+ if ("0.2.49".length > 0) {
9726
+ cached = "0.2.49";
9727
9727
  return cached;
9728
9728
  }
9729
9729
  cached = "unknown";
@@ -11311,16 +11311,16 @@ function isAllSchemas(schema) {
11311
11311
  }
11312
11312
  function buildSchemaQuery(schema) {
11313
11313
  const cols = "c.table_schema, c.table_name, c.column_name, c.data_type";
11314
- const join35 = "FROM information_schema.columns c JOIN pg_tables t ON c.table_name = t.tablename AND c.table_schema = t.schemaname ";
11314
+ const join36 = "FROM information_schema.columns c JOIN pg_tables t ON c.table_name = t.tablename AND c.table_schema = t.schemaname ";
11315
11315
  if (isAllSchemas(schema)) {
11316
11316
  return {
11317
- text: `SELECT ${cols} ` + join35 + `WHERE ${SYSTEM_SCHEMA_FILTER} ORDER BY c.table_schema, c.table_name, c.ordinal_position`,
11317
+ text: `SELECT ${cols} ` + join36 + `WHERE ${SYSTEM_SCHEMA_FILTER} ORDER BY c.table_schema, c.table_name, c.ordinal_position`,
11318
11318
  values: []
11319
11319
  };
11320
11320
  }
11321
11321
  const one = (schema ?? "").trim() || "public";
11322
11322
  return {
11323
- text: `SELECT ${cols} ` + join35 + "WHERE c.table_schema = $1 ORDER BY c.table_name, c.ordinal_position",
11323
+ text: `SELECT ${cols} ` + join36 + "WHERE c.table_schema = $1 ORDER BY c.table_name, c.ordinal_position",
11324
11324
  values: [one]
11325
11325
  };
11326
11326
  }
@@ -11481,7 +11481,7 @@ async function withLakebaseRollback(opts, fn) {
11481
11481
  // scripts/lakebase/create-project.ts
11482
11482
  init_esm_shims();
11483
11483
  import * as fs18 from "fs";
11484
- import * as path17 from "path";
11484
+ import * as path18 from "path";
11485
11485
 
11486
11486
  // scripts/lakebase/project-verify.ts
11487
11487
  init_esm_shims();
@@ -13066,6 +13066,7 @@ async function removeRunner(args) {
13066
13066
 
13067
13067
  // scripts/util/ci-secrets.ts
13068
13068
  init_esm_shims();
13069
+ import * as path16 from "path";
13069
13070
 
13070
13071
  // scripts/git/remote.ts
13071
13072
  init_esm_shims();
@@ -13128,13 +13129,40 @@ async function deleteRemoteBranch(args) {
13128
13129
 
13129
13130
  // scripts/util/ci-secrets.ts
13130
13131
  var REQUIRED_CI_SECRETS = ["DATABRICKS_HOST", "LAKEBASE_PROJECT_ID", "DATABRICKS_TOKEN"];
13132
+ var CI_TOKEN_LIFETIME_SECONDS = 7776e3;
13133
+ var CI_TOKEN_REMINT_MARGIN_SECONDS = 86400;
13134
+ function ciTokenComment(ownerRepo) {
13135
+ const repoName = ownerRepo.includes("/") ? ownerRepo.slice(ownerRepo.lastIndexOf("/") + 1) : ownerRepo;
13136
+ return `GitHub Actions (${repoName})`;
13137
+ }
13131
13138
  async function missingCiSecrets(ownerRepo) {
13132
13139
  const present = new Set(await listSecretNames(ownerRepo));
13133
13140
  return REQUIRED_CI_SECRETS.filter((n) => !present.has(n));
13134
13141
  }
13142
+ async function mintCiToken(args) {
13143
+ try {
13144
+ const raw = await runDatabricks(
13145
+ ["tokens", "create", "--comment", args.comment, "--lifetime-seconds", String(args.lifetimeSeconds), "-o", "json"],
13146
+ { host: args.databricksHost, cwd: args.projectDir, timeout: 3e4 }
13147
+ );
13148
+ const parsed = JSON.parse(raw.slice(Math.max(0, raw.indexOf("{"))));
13149
+ const token = parsed.token_value || parsed.token || "";
13150
+ if (token) return token;
13151
+ } catch {
13152
+ }
13153
+ try {
13154
+ const raw = await runDatabricks(["auth", "token", "-o", "json"], {
13155
+ host: args.databricksHost,
13156
+ cwd: args.projectDir,
13157
+ timeout: 3e4
13158
+ });
13159
+ const parsed = JSON.parse(raw.slice(Math.max(0, raw.indexOf("{"))));
13160
+ return parsed.access_token || "";
13161
+ } catch {
13162
+ return "";
13163
+ }
13164
+ }
13135
13165
  async function syncCiSecrets(args) {
13136
- const lifetime = args.lifetimeSeconds ?? 86400;
13137
- const comment = args.comment ?? "GitHub Actions CI";
13138
13166
  const ownerRepo = args.ownerRepo ?? await getOwnerRepo(args.projectDir);
13139
13167
  if (!ownerRepo) {
13140
13168
  throw new Error("Could not resolve GitHub repository from git remote");
@@ -13145,27 +13173,104 @@ async function syncCiSecrets(args) {
13145
13173
  if (!args.lakebaseProjectId) {
13146
13174
  throw new Error("syncCiSecrets: lakebaseProjectId is required");
13147
13175
  }
13176
+ const lifetime = args.lifetimeSeconds ?? CI_TOKEN_LIFETIME_SECONDS;
13177
+ const comment = args.comment ?? ciTokenComment(ownerRepo);
13148
13178
  const secrets = {
13149
13179
  DATABRICKS_HOST: args.databricksHost,
13150
13180
  LAKEBASE_PROJECT_ID: args.lakebaseProjectId
13151
13181
  };
13182
+ const token = await mintCiToken({
13183
+ databricksHost: args.databricksHost,
13184
+ projectDir: args.projectDir,
13185
+ comment,
13186
+ lifetimeSeconds: lifetime
13187
+ });
13188
+ if (token) secrets.DATABRICKS_TOKEN = token;
13189
+ await setRepoSecrets(ownerRepo, secrets);
13190
+ }
13191
+ async function ciTokenExpiry(args) {
13192
+ const comment = ciTokenComment(args.ownerRepo);
13193
+ let raw;
13152
13194
  try {
13153
- const tokenRaw = await runDatabricks(
13154
- ["tokens", "create", "--comment", comment, "--lifetime-seconds", String(lifetime), "-o", "json"],
13155
- { host: args.databricksHost, cwd: args.projectDir, timeout: 3e4 }
13156
- );
13157
- const parsed = JSON.parse(tokenRaw);
13158
- const token = parsed.token_value || parsed.token || "";
13159
- if (token) secrets.DATABRICKS_TOKEN = token;
13195
+ raw = await runDatabricks(["tokens", "list", "-o", "json"], {
13196
+ host: args.databricksHost,
13197
+ cwd: args.projectDir,
13198
+ timeout: 3e4
13199
+ });
13160
13200
  } catch {
13201
+ return null;
13202
+ }
13203
+ let infos;
13204
+ try {
13205
+ const parsed = JSON.parse(raw.slice(Math.max(0, raw.indexOf("["))));
13206
+ infos = Array.isArray(parsed) ? parsed : [];
13207
+ } catch {
13208
+ return null;
13209
+ }
13210
+ const expiries = infos.filter((t) => t.comment === comment).map((t) => t.expiry_time === -1 ? Number.POSITIVE_INFINITY : Number(t.expiry_time)).filter((n) => Number.isFinite(n) || n === Number.POSITIVE_INFINITY);
13211
+ if (expiries.length === 0) return null;
13212
+ return Math.max(...expiries);
13213
+ }
13214
+ async function ensureCiSecretsFresh(args) {
13215
+ const now = args.now ?? Date.now;
13216
+ const marginMs = (args.marginSeconds ?? CI_TOKEN_REMINT_MARGIN_SECONDS) * 1e3;
13217
+ const ownerRepo = args.ownerRepo ?? await getOwnerRepo(args.projectDir);
13218
+ if (!ownerRepo) {
13219
+ throw new Error("Could not resolve GitHub repository from git remote");
13220
+ }
13221
+ const sync2 = () => syncCiSecrets({
13222
+ projectDir: args.projectDir,
13223
+ databricksHost: args.databricksHost,
13224
+ lakebaseProjectId: args.lakebaseProjectId,
13225
+ ownerRepo
13226
+ });
13227
+ const missing = await missingCiSecrets(ownerRepo);
13228
+ if (missing.length > 0) {
13229
+ await sync2();
13230
+ return { action: "provisioned", reason: `CI secret(s) were missing (${missing.join(", ")}); provisioned.` };
13231
+ }
13232
+ const expiry = await ciTokenExpiry({ projectDir: args.projectDir, databricksHost: args.databricksHost, ownerRepo });
13233
+ if (expiry === null) {
13234
+ await sync2();
13235
+ return { action: "reminted", reason: "no live CI token found (expired or minted under a different identity); re-minted." };
13236
+ }
13237
+ if (expiry !== Number.POSITIVE_INFINITY && expiry < now() + marginMs) {
13238
+ await sync2();
13239
+ const hrs = Math.max(0, Math.round((expiry - now()) / 36e5));
13240
+ return { action: "reminted", reason: `CI token expires in ~${hrs}h (within the re-mint margin); re-minted.` };
13241
+ }
13242
+ return { action: "ok", reason: "CI token is current." };
13243
+ }
13244
+ async function ensureCiSecretsFreshFromEnv(projectDir, opts) {
13245
+ const envPath = path16.join(projectDir, ".env");
13246
+ const databricksHost = readEnvVar(envPath, "DATABRICKS_HOST");
13247
+ const lakebaseProjectId = readEnvVar(envPath, "LAKEBASE_PROJECT_ID");
13248
+ if (!databricksHost || !lakebaseProjectId) {
13249
+ return {
13250
+ action: "skipped",
13251
+ reason: "CI-auth preflight skipped: .env is missing DATABRICKS_HOST / LAKEBASE_PROJECT_ID."
13252
+ };
13253
+ }
13254
+ try {
13255
+ return await ensureCiSecretsFresh({
13256
+ projectDir,
13257
+ databricksHost,
13258
+ lakebaseProjectId,
13259
+ ownerRepo: opts?.ownerRepo,
13260
+ marginSeconds: opts?.marginSeconds
13261
+ });
13262
+ } catch (err) {
13263
+ return {
13264
+ action: "failed",
13265
+ reason: `CI-auth preflight could not re-mint (${err instanceof Error ? err.message : String(err)}); if CI fails on auth, run lakebase-sync-ci-secrets.`
13266
+ };
13161
13267
  }
13162
- await setRepoSecrets(ownerRepo, secrets);
13163
13268
  }
13164
13269
 
13165
13270
  // scripts/lakebase/scm-workflow-state.ts
13166
13271
  init_esm_shims();
13167
13272
  import * as fs17 from "fs";
13168
- import * as path16 from "path";
13273
+ import * as path17 from "path";
13169
13274
  import { execFileSync as execFileSync6 } from "child_process";
13170
13275
  function isGitTracked(projectDir, rel) {
13171
13276
  try {
@@ -13184,7 +13289,7 @@ function ensureWorkflowStateUntracked(projectDir) {
13184
13289
  } catch {
13185
13290
  }
13186
13291
  try {
13187
- const gitignore = path16.join(projectDir, ".gitignore");
13292
+ const gitignore = path17.join(projectDir, ".gitignore");
13188
13293
  const existing = fs17.existsSync(gitignore) ? fs17.readFileSync(gitignore, "utf8") : "";
13189
13294
  if (!existing.split("\n").some((l) => l.trim() === rel)) {
13190
13295
  const sep4 = existing === "" || existing.endsWith("\n") ? "" : "\n";
@@ -13212,7 +13317,7 @@ var STATE_INDEX = SCM_STATES.reduce(
13212
13317
  );
13213
13318
  var STATE_FILE_REL = ".lakebase/workflow-state.json";
13214
13319
  function stateFilePath(projectDir) {
13215
- return path16.join(projectDir, STATE_FILE_REL);
13320
+ return path17.join(projectDir, STATE_FILE_REL);
13216
13321
  }
13217
13322
  function readWorkflowState(projectDir) {
13218
13323
  const p = stateFilePath(projectDir);
@@ -13252,7 +13357,7 @@ function writeWorkflowState(projectDir, state) {
13252
13357
  ${summary}`);
13253
13358
  }
13254
13359
  ensureWorkflowStateUntracked(projectDir);
13255
- const dir = path16.join(projectDir, ".lakebase");
13360
+ const dir = path17.join(projectDir, ".lakebase");
13256
13361
  fs17.mkdirSync(dir, { recursive: true });
13257
13362
  const target = stateFilePath(projectDir);
13258
13363
  const tmp = `${target}.tmp`;
@@ -13483,7 +13588,7 @@ function orderForOutput(state) {
13483
13588
  async function createProject(input, progress) {
13484
13589
  const report = progress ?? (() => {
13485
13590
  });
13486
- const projectDir = path17.join(input.parentDir, input.projectName);
13591
+ const projectDir = path18.join(input.parentDir, input.projectName);
13487
13592
  const lakebaseProjectId = input.projectName;
13488
13593
  const host = input.databricksHost.replace(/\/+$/, "");
13489
13594
  const useGithub = input.createGithubRepo !== false;
@@ -13644,8 +13749,6 @@ Last probe error:
13644
13749
  projectDir,
13645
13750
  databricksHost: host,
13646
13751
  lakebaseProjectId,
13647
- comment: "GitHub Actions CI",
13648
- lifetimeSeconds: 86400,
13649
13752
  ownerRepo: fullRepoName
13650
13753
  });
13651
13754
  const missing = await missingCiSecrets(fullRepoName);
@@ -13712,9 +13815,9 @@ Last probe error:
13712
13815
  const scmRef = envRef || (ver !== "unknown" ? `v${ver}` : "");
13713
13816
  if (scmRef) {
13714
13817
  try {
13715
- const dir = path17.join(projectDir, ".lakebase");
13818
+ const dir = path18.join(projectDir, ".lakebase");
13716
13819
  fs18.mkdirSync(dir, { recursive: true });
13717
- fs18.writeFileSync(path17.join(dir, "scm-utils-ref"), `${scmRef}
13820
+ fs18.writeFileSync(path18.join(dir, "scm-utils-ref"), `${scmRef}
13718
13821
  `, "utf8");
13719
13822
  } catch (err) {
13720
13823
  warnings.push(`Substrate ref pin failed (advisory): ${err instanceof Error ? err.message : String(err)}.`);
@@ -13725,9 +13828,9 @@ Last probe error:
13725
13828
  const kitRef = process.env.LAKEBASE_KIT_REF?.trim();
13726
13829
  if (kitRef) {
13727
13830
  try {
13728
- const dir = path17.join(projectDir, ".lakebase");
13831
+ const dir = path18.join(projectDir, ".lakebase");
13729
13832
  fs18.mkdirSync(dir, { recursive: true });
13730
- fs18.writeFileSync(path17.join(dir, "kit-ref"), `${kitRef}
13833
+ fs18.writeFileSync(path18.join(dir, "kit-ref"), `${kitRef}
13731
13834
  `, "utf8");
13732
13835
  } catch (err) {
13733
13836
  warnings.push(`Kit ref pin failed (advisory): ${err instanceof Error ? err.message : String(err)}.`);
@@ -13809,7 +13912,7 @@ Last probe error:
13809
13912
  init_esm_shims();
13810
13913
  import * as cp5 from "child_process";
13811
13914
  import * as fs19 from "fs";
13812
- import * as path18 from "path";
13915
+ import * as path19 from "path";
13813
13916
  async function adoptLakebaseProject(args) {
13814
13917
  const warnings = [];
13815
13918
  const filesWritten = [];
@@ -13818,7 +13921,7 @@ async function adoptLakebaseProject(args) {
13818
13921
  if (!fs19.existsSync(args.projectDir)) {
13819
13922
  throw new Error(`adoptLakebaseProject: project directory does not exist: ${args.projectDir}`);
13820
13923
  }
13821
- if (!fs19.existsSync(path18.join(args.projectDir, ".git"))) {
13924
+ if (!fs19.existsSync(path19.join(args.projectDir, ".git"))) {
13822
13925
  throw new Error(
13823
13926
  `adoptLakebaseProject: ${args.projectDir} is not a git repo. Run \`git init\` first, or pass an existing repo path.`
13824
13927
  );
@@ -13884,7 +13987,7 @@ async function adoptLakebaseProject(args) {
13884
13987
  };
13885
13988
  }
13886
13989
  function assertEnvCompatibility(projectDir, expectedProjectId) {
13887
- const envPath = path18.join(projectDir, ".env");
13990
+ const envPath = path19.join(projectDir, ".env");
13888
13991
  if (!fs19.existsSync(envPath)) return;
13889
13992
  const content = fs19.readFileSync(envPath, "utf8");
13890
13993
  const match = content.match(/^LAKEBASE_PROJECT_ID\s*=\s*(.+?)\s*$/m);
@@ -13900,7 +14003,7 @@ function assertAdoptionPreflight(args) {
13900
14003
  if (!fs19.existsSync(args.projectDir)) {
13901
14004
  throw new Error(`assertAdoptionPreflight: project directory does not exist: ${args.projectDir}`);
13902
14005
  }
13903
- if (!fs19.existsSync(path18.join(args.projectDir, ".git"))) {
14006
+ if (!fs19.existsSync(path19.join(args.projectDir, ".git"))) {
13904
14007
  throw new Error(
13905
14008
  `assertAdoptionPreflight: ${args.projectDir} is not a git repo.`
13906
14009
  );
@@ -13914,7 +14017,7 @@ function _testMakeBrownfieldFixture(opts) {
13914
14017
  cp5.execSync("git init --quiet", { cwd: opts.dir, stdio: "pipe" });
13915
14018
  if (opts.packageJson) {
13916
14019
  fs19.writeFileSync(
13917
- path18.join(opts.dir, "package.json"),
14020
+ path19.join(opts.dir, "package.json"),
13918
14021
  JSON.stringify(opts.packageJson, null, 2) + "\n"
13919
14022
  );
13920
14023
  }
@@ -13923,7 +14026,7 @@ function _testMakeBrownfieldFixture(opts) {
13923
14026
  // scripts/lakebase/infra-runner.ts
13924
14027
  init_esm_shims();
13925
14028
  import * as fs27 from "fs";
13926
- import * as path27 from "path";
14029
+ import * as path28 from "path";
13927
14030
 
13928
14031
  // scripts/lakebase/schema-diff.ts
13929
14032
  init_esm_shims();
@@ -14120,12 +14223,12 @@ function dbcli6(args) {
14120
14223
  // scripts/lakebase/schema-migrate.ts
14121
14224
  init_esm_shims();
14122
14225
  import * as fs26 from "fs";
14123
- import * as path26 from "path";
14226
+ import * as path27 from "path";
14124
14227
 
14125
14228
  // scripts/lakebase/migration-layout.ts
14126
14229
  init_esm_shims();
14127
14230
  import * as fs20 from "fs";
14128
- import * as path19 from "path";
14231
+ import * as path20 from "path";
14129
14232
  var MIGRATION_LANGUAGES = [
14130
14233
  "java",
14131
14234
  "kotlin",
@@ -14141,13 +14244,13 @@ var MIGRATION_DEFAULTS = {
14141
14244
  unknown: { path: "src/main/resources/db/migration", pattern: /^V\d+.*\.sql$/i, glob: "*.sql" }
14142
14245
  };
14143
14246
  function detectLanguageAt(dir) {
14144
- if (fs20.existsSync(path19.join(dir, "pom.xml"))) {
14145
- const kotlinDir = path19.join(dir, "src", "main", "kotlin");
14247
+ if (fs20.existsSync(path20.join(dir, "pom.xml"))) {
14248
+ const kotlinDir = path20.join(dir, "src", "main", "kotlin");
14146
14249
  if (fs20.existsSync(kotlinDir)) {
14147
14250
  return "kotlin";
14148
14251
  }
14149
14252
  try {
14150
- const pom = fs20.readFileSync(path19.join(dir, "pom.xml"), "utf-8");
14253
+ const pom = fs20.readFileSync(path20.join(dir, "pom.xml"), "utf-8");
14151
14254
  if (pom.includes("kotlin-maven-plugin")) {
14152
14255
  return "kotlin";
14153
14256
  }
@@ -14155,10 +14258,10 @@ function detectLanguageAt(dir) {
14155
14258
  }
14156
14259
  return "java";
14157
14260
  }
14158
- if (fs20.existsSync(path19.join(dir, "pyproject.toml")) || fs20.existsSync(path19.join(dir, "requirements.txt")) || fs20.existsSync(path19.join(dir, "alembic.ini"))) {
14261
+ if (fs20.existsSync(path20.join(dir, "pyproject.toml")) || fs20.existsSync(path20.join(dir, "requirements.txt")) || fs20.existsSync(path20.join(dir, "alembic.ini"))) {
14159
14262
  return "python";
14160
14263
  }
14161
- if (fs20.existsSync(path19.join(dir, "package.json"))) {
14264
+ if (fs20.existsSync(path20.join(dir, "package.json"))) {
14162
14265
  return "nodejs";
14163
14266
  }
14164
14267
  return "unknown";
@@ -14179,14 +14282,14 @@ function resolveMigrationLanguage(projectDir, configuredMigrationPath, override)
14179
14282
  if (!rel) {
14180
14283
  return "unknown";
14181
14284
  }
14182
- const rootResolved = path19.resolve(projectDir);
14183
- let dir = path19.resolve(projectDir, rel);
14184
- while (dir === rootResolved || dir.startsWith(rootResolved + path19.sep)) {
14285
+ const rootResolved = path20.resolve(projectDir);
14286
+ let dir = path20.resolve(projectDir, rel);
14287
+ while (dir === rootResolved || dir.startsWith(rootResolved + path20.sep)) {
14185
14288
  const lang = detectLanguageAt(dir);
14186
14289
  if (lang !== "unknown") {
14187
14290
  return lang;
14188
14291
  }
14189
- const parent = path19.dirname(dir);
14292
+ const parent = path20.dirname(dir);
14190
14293
  if (parent === dir) {
14191
14294
  break;
14192
14295
  }
@@ -14220,17 +14323,17 @@ function resolveMigrationLayout(args) {
14220
14323
  // scripts/lakebase/adapters/alembic-adapter.ts
14221
14324
  init_esm_shims();
14222
14325
  import * as fs22 from "fs";
14223
- import * as path21 from "path";
14326
+ import * as path22 from "path";
14224
14327
 
14225
14328
  // scripts/lakebase/schema-migrate-runners/alembic.ts
14226
14329
  init_esm_shims();
14227
14330
  import { spawn as spawn5 } from "child_process";
14228
14331
  import * as fs21 from "fs";
14229
- import * as path20 from "path";
14332
+ import * as path21 from "path";
14230
14333
  function resolveAlembicBin(projectDir) {
14231
14334
  const candidates = [
14232
- path20.join(projectDir, ".venv", "bin", "alembic"),
14233
- path20.join(projectDir, "venv", "bin", "alembic")
14335
+ path21.join(projectDir, ".venv", "bin", "alembic"),
14336
+ path21.join(projectDir, "venv", "bin", "alembic")
14234
14337
  ];
14235
14338
  for (const candidate of candidates) {
14236
14339
  try {
@@ -14244,7 +14347,7 @@ function spawnAlembic(projectDir, args, dsn) {
14244
14347
  return new Promise((resolve2, reject) => {
14245
14348
  const bin = resolveAlembicBin(projectDir);
14246
14349
  const env = { ...process.env };
14247
- env.PYTHONPATH = [projectDir, process.env.PYTHONPATH].filter(Boolean).join(path20.delimiter);
14350
+ env.PYTHONPATH = [projectDir, process.env.PYTHONPATH].filter(Boolean).join(path21.delimiter);
14248
14351
  if (dsn) env.DATABASE_URL = dsn;
14249
14352
  const child = spawn5(bin, args, {
14250
14353
  cwd: projectDir,
@@ -14292,10 +14395,10 @@ async function createAlembicRevision(opts) {
14292
14395
  const m = stdout.match(/Generating\s+(\S+\.py)/);
14293
14396
  if (m) return m[1].trim();
14294
14397
  for (const rel of ["migrations/versions", "alembic/versions"]) {
14295
- const dir = path20.join(opts.projectDir, rel);
14398
+ const dir = path21.join(opts.projectDir, rel);
14296
14399
  if (!fs21.existsSync(dir)) continue;
14297
14400
  const hit = fs21.readdirSync(dir).find((f) => f.startsWith(`${opts.revId}_`) && f.endsWith(".py"));
14298
- if (hit) return path20.join(dir, hit);
14401
+ if (hit) return path21.join(dir, hit);
14299
14402
  }
14300
14403
  throw new SchemaMigrationError(
14301
14404
  `alembic revision succeeded but the created file could not be located.
@@ -14430,8 +14533,8 @@ async function buildDsn2(args) {
14430
14533
  }
14431
14534
  function findVersionsDir(projectDir) {
14432
14535
  const candidates = [
14433
- path21.join(projectDir, "migrations", "versions"),
14434
- path21.join(projectDir, "alembic", "versions")
14536
+ path22.join(projectDir, "migrations", "versions"),
14537
+ path22.join(projectDir, "alembic", "versions")
14435
14538
  ];
14436
14539
  return candidates.find((p) => fs22.existsSync(p));
14437
14540
  }
@@ -14463,9 +14566,9 @@ var AlembicAdapter = {
14463
14566
  * here. Callers can still force-select via project.yaml#migration_tool.
14464
14567
  */
14465
14568
  detect(projectDir) {
14466
- if (fs22.existsSync(path21.join(projectDir, "alembic.ini"))) return true;
14467
- if (fs22.existsSync(path21.join(projectDir, "migrations", "env.py"))) return true;
14468
- if (fs22.existsSync(path21.join(projectDir, "alembic", "env.py"))) return true;
14569
+ if (fs22.existsSync(path22.join(projectDir, "alembic.ini"))) return true;
14570
+ if (fs22.existsSync(path22.join(projectDir, "migrations", "env.py"))) return true;
14571
+ if (fs22.existsSync(path22.join(projectDir, "alembic", "env.py"))) return true;
14469
14572
  return false;
14470
14573
  },
14471
14574
  async apply(args) {
@@ -14571,7 +14674,7 @@ var AlembicAdapter = {
14571
14674
  autogenerate: !!args.autogenerate,
14572
14675
  dsn
14573
14676
  });
14574
- return { status: "ok", version: revId, filename: path21.basename(created), path: created };
14677
+ return { status: "ok", version: revId, filename: path22.basename(created), path: created };
14575
14678
  } catch (err) {
14576
14679
  return {
14577
14680
  status: "error",
@@ -14588,7 +14691,7 @@ var AlembicAdapter = {
14588
14691
  if (heads.length <= 1) return { status: "noop", headsBefore: heads };
14589
14692
  if (args.dryRun) return { status: "ok", headsBefore: heads };
14590
14693
  const created = await mergeAlembicHeads(args.projectDir, args.message ?? "merge heads");
14591
- const mergeRevision = path21.basename(created).replace(/\.py$/, "").split("_")[0];
14694
+ const mergeRevision = path22.basename(created).replace(/\.py$/, "").split("_")[0];
14592
14695
  return { status: "ok", headsBefore: heads, mergeRevision, path: created };
14593
14696
  } catch (err) {
14594
14697
  return {
@@ -14604,12 +14707,12 @@ registerSchemaMigrationAdapter(AlembicAdapter);
14604
14707
  // scripts/lakebase/adapters/flyway-adapter.ts
14605
14708
  init_esm_shims();
14606
14709
  import * as fs23 from "fs";
14607
- import * as path23 from "path";
14710
+ import * as path24 from "path";
14608
14711
 
14609
14712
  // scripts/lakebase/schema-migrate-runners/flyway.ts
14610
14713
  init_esm_shims();
14611
14714
  import { spawn as spawn6 } from "child_process";
14612
- import * as path22 from "path";
14715
+ import * as path23 from "path";
14613
14716
  function dsnToFlywayEnv(dsn) {
14614
14717
  const u = new URL(dsn);
14615
14718
  const user = decodeURIComponent(u.username);
@@ -14619,7 +14722,7 @@ function dsnToFlywayEnv(dsn) {
14619
14722
  return { url, user, password };
14620
14723
  }
14621
14724
  function migrationsLocation(projectDir) {
14622
- return `filesystem:${path22.join(projectDir, "src", "main", "resources", "db", "migration")}`;
14725
+ return `filesystem:${path23.join(projectDir, "src", "main", "resources", "db", "migration")}`;
14623
14726
  }
14624
14727
  function runFlyway(ctx, args) {
14625
14728
  const { url, user, password } = dsnToFlywayEnv(ctx.dsn);
@@ -14720,7 +14823,7 @@ async function statusFlyway(ctx) {
14720
14823
  if (state === "SUCCESS" || state === "BASELINE") {
14721
14824
  current = m.version;
14722
14825
  } else if (state === "PENDING") {
14723
- const filename = m.filepath ? path22.basename(m.filepath) : `V${m.version}__migration.sql`;
14826
+ const filename = m.filepath ? path23.basename(m.filepath) : `V${m.version}__migration.sql`;
14724
14827
  pending.push({
14725
14828
  version: m.version,
14726
14829
  filename,
@@ -14743,7 +14846,7 @@ async function buildDsn3(args) {
14743
14846
  return result.url;
14744
14847
  }
14745
14848
  function listFlywayFiles(projectDir) {
14746
- const dir = path23.join(projectDir, "src", "main", "resources", "db", "migration");
14849
+ const dir = path24.join(projectDir, "src", "main", "resources", "db", "migration");
14747
14850
  if (!fs23.existsSync(dir)) return [];
14748
14851
  const files = fs23.readdirSync(dir).filter((f) => /^V\d+(\.\d+)*__.+\.sql$/.test(f));
14749
14852
  return files.map((filename) => {
@@ -14768,7 +14871,7 @@ var FlywayAdapter = {
14768
14871
  id: "flyway",
14769
14872
  languages: ["java", "kotlin"],
14770
14873
  detect(projectDir) {
14771
- return fs23.existsSync(path23.join(projectDir, "pom.xml"));
14874
+ return fs23.existsSync(path24.join(projectDir, "pom.xml"));
14772
14875
  },
14773
14876
  async apply(args) {
14774
14877
  const dsn = await buildDsn3(args);
@@ -14826,12 +14929,12 @@ var FlywayAdapter = {
14826
14929
  // optional-protocol shape makes this additive.
14827
14930
  async newMigration(args) {
14828
14931
  try {
14829
- const dir = path23.join(args.projectDir, "src", "main", "resources", "db", "migration");
14932
+ const dir = path24.join(args.projectDir, "src", "main", "resources", "db", "migration");
14830
14933
  fs23.mkdirSync(dir, { recursive: true });
14831
14934
  const version = migrationTimestamp();
14832
14935
  const slug = migrationSlug2(args.slug);
14833
14936
  const filename = `V${version}__${slug}.sql`;
14834
- const full = path23.join(dir, filename);
14937
+ const full = path24.join(dir, filename);
14835
14938
  if (fs23.existsSync(full)) throw new Error(`${filename} already exists`);
14836
14939
  fs23.writeFileSync(
14837
14940
  full,
@@ -14857,17 +14960,17 @@ registerSchemaMigrationAdapter(FlywayAdapter);
14857
14960
  // scripts/lakebase/adapters/knex-adapter.ts
14858
14961
  init_esm_shims();
14859
14962
  import * as fs25 from "fs";
14860
- import * as path25 from "path";
14963
+ import * as path26 from "path";
14861
14964
 
14862
14965
  // scripts/lakebase/schema-migrate-runners/knex.ts
14863
14966
  init_esm_shims();
14864
14967
  import { spawn as spawn7 } from "child_process";
14865
14968
  import * as fs24 from "fs";
14866
- import * as path24 from "path";
14969
+ import * as path25 from "path";
14867
14970
  var KNEXFILE_VARIANTS = ["knexfile.js", "knexfile.ts", "knexfile.mjs", "knexfile.cjs"];
14868
14971
  function findKnexfile(projectDir) {
14869
14972
  for (const name of KNEXFILE_VARIANTS) {
14870
- const p = path24.join(projectDir, name);
14973
+ const p = path25.join(projectDir, name);
14871
14974
  if (fs24.existsSync(p)) return p;
14872
14975
  }
14873
14976
  return void 0;
@@ -15020,7 +15123,7 @@ async function buildDsn4(args) {
15020
15123
  }
15021
15124
  var KNEXFILE_VARIANTS2 = ["knexfile.js", "knexfile.ts", "knexfile.mjs", "knexfile.cjs"];
15022
15125
  function listKnexFiles(projectDir) {
15023
- const dir = path25.join(projectDir, "migrations");
15126
+ const dir = path26.join(projectDir, "migrations");
15024
15127
  if (!fs25.existsSync(dir)) return [];
15025
15128
  const files = fs25.readdirSync(dir).filter((f) => (f.endsWith(".js") || f.endsWith(".ts")) && !f.startsWith("."));
15026
15129
  return files.map((filename) => {
@@ -15042,7 +15145,7 @@ var KnexAdapter = {
15042
15145
  * project.yaml#migration_tool.
15043
15146
  */
15044
15147
  detect(projectDir) {
15045
- return KNEXFILE_VARIANTS2.some((name) => fs25.existsSync(path25.join(projectDir, name)));
15148
+ return KNEXFILE_VARIANTS2.some((name) => fs25.existsSync(path26.join(projectDir, name)));
15046
15149
  },
15047
15150
  async apply(args) {
15048
15151
  const dsn = await buildDsn4(args);
@@ -15115,9 +15218,9 @@ var KnexAdapter = {
15115
15218
  async newMigration(args) {
15116
15219
  try {
15117
15220
  const created = await createKnexMigration({ projectDir: args.projectDir, slug: migrationSlug2(args.slug) });
15118
- const stem = path25.basename(created).replace(/\.(js|ts)$/, "");
15221
+ const stem = path26.basename(created).replace(/\.(js|ts)$/, "");
15119
15222
  const version = stem.match(/^(\d{14})_/)?.[1] ?? stem;
15120
- return { status: "ok", version, filename: path25.basename(created), path: created };
15223
+ return { status: "ok", version, filename: path26.basename(created), path: created };
15121
15224
  } catch (err) {
15122
15225
  return {
15123
15226
  status: "error",
@@ -15174,7 +15277,7 @@ function listSchemaMigrations(args = {}) {
15174
15277
  }
15175
15278
  }
15176
15279
  function listFlywayMigrations(projectDir) {
15177
- const dir = path26.join(projectDir, "src", "main", "resources", "db", "migration");
15280
+ const dir = path27.join(projectDir, "src", "main", "resources", "db", "migration");
15178
15281
  if (!fs26.existsSync(dir)) return [];
15179
15282
  const files = fs26.readdirSync(dir).filter((f) => /^V\d+(\.\d+)*__.+\.sql$/.test(f));
15180
15283
  return files.map((filename) => {
@@ -15186,8 +15289,8 @@ function listFlywayMigrations(projectDir) {
15186
15289
  }
15187
15290
  function listAlembicMigrations(projectDir) {
15188
15291
  const candidates = [
15189
- path26.join(projectDir, "migrations", "versions"),
15190
- path26.join(projectDir, "alembic", "versions")
15292
+ path27.join(projectDir, "migrations", "versions"),
15293
+ path27.join(projectDir, "alembic", "versions")
15191
15294
  ];
15192
15295
  const dir = candidates.find((p) => fs26.existsSync(p));
15193
15296
  if (!dir) return [];
@@ -15201,7 +15304,7 @@ function listAlembicMigrations(projectDir) {
15201
15304
  }).sort((a, b) => a.filename.localeCompare(b.filename));
15202
15305
  }
15203
15306
  function listKnexMigrations(projectDir) {
15204
- const dir = path26.join(projectDir, "migrations");
15307
+ const dir = path27.join(projectDir, "migrations");
15205
15308
  if (!fs26.existsSync(dir)) return [];
15206
15309
  const files = fs26.readdirSync(dir).filter((f) => (f.endsWith(".js") || f.endsWith(".ts")) && !f.startsWith("."));
15207
15310
  return files.map((filename) => {
@@ -15379,7 +15482,7 @@ async function runInfraSuite(args) {
15379
15482
  duration_ms: Date.now() - start
15380
15483
  };
15381
15484
  if (args.junitOutput) {
15382
- fs27.mkdirSync(path27.dirname(args.junitOutput), { recursive: true });
15485
+ fs27.mkdirSync(path28.dirname(args.junitOutput), { recursive: true });
15383
15486
  fs27.writeFileSync(args.junitOutput, formatJUnit(result), "utf8");
15384
15487
  }
15385
15488
  return result;
@@ -15427,7 +15530,7 @@ function escapeXml(s) {
15427
15530
  // scripts/lakebase/scm-claim-feature.ts
15428
15531
  init_esm_shims();
15429
15532
  import * as fs28 from "fs";
15430
- import * as path28 from "path";
15533
+ import * as path29 from "path";
15431
15534
  var ScmClaimError = class extends Error {
15432
15535
  constructor(message, code) {
15433
15536
  super(message);
@@ -15484,7 +15587,7 @@ async function claimFeatureBranch(args) {
15484
15587
  const current = readWorkflowState(args.projectDir);
15485
15588
  if (!current) {
15486
15589
  throw new ScmClaimError(
15487
- `No SCM workflow state found at ${path28.join(args.projectDir, ".lakebase/workflow-state.json")}. Run lakebase-create-project to scaffold, or re-seed via the substrate.`,
15590
+ `No SCM workflow state found at ${path29.join(args.projectDir, ".lakebase/workflow-state.json")}. Run lakebase-create-project to scaffold, or re-seed via the substrate.`,
15488
15591
  "no-state-file"
15489
15592
  );
15490
15593
  }
@@ -15589,7 +15692,7 @@ function alreadyClaimedSentinel(state) {
15589
15692
  }
15590
15693
  function workflowStateFileExists(projectDir) {
15591
15694
  return fs28.existsSync(
15592
- path28.join(projectDir, ".lakebase/workflow-state.json")
15695
+ path29.join(projectDir, ".lakebase/workflow-state.json")
15593
15696
  );
15594
15697
  }
15595
15698
 
@@ -15853,6 +15956,7 @@ async function preparePr(args) {
15853
15956
  "no-github-remote"
15854
15957
  );
15855
15958
  }
15959
+ const ciFreshness = await ensureCiSecretsFreshFromEnv(args.projectDir, { ownerRepo });
15856
15960
  const now = (args.now ?? (() => /* @__PURE__ */ new Date()))();
15857
15961
  let prUrl = args.prUrlOverride ?? "";
15858
15962
  let prCreated = false;
@@ -15897,7 +16001,7 @@ async function preparePr(args) {
15897
16001
  pushed_at: now.toISOString()
15898
16002
  };
15899
16003
  writeWorkflowState(args.projectDir, next);
15900
- return { state: next, prUrl, prCreated };
16004
+ return { state: next, prUrl, prCreated, ciFreshness };
15901
16005
  }
15902
16006
  async function ensureAheadOfParent(cwd, branch, parent) {
15903
16007
  try {
@@ -16111,11 +16215,11 @@ async function sync(args) {
16111
16215
  // scripts/lakebase/workflow-drift.ts
16112
16216
  init_esm_shims();
16113
16217
  import * as fs29 from "fs";
16114
- import * as path29 from "path";
16218
+ import * as path30 from "path";
16115
16219
  function findKitTemplatesDir(start) {
16116
16220
  let dir = start;
16117
16221
  for (let i = 0; i < 6; i++) {
16118
- const candidate = path29.join(
16222
+ const candidate = path30.join(
16119
16223
  dir,
16120
16224
  "templates",
16121
16225
  "project",
@@ -16124,7 +16228,7 @@ function findKitTemplatesDir(start) {
16124
16228
  "workflows"
16125
16229
  );
16126
16230
  if (fs29.existsSync(candidate)) return candidate;
16127
- const parent = path29.dirname(dir);
16231
+ const parent = path30.dirname(dir);
16128
16232
  if (parent === dir) break;
16129
16233
  dir = parent;
16130
16234
  }
@@ -16148,13 +16252,13 @@ function unifiedDiff(name, projectContent, templateContent) {
16148
16252
  return out.join("\n");
16149
16253
  }
16150
16254
  function detectWorkflowDrift(args) {
16151
- const projectWorkflowsDir = path29.join(
16255
+ const projectWorkflowsDir = path30.join(
16152
16256
  args.projectDir,
16153
16257
  ".github",
16154
16258
  "workflows"
16155
16259
  );
16156
- const here = path29.dirname(new URL(import.meta.url).pathname);
16157
- const kitWorkflowsDir = args.kitDir ? path29.join(
16260
+ const here = path30.dirname(new URL(import.meta.url).pathname);
16261
+ const kitWorkflowsDir = args.kitDir ? path30.join(
16158
16262
  args.kitDir,
16159
16263
  "templates",
16160
16264
  "project",
@@ -16169,8 +16273,8 @@ function detectWorkflowDrift(args) {
16169
16273
  const files = [];
16170
16274
  for (const name of templateFiles) {
16171
16275
  seen.add(name);
16172
- const projectPath2 = path29.join(projectWorkflowsDir, name);
16173
- const templatePath = path29.join(kitWorkflowsDir, name);
16276
+ const projectPath2 = path30.join(projectWorkflowsDir, name);
16277
+ const templatePath = path30.join(kitWorkflowsDir, name);
16174
16278
  if (!fs29.existsSync(projectPath2)) {
16175
16279
  files.push({ name, status: "missing" });
16176
16280
  continue;
@@ -16207,10 +16311,10 @@ function detectWorkflowDrift(args) {
16207
16311
  function readKitVersion(kitWorkflowsDir) {
16208
16312
  let dir = kitWorkflowsDir;
16209
16313
  for (let i = 0; i < 5; i++) {
16210
- dir = path29.dirname(dir);
16314
+ dir = path30.dirname(dir);
16211
16315
  }
16212
16316
  try {
16213
- const raw = fs29.readFileSync(path29.join(dir, "package.json"), "utf-8");
16317
+ const raw = fs29.readFileSync(path30.join(dir, "package.json"), "utf-8");
16214
16318
  const pkg = JSON.parse(raw);
16215
16319
  return typeof pkg.version === "string" ? pkg.version : "unknown";
16216
16320
  } catch {
@@ -16227,7 +16331,7 @@ var COMMAND_HOOK_FILE_PATTERN = /\.(pre|post)-hook\.md$/;
16227
16331
  function findKitCommandsDir(start) {
16228
16332
  let dir = start;
16229
16333
  for (let i = 0; i < 6; i++) {
16230
- const candidate = path29.join(
16334
+ const candidate = path30.join(
16231
16335
  dir,
16232
16336
  "templates",
16233
16337
  "project",
@@ -16236,7 +16340,7 @@ function findKitCommandsDir(start) {
16236
16340
  "commands"
16237
16341
  );
16238
16342
  if (fs29.existsSync(candidate)) return candidate;
16239
- const parent = path29.dirname(dir);
16343
+ const parent = path30.dirname(dir);
16240
16344
  if (parent === dir) break;
16241
16345
  dir = parent;
16242
16346
  }
@@ -16249,9 +16353,9 @@ function parsePinnedVersion(content) {
16249
16353
  return m ? m[1] : void 0;
16250
16354
  }
16251
16355
  function detectCommandDrift(args) {
16252
- const projectCommandsDir = path29.join(args.projectDir, ".claude", "commands");
16253
- const here = path29.dirname(new URL(import.meta.url).pathname);
16254
- const kitCommandsDir = args.kitDir ? path29.join(args.kitDir, "templates", "project", "common", ".claude", "commands") : findKitCommandsDir(here);
16356
+ const projectCommandsDir = path30.join(args.projectDir, ".claude", "commands");
16357
+ const here = path30.dirname(new URL(import.meta.url).pathname);
16358
+ const kitCommandsDir = args.kitDir ? path30.join(args.kitDir, "templates", "project", "common", ".claude", "commands") : findKitCommandsDir(here);
16255
16359
  const kitVersion = readKitVersionFromCommandsDir(kitCommandsDir);
16256
16360
  const templateFiles = fs29.existsSync(kitCommandsDir) ? fs29.readdirSync(kitCommandsDir).filter((f) => f.endsWith(".md") && !COMMAND_HOOK_FILE_PATTERN.test(f)) : [];
16257
16361
  const projectFiles = fs29.existsSync(projectCommandsDir) ? fs29.readdirSync(projectCommandsDir).filter((f) => f.endsWith(".md") && !COMMAND_HOOK_FILE_PATTERN.test(f)) : [];
@@ -16259,8 +16363,8 @@ function detectCommandDrift(args) {
16259
16363
  const files = [];
16260
16364
  for (const name of templateFiles) {
16261
16365
  seen.add(name);
16262
- const projectPath2 = path29.join(projectCommandsDir, name);
16263
- const templatePath = path29.join(kitCommandsDir, name);
16366
+ const projectPath2 = path30.join(projectCommandsDir, name);
16367
+ const templatePath = path30.join(kitCommandsDir, name);
16264
16368
  const templateRaw = fs29.readFileSync(templatePath, "utf8");
16265
16369
  if (!fs29.existsSync(projectPath2)) {
16266
16370
  files.push({ name, status: "missing", kit_version: kitVersion });
@@ -16304,10 +16408,10 @@ function detectCommandDrift(args) {
16304
16408
  function readKitVersionFromCommandsDir(kitCommandsDir) {
16305
16409
  let dir = kitCommandsDir;
16306
16410
  for (let i = 0; i < 5; i++) {
16307
- dir = path29.dirname(dir);
16411
+ dir = path30.dirname(dir);
16308
16412
  }
16309
16413
  try {
16310
- const raw = fs29.readFileSync(path29.join(dir, "package.json"), "utf-8");
16414
+ const raw = fs29.readFileSync(path30.join(dir, "package.json"), "utf-8");
16311
16415
  const pkg = JSON.parse(raw);
16312
16416
  return typeof pkg.version === "string" ? pkg.version : "unknown";
16313
16417
  } catch {
@@ -16324,13 +16428,13 @@ function detectScaffoldedDrift(args) {
16324
16428
  };
16325
16429
  }
16326
16430
  function updateWorkflows(args) {
16327
- const projectWorkflowsDir = path29.join(
16431
+ const projectWorkflowsDir = path30.join(
16328
16432
  args.projectDir,
16329
16433
  ".github",
16330
16434
  "workflows"
16331
16435
  );
16332
- const here = path29.dirname(new URL(import.meta.url).pathname);
16333
- const kitWorkflowsDir = args.kitDir ? path29.join(
16436
+ const here = path30.dirname(new URL(import.meta.url).pathname);
16437
+ const kitWorkflowsDir = args.kitDir ? path30.join(
16334
16438
  args.kitDir,
16335
16439
  "templates",
16336
16440
  "project",
@@ -16351,8 +16455,8 @@ function updateWorkflows(args) {
16351
16455
  const files = [];
16352
16456
  for (const name of templateFiles) {
16353
16457
  seen.add(name);
16354
- const projectPath2 = path29.join(projectWorkflowsDir, name);
16355
- const templatePath = path29.join(kitWorkflowsDir, name);
16458
+ const projectPath2 = path30.join(projectWorkflowsDir, name);
16459
+ const templatePath = path30.join(kitWorkflowsDir, name);
16356
16460
  const templateRaw = fs29.readFileSync(templatePath, "utf-8");
16357
16461
  const desired = substitute ? applyPlaceholders(templateRaw, version) : templateRaw;
16358
16462
  const existed = fs29.existsSync(projectPath2);
@@ -16373,7 +16477,7 @@ function updateWorkflows(args) {
16373
16477
  if (pruneExtras) {
16374
16478
  for (const name of projectFiles) {
16375
16479
  if (seen.has(name)) continue;
16376
- const projectPath2 = path29.join(projectWorkflowsDir, name);
16480
+ const projectPath2 = path30.join(projectWorkflowsDir, name);
16377
16481
  if (!dryRun) {
16378
16482
  fs29.unlinkSync(projectPath2);
16379
16483
  }
@@ -16429,6 +16533,48 @@ function shaMigratePredicate(mergeCommitSha) {
16429
16533
  return !!run.headSha && run.headSha === mergeCommitSha;
16430
16534
  };
16431
16535
  }
16536
+ var PROMOTE_RUNSTATE_CONFLICT = /(^|\/)\.consort\/(features\/[^/]+\/)?pipeline\.json$/;
16537
+ async function reconcilePromoteRunState(projectDir, baseBranch) {
16538
+ const o = { cwd: projectDir, timeout: 3e4 };
16539
+ const dirty = (await exec2("git status --porcelain", o).catch(() => "")).split("\n").map((l) => l.slice(3).trim()).filter(Boolean);
16540
+ const nonRunStateDirty = dirty.filter((p) => !p.startsWith(".consort/"));
16541
+ if (nonRunStateDirty.length > 0) {
16542
+ throw new ScmMergeError(
16543
+ `working tree has uncommitted non-run-state changes (${nonRunStateDirty.slice(0, 5).join(", ")}${nonRunStateDirty.length > 5 ? ", \u2026" : ""}); commit or stash before promoting.`,
16544
+ "promote-conflict"
16545
+ );
16546
+ }
16547
+ if (dirty.length > 0) {
16548
+ await exec2(`git add ${shellEscape2(".consort")}`, o);
16549
+ await exec2(`git commit -m ${shellEscape2("chore(run): checkpoint run state before promotion")}`, o);
16550
+ }
16551
+ await exec2(`git fetch origin ${shellEscape2(baseBranch)}`, o).catch(() => {
16552
+ });
16553
+ let conflicted = false;
16554
+ try {
16555
+ await exec2(`git merge --no-ff -m ${shellEscape2(`merge ${baseBranch} for promotion`)} ${shellEscape2(`origin/${baseBranch}`)}`, o);
16556
+ } catch {
16557
+ conflicted = true;
16558
+ }
16559
+ if (!conflicted) return { reconciled: false };
16560
+ const u = (await exec2("git diff --name-only --diff-filter=U", o).catch(() => "")).split("\n").map((s) => s.trim()).filter(Boolean);
16561
+ const other = u.filter((p) => !PROMOTE_RUNSTATE_CONFLICT.test(p));
16562
+ if (u.length === 0 || other.length > 0) {
16563
+ await exec2("git merge --abort", o).catch(() => {
16564
+ });
16565
+ throw new ScmMergeError(
16566
+ other.length > 0 ? `promotion conflicts on non-ledger file(s): ${other.join(", ")}. Resolve the real divergence manually, then re-run the promote.` : `promotion merge failed with no resolvable conflicts; resolve manually and re-run.`,
16567
+ "promote-conflict"
16568
+ );
16569
+ }
16570
+ for (const p of u) {
16571
+ await exec2(`git checkout --ours -- ${shellEscape2(p)}`, o);
16572
+ await exec2(`git add -- ${shellEscape2(p)}`, o);
16573
+ }
16574
+ await exec2(`git commit -m ${shellEscape2(`merge ${baseBranch} for promotion (pipeline.json ledger kept at run state)`)}`, o);
16575
+ await pushCurrentBranchForPr({ cwd: projectDir });
16576
+ return { reconciled: true, detail: `reconciled ${u.length} ledger path(s) to the run side + pushed` };
16577
+ }
16432
16578
  async function defaultRefreshPromoteWorkflows(projectDir) {
16433
16579
  const drift = detectWorkflowDrift({ projectDir });
16434
16580
  const touched = drift.files.filter((f) => f.status === "drifted").map((f) => f.name);
@@ -16518,6 +16664,24 @@ async function mergeFeature(args) {
16518
16664
  );
16519
16665
  }
16520
16666
  }
16667
+ {
16668
+ const ensure = args.ensureCiFresh ?? ((repo) => ensureCiSecretsFreshFromEnv(args.projectDir, { ownerRepo: repo }));
16669
+ try {
16670
+ const r = await ensure(ownerRepo);
16671
+ if (r.action !== "ok" && r.action !== "skipped") preflightNotes.push(`CI-auth preflight: ${r.reason}`);
16672
+ } catch (err) {
16673
+ preflightNotes.push(`CI-auth preflight skipped (${err instanceof Error ? err.message : String(err)}); if the promote CI fails on auth, run lakebase-sync-ci-secrets.`);
16674
+ }
16675
+ }
16676
+ {
16677
+ const fetchState = args.fetchPrState ?? ((o, h) => getPullRequest(o, h));
16678
+ const prState = await fetchState(ownerRepo, current.branch).catch(() => void 0);
16679
+ if (prState?.mergeableState === "dirty") {
16680
+ const reconcile = args.reconcileRunState ?? ((base) => reconcilePromoteRunState(args.projectDir, base));
16681
+ const r = await reconcile(gitBase);
16682
+ if (r.reconciled) preflightNotes.push(`Reconciled a pipeline.json-ledger promotion conflict to the run side${r.detail ? ` (${r.detail})` : ""}.`);
16683
+ }
16684
+ }
16521
16685
  let paired;
16522
16686
  try {
16523
16687
  paired = await mergePairedPullRequest({
@@ -16949,11 +17113,11 @@ function parentForTopology(t, defaultLeaf) {
16949
17113
  // scripts/lakebase/scm-doctor.ts
16950
17114
  init_esm_shims();
16951
17115
  import * as fs30 from "fs";
16952
- import * as path30 from "path";
17116
+ import * as path31 from "path";
16953
17117
  var FEATURE_PREFIX = "feature/";
16954
17118
  var TIER_LEAFS2 = DEFAULT_PROTECTED_TIER_NAMES;
16955
17119
  function readEnv(projectDir) {
16956
- const envPath = path30.join(projectDir, ".env");
17120
+ const envPath = path31.join(projectDir, ".env");
16957
17121
  const out = /* @__PURE__ */ new Map();
16958
17122
  if (!fs30.existsSync(envPath)) return out;
16959
17123
  const lines = fs30.readFileSync(envPath, "utf8").split("\n");
@@ -17011,7 +17175,7 @@ async function runDoctor(args, deps = {}) {
17011
17175
  }
17012
17176
  for (const wf of ["pr.yml", "merge.yml"]) {
17013
17177
  try {
17014
- const p = path30.join(projectDir, ".github", "workflows", wf);
17178
+ const p = path31.join(projectDir, ".github", "workflows", wf);
17015
17179
  if (!fs30.existsSync(p)) continue;
17016
17180
  const body = fs30.readFileSync(p, "utf8");
17017
17181
  if (/lakebase-scm-utils#v\d/.test(body)) {
@@ -17228,7 +17392,7 @@ async function fixFinding(args) {
17228
17392
  );
17229
17393
  }
17230
17394
  const sanitized = sanitizeBranchName(branch);
17231
- const envFile = path30.join(args.projectDir, ".env");
17395
+ const envFile = path31.join(args.projectDir, ".env");
17232
17396
  updateEnvConnection({
17233
17397
  envPath: envFile,
17234
17398
  projectId: readEnvVar(envFile, "LAKEBASE_PROJECT_ID") ?? "",
@@ -17331,10 +17495,10 @@ function shellEscape3(s) {
17331
17495
  // scripts/lakebase/doctor.ts
17332
17496
  init_esm_shims();
17333
17497
  import * as fs31 from "fs";
17334
- import * as path31 from "path";
17498
+ import * as path32 from "path";
17335
17499
  import * as cp6 from "child_process";
17336
17500
  function readEnvFile(projectDir) {
17337
- const envPath = path31.join(projectDir, ".env");
17501
+ const envPath = path32.join(projectDir, ".env");
17338
17502
  if (!fs31.existsSync(envPath)) return {};
17339
17503
  const out = {};
17340
17504
  for (const line of fs31.readFileSync(envPath, "utf8").split("\n")) {
@@ -17592,7 +17756,7 @@ function checkEnv(projectDir) {
17592
17756
  name: "env-file",
17593
17757
  status: "warn",
17594
17758
  message: ".env not found",
17595
- detail: { projectDir, envPath: path31.join(projectDir, ".env") },
17759
+ detail: { projectDir, envPath: path32.join(projectDir, ".env") },
17596
17760
  hint: "Run `lakebase-branch sync-env`, or `git checkout <branch>` so the post-checkout hook writes .env (or copy .env.example). The kit stores connection METADATA only , no DB token (the app mints a short-lived credential at runtime)."
17597
17761
  };
17598
17762
  }
@@ -18086,7 +18250,7 @@ async function listMigrationsOnBranch(args) {
18086
18250
  // scripts/git/commits.ts
18087
18251
  init_esm_shims();
18088
18252
  import { existsSync as existsSync30 } from "fs";
18089
- import { join as join33 } from "path";
18253
+ import { join as join34 } from "path";
18090
18254
  var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
18091
18255
  ".py",
18092
18256
  ".ts",
@@ -18189,7 +18353,7 @@ async function commitAllIfChanged(args) {
18189
18353
  await exec2("git add -A", { cwd: args.cwd });
18190
18354
  }
18191
18355
  for (const inc of args.include ?? []) {
18192
- if (existsSync30(join33(args.cwd, inc))) {
18356
+ if (existsSync30(join34(args.cwd, inc))) {
18193
18357
  await exec2(`git add -f -- ${shq(inc)}`, { cwd: args.cwd });
18194
18358
  }
18195
18359
  }
@@ -18332,13 +18496,13 @@ async function stashDropAll(args) {
18332
18496
  // scripts/git/rebase.ts
18333
18497
  init_esm_shims();
18334
18498
  import * as fs32 from "fs";
18335
- import * as path32 from "path";
18499
+ import * as path33 from "path";
18336
18500
  async function abortRebase(args) {
18337
18501
  await exec2("git rebase --abort", { cwd: args.cwd });
18338
18502
  }
18339
18503
  async function isRebasing(args) {
18340
18504
  try {
18341
- return fs32.existsSync(path32.join(args.cwd, ".git/rebase-merge")) || fs32.existsSync(path32.join(args.cwd, ".git/rebase-apply"));
18505
+ return fs32.existsSync(path33.join(args.cwd, ".git/rebase-merge")) || fs32.existsSync(path33.join(args.cwd, ".git/rebase-apply"));
18342
18506
  } catch {
18343
18507
  return false;
18344
18508
  }
@@ -18554,6 +18718,8 @@ function withProxyEnv(base = {}) {
18554
18718
  return out;
18555
18719
  }
18556
18720
  export {
18721
+ CI_TOKEN_LIFETIME_SECONDS,
18722
+ CI_TOKEN_REMINT_MARGIN_SECONDS,
18557
18723
  CONVENTION_TIER_DEFAULTS,
18558
18724
  DEFAULT_PROTECTED_TIER_NAMES,
18559
18725
  FIXABLE_FINDING_IDS,
@@ -18617,6 +18783,8 @@ export {
18617
18783
  checkoutDetached,
18618
18784
  checkoutPaired,
18619
18785
  cherryPick,
18786
+ ciTokenComment,
18787
+ ciTokenExpiry,
18620
18788
  claimFeatureBranch,
18621
18789
  clearRetentionCache,
18622
18790
  cloneRepo,
@@ -18687,6 +18855,8 @@ export {
18687
18855
  endpointPath,
18688
18856
  ensureAppEndpoint,
18689
18857
  ensureCachedArchive,
18858
+ ensureCiSecretsFresh,
18859
+ ensureCiSecretsFreshFromEnv,
18690
18860
  ensureEndpoint,
18691
18861
  ensureLakebaseSecretAuth,
18692
18862
  ensureProfilePinned,
@@ -18820,6 +18990,7 @@ export {
18820
18990
  readTargets,
18821
18991
  readWorkflowState,
18822
18992
  rebaseBranch,
18993
+ reconcilePromoteRunState,
18823
18994
  reconcileTierToOrigin,
18824
18995
  recoverOrphans,
18825
18996
  release,