@revturbine/cli 0.11.0 → 0.12.0

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 (2) hide show
  1. package/dist/cli.js +28 -13
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -9041,13 +9041,21 @@ function formatDiff(diff) {
9041
9041
  return lines.join("\n");
9042
9042
  }
9043
9043
 
9044
+ // src/lib/prune.ts
9045
+ function pruneQuery(prune) {
9046
+ if (prune === false) return "?prune=false";
9047
+ if (prune === true) return "?prune=force";
9048
+ return "";
9049
+ }
9050
+
9044
9051
  // src/lib/config-validate.ts
9045
9052
  var BLOCKING_SEVERITIES = /* @__PURE__ */ new Set(["error_draft", "error_launch"]);
9046
- function isBlockingFinding(finding2) {
9053
+ function isBlockingFinding(finding2, force = false) {
9054
+ if (force && finding2.severity === "error_launch") return false;
9047
9055
  return BLOCKING_SEVERITIES.has(finding2.severity);
9048
9056
  }
9049
- function hasBlockingFindings(findings) {
9050
- return findings.some(isBlockingFinding);
9057
+ function hasBlockingFindings(findings, force = false) {
9058
+ return findings.some((f) => isBlockingFinding(f, force));
9051
9059
  }
9052
9060
  function formatFindings(findings) {
9053
9061
  if (findings.length === 0) return "No validation findings.";
@@ -9733,17 +9741,22 @@ async function loadVersion(conn, sel) {
9733
9741
  return downloadConfig(conn, await requireOpenDraft(conn));
9734
9742
  }
9735
9743
  }
9736
- async function launchDraft(conn, playbookVersionId) {
9744
+ async function launchDraft(conn, playbookVersionId, force = false) {
9737
9745
  diag(`Launch gate: validating draft ${playbookVersionId} \u2026`);
9738
9746
  const validation = await fetchValidation(conn.url, playbookVersionId, conn.headers);
9739
9747
  if (!validation.ok) httpFail(conn, "validate", validation.status, validation.error);
9740
9748
  if (validation.findings.length > 0) diagRaw(formatFindings(validation.findings));
9741
- if (hasBlockingFindings(validation.findings)) {
9749
+ if (hasBlockingFindings(validation.findings, force)) {
9742
9750
  fail(EXIT.VALIDATION, "blocking findings \u2014 this draft cannot launch.");
9743
9751
  }
9752
+ const overridden = force ? validation.findings.filter((f) => f.severity === "error_launch") : [];
9753
+ if (overridden.length > 0) {
9754
+ diag(`--force: overriding ${overridden.length} launch-gate finding(s) (error_launch).`);
9755
+ }
9744
9756
  diag(`Launching playbook version ${playbookVersionId} (submit \u2192 approve \u2192 deploy) \u2026`);
9745
9757
  for (const step of ["submit", "approve", "deploy"]) {
9746
- const { res, json } = await postJson(conn, `/api/playbook-versions/${playbookVersionId}/${step}`, {});
9758
+ const body = step === "deploy" ? { force } : {};
9759
+ const { res, json } = await postJson(conn, `/api/playbook-versions/${playbookVersionId}/${step}`, body);
9747
9760
  if (!res.ok) {
9748
9761
  diag(`\u2717 ${step} failed (${res.status}). The draft is staged but NOT live.`);
9749
9762
  diagRaw(` ${JSON.stringify(json)}`);
@@ -10266,12 +10279,14 @@ program.command("show").description(`Render a summary view of a config version.
10266
10279
  emit(data, Boolean(opts.json), text);
10267
10280
  }
10268
10281
  );
10269
- program.command("upload").description("Stage a Config File as the open draft (POST /api/config/import).").argument("<config>", "Path to a Config File").option("-u, --url <url>", "RevTurbine instance URL", DEFAULT_URL).option("-t, --tenant-id <id>", "x-tenant-id (defaults to the stored token tenant)").action(async (configFile2, opts) => {
10282
+ var PRUNE_OPTION = ["--prune", "Confirm a convergent delete past the mass-deletion guard (removes entities absent from the file)"];
10283
+ var NO_PRUNE_OPTION = ["--no-prune", "Additive import \u2014 keep entities that are absent from the file (disables convergent delete)"];
10284
+ program.command("upload").description("Stage a Config File as the open draft (POST /api/config/import).").argument("<config>", "Path to a Config File").option("-u, --url <url>", "RevTurbine instance URL", DEFAULT_URL).option("-t, --tenant-id <id>", "x-tenant-id (defaults to the stored token tenant)").option(...PRUNE_OPTION).option(...NO_PRUNE_OPTION).action(async (configFile2, opts) => {
10270
10285
  const config = verifyConfig(configFile2);
10271
10286
  if (config === null) fail(EXIT.VALIDATION, `Fix the issues above in ${configFile2}, then re-run.`);
10272
10287
  const conn = connect(opts.url, uploadTenantFor(opts.url, config, opts.tenantId));
10273
10288
  diag(`Staging ${configFile2} as the open draft (${conn.url}/api/config/import) \u2026`);
10274
- const { res, json } = await postJson(conn, "/api/config/import", config);
10289
+ const { res, json } = await postJson(conn, `/api/config/import${pruneQuery(opts.prune)}`, config);
10275
10290
  if (!res.ok) {
10276
10291
  if (json && typeof json === "object" && json.stage) {
10277
10292
  diagRaw(` stage: ${json.stage}`);
@@ -10287,7 +10302,7 @@ program.command("upload").description("Stage a Config File as the open draft (PO
10287
10302
  if (playbookVersionId) diagRaw(` playbook_version_id: ${playbookVersionId}`);
10288
10303
  diag("Launch it with `revturbine launch --draft`, or from the UI (Drafts & Releases).");
10289
10304
  });
10290
- program.command("launch").description("Take a config live as a new Release: validate (launch gate), then submit \u2192 approve \u2192 deploy. Synchronous.").argument("[file]", "Config File to upload and launch directly").option("--draft", "Launch the already-open draft").option("-u, --url <url>", "RevTurbine instance URL", DEFAULT_URL).option("-t, --tenant-id <id>", "x-tenant-id (defaults to the stored token tenant)").option("--yes", "Accepted for parity with discard/restore; launch has no confirmation prompt").action(async (file, opts) => {
10305
+ program.command("launch").description("Take a config live as a new Release: validate (launch gate), then submit \u2192 approve \u2192 deploy. Synchronous.").argument("[file]", "Config File to upload and launch directly").option("--draft", "Launch the already-open draft").option("--force", "Launch past incomplete-but-valid findings (error_launch); structural errors (error_draft) still block").option("-u, --url <url>", "RevTurbine instance URL", DEFAULT_URL).option("-t, --tenant-id <id>", "x-tenant-id (defaults to the stored token tenant)").option(...PRUNE_OPTION).option(...NO_PRUNE_OPTION).option("--yes", "Accepted for parity with discard/restore; launch has no confirmation prompt").action(async (file, opts) => {
10291
10306
  const [sel] = requireSelectors(opts, file ? [file] : [], { count: 1, allowed: ["file", "draft"], command: "launch" });
10292
10307
  let conn;
10293
10308
  let playbookVersionId;
@@ -10296,7 +10311,7 @@ program.command("launch").description("Take a config live as a new Release: vali
10296
10311
  if (config === null) fail(EXIT.VALIDATION, `Fix the issues above in ${sel.path}, then re-run.`);
10297
10312
  conn = connect(opts.url, uploadTenantFor(opts.url, config, opts.tenantId));
10298
10313
  diag(`Staging ${sel.path} as the open draft \u2026`);
10299
- const { res, json } = await postJson(conn, "/api/config/import", config);
10314
+ const { res, json } = await postJson(conn, `/api/config/import${pruneQuery(opts.prune)}`, config);
10300
10315
  if (!res.ok) {
10301
10316
  diagRaw(` ${JSON.stringify(json)}`);
10302
10317
  httpFail(conn, "upload", res.status);
@@ -10308,7 +10323,7 @@ program.command("launch").description("Take a config live as a new Release: vali
10308
10323
  conn = connect(opts.url, opts.tenantId);
10309
10324
  playbookVersionId = await requireOpenDraft(conn);
10310
10325
  }
10311
- await launchDraft(conn, playbookVersionId);
10326
+ await launchDraft(conn, playbookVersionId, !!opts.force);
10312
10327
  });
10313
10328
  program.command("discard").description("Discard (archive) the open draft so a fresh one can start.").argument("[playbook-version-id]", "A specific draft id (default: the open draft)").option("--draft", "The open draft (explicit form of the default)").option("-u, --url <url>", "RevTurbine instance URL", DEFAULT_URL).option("-t, --tenant-id <id>", "x-tenant-id (defaults to the stored token tenant)").option("--yes", "Skip the confirmation prompt").action(async (id, opts) => {
10314
10329
  const conn = connect(opts.url, opts.tenantId);
@@ -10318,7 +10333,7 @@ program.command("discard").description("Discard (archive) the open draft so a fr
10318
10333
  if (!res.ok) httpFail(conn, "discard", res.status, json);
10319
10334
  diag(`\u2713 Discarded draft ${playbookVersionId}.`);
10320
10335
  });
10321
- program.command("restore").description("Stage a draft that restores a past release (from its frozen snapshot); `--launch` takes it live. Halts if a draft is already open.").argument("<playbook-version-id>", "The deployed playbook version to restore (see `history`)").option("--launch", "Launch the restoring draft immediately (gate + submit \u2192 approve \u2192 deploy)").option("-u, --url <url>", "RevTurbine instance URL", DEFAULT_URL).option("-t, --tenant-id <id>", "x-tenant-id (defaults to the stored token tenant)").option("--yes", "Skip the confirmation prompt").action(async (playbookVersionId, opts) => {
10336
+ program.command("restore").description("Stage a draft that restores a past release (from its frozen snapshot); `--launch` takes it live. Halts if a draft is already open.").argument("<playbook-version-id>", "The deployed playbook version to restore (see `history`)").option("--launch", "Launch the restoring draft immediately (gate + submit \u2192 approve \u2192 deploy)").option("--force", "With --launch, launch past incomplete-but-valid findings (error_launch); error_draft still blocks").option("-u, --url <url>", "RevTurbine instance URL", DEFAULT_URL).option("-t, --tenant-id <id>", "x-tenant-id (defaults to the stored token tenant)").option("--yes", "Skip the confirmation prompt").action(async (playbookVersionId, opts) => {
10322
10337
  const conn = connect(opts.url, opts.tenantId);
10323
10338
  await confirmOrExit(
10324
10339
  `Restore playbook version ${playbookVersionId} on ${conn.url}?${opts.launch ? " --launch will take it LIVE." : " (stages a draft; launch separately)"}`,
@@ -10333,7 +10348,7 @@ program.command("restore").description("Stage a draft that restores a past relea
10333
10348
  return;
10334
10349
  }
10335
10350
  if (!reverting) fail(EXIT.SERVER, "No reverting draft id returned \u2014 cannot launch.");
10336
- await launchDraft(conn, reverting);
10351
+ await launchDraft(conn, reverting, !!opts.force);
10337
10352
  });
10338
10353
  program.command("status").description("The current live Release and the open draft, side by side.").option("-u, --url <url>", "RevTurbine instance URL", DEFAULT_URL).option("-t, --tenant-id <id>", "x-tenant-id (defaults to the stored token tenant)").option("--json", "Machine-readable output").action(async (opts) => {
10339
10354
  const conn = connect(opts.url, opts.tenantId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@revturbine/cli",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "revturbine — verify RevTurbine ExportedConfig files and ship them to a RevTurbine instance through the Change Set lifecycle.",
5
5
  "license": "MIT",
6
6
  "repository": {