@databricks-solutions/lakebase-scm-utils 0.2.48 → 0.2.50

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 (54) 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 +2184 -2054
  4. package/dist/scripts/index.d.cts +3 -2
  5. package/dist/scripts/index.d.ts +3 -2
  6. package/dist/scripts/index.js +259 -136
  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 +2170 -2052
  18. package/dist/scripts/lakebase/index.d.cts +21 -2
  19. package/dist/scripts/lakebase/index.d.ts +21 -2
  20. package/dist/scripts/lakebase/index.js +249 -132
  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-cleanup.cli.cjs +5 -2
  32. package/dist/scripts/lakebase/scm-cleanup.cli.js +5 -2
  33. package/dist/scripts/lakebase/scm-doctor.cli.cjs +2 -2
  34. package/dist/scripts/lakebase/scm-doctor.cli.js +2 -2
  35. package/dist/scripts/lakebase/scm-merge.cli.cjs +2216 -1994
  36. package/dist/scripts/lakebase/scm-merge.cli.js +296 -74
  37. package/dist/scripts/lakebase/scm-prepare-pr.cli.cjs +479 -86
  38. package/dist/scripts/lakebase/scm-prepare-pr.cli.js +479 -86
  39. package/dist/scripts/lakebase/scm-reconcile-tier.cli.cjs +2 -2
  40. package/dist/scripts/lakebase/scm-reconcile-tier.cli.js +2 -2
  41. package/dist/scripts/lakebase/sync-ci-secrets.cli.cjs +56 -30
  42. package/dist/scripts/lakebase/sync-ci-secrets.cli.js +56 -30
  43. package/dist/scripts/util/index.cjs +142 -25
  44. package/dist/scripts/util/index.d.cts +2 -27
  45. package/dist/scripts/util/index.d.ts +2 -27
  46. package/dist/scripts/util/index.js +136 -25
  47. package/package.json +1 -1
  48. package/scripts/lakebase/create-project.ts +2 -2
  49. package/scripts/lakebase/lakebase-project.ts +17 -3
  50. package/scripts/lakebase/scm-merge.ts +21 -0
  51. package/scripts/lakebase/scm-prepare-pr.ts +10 -1
  52. package/scripts/lakebase/sync-ci-secrets.cli.ts +2 -2
  53. package/scripts/util/ci-secrets.ts +212 -26
  54. 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);
@@ -9442,9 +9442,12 @@ function assertCreatedProjectReady(projectId, verified, reportedState) {
9442
9442
  }
9443
9443
  return { uid: verified.uid, name: verified.name, state: verified.state ?? "READY" };
9444
9444
  }
9445
+ function deleteProjectArgs(projectId) {
9446
+ const name = projectId.startsWith("projects/") ? projectId : `projects/${projectId}`;
9447
+ return ["postgres", "delete-project", name, "--purge", "-o", "json"];
9448
+ }
9445
9449
  async function deleteLakebaseProject(args) {
9446
- const name = args.projectId.startsWith("projects/") ? args.projectId : `projects/${args.projectId}`;
9447
- await dbcli3(["postgres", "delete-project", name, "-o", "json"], args.host);
9450
+ await dbcli3(deleteProjectArgs(args.projectId), args.host);
9448
9451
  }
9449
9452
  function findDefaultBranchName(items) {
9450
9453
  const def = items.find((b) => b.status?.default === true || b.is_default === true);
@@ -9722,8 +9725,8 @@ var PKG_NAME = "@databricks-solutions/lakebase-scm-utils";
9722
9725
  var cached;
9723
9726
  function substrateSelfVersion() {
9724
9727
  if (cached !== void 0) return cached;
9725
- if ("0.2.48".length > 0) {
9726
- cached = "0.2.48";
9728
+ if ("0.2.50".length > 0) {
9729
+ cached = "0.2.50";
9727
9730
  return cached;
9728
9731
  }
9729
9732
  cached = "unknown";
@@ -11311,16 +11314,16 @@ function isAllSchemas(schema) {
11311
11314
  }
11312
11315
  function buildSchemaQuery(schema) {
11313
11316
  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 ";
11317
+ const join36 = "FROM information_schema.columns c JOIN pg_tables t ON c.table_name = t.tablename AND c.table_schema = t.schemaname ";
11315
11318
  if (isAllSchemas(schema)) {
11316
11319
  return {
11317
- text: `SELECT ${cols} ` + join35 + `WHERE ${SYSTEM_SCHEMA_FILTER} ORDER BY c.table_schema, c.table_name, c.ordinal_position`,
11320
+ text: `SELECT ${cols} ` + join36 + `WHERE ${SYSTEM_SCHEMA_FILTER} ORDER BY c.table_schema, c.table_name, c.ordinal_position`,
11318
11321
  values: []
11319
11322
  };
11320
11323
  }
11321
11324
  const one = (schema ?? "").trim() || "public";
11322
11325
  return {
11323
- text: `SELECT ${cols} ` + join35 + "WHERE c.table_schema = $1 ORDER BY c.table_name, c.ordinal_position",
11326
+ text: `SELECT ${cols} ` + join36 + "WHERE c.table_schema = $1 ORDER BY c.table_name, c.ordinal_position",
11324
11327
  values: [one]
11325
11328
  };
11326
11329
  }
@@ -11481,7 +11484,7 @@ async function withLakebaseRollback(opts, fn) {
11481
11484
  // scripts/lakebase/create-project.ts
11482
11485
  init_esm_shims();
11483
11486
  import * as fs18 from "fs";
11484
- import * as path17 from "path";
11487
+ import * as path18 from "path";
11485
11488
 
11486
11489
  // scripts/lakebase/project-verify.ts
11487
11490
  init_esm_shims();
@@ -13066,6 +13069,7 @@ async function removeRunner(args) {
13066
13069
 
13067
13070
  // scripts/util/ci-secrets.ts
13068
13071
  init_esm_shims();
13072
+ import * as path16 from "path";
13069
13073
 
13070
13074
  // scripts/git/remote.ts
13071
13075
  init_esm_shims();
@@ -13128,13 +13132,40 @@ async function deleteRemoteBranch(args) {
13128
13132
 
13129
13133
  // scripts/util/ci-secrets.ts
13130
13134
  var REQUIRED_CI_SECRETS = ["DATABRICKS_HOST", "LAKEBASE_PROJECT_ID", "DATABRICKS_TOKEN"];
13135
+ var CI_TOKEN_LIFETIME_SECONDS = 7776e3;
13136
+ var CI_TOKEN_REMINT_MARGIN_SECONDS = 86400;
13137
+ function ciTokenComment(ownerRepo) {
13138
+ const repoName = ownerRepo.includes("/") ? ownerRepo.slice(ownerRepo.lastIndexOf("/") + 1) : ownerRepo;
13139
+ return `GitHub Actions (${repoName})`;
13140
+ }
13131
13141
  async function missingCiSecrets(ownerRepo) {
13132
13142
  const present = new Set(await listSecretNames(ownerRepo));
13133
13143
  return REQUIRED_CI_SECRETS.filter((n) => !present.has(n));
13134
13144
  }
13145
+ async function mintCiToken(args) {
13146
+ try {
13147
+ const raw = await runDatabricks(
13148
+ ["tokens", "create", "--comment", args.comment, "--lifetime-seconds", String(args.lifetimeSeconds), "-o", "json"],
13149
+ { host: args.databricksHost, cwd: args.projectDir, timeout: 3e4 }
13150
+ );
13151
+ const parsed = JSON.parse(raw.slice(Math.max(0, raw.indexOf("{"))));
13152
+ const token = parsed.token_value || parsed.token || "";
13153
+ if (token) return token;
13154
+ } catch {
13155
+ }
13156
+ try {
13157
+ const raw = await runDatabricks(["auth", "token", "-o", "json"], {
13158
+ host: args.databricksHost,
13159
+ cwd: args.projectDir,
13160
+ timeout: 3e4
13161
+ });
13162
+ const parsed = JSON.parse(raw.slice(Math.max(0, raw.indexOf("{"))));
13163
+ return parsed.access_token || "";
13164
+ } catch {
13165
+ return "";
13166
+ }
13167
+ }
13135
13168
  async function syncCiSecrets(args) {
13136
- const lifetime = args.lifetimeSeconds ?? 86400;
13137
- const comment = args.comment ?? "GitHub Actions CI";
13138
13169
  const ownerRepo = args.ownerRepo ?? await getOwnerRepo(args.projectDir);
13139
13170
  if (!ownerRepo) {
13140
13171
  throw new Error("Could not resolve GitHub repository from git remote");
@@ -13145,27 +13176,104 @@ async function syncCiSecrets(args) {
13145
13176
  if (!args.lakebaseProjectId) {
13146
13177
  throw new Error("syncCiSecrets: lakebaseProjectId is required");
13147
13178
  }
13179
+ const lifetime = args.lifetimeSeconds ?? CI_TOKEN_LIFETIME_SECONDS;
13180
+ const comment = args.comment ?? ciTokenComment(ownerRepo);
13148
13181
  const secrets = {
13149
13182
  DATABRICKS_HOST: args.databricksHost,
13150
13183
  LAKEBASE_PROJECT_ID: args.lakebaseProjectId
13151
13184
  };
13185
+ const token = await mintCiToken({
13186
+ databricksHost: args.databricksHost,
13187
+ projectDir: args.projectDir,
13188
+ comment,
13189
+ lifetimeSeconds: lifetime
13190
+ });
13191
+ if (token) secrets.DATABRICKS_TOKEN = token;
13192
+ await setRepoSecrets(ownerRepo, secrets);
13193
+ }
13194
+ async function ciTokenExpiry(args) {
13195
+ const comment = ciTokenComment(args.ownerRepo);
13196
+ let raw;
13152
13197
  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;
13198
+ raw = await runDatabricks(["tokens", "list", "-o", "json"], {
13199
+ host: args.databricksHost,
13200
+ cwd: args.projectDir,
13201
+ timeout: 3e4
13202
+ });
13160
13203
  } catch {
13204
+ return null;
13205
+ }
13206
+ let infos;
13207
+ try {
13208
+ const parsed = JSON.parse(raw.slice(Math.max(0, raw.indexOf("["))));
13209
+ infos = Array.isArray(parsed) ? parsed : [];
13210
+ } catch {
13211
+ return null;
13212
+ }
13213
+ 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);
13214
+ if (expiries.length === 0) return null;
13215
+ return Math.max(...expiries);
13216
+ }
13217
+ async function ensureCiSecretsFresh(args) {
13218
+ const now = args.now ?? Date.now;
13219
+ const marginMs = (args.marginSeconds ?? CI_TOKEN_REMINT_MARGIN_SECONDS) * 1e3;
13220
+ const ownerRepo = args.ownerRepo ?? await getOwnerRepo(args.projectDir);
13221
+ if (!ownerRepo) {
13222
+ throw new Error("Could not resolve GitHub repository from git remote");
13223
+ }
13224
+ const sync2 = () => syncCiSecrets({
13225
+ projectDir: args.projectDir,
13226
+ databricksHost: args.databricksHost,
13227
+ lakebaseProjectId: args.lakebaseProjectId,
13228
+ ownerRepo
13229
+ });
13230
+ const missing = await missingCiSecrets(ownerRepo);
13231
+ if (missing.length > 0) {
13232
+ await sync2();
13233
+ return { action: "provisioned", reason: `CI secret(s) were missing (${missing.join(", ")}); provisioned.` };
13234
+ }
13235
+ const expiry = await ciTokenExpiry({ projectDir: args.projectDir, databricksHost: args.databricksHost, ownerRepo });
13236
+ if (expiry === null) {
13237
+ await sync2();
13238
+ return { action: "reminted", reason: "no live CI token found (expired or minted under a different identity); re-minted." };
13239
+ }
13240
+ if (expiry !== Number.POSITIVE_INFINITY && expiry < now() + marginMs) {
13241
+ await sync2();
13242
+ const hrs = Math.max(0, Math.round((expiry - now()) / 36e5));
13243
+ return { action: "reminted", reason: `CI token expires in ~${hrs}h (within the re-mint margin); re-minted.` };
13244
+ }
13245
+ return { action: "ok", reason: "CI token is current." };
13246
+ }
13247
+ async function ensureCiSecretsFreshFromEnv(projectDir, opts) {
13248
+ const envPath = path16.join(projectDir, ".env");
13249
+ const databricksHost = readEnvVar(envPath, "DATABRICKS_HOST");
13250
+ const lakebaseProjectId = readEnvVar(envPath, "LAKEBASE_PROJECT_ID");
13251
+ if (!databricksHost || !lakebaseProjectId) {
13252
+ return {
13253
+ action: "skipped",
13254
+ reason: "CI-auth preflight skipped: .env is missing DATABRICKS_HOST / LAKEBASE_PROJECT_ID."
13255
+ };
13256
+ }
13257
+ try {
13258
+ return await ensureCiSecretsFresh({
13259
+ projectDir,
13260
+ databricksHost,
13261
+ lakebaseProjectId,
13262
+ ownerRepo: opts?.ownerRepo,
13263
+ marginSeconds: opts?.marginSeconds
13264
+ });
13265
+ } catch (err) {
13266
+ return {
13267
+ action: "failed",
13268
+ 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.`
13269
+ };
13161
13270
  }
13162
- await setRepoSecrets(ownerRepo, secrets);
13163
13271
  }
13164
13272
 
13165
13273
  // scripts/lakebase/scm-workflow-state.ts
13166
13274
  init_esm_shims();
13167
13275
  import * as fs17 from "fs";
13168
- import * as path16 from "path";
13276
+ import * as path17 from "path";
13169
13277
  import { execFileSync as execFileSync6 } from "child_process";
13170
13278
  function isGitTracked(projectDir, rel) {
13171
13279
  try {
@@ -13184,7 +13292,7 @@ function ensureWorkflowStateUntracked(projectDir) {
13184
13292
  } catch {
13185
13293
  }
13186
13294
  try {
13187
- const gitignore = path16.join(projectDir, ".gitignore");
13295
+ const gitignore = path17.join(projectDir, ".gitignore");
13188
13296
  const existing = fs17.existsSync(gitignore) ? fs17.readFileSync(gitignore, "utf8") : "";
13189
13297
  if (!existing.split("\n").some((l) => l.trim() === rel)) {
13190
13298
  const sep4 = existing === "" || existing.endsWith("\n") ? "" : "\n";
@@ -13212,7 +13320,7 @@ var STATE_INDEX = SCM_STATES.reduce(
13212
13320
  );
13213
13321
  var STATE_FILE_REL = ".lakebase/workflow-state.json";
13214
13322
  function stateFilePath(projectDir) {
13215
- return path16.join(projectDir, STATE_FILE_REL);
13323
+ return path17.join(projectDir, STATE_FILE_REL);
13216
13324
  }
13217
13325
  function readWorkflowState(projectDir) {
13218
13326
  const p = stateFilePath(projectDir);
@@ -13252,7 +13360,7 @@ function writeWorkflowState(projectDir, state) {
13252
13360
  ${summary}`);
13253
13361
  }
13254
13362
  ensureWorkflowStateUntracked(projectDir);
13255
- const dir = path16.join(projectDir, ".lakebase");
13363
+ const dir = path17.join(projectDir, ".lakebase");
13256
13364
  fs17.mkdirSync(dir, { recursive: true });
13257
13365
  const target = stateFilePath(projectDir);
13258
13366
  const tmp = `${target}.tmp`;
@@ -13483,7 +13591,7 @@ function orderForOutput(state) {
13483
13591
  async function createProject(input, progress) {
13484
13592
  const report = progress ?? (() => {
13485
13593
  });
13486
- const projectDir = path17.join(input.parentDir, input.projectName);
13594
+ const projectDir = path18.join(input.parentDir, input.projectName);
13487
13595
  const lakebaseProjectId = input.projectName;
13488
13596
  const host = input.databricksHost.replace(/\/+$/, "");
13489
13597
  const useGithub = input.createGithubRepo !== false;
@@ -13644,8 +13752,6 @@ Last probe error:
13644
13752
  projectDir,
13645
13753
  databricksHost: host,
13646
13754
  lakebaseProjectId,
13647
- comment: "GitHub Actions CI",
13648
- lifetimeSeconds: 86400,
13649
13755
  ownerRepo: fullRepoName
13650
13756
  });
13651
13757
  const missing = await missingCiSecrets(fullRepoName);
@@ -13712,9 +13818,9 @@ Last probe error:
13712
13818
  const scmRef = envRef || (ver !== "unknown" ? `v${ver}` : "");
13713
13819
  if (scmRef) {
13714
13820
  try {
13715
- const dir = path17.join(projectDir, ".lakebase");
13821
+ const dir = path18.join(projectDir, ".lakebase");
13716
13822
  fs18.mkdirSync(dir, { recursive: true });
13717
- fs18.writeFileSync(path17.join(dir, "scm-utils-ref"), `${scmRef}
13823
+ fs18.writeFileSync(path18.join(dir, "scm-utils-ref"), `${scmRef}
13718
13824
  `, "utf8");
13719
13825
  } catch (err) {
13720
13826
  warnings.push(`Substrate ref pin failed (advisory): ${err instanceof Error ? err.message : String(err)}.`);
@@ -13725,9 +13831,9 @@ Last probe error:
13725
13831
  const kitRef = process.env.LAKEBASE_KIT_REF?.trim();
13726
13832
  if (kitRef) {
13727
13833
  try {
13728
- const dir = path17.join(projectDir, ".lakebase");
13834
+ const dir = path18.join(projectDir, ".lakebase");
13729
13835
  fs18.mkdirSync(dir, { recursive: true });
13730
- fs18.writeFileSync(path17.join(dir, "kit-ref"), `${kitRef}
13836
+ fs18.writeFileSync(path18.join(dir, "kit-ref"), `${kitRef}
13731
13837
  `, "utf8");
13732
13838
  } catch (err) {
13733
13839
  warnings.push(`Kit ref pin failed (advisory): ${err instanceof Error ? err.message : String(err)}.`);
@@ -13809,7 +13915,7 @@ Last probe error:
13809
13915
  init_esm_shims();
13810
13916
  import * as cp5 from "child_process";
13811
13917
  import * as fs19 from "fs";
13812
- import * as path18 from "path";
13918
+ import * as path19 from "path";
13813
13919
  async function adoptLakebaseProject(args) {
13814
13920
  const warnings = [];
13815
13921
  const filesWritten = [];
@@ -13818,7 +13924,7 @@ async function adoptLakebaseProject(args) {
13818
13924
  if (!fs19.existsSync(args.projectDir)) {
13819
13925
  throw new Error(`adoptLakebaseProject: project directory does not exist: ${args.projectDir}`);
13820
13926
  }
13821
- if (!fs19.existsSync(path18.join(args.projectDir, ".git"))) {
13927
+ if (!fs19.existsSync(path19.join(args.projectDir, ".git"))) {
13822
13928
  throw new Error(
13823
13929
  `adoptLakebaseProject: ${args.projectDir} is not a git repo. Run \`git init\` first, or pass an existing repo path.`
13824
13930
  );
@@ -13884,7 +13990,7 @@ async function adoptLakebaseProject(args) {
13884
13990
  };
13885
13991
  }
13886
13992
  function assertEnvCompatibility(projectDir, expectedProjectId) {
13887
- const envPath = path18.join(projectDir, ".env");
13993
+ const envPath = path19.join(projectDir, ".env");
13888
13994
  if (!fs19.existsSync(envPath)) return;
13889
13995
  const content = fs19.readFileSync(envPath, "utf8");
13890
13996
  const match = content.match(/^LAKEBASE_PROJECT_ID\s*=\s*(.+?)\s*$/m);
@@ -13900,7 +14006,7 @@ function assertAdoptionPreflight(args) {
13900
14006
  if (!fs19.existsSync(args.projectDir)) {
13901
14007
  throw new Error(`assertAdoptionPreflight: project directory does not exist: ${args.projectDir}`);
13902
14008
  }
13903
- if (!fs19.existsSync(path18.join(args.projectDir, ".git"))) {
14009
+ if (!fs19.existsSync(path19.join(args.projectDir, ".git"))) {
13904
14010
  throw new Error(
13905
14011
  `assertAdoptionPreflight: ${args.projectDir} is not a git repo.`
13906
14012
  );
@@ -13914,7 +14020,7 @@ function _testMakeBrownfieldFixture(opts) {
13914
14020
  cp5.execSync("git init --quiet", { cwd: opts.dir, stdio: "pipe" });
13915
14021
  if (opts.packageJson) {
13916
14022
  fs19.writeFileSync(
13917
- path18.join(opts.dir, "package.json"),
14023
+ path19.join(opts.dir, "package.json"),
13918
14024
  JSON.stringify(opts.packageJson, null, 2) + "\n"
13919
14025
  );
13920
14026
  }
@@ -13923,7 +14029,7 @@ function _testMakeBrownfieldFixture(opts) {
13923
14029
  // scripts/lakebase/infra-runner.ts
13924
14030
  init_esm_shims();
13925
14031
  import * as fs27 from "fs";
13926
- import * as path27 from "path";
14032
+ import * as path28 from "path";
13927
14033
 
13928
14034
  // scripts/lakebase/schema-diff.ts
13929
14035
  init_esm_shims();
@@ -14120,12 +14226,12 @@ function dbcli6(args) {
14120
14226
  // scripts/lakebase/schema-migrate.ts
14121
14227
  init_esm_shims();
14122
14228
  import * as fs26 from "fs";
14123
- import * as path26 from "path";
14229
+ import * as path27 from "path";
14124
14230
 
14125
14231
  // scripts/lakebase/migration-layout.ts
14126
14232
  init_esm_shims();
14127
14233
  import * as fs20 from "fs";
14128
- import * as path19 from "path";
14234
+ import * as path20 from "path";
14129
14235
  var MIGRATION_LANGUAGES = [
14130
14236
  "java",
14131
14237
  "kotlin",
@@ -14141,13 +14247,13 @@ var MIGRATION_DEFAULTS = {
14141
14247
  unknown: { path: "src/main/resources/db/migration", pattern: /^V\d+.*\.sql$/i, glob: "*.sql" }
14142
14248
  };
14143
14249
  function detectLanguageAt(dir) {
14144
- if (fs20.existsSync(path19.join(dir, "pom.xml"))) {
14145
- const kotlinDir = path19.join(dir, "src", "main", "kotlin");
14250
+ if (fs20.existsSync(path20.join(dir, "pom.xml"))) {
14251
+ const kotlinDir = path20.join(dir, "src", "main", "kotlin");
14146
14252
  if (fs20.existsSync(kotlinDir)) {
14147
14253
  return "kotlin";
14148
14254
  }
14149
14255
  try {
14150
- const pom = fs20.readFileSync(path19.join(dir, "pom.xml"), "utf-8");
14256
+ const pom = fs20.readFileSync(path20.join(dir, "pom.xml"), "utf-8");
14151
14257
  if (pom.includes("kotlin-maven-plugin")) {
14152
14258
  return "kotlin";
14153
14259
  }
@@ -14155,10 +14261,10 @@ function detectLanguageAt(dir) {
14155
14261
  }
14156
14262
  return "java";
14157
14263
  }
14158
- if (fs20.existsSync(path19.join(dir, "pyproject.toml")) || fs20.existsSync(path19.join(dir, "requirements.txt")) || fs20.existsSync(path19.join(dir, "alembic.ini"))) {
14264
+ if (fs20.existsSync(path20.join(dir, "pyproject.toml")) || fs20.existsSync(path20.join(dir, "requirements.txt")) || fs20.existsSync(path20.join(dir, "alembic.ini"))) {
14159
14265
  return "python";
14160
14266
  }
14161
- if (fs20.existsSync(path19.join(dir, "package.json"))) {
14267
+ if (fs20.existsSync(path20.join(dir, "package.json"))) {
14162
14268
  return "nodejs";
14163
14269
  }
14164
14270
  return "unknown";
@@ -14179,14 +14285,14 @@ function resolveMigrationLanguage(projectDir, configuredMigrationPath, override)
14179
14285
  if (!rel) {
14180
14286
  return "unknown";
14181
14287
  }
14182
- const rootResolved = path19.resolve(projectDir);
14183
- let dir = path19.resolve(projectDir, rel);
14184
- while (dir === rootResolved || dir.startsWith(rootResolved + path19.sep)) {
14288
+ const rootResolved = path20.resolve(projectDir);
14289
+ let dir = path20.resolve(projectDir, rel);
14290
+ while (dir === rootResolved || dir.startsWith(rootResolved + path20.sep)) {
14185
14291
  const lang = detectLanguageAt(dir);
14186
14292
  if (lang !== "unknown") {
14187
14293
  return lang;
14188
14294
  }
14189
- const parent = path19.dirname(dir);
14295
+ const parent = path20.dirname(dir);
14190
14296
  if (parent === dir) {
14191
14297
  break;
14192
14298
  }
@@ -14220,17 +14326,17 @@ function resolveMigrationLayout(args) {
14220
14326
  // scripts/lakebase/adapters/alembic-adapter.ts
14221
14327
  init_esm_shims();
14222
14328
  import * as fs22 from "fs";
14223
- import * as path21 from "path";
14329
+ import * as path22 from "path";
14224
14330
 
14225
14331
  // scripts/lakebase/schema-migrate-runners/alembic.ts
14226
14332
  init_esm_shims();
14227
14333
  import { spawn as spawn5 } from "child_process";
14228
14334
  import * as fs21 from "fs";
14229
- import * as path20 from "path";
14335
+ import * as path21 from "path";
14230
14336
  function resolveAlembicBin(projectDir) {
14231
14337
  const candidates = [
14232
- path20.join(projectDir, ".venv", "bin", "alembic"),
14233
- path20.join(projectDir, "venv", "bin", "alembic")
14338
+ path21.join(projectDir, ".venv", "bin", "alembic"),
14339
+ path21.join(projectDir, "venv", "bin", "alembic")
14234
14340
  ];
14235
14341
  for (const candidate of candidates) {
14236
14342
  try {
@@ -14244,7 +14350,7 @@ function spawnAlembic(projectDir, args, dsn) {
14244
14350
  return new Promise((resolve2, reject) => {
14245
14351
  const bin = resolveAlembicBin(projectDir);
14246
14352
  const env = { ...process.env };
14247
- env.PYTHONPATH = [projectDir, process.env.PYTHONPATH].filter(Boolean).join(path20.delimiter);
14353
+ env.PYTHONPATH = [projectDir, process.env.PYTHONPATH].filter(Boolean).join(path21.delimiter);
14248
14354
  if (dsn) env.DATABASE_URL = dsn;
14249
14355
  const child = spawn5(bin, args, {
14250
14356
  cwd: projectDir,
@@ -14292,10 +14398,10 @@ async function createAlembicRevision(opts) {
14292
14398
  const m = stdout.match(/Generating\s+(\S+\.py)/);
14293
14399
  if (m) return m[1].trim();
14294
14400
  for (const rel of ["migrations/versions", "alembic/versions"]) {
14295
- const dir = path20.join(opts.projectDir, rel);
14401
+ const dir = path21.join(opts.projectDir, rel);
14296
14402
  if (!fs21.existsSync(dir)) continue;
14297
14403
  const hit = fs21.readdirSync(dir).find((f) => f.startsWith(`${opts.revId}_`) && f.endsWith(".py"));
14298
- if (hit) return path20.join(dir, hit);
14404
+ if (hit) return path21.join(dir, hit);
14299
14405
  }
14300
14406
  throw new SchemaMigrationError(
14301
14407
  `alembic revision succeeded but the created file could not be located.
@@ -14430,8 +14536,8 @@ async function buildDsn2(args) {
14430
14536
  }
14431
14537
  function findVersionsDir(projectDir) {
14432
14538
  const candidates = [
14433
- path21.join(projectDir, "migrations", "versions"),
14434
- path21.join(projectDir, "alembic", "versions")
14539
+ path22.join(projectDir, "migrations", "versions"),
14540
+ path22.join(projectDir, "alembic", "versions")
14435
14541
  ];
14436
14542
  return candidates.find((p) => fs22.existsSync(p));
14437
14543
  }
@@ -14463,9 +14569,9 @@ var AlembicAdapter = {
14463
14569
  * here. Callers can still force-select via project.yaml#migration_tool.
14464
14570
  */
14465
14571
  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;
14572
+ if (fs22.existsSync(path22.join(projectDir, "alembic.ini"))) return true;
14573
+ if (fs22.existsSync(path22.join(projectDir, "migrations", "env.py"))) return true;
14574
+ if (fs22.existsSync(path22.join(projectDir, "alembic", "env.py"))) return true;
14469
14575
  return false;
14470
14576
  },
14471
14577
  async apply(args) {
@@ -14571,7 +14677,7 @@ var AlembicAdapter = {
14571
14677
  autogenerate: !!args.autogenerate,
14572
14678
  dsn
14573
14679
  });
14574
- return { status: "ok", version: revId, filename: path21.basename(created), path: created };
14680
+ return { status: "ok", version: revId, filename: path22.basename(created), path: created };
14575
14681
  } catch (err) {
14576
14682
  return {
14577
14683
  status: "error",
@@ -14588,7 +14694,7 @@ var AlembicAdapter = {
14588
14694
  if (heads.length <= 1) return { status: "noop", headsBefore: heads };
14589
14695
  if (args.dryRun) return { status: "ok", headsBefore: heads };
14590
14696
  const created = await mergeAlembicHeads(args.projectDir, args.message ?? "merge heads");
14591
- const mergeRevision = path21.basename(created).replace(/\.py$/, "").split("_")[0];
14697
+ const mergeRevision = path22.basename(created).replace(/\.py$/, "").split("_")[0];
14592
14698
  return { status: "ok", headsBefore: heads, mergeRevision, path: created };
14593
14699
  } catch (err) {
14594
14700
  return {
@@ -14604,12 +14710,12 @@ registerSchemaMigrationAdapter(AlembicAdapter);
14604
14710
  // scripts/lakebase/adapters/flyway-adapter.ts
14605
14711
  init_esm_shims();
14606
14712
  import * as fs23 from "fs";
14607
- import * as path23 from "path";
14713
+ import * as path24 from "path";
14608
14714
 
14609
14715
  // scripts/lakebase/schema-migrate-runners/flyway.ts
14610
14716
  init_esm_shims();
14611
14717
  import { spawn as spawn6 } from "child_process";
14612
- import * as path22 from "path";
14718
+ import * as path23 from "path";
14613
14719
  function dsnToFlywayEnv(dsn) {
14614
14720
  const u = new URL(dsn);
14615
14721
  const user = decodeURIComponent(u.username);
@@ -14619,7 +14725,7 @@ function dsnToFlywayEnv(dsn) {
14619
14725
  return { url, user, password };
14620
14726
  }
14621
14727
  function migrationsLocation(projectDir) {
14622
- return `filesystem:${path22.join(projectDir, "src", "main", "resources", "db", "migration")}`;
14728
+ return `filesystem:${path23.join(projectDir, "src", "main", "resources", "db", "migration")}`;
14623
14729
  }
14624
14730
  function runFlyway(ctx, args) {
14625
14731
  const { url, user, password } = dsnToFlywayEnv(ctx.dsn);
@@ -14720,7 +14826,7 @@ async function statusFlyway(ctx) {
14720
14826
  if (state === "SUCCESS" || state === "BASELINE") {
14721
14827
  current = m.version;
14722
14828
  } else if (state === "PENDING") {
14723
- const filename = m.filepath ? path22.basename(m.filepath) : `V${m.version}__migration.sql`;
14829
+ const filename = m.filepath ? path23.basename(m.filepath) : `V${m.version}__migration.sql`;
14724
14830
  pending.push({
14725
14831
  version: m.version,
14726
14832
  filename,
@@ -14743,7 +14849,7 @@ async function buildDsn3(args) {
14743
14849
  return result.url;
14744
14850
  }
14745
14851
  function listFlywayFiles(projectDir) {
14746
- const dir = path23.join(projectDir, "src", "main", "resources", "db", "migration");
14852
+ const dir = path24.join(projectDir, "src", "main", "resources", "db", "migration");
14747
14853
  if (!fs23.existsSync(dir)) return [];
14748
14854
  const files = fs23.readdirSync(dir).filter((f) => /^V\d+(\.\d+)*__.+\.sql$/.test(f));
14749
14855
  return files.map((filename) => {
@@ -14768,7 +14874,7 @@ var FlywayAdapter = {
14768
14874
  id: "flyway",
14769
14875
  languages: ["java", "kotlin"],
14770
14876
  detect(projectDir) {
14771
- return fs23.existsSync(path23.join(projectDir, "pom.xml"));
14877
+ return fs23.existsSync(path24.join(projectDir, "pom.xml"));
14772
14878
  },
14773
14879
  async apply(args) {
14774
14880
  const dsn = await buildDsn3(args);
@@ -14826,12 +14932,12 @@ var FlywayAdapter = {
14826
14932
  // optional-protocol shape makes this additive.
14827
14933
  async newMigration(args) {
14828
14934
  try {
14829
- const dir = path23.join(args.projectDir, "src", "main", "resources", "db", "migration");
14935
+ const dir = path24.join(args.projectDir, "src", "main", "resources", "db", "migration");
14830
14936
  fs23.mkdirSync(dir, { recursive: true });
14831
14937
  const version = migrationTimestamp();
14832
14938
  const slug = migrationSlug2(args.slug);
14833
14939
  const filename = `V${version}__${slug}.sql`;
14834
- const full = path23.join(dir, filename);
14940
+ const full = path24.join(dir, filename);
14835
14941
  if (fs23.existsSync(full)) throw new Error(`${filename} already exists`);
14836
14942
  fs23.writeFileSync(
14837
14943
  full,
@@ -14857,17 +14963,17 @@ registerSchemaMigrationAdapter(FlywayAdapter);
14857
14963
  // scripts/lakebase/adapters/knex-adapter.ts
14858
14964
  init_esm_shims();
14859
14965
  import * as fs25 from "fs";
14860
- import * as path25 from "path";
14966
+ import * as path26 from "path";
14861
14967
 
14862
14968
  // scripts/lakebase/schema-migrate-runners/knex.ts
14863
14969
  init_esm_shims();
14864
14970
  import { spawn as spawn7 } from "child_process";
14865
14971
  import * as fs24 from "fs";
14866
- import * as path24 from "path";
14972
+ import * as path25 from "path";
14867
14973
  var KNEXFILE_VARIANTS = ["knexfile.js", "knexfile.ts", "knexfile.mjs", "knexfile.cjs"];
14868
14974
  function findKnexfile(projectDir) {
14869
14975
  for (const name of KNEXFILE_VARIANTS) {
14870
- const p = path24.join(projectDir, name);
14976
+ const p = path25.join(projectDir, name);
14871
14977
  if (fs24.existsSync(p)) return p;
14872
14978
  }
14873
14979
  return void 0;
@@ -15020,7 +15126,7 @@ async function buildDsn4(args) {
15020
15126
  }
15021
15127
  var KNEXFILE_VARIANTS2 = ["knexfile.js", "knexfile.ts", "knexfile.mjs", "knexfile.cjs"];
15022
15128
  function listKnexFiles(projectDir) {
15023
- const dir = path25.join(projectDir, "migrations");
15129
+ const dir = path26.join(projectDir, "migrations");
15024
15130
  if (!fs25.existsSync(dir)) return [];
15025
15131
  const files = fs25.readdirSync(dir).filter((f) => (f.endsWith(".js") || f.endsWith(".ts")) && !f.startsWith("."));
15026
15132
  return files.map((filename) => {
@@ -15042,7 +15148,7 @@ var KnexAdapter = {
15042
15148
  * project.yaml#migration_tool.
15043
15149
  */
15044
15150
  detect(projectDir) {
15045
- return KNEXFILE_VARIANTS2.some((name) => fs25.existsSync(path25.join(projectDir, name)));
15151
+ return KNEXFILE_VARIANTS2.some((name) => fs25.existsSync(path26.join(projectDir, name)));
15046
15152
  },
15047
15153
  async apply(args) {
15048
15154
  const dsn = await buildDsn4(args);
@@ -15115,9 +15221,9 @@ var KnexAdapter = {
15115
15221
  async newMigration(args) {
15116
15222
  try {
15117
15223
  const created = await createKnexMigration({ projectDir: args.projectDir, slug: migrationSlug2(args.slug) });
15118
- const stem = path25.basename(created).replace(/\.(js|ts)$/, "");
15224
+ const stem = path26.basename(created).replace(/\.(js|ts)$/, "");
15119
15225
  const version = stem.match(/^(\d{14})_/)?.[1] ?? stem;
15120
- return { status: "ok", version, filename: path25.basename(created), path: created };
15226
+ return { status: "ok", version, filename: path26.basename(created), path: created };
15121
15227
  } catch (err) {
15122
15228
  return {
15123
15229
  status: "error",
@@ -15174,7 +15280,7 @@ function listSchemaMigrations(args = {}) {
15174
15280
  }
15175
15281
  }
15176
15282
  function listFlywayMigrations(projectDir) {
15177
- const dir = path26.join(projectDir, "src", "main", "resources", "db", "migration");
15283
+ const dir = path27.join(projectDir, "src", "main", "resources", "db", "migration");
15178
15284
  if (!fs26.existsSync(dir)) return [];
15179
15285
  const files = fs26.readdirSync(dir).filter((f) => /^V\d+(\.\d+)*__.+\.sql$/.test(f));
15180
15286
  return files.map((filename) => {
@@ -15186,8 +15292,8 @@ function listFlywayMigrations(projectDir) {
15186
15292
  }
15187
15293
  function listAlembicMigrations(projectDir) {
15188
15294
  const candidates = [
15189
- path26.join(projectDir, "migrations", "versions"),
15190
- path26.join(projectDir, "alembic", "versions")
15295
+ path27.join(projectDir, "migrations", "versions"),
15296
+ path27.join(projectDir, "alembic", "versions")
15191
15297
  ];
15192
15298
  const dir = candidates.find((p) => fs26.existsSync(p));
15193
15299
  if (!dir) return [];
@@ -15201,7 +15307,7 @@ function listAlembicMigrations(projectDir) {
15201
15307
  }).sort((a, b) => a.filename.localeCompare(b.filename));
15202
15308
  }
15203
15309
  function listKnexMigrations(projectDir) {
15204
- const dir = path26.join(projectDir, "migrations");
15310
+ const dir = path27.join(projectDir, "migrations");
15205
15311
  if (!fs26.existsSync(dir)) return [];
15206
15312
  const files = fs26.readdirSync(dir).filter((f) => (f.endsWith(".js") || f.endsWith(".ts")) && !f.startsWith("."));
15207
15313
  return files.map((filename) => {
@@ -15379,7 +15485,7 @@ async function runInfraSuite(args) {
15379
15485
  duration_ms: Date.now() - start
15380
15486
  };
15381
15487
  if (args.junitOutput) {
15382
- fs27.mkdirSync(path27.dirname(args.junitOutput), { recursive: true });
15488
+ fs27.mkdirSync(path28.dirname(args.junitOutput), { recursive: true });
15383
15489
  fs27.writeFileSync(args.junitOutput, formatJUnit(result), "utf8");
15384
15490
  }
15385
15491
  return result;
@@ -15427,7 +15533,7 @@ function escapeXml(s) {
15427
15533
  // scripts/lakebase/scm-claim-feature.ts
15428
15534
  init_esm_shims();
15429
15535
  import * as fs28 from "fs";
15430
- import * as path28 from "path";
15536
+ import * as path29 from "path";
15431
15537
  var ScmClaimError = class extends Error {
15432
15538
  constructor(message, code) {
15433
15539
  super(message);
@@ -15484,7 +15590,7 @@ async function claimFeatureBranch(args) {
15484
15590
  const current = readWorkflowState(args.projectDir);
15485
15591
  if (!current) {
15486
15592
  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.`,
15593
+ `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
15594
  "no-state-file"
15489
15595
  );
15490
15596
  }
@@ -15589,7 +15695,7 @@ function alreadyClaimedSentinel(state) {
15589
15695
  }
15590
15696
  function workflowStateFileExists(projectDir) {
15591
15697
  return fs28.existsSync(
15592
- path28.join(projectDir, ".lakebase/workflow-state.json")
15698
+ path29.join(projectDir, ".lakebase/workflow-state.json")
15593
15699
  );
15594
15700
  }
15595
15701
 
@@ -15853,6 +15959,7 @@ async function preparePr(args) {
15853
15959
  "no-github-remote"
15854
15960
  );
15855
15961
  }
15962
+ const ciFreshness = await ensureCiSecretsFreshFromEnv(args.projectDir, { ownerRepo });
15856
15963
  const now = (args.now ?? (() => /* @__PURE__ */ new Date()))();
15857
15964
  let prUrl = args.prUrlOverride ?? "";
15858
15965
  let prCreated = false;
@@ -15897,7 +16004,7 @@ async function preparePr(args) {
15897
16004
  pushed_at: now.toISOString()
15898
16005
  };
15899
16006
  writeWorkflowState(args.projectDir, next);
15900
- return { state: next, prUrl, prCreated };
16007
+ return { state: next, prUrl, prCreated, ciFreshness };
15901
16008
  }
15902
16009
  async function ensureAheadOfParent(cwd, branch, parent) {
15903
16010
  try {
@@ -16111,11 +16218,11 @@ async function sync(args) {
16111
16218
  // scripts/lakebase/workflow-drift.ts
16112
16219
  init_esm_shims();
16113
16220
  import * as fs29 from "fs";
16114
- import * as path29 from "path";
16221
+ import * as path30 from "path";
16115
16222
  function findKitTemplatesDir(start) {
16116
16223
  let dir = start;
16117
16224
  for (let i = 0; i < 6; i++) {
16118
- const candidate = path29.join(
16225
+ const candidate = path30.join(
16119
16226
  dir,
16120
16227
  "templates",
16121
16228
  "project",
@@ -16124,7 +16231,7 @@ function findKitTemplatesDir(start) {
16124
16231
  "workflows"
16125
16232
  );
16126
16233
  if (fs29.existsSync(candidate)) return candidate;
16127
- const parent = path29.dirname(dir);
16234
+ const parent = path30.dirname(dir);
16128
16235
  if (parent === dir) break;
16129
16236
  dir = parent;
16130
16237
  }
@@ -16148,13 +16255,13 @@ function unifiedDiff(name, projectContent, templateContent) {
16148
16255
  return out.join("\n");
16149
16256
  }
16150
16257
  function detectWorkflowDrift(args) {
16151
- const projectWorkflowsDir = path29.join(
16258
+ const projectWorkflowsDir = path30.join(
16152
16259
  args.projectDir,
16153
16260
  ".github",
16154
16261
  "workflows"
16155
16262
  );
16156
- const here = path29.dirname(new URL(import.meta.url).pathname);
16157
- const kitWorkflowsDir = args.kitDir ? path29.join(
16263
+ const here = path30.dirname(new URL(import.meta.url).pathname);
16264
+ const kitWorkflowsDir = args.kitDir ? path30.join(
16158
16265
  args.kitDir,
16159
16266
  "templates",
16160
16267
  "project",
@@ -16169,8 +16276,8 @@ function detectWorkflowDrift(args) {
16169
16276
  const files = [];
16170
16277
  for (const name of templateFiles) {
16171
16278
  seen.add(name);
16172
- const projectPath2 = path29.join(projectWorkflowsDir, name);
16173
- const templatePath = path29.join(kitWorkflowsDir, name);
16279
+ const projectPath2 = path30.join(projectWorkflowsDir, name);
16280
+ const templatePath = path30.join(kitWorkflowsDir, name);
16174
16281
  if (!fs29.existsSync(projectPath2)) {
16175
16282
  files.push({ name, status: "missing" });
16176
16283
  continue;
@@ -16207,10 +16314,10 @@ function detectWorkflowDrift(args) {
16207
16314
  function readKitVersion(kitWorkflowsDir) {
16208
16315
  let dir = kitWorkflowsDir;
16209
16316
  for (let i = 0; i < 5; i++) {
16210
- dir = path29.dirname(dir);
16317
+ dir = path30.dirname(dir);
16211
16318
  }
16212
16319
  try {
16213
- const raw = fs29.readFileSync(path29.join(dir, "package.json"), "utf-8");
16320
+ const raw = fs29.readFileSync(path30.join(dir, "package.json"), "utf-8");
16214
16321
  const pkg = JSON.parse(raw);
16215
16322
  return typeof pkg.version === "string" ? pkg.version : "unknown";
16216
16323
  } catch {
@@ -16227,7 +16334,7 @@ var COMMAND_HOOK_FILE_PATTERN = /\.(pre|post)-hook\.md$/;
16227
16334
  function findKitCommandsDir(start) {
16228
16335
  let dir = start;
16229
16336
  for (let i = 0; i < 6; i++) {
16230
- const candidate = path29.join(
16337
+ const candidate = path30.join(
16231
16338
  dir,
16232
16339
  "templates",
16233
16340
  "project",
@@ -16236,7 +16343,7 @@ function findKitCommandsDir(start) {
16236
16343
  "commands"
16237
16344
  );
16238
16345
  if (fs29.existsSync(candidate)) return candidate;
16239
- const parent = path29.dirname(dir);
16346
+ const parent = path30.dirname(dir);
16240
16347
  if (parent === dir) break;
16241
16348
  dir = parent;
16242
16349
  }
@@ -16249,9 +16356,9 @@ function parsePinnedVersion(content) {
16249
16356
  return m ? m[1] : void 0;
16250
16357
  }
16251
16358
  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);
16359
+ const projectCommandsDir = path30.join(args.projectDir, ".claude", "commands");
16360
+ const here = path30.dirname(new URL(import.meta.url).pathname);
16361
+ const kitCommandsDir = args.kitDir ? path30.join(args.kitDir, "templates", "project", "common", ".claude", "commands") : findKitCommandsDir(here);
16255
16362
  const kitVersion = readKitVersionFromCommandsDir(kitCommandsDir);
16256
16363
  const templateFiles = fs29.existsSync(kitCommandsDir) ? fs29.readdirSync(kitCommandsDir).filter((f) => f.endsWith(".md") && !COMMAND_HOOK_FILE_PATTERN.test(f)) : [];
16257
16364
  const projectFiles = fs29.existsSync(projectCommandsDir) ? fs29.readdirSync(projectCommandsDir).filter((f) => f.endsWith(".md") && !COMMAND_HOOK_FILE_PATTERN.test(f)) : [];
@@ -16259,8 +16366,8 @@ function detectCommandDrift(args) {
16259
16366
  const files = [];
16260
16367
  for (const name of templateFiles) {
16261
16368
  seen.add(name);
16262
- const projectPath2 = path29.join(projectCommandsDir, name);
16263
- const templatePath = path29.join(kitCommandsDir, name);
16369
+ const projectPath2 = path30.join(projectCommandsDir, name);
16370
+ const templatePath = path30.join(kitCommandsDir, name);
16264
16371
  const templateRaw = fs29.readFileSync(templatePath, "utf8");
16265
16372
  if (!fs29.existsSync(projectPath2)) {
16266
16373
  files.push({ name, status: "missing", kit_version: kitVersion });
@@ -16304,10 +16411,10 @@ function detectCommandDrift(args) {
16304
16411
  function readKitVersionFromCommandsDir(kitCommandsDir) {
16305
16412
  let dir = kitCommandsDir;
16306
16413
  for (let i = 0; i < 5; i++) {
16307
- dir = path29.dirname(dir);
16414
+ dir = path30.dirname(dir);
16308
16415
  }
16309
16416
  try {
16310
- const raw = fs29.readFileSync(path29.join(dir, "package.json"), "utf-8");
16417
+ const raw = fs29.readFileSync(path30.join(dir, "package.json"), "utf-8");
16311
16418
  const pkg = JSON.parse(raw);
16312
16419
  return typeof pkg.version === "string" ? pkg.version : "unknown";
16313
16420
  } catch {
@@ -16324,13 +16431,13 @@ function detectScaffoldedDrift(args) {
16324
16431
  };
16325
16432
  }
16326
16433
  function updateWorkflows(args) {
16327
- const projectWorkflowsDir = path29.join(
16434
+ const projectWorkflowsDir = path30.join(
16328
16435
  args.projectDir,
16329
16436
  ".github",
16330
16437
  "workflows"
16331
16438
  );
16332
- const here = path29.dirname(new URL(import.meta.url).pathname);
16333
- const kitWorkflowsDir = args.kitDir ? path29.join(
16439
+ const here = path30.dirname(new URL(import.meta.url).pathname);
16440
+ const kitWorkflowsDir = args.kitDir ? path30.join(
16334
16441
  args.kitDir,
16335
16442
  "templates",
16336
16443
  "project",
@@ -16351,8 +16458,8 @@ function updateWorkflows(args) {
16351
16458
  const files = [];
16352
16459
  for (const name of templateFiles) {
16353
16460
  seen.add(name);
16354
- const projectPath2 = path29.join(projectWorkflowsDir, name);
16355
- const templatePath = path29.join(kitWorkflowsDir, name);
16461
+ const projectPath2 = path30.join(projectWorkflowsDir, name);
16462
+ const templatePath = path30.join(kitWorkflowsDir, name);
16356
16463
  const templateRaw = fs29.readFileSync(templatePath, "utf-8");
16357
16464
  const desired = substitute ? applyPlaceholders(templateRaw, version) : templateRaw;
16358
16465
  const existed = fs29.existsSync(projectPath2);
@@ -16373,7 +16480,7 @@ function updateWorkflows(args) {
16373
16480
  if (pruneExtras) {
16374
16481
  for (const name of projectFiles) {
16375
16482
  if (seen.has(name)) continue;
16376
- const projectPath2 = path29.join(projectWorkflowsDir, name);
16483
+ const projectPath2 = path30.join(projectWorkflowsDir, name);
16377
16484
  if (!dryRun) {
16378
16485
  fs29.unlinkSync(projectPath2);
16379
16486
  }
@@ -16560,6 +16667,15 @@ async function mergeFeature(args) {
16560
16667
  );
16561
16668
  }
16562
16669
  }
16670
+ {
16671
+ const ensure = args.ensureCiFresh ?? ((repo) => ensureCiSecretsFreshFromEnv(args.projectDir, { ownerRepo: repo }));
16672
+ try {
16673
+ const r = await ensure(ownerRepo);
16674
+ if (r.action !== "ok" && r.action !== "skipped") preflightNotes.push(`CI-auth preflight: ${r.reason}`);
16675
+ } catch (err) {
16676
+ 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.`);
16677
+ }
16678
+ }
16563
16679
  {
16564
16680
  const fetchState = args.fetchPrState ?? ((o, h) => getPullRequest(o, h));
16565
16681
  const prState = await fetchState(ownerRepo, current.branch).catch(() => void 0);
@@ -17000,11 +17116,11 @@ function parentForTopology(t, defaultLeaf) {
17000
17116
  // scripts/lakebase/scm-doctor.ts
17001
17117
  init_esm_shims();
17002
17118
  import * as fs30 from "fs";
17003
- import * as path30 from "path";
17119
+ import * as path31 from "path";
17004
17120
  var FEATURE_PREFIX = "feature/";
17005
17121
  var TIER_LEAFS2 = DEFAULT_PROTECTED_TIER_NAMES;
17006
17122
  function readEnv(projectDir) {
17007
- const envPath = path30.join(projectDir, ".env");
17123
+ const envPath = path31.join(projectDir, ".env");
17008
17124
  const out = /* @__PURE__ */ new Map();
17009
17125
  if (!fs30.existsSync(envPath)) return out;
17010
17126
  const lines = fs30.readFileSync(envPath, "utf8").split("\n");
@@ -17062,7 +17178,7 @@ async function runDoctor(args, deps = {}) {
17062
17178
  }
17063
17179
  for (const wf of ["pr.yml", "merge.yml"]) {
17064
17180
  try {
17065
- const p = path30.join(projectDir, ".github", "workflows", wf);
17181
+ const p = path31.join(projectDir, ".github", "workflows", wf);
17066
17182
  if (!fs30.existsSync(p)) continue;
17067
17183
  const body = fs30.readFileSync(p, "utf8");
17068
17184
  if (/lakebase-scm-utils#v\d/.test(body)) {
@@ -17279,7 +17395,7 @@ async function fixFinding(args) {
17279
17395
  );
17280
17396
  }
17281
17397
  const sanitized = sanitizeBranchName(branch);
17282
- const envFile = path30.join(args.projectDir, ".env");
17398
+ const envFile = path31.join(args.projectDir, ".env");
17283
17399
  updateEnvConnection({
17284
17400
  envPath: envFile,
17285
17401
  projectId: readEnvVar(envFile, "LAKEBASE_PROJECT_ID") ?? "",
@@ -17382,10 +17498,10 @@ function shellEscape3(s) {
17382
17498
  // scripts/lakebase/doctor.ts
17383
17499
  init_esm_shims();
17384
17500
  import * as fs31 from "fs";
17385
- import * as path31 from "path";
17501
+ import * as path32 from "path";
17386
17502
  import * as cp6 from "child_process";
17387
17503
  function readEnvFile(projectDir) {
17388
- const envPath = path31.join(projectDir, ".env");
17504
+ const envPath = path32.join(projectDir, ".env");
17389
17505
  if (!fs31.existsSync(envPath)) return {};
17390
17506
  const out = {};
17391
17507
  for (const line of fs31.readFileSync(envPath, "utf8").split("\n")) {
@@ -17643,7 +17759,7 @@ function checkEnv(projectDir) {
17643
17759
  name: "env-file",
17644
17760
  status: "warn",
17645
17761
  message: ".env not found",
17646
- detail: { projectDir, envPath: path31.join(projectDir, ".env") },
17762
+ detail: { projectDir, envPath: path32.join(projectDir, ".env") },
17647
17763
  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)."
17648
17764
  };
17649
17765
  }
@@ -18137,7 +18253,7 @@ async function listMigrationsOnBranch(args) {
18137
18253
  // scripts/git/commits.ts
18138
18254
  init_esm_shims();
18139
18255
  import { existsSync as existsSync30 } from "fs";
18140
- import { join as join33 } from "path";
18256
+ import { join as join34 } from "path";
18141
18257
  var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
18142
18258
  ".py",
18143
18259
  ".ts",
@@ -18240,7 +18356,7 @@ async function commitAllIfChanged(args) {
18240
18356
  await exec2("git add -A", { cwd: args.cwd });
18241
18357
  }
18242
18358
  for (const inc of args.include ?? []) {
18243
- if (existsSync30(join33(args.cwd, inc))) {
18359
+ if (existsSync30(join34(args.cwd, inc))) {
18244
18360
  await exec2(`git add -f -- ${shq(inc)}`, { cwd: args.cwd });
18245
18361
  }
18246
18362
  }
@@ -18383,13 +18499,13 @@ async function stashDropAll(args) {
18383
18499
  // scripts/git/rebase.ts
18384
18500
  init_esm_shims();
18385
18501
  import * as fs32 from "fs";
18386
- import * as path32 from "path";
18502
+ import * as path33 from "path";
18387
18503
  async function abortRebase(args) {
18388
18504
  await exec2("git rebase --abort", { cwd: args.cwd });
18389
18505
  }
18390
18506
  async function isRebasing(args) {
18391
18507
  try {
18392
- return fs32.existsSync(path32.join(args.cwd, ".git/rebase-merge")) || fs32.existsSync(path32.join(args.cwd, ".git/rebase-apply"));
18508
+ return fs32.existsSync(path33.join(args.cwd, ".git/rebase-merge")) || fs32.existsSync(path33.join(args.cwd, ".git/rebase-apply"));
18393
18509
  } catch {
18394
18510
  return false;
18395
18511
  }
@@ -18605,6 +18721,8 @@ function withProxyEnv(base = {}) {
18605
18721
  return out;
18606
18722
  }
18607
18723
  export {
18724
+ CI_TOKEN_LIFETIME_SECONDS,
18725
+ CI_TOKEN_REMINT_MARGIN_SECONDS,
18608
18726
  CONVENTION_TIER_DEFAULTS,
18609
18727
  DEFAULT_PROTECTED_TIER_NAMES,
18610
18728
  FIXABLE_FINDING_IDS,
@@ -18668,6 +18786,8 @@ export {
18668
18786
  checkoutDetached,
18669
18787
  checkoutPaired,
18670
18788
  cherryPick,
18789
+ ciTokenComment,
18790
+ ciTokenExpiry,
18671
18791
  claimFeatureBranch,
18672
18792
  clearRetentionCache,
18673
18793
  cloneRepo,
@@ -18704,6 +18824,7 @@ export {
18704
18824
  deleteLakebaseProject,
18705
18825
  deleteLocalBranch,
18706
18826
  deletePairedBranch,
18827
+ deleteProjectArgs,
18707
18828
  deleteRemoteBranch,
18708
18829
  deleteRemoteTag,
18709
18830
  deleteRepo,
@@ -18738,6 +18859,8 @@ export {
18738
18859
  endpointPath,
18739
18860
  ensureAppEndpoint,
18740
18861
  ensureCachedArchive,
18862
+ ensureCiSecretsFresh,
18863
+ ensureCiSecretsFreshFromEnv,
18741
18864
  ensureEndpoint,
18742
18865
  ensureLakebaseSecretAuth,
18743
18866
  ensureProfilePinned,