@databricks-solutions/lakebase-scm-utils 0.3.6 → 0.3.8

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 (51) hide show
  1. package/dist/scripts/index.cjs +40 -21
  2. package/dist/scripts/index.d.cts +1 -1
  3. package/dist/scripts/index.d.ts +1 -1
  4. package/dist/scripts/index.js +39 -21
  5. package/dist/scripts/lakebase/ci-resolve-branch.cli.cjs +2 -2
  6. package/dist/scripts/lakebase/ci-resolve-branch.cli.js +2 -2
  7. package/dist/scripts/lakebase/collapse-heads.cli.cjs +2 -2
  8. package/dist/scripts/lakebase/collapse-heads.cli.js +2 -2
  9. package/dist/scripts/lakebase/detect-language.cli.cjs +2 -2
  10. package/dist/scripts/lakebase/detect-language.cli.js +2 -2
  11. package/dist/scripts/lakebase/doctor.cli.cjs +2 -2
  12. package/dist/scripts/lakebase/doctor.cli.js +2 -2
  13. package/dist/scripts/lakebase/get-connection.cli.cjs +2 -2
  14. package/dist/scripts/lakebase/get-connection.cli.js +2 -2
  15. package/dist/scripts/lakebase/index.cjs +40 -21
  16. package/dist/scripts/lakebase/index.d.cts +13 -4
  17. package/dist/scripts/lakebase/index.d.ts +13 -4
  18. package/dist/scripts/lakebase/index.js +39 -21
  19. package/dist/scripts/lakebase/infra-runner.cli.cjs +2 -2
  20. package/dist/scripts/lakebase/infra-runner.cli.js +2 -2
  21. package/dist/scripts/lakebase/new-migration.cli.cjs +2 -2
  22. package/dist/scripts/lakebase/new-migration.cli.js +2 -2
  23. package/dist/scripts/lakebase/schema-diff.cli.cjs +2 -2
  24. package/dist/scripts/lakebase/schema-diff.cli.js +2 -2
  25. package/dist/scripts/lakebase/schema-migrate.cli.cjs +2 -2
  26. package/dist/scripts/lakebase/schema-migrate.cli.js +2 -2
  27. package/dist/scripts/lakebase/scm-abandon-feature.cli.cjs +37 -22
  28. package/dist/scripts/lakebase/scm-abandon-feature.cli.js +34 -22
  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 +36 -21
  32. package/dist/scripts/lakebase/scm-doctor.cli.js +36 -21
  33. package/dist/scripts/lakebase/scm-merge.cli.cjs +2 -2
  34. package/dist/scripts/lakebase/scm-merge.cli.js +2 -2
  35. package/dist/scripts/lakebase/scm-park-feature.cli.cjs +586 -0
  36. package/dist/scripts/lakebase/scm-park-feature.cli.d.cts +4 -0
  37. package/dist/scripts/lakebase/scm-park-feature.cli.d.ts +4 -0
  38. package/dist/scripts/lakebase/scm-park-feature.cli.js +546 -0
  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/util/index.cjs +2 -2
  42. package/dist/scripts/util/index.js +2 -2
  43. package/package.json +2 -1
  44. package/scripts/lakebase/scm-abandon-feature.cli.ts +3 -3
  45. package/scripts/lakebase/scm-abandon-feature.ts +19 -83
  46. package/scripts/lakebase/scm-park-feature.cli.ts +150 -0
  47. package/scripts/lakebase/scm-park-feature.ts +71 -0
  48. package/scripts/lakebase/scm-release-claim.ts +146 -0
  49. package/templates/project/common/consort-capture.ts +121 -3
  50. package/templates/project/python/tests/__pycache__/conftest.cpython-314.pyc +0 -0
  51. package/templates/project/python/tests/conftest.py +90 -1
@@ -0,0 +1,546 @@
1
+ #!/usr/bin/env node
2
+
3
+ // scripts/lakebase/scm-park-feature.cli.ts
4
+ import * as path2 from "path";
5
+
6
+ // scripts/util/cli-entry.ts
7
+ import { realpathSync } from "fs";
8
+ import { fileURLToPath } from "url";
9
+ function isCliEntry(importMetaUrl) {
10
+ const invokedRaw = process.argv[1];
11
+ if (!invokedRaw) return false;
12
+ let invokedResolved;
13
+ let moduleResolved;
14
+ try {
15
+ invokedResolved = realpathSync(invokedRaw);
16
+ } catch {
17
+ return false;
18
+ }
19
+ try {
20
+ moduleResolved = realpathSync(fileURLToPath(importMetaUrl));
21
+ } catch {
22
+ return false;
23
+ }
24
+ return invokedResolved === moduleResolved;
25
+ }
26
+
27
+ // scripts/util/exec.ts
28
+ import * as cp from "child_process";
29
+ function shq(s) {
30
+ return `'${s.replace(/'/g, "'\\''")}'`;
31
+ }
32
+ function exec2(command, opts = {}) {
33
+ return new Promise((resolve2, reject) => {
34
+ const options = {
35
+ cwd: opts.cwd,
36
+ timeout: opts.timeout ?? 6e4
37
+ };
38
+ if (opts.env) {
39
+ options.env = { ...process.env, ...opts.env };
40
+ }
41
+ cp.exec(command, options, (err, stdout, stderr) => {
42
+ if (err) {
43
+ const msg = String(stderr || err.message);
44
+ reject(new Error(`${command}: ${msg}`));
45
+ return;
46
+ }
47
+ resolve2(String(stdout).trim());
48
+ });
49
+ });
50
+ }
51
+
52
+ // scripts/git/inspect.ts
53
+ async function getCurrentBranch(args) {
54
+ try {
55
+ const name = await exec2("git rev-parse --abbrev-ref HEAD", {
56
+ cwd: args.cwd
57
+ });
58
+ return name === "HEAD" ? "" : name;
59
+ } catch {
60
+ return "";
61
+ }
62
+ }
63
+
64
+ // scripts/git/status.ts
65
+ async function isDirty(args) {
66
+ try {
67
+ const ignore = args.ignore ?? [];
68
+ const untrackedFlag = args.untracked === false ? " --untracked-files=no" : "";
69
+ let command = `git status --porcelain${untrackedFlag}`;
70
+ if (ignore.length > 0) {
71
+ const excludes = ignore.map((p) => shq(`:(exclude)${p.replace(/\/+$/, "")}`)).join(" ");
72
+ command = `git status --porcelain${untrackedFlag} -- . ${excludes}`;
73
+ }
74
+ const out = await exec2(command, { cwd: args.cwd });
75
+ return out.trim().length > 0;
76
+ } catch {
77
+ return false;
78
+ }
79
+ }
80
+
81
+ // scripts/lakebase/scm-workflow-state.ts
82
+ import * as fs from "fs";
83
+ import * as path from "path";
84
+ import { execFileSync } from "child_process";
85
+ function isGitTracked(projectDir, rel) {
86
+ try {
87
+ execFileSync("git", ["ls-files", "--error-unmatch", "--", rel], { cwd: projectDir, stdio: "ignore" });
88
+ return true;
89
+ } catch {
90
+ return false;
91
+ }
92
+ }
93
+ function ensureWorkflowStateUntracked(projectDir) {
94
+ const rel = ".lakebase/workflow-state.json";
95
+ try {
96
+ if (isGitTracked(projectDir, rel)) {
97
+ execFileSync("git", ["rm", "--cached", "--quiet", "--ignore-unmatch", "--", rel], { cwd: projectDir, stdio: "ignore" });
98
+ }
99
+ } catch {
100
+ }
101
+ try {
102
+ const gitignore = path.join(projectDir, ".gitignore");
103
+ const existing = fs.existsSync(gitignore) ? fs.readFileSync(gitignore, "utf8") : "";
104
+ if (!existing.split("\n").some((l) => l.trim() === rel)) {
105
+ const sep = existing === "" || existing.endsWith("\n") ? "" : "\n";
106
+ fs.appendFileSync(
107
+ gitignore,
108
+ `${sep}# Runtime SCM claim state (per working tree): a branch checkout must never restore
109
+ # a stale committed claim over the live one (issue #203 / Finding 28).
110
+ ${rel}
111
+ `
112
+ );
113
+ }
114
+ } catch {
115
+ }
116
+ }
117
+ var SCM_STATES = [
118
+ "scaffold-complete",
119
+ "feature-claimed",
120
+ "pr-ready",
121
+ "ci-green",
122
+ "merged"
123
+ ];
124
+ var STATE_INDEX = SCM_STATES.reduce(
125
+ (acc, s, i) => ({ ...acc, [s]: i }),
126
+ {}
127
+ );
128
+ var STATE_FILE_REL = ".lakebase/workflow-state.json";
129
+ function stateFilePath(projectDir) {
130
+ return path.join(projectDir, STATE_FILE_REL);
131
+ }
132
+ function readWorkflowState(projectDir) {
133
+ const p = stateFilePath(projectDir);
134
+ if (!fs.existsSync(p)) return null;
135
+ const raw = fs.readFileSync(p, "utf8");
136
+ let parsed;
137
+ try {
138
+ parsed = JSON.parse(raw);
139
+ } catch (e) {
140
+ throw new Error(
141
+ `Failed to parse ${STATE_FILE_REL}: ${e.message}`
142
+ );
143
+ }
144
+ const result = validateWorkflowState(parsed);
145
+ if (!result.ok) {
146
+ const summary = result.errors.map((e) => ` - ${e.path}: ${e.message}`).join("\n");
147
+ throw new Error(
148
+ `Invalid ${STATE_FILE_REL}:
149
+ ${summary}
150
+
151
+ Fix the file or delete it to re-init.`
152
+ );
153
+ }
154
+ return result.value;
155
+ }
156
+ function writeWorkflowState(projectDir, state) {
157
+ const result = validateWorkflowState(state);
158
+ if (!result.ok) {
159
+ const summary = result.errors.map((e) => ` - ${e.path}: ${e.message}`).join("\n");
160
+ throw new Error(`Refusing to write invalid SCM state:
161
+ ${summary}`);
162
+ }
163
+ ensureWorkflowStateUntracked(projectDir);
164
+ const dir = path.join(projectDir, ".lakebase");
165
+ fs.mkdirSync(dir, { recursive: true });
166
+ const target = stateFilePath(projectDir);
167
+ const tmp = `${target}.tmp`;
168
+ const ordered = orderForOutput(result.value);
169
+ fs.writeFileSync(tmp, `${JSON.stringify(ordered, null, 2)}
170
+ `, "utf8");
171
+ fs.renameSync(tmp, target);
172
+ }
173
+ function validateWorkflowState(value) {
174
+ const errors = [];
175
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
176
+ return {
177
+ ok: false,
178
+ errors: [{ path: "$", message: "must be an object" }]
179
+ };
180
+ }
181
+ const v = value;
182
+ if (v.version !== 1) {
183
+ errors.push({ path: "version", message: `must be 1, got ${String(v.version)}` });
184
+ }
185
+ if (typeof v.state !== "string" || !SCM_STATES.includes(v.state)) {
186
+ errors.push({
187
+ path: "state",
188
+ message: `must be one of ${SCM_STATES.join(" | ")}`
189
+ });
190
+ }
191
+ if (v.tier_topology !== 1 && v.tier_topology !== 2 && v.tier_topology !== 3) {
192
+ errors.push({
193
+ path: "tier_topology",
194
+ message: "must be 1, 2, or 3"
195
+ });
196
+ }
197
+ if (typeof v.project_id !== "string" || v.project_id.length === 0) {
198
+ errors.push({
199
+ path: "project_id",
200
+ message: "must be a non-empty string"
201
+ });
202
+ }
203
+ const stringFields = [
204
+ "feature_id",
205
+ "branch",
206
+ "parent_branch",
207
+ "lakebase_branch_uid",
208
+ "claimed_at",
209
+ "pr_url",
210
+ "pushed_at",
211
+ "ci_run_url",
212
+ "ci_green_at",
213
+ "merged_at",
214
+ "migrate_run_url",
215
+ "migrate_completed_at",
216
+ "$schema"
217
+ ];
218
+ for (const key of stringFields) {
219
+ if (v[key] === void 0) continue;
220
+ if (typeof v[key] !== "string" || v[key].length === 0) {
221
+ errors.push({
222
+ path: key,
223
+ message: "must be a non-empty string when present"
224
+ });
225
+ }
226
+ }
227
+ const requiredForState = {
228
+ "scaffold-complete": [],
229
+ "feature-claimed": [
230
+ "feature_id",
231
+ "branch",
232
+ "parent_branch",
233
+ "lakebase_branch_uid",
234
+ "claimed_at"
235
+ ],
236
+ "pr-ready": [
237
+ "feature_id",
238
+ "branch",
239
+ "parent_branch",
240
+ "lakebase_branch_uid",
241
+ "claimed_at",
242
+ "pr_url",
243
+ "pushed_at"
244
+ ],
245
+ "ci-green": [
246
+ "feature_id",
247
+ "branch",
248
+ "parent_branch",
249
+ "lakebase_branch_uid",
250
+ "claimed_at",
251
+ "pr_url",
252
+ "pushed_at",
253
+ "ci_run_url",
254
+ "ci_green_at"
255
+ ],
256
+ merged: [
257
+ "feature_id",
258
+ "branch",
259
+ "parent_branch",
260
+ "lakebase_branch_uid",
261
+ "claimed_at",
262
+ "pr_url",
263
+ "pushed_at",
264
+ "ci_run_url",
265
+ "ci_green_at",
266
+ "merged_at"
267
+ ]
268
+ };
269
+ if (typeof v.state === "string" && SCM_STATES.includes(v.state)) {
270
+ for (const key of requiredForState[v.state]) {
271
+ if (v[key] === void 0) {
272
+ errors.push({
273
+ path: key,
274
+ message: `required when state is "${v.state}"`
275
+ });
276
+ }
277
+ }
278
+ }
279
+ const allowedKeys = /* @__PURE__ */ new Set([
280
+ "$schema",
281
+ "version",
282
+ "state",
283
+ "tier_topology",
284
+ "project_id",
285
+ "feature_id",
286
+ "branch",
287
+ "parent_branch",
288
+ "lakebase_branch_uid",
289
+ "claimed_at",
290
+ "pr_url",
291
+ "pushed_at",
292
+ "ci_run_url",
293
+ "ci_green_at",
294
+ "merged_at",
295
+ "migrate_run_url",
296
+ "migrate_completed_at"
297
+ ]);
298
+ for (const key of Object.keys(v)) {
299
+ if (!allowedKeys.has(key)) {
300
+ errors.push({ path: key, message: "unknown property" });
301
+ }
302
+ }
303
+ if (errors.length > 0) return { ok: false, errors };
304
+ return { ok: true, value: v };
305
+ }
306
+ function orderForOutput(state) {
307
+ const keyOrder = [
308
+ "$schema",
309
+ "version",
310
+ "state",
311
+ "tier_topology",
312
+ "project_id",
313
+ "feature_id",
314
+ "branch",
315
+ "parent_branch",
316
+ "lakebase_branch_uid",
317
+ "claimed_at",
318
+ "pr_url",
319
+ "pushed_at",
320
+ "ci_run_url",
321
+ "ci_green_at",
322
+ "merged_at",
323
+ "migrate_run_url",
324
+ "migrate_completed_at"
325
+ ];
326
+ const out = {};
327
+ for (const k of keyOrder) {
328
+ if (state[k] !== void 0) {
329
+ out[k] = state[k];
330
+ }
331
+ }
332
+ return out;
333
+ }
334
+
335
+ // scripts/lakebase/scm-release-claim.ts
336
+ var ScmReleaseError = class extends Error {
337
+ constructor(message, code) {
338
+ super(message);
339
+ this.code = code;
340
+ this.name = "ScmReleaseError";
341
+ }
342
+ code;
343
+ };
344
+ async function prepareClaimRelease(args, verb) {
345
+ const current = readWorkflowState(args.projectDir);
346
+ if (!current) {
347
+ throw new ScmReleaseError(
348
+ `No SCM workflow state found at ${args.projectDir}/.lakebase/workflow-state.json.`,
349
+ "no-state-file"
350
+ );
351
+ }
352
+ if (current.state !== "feature-claimed") {
353
+ throw new ScmReleaseError(
354
+ `${verb} refuses state "${current.state}". Only feature-claimed is ${verb}able; later states must complete or be reverted via gh.`,
355
+ "bad-precondition"
356
+ );
357
+ }
358
+ if (!current.feature_id || !current.branch || !current.parent_branch || !current.lakebase_branch_uid) {
359
+ throw new ScmReleaseError(
360
+ `feature-claimed row is missing required invariants. Cannot ${verb} safely; consider re-adopting state first.`,
361
+ "missing-claim-fields"
362
+ );
363
+ }
364
+ if (!args.force) {
365
+ const dirty = await isDirty({ cwd: args.projectDir });
366
+ if (dirty) {
367
+ throw new ScmReleaseError(
368
+ `Working tree has uncommitted changes; refusing to ${verb} (switching HEAD off the feature branch would strand them). Commit / stash / discard first, or pass --force.`,
369
+ "dirty-working-tree"
370
+ );
371
+ }
372
+ }
373
+ const switchTo = args.switchTo ?? current.parent_branch;
374
+ const warnings = [];
375
+ const headBranch = await getCurrentBranch({ cwd: args.projectDir });
376
+ if (headBranch === current.branch) {
377
+ try {
378
+ await exec2(`git checkout ${JSON.stringify(switchTo)}`, {
379
+ cwd: args.projectDir,
380
+ timeout: 1e4
381
+ });
382
+ } catch (err) {
383
+ warnings.push(
384
+ `git checkout ${switchTo} failed: ${err instanceof Error ? err.message : String(err)}. Local branch state may be left on the feature branch.`
385
+ );
386
+ }
387
+ }
388
+ return { current, switchTo, warnings };
389
+ }
390
+ function resetToScaffoldComplete(projectDir, current) {
391
+ const reset = {
392
+ $schema: current.$schema,
393
+ version: 1,
394
+ state: "scaffold-complete",
395
+ tier_topology: current.tier_topology,
396
+ project_id: current.project_id
397
+ };
398
+ writeWorkflowState(projectDir, reset);
399
+ return reset;
400
+ }
401
+
402
+ // scripts/lakebase/scm-park-feature.ts
403
+ async function parkFeatureBranch(args) {
404
+ const { current, switchTo, warnings } = await prepareClaimRelease(
405
+ { projectDir: args.projectDir, force: args.force, switchTo: args.switchTo },
406
+ "park"
407
+ );
408
+ const reset = resetToScaffoldComplete(args.projectDir, current);
409
+ return {
410
+ state: reset,
411
+ keptBranch: current.branch,
412
+ parkedFeatureId: current.feature_id,
413
+ switchedTo: switchTo,
414
+ warnings
415
+ };
416
+ }
417
+
418
+ // scripts/lakebase/scm-park-feature.cli.ts
419
+ function parseArgs(argv) {
420
+ const out = {};
421
+ for (let i = 0; i < argv.length; i++) {
422
+ const a = argv[i];
423
+ switch (a) {
424
+ case "--project-dir":
425
+ case "--cwd":
426
+ out.projectDir = argv[++i];
427
+ break;
428
+ case "--switch-to":
429
+ out.switchTo = argv[++i];
430
+ break;
431
+ case "--force":
432
+ out.force = true;
433
+ break;
434
+ case "--json":
435
+ out.json = true;
436
+ break;
437
+ case "--pretty":
438
+ out.pretty = true;
439
+ break;
440
+ case "--help":
441
+ case "-h":
442
+ out.help = true;
443
+ break;
444
+ }
445
+ }
446
+ return out;
447
+ }
448
+ var HELP = `lakebase-scm-park-feature (phase B+)
449
+
450
+ Release a feature claim but KEEP its paired branch: switch HEAD to the parent
451
+ tier, reset workflow-state to scaffold-complete, leave the Lakebase + git
452
+ branch intact. Frees the single SCM claim so another feature can be claimed
453
+ and driven; resume the parked feature later with
454
+ 'lakebase-scm-claim-feature-branch <feature>' (it reuses the kept branch).
455
+
456
+ Usage:
457
+ lakebase-scm-park-feature [flags]
458
+
459
+ Flags:
460
+ --project-dir <dir> Project root (default: cwd)
461
+ --switch-to <branch> Branch to checkout after releasing (default: parent_branch)
462
+ --force Allow park even with a dirty working tree
463
+ (edits are kept but stranded on the feature branch)
464
+ --json Machine-readable JSON output
465
+ --pretty Pretty-print JSON (only with --json)
466
+ -h, --help Show this help
467
+
468
+ Exit codes:
469
+ 0 = parked (claim freed, branch kept, state reset to scaffold-complete)
470
+ 1 = no state file
471
+ 2 = precondition refused (wrong state, dirty tree without --force, missing fields)
472
+ 3 = substrate failure
473
+ `;
474
+ function renderHuman(report) {
475
+ if (!report.ok) {
476
+ return `lakebase-scm-park-feature: ${report.error?.code}
477
+
478
+ ${report.error?.message}`;
479
+ }
480
+ const r = report.result;
481
+ const lines = ["Feature parked (branch kept):"];
482
+ lines.push(` state : ${r.state.state}`);
483
+ lines.push(` parked_feature : ${r.parkedFeatureId}`);
484
+ lines.push(` kept_branch : ${r.keptBranch}`);
485
+ lines.push(` switched_to : ${r.switchedTo}`);
486
+ lines.push("");
487
+ lines.push(` resume with : lakebase-scm-claim-feature-branch ${r.parkedFeatureId}`);
488
+ if (r.warnings.length > 0) {
489
+ lines.push("");
490
+ lines.push("warnings:");
491
+ for (const w of r.warnings) lines.push(` - ${w}`);
492
+ }
493
+ return lines.join("\n");
494
+ }
495
+ function exitCodeForError(err) {
496
+ if (err instanceof ScmReleaseError) {
497
+ if (err.code === "no-state-file") return 1;
498
+ return 2;
499
+ }
500
+ return 3;
501
+ }
502
+ async function runScmParkFeatureCli(argv) {
503
+ const args = parseArgs(argv);
504
+ if (args.help) {
505
+ process.stdout.write(`${HELP}
506
+ `);
507
+ return 0;
508
+ }
509
+ const projectDir = path2.resolve(args.projectDir ?? process.cwd());
510
+ try {
511
+ const result = await parkFeatureBranch({
512
+ projectDir,
513
+ switchTo: args.switchTo,
514
+ force: args.force
515
+ });
516
+ const report = { ok: true, result };
517
+ if (args.json) {
518
+ const indent = args.pretty ? 2 : 0;
519
+ process.stdout.write(`${JSON.stringify(report, null, indent)}
520
+ `);
521
+ } else {
522
+ process.stdout.write(`${renderHuman(report)}
523
+ `);
524
+ }
525
+ return 0;
526
+ } catch (e) {
527
+ const err = e;
528
+ const code = err instanceof ScmReleaseError ? err.code : "substrate-failure";
529
+ const report = { ok: false, error: { code, message: err.message } };
530
+ if (args.json) {
531
+ const indent = args.pretty ? 2 : 0;
532
+ process.stdout.write(`${JSON.stringify(report, null, indent)}
533
+ `);
534
+ } else {
535
+ process.stderr.write(`${renderHuman(report)}
536
+ `);
537
+ }
538
+ return exitCodeForError(err);
539
+ }
540
+ }
541
+ if (isCliEntry(import.meta.url)) {
542
+ void runScmParkFeatureCli(process.argv.slice(2)).then((c) => process.exit(c));
543
+ }
544
+ export {
545
+ runScmParkFeatureCli
546
+ };
@@ -79288,8 +79288,8 @@ var PKG_NAME = "@databricks-solutions/lakebase-scm-utils";
79288
79288
  var cached;
79289
79289
  function substrateSelfVersion() {
79290
79290
  if (cached !== void 0) return cached;
79291
- if ("0.3.6".length > 0) {
79292
- cached = "0.3.6";
79291
+ if ("0.3.8".length > 0) {
79292
+ cached = "0.3.8";
79293
79293
  return cached;
79294
79294
  }
79295
79295
  cached = "unknown";
@@ -389,8 +389,8 @@ var PKG_NAME = "@databricks-solutions/lakebase-scm-utils";
389
389
  var cached;
390
390
  function substrateSelfVersion() {
391
391
  if (cached !== void 0) return cached;
392
- if ("0.3.6".length > 0) {
393
- cached = "0.3.6";
392
+ if ("0.3.8".length > 0) {
393
+ cached = "0.3.8";
394
394
  return cached;
395
395
  }
396
396
  cached = "unknown";
@@ -1667,8 +1667,8 @@ var PKG_NAME = "@databricks-solutions/lakebase-scm-utils";
1667
1667
  var cached;
1668
1668
  function substrateSelfVersion() {
1669
1669
  if (cached !== void 0) return cached;
1670
- if ("0.3.6".length > 0) {
1671
- cached = "0.3.6";
1670
+ if ("0.3.8".length > 0) {
1671
+ cached = "0.3.8";
1672
1672
  return cached;
1673
1673
  }
1674
1674
  cached = "unknown";
@@ -1630,8 +1630,8 @@ var PKG_NAME = "@databricks-solutions/lakebase-scm-utils";
1630
1630
  var cached;
1631
1631
  function substrateSelfVersion() {
1632
1632
  if (cached !== void 0) return cached;
1633
- if ("0.3.6".length > 0) {
1634
- cached = "0.3.6";
1633
+ if ("0.3.8".length > 0) {
1634
+ cached = "0.3.8";
1635
1635
  return cached;
1636
1636
  }
1637
1637
  cached = "unknown";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@databricks-solutions/lakebase-scm-utils",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
4
  "description": "Lakebase SCM + substrate engine: database branching, paired-branch SCM workflows, connection/credential minting, schema migration, project scaffold + deploy primitives, and the git/github utility layer. Extracted from consort for portable, standalone consumption by lakebase-scm-extension and the SFTDD orchestration.",
5
5
  "type": "module",
6
6
  "private": false,
@@ -97,6 +97,7 @@
97
97
  "lakebase-schema-migrate": "./dist/scripts/lakebase/schema-migrate.cli.js",
98
98
  "lakebase-scm-abandon-feature": "./dist/scripts/lakebase/scm-abandon-feature.cli.js",
99
99
  "lakebase-scm-adopt-state": "./dist/scripts/lakebase/scm-adopt-state.cli.js",
100
+ "lakebase-scm-park-feature": "./dist/scripts/lakebase/scm-park-feature.cli.js",
100
101
  "lakebase-scm-claim-feature-branch": "./dist/scripts/lakebase/scm-claim-feature.cli.js",
101
102
  "lakebase-scm-cleanup": "./dist/scripts/lakebase/scm-cleanup.cli.js",
102
103
  "lakebase-scm-doctor": "./dist/scripts/lakebase/scm-doctor.cli.js",
@@ -5,7 +5,7 @@
5
5
  import * as path from "node:path";
6
6
  import { isCliEntry } from "../util/cli-entry.js";
7
7
  import {
8
- ScmAbandonError,
8
+ ScmReleaseError,
9
9
  abandonFeatureBranch,
10
10
  type AbandonFeatureResult,
11
11
  } from "./scm-abandon-feature.js";
@@ -103,7 +103,7 @@ function renderHuman(report: AbandonReport): string {
103
103
  }
104
104
 
105
105
  function exitCodeForError(err: Error): number {
106
- if (err instanceof ScmAbandonError) {
106
+ if (err instanceof ScmReleaseError) {
107
107
  if (err.code === "no-state-file") return 1;
108
108
  return 2;
109
109
  }
@@ -137,7 +137,7 @@ export async function runScmAbandonFeatureCli(
137
137
  } catch (e) {
138
138
  const err = e as Error;
139
139
  const code =
140
- err instanceof ScmAbandonError ? err.code : "substrate-failure";
140
+ err instanceof ScmReleaseError ? err.code : "substrate-failure";
141
141
  const report: AbandonReport = {
142
142
  ok: false,
143
143
  error: { code, message: err.message },