@jr2/cli 0.3.0 → 0.5.1

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.
@@ -7,28 +7,29 @@
7
7
  // The one Instance verb that reads the Instance without asserting it: a Kit mismatch is a LINE here,
8
8
  // never the ADR-0056 refusal, because this is the verb you reach for when that refusal fires. Every
9
9
  // probe is best-effort and every answer is a report — exit 0 whatever it finds. Nothing here
10
- // computes what `jr2 up` computes: no bundle staging, no local content hash.
10
+ // computes what `jr2 up` computes: no Assembly staging, no local hash. The deployed hash is the
11
+ // current Assembly's, as the Orchestrator reports it on `/healthz` (ADR-0069).
11
12
  //
12
13
  // A REPORT verb, not a result verb: the human table goes to stdout, `--json` swaps in the one object.
13
14
 
14
15
  import { basename } from "node:path";
15
- import { parseArgs } from "node:util";
16
- import { IMAGES_CONFIGMAP, IMAGES_KEY, INSTANCE_SECRET, loadConfig, type ImageRefs } from "@jr2/orchestrator";
16
+ import { IMAGES_CONFIGMAP, IMAGES_KEY, INSTANCE_SECRET, loadConfig, type ImageRefs } from "@jr2/orchestrator/internal";
17
17
  import { detectKitCheckout, KIT_IMAGE_HOME } from "../build.ts";
18
18
  import type { InstanceIdentity } from "../client.ts";
19
- import { compareVersions, LABEL_HASH, LABEL_VERSION, OPERATOR_DEPLOYMENT, OPERATOR_NAMESPACE } from "../deploy.ts";
19
+ import { compareVersions, LABEL_VERSION, OPERATOR_DEPLOYMENT, OPERATOR_NAMESPACE } from "../deploy.ts";
20
20
  import { handedOffFrom } from "../handoff.ts";
21
- import { TARGET_ARGS, targetOptions, type TargetOptions } from "../instance.ts";
21
+ import { targetOptions, type TargetOptions } from "../instance.ts";
22
22
  import { checkKitVersion, CLI_ROOT, CLI_VERSION } from "../kit-version.ts";
23
23
  import { kubectlAdmin, kubectlKube, ORCHESTRATOR_PORT, ORCHESTRATOR_SERVICE, type KubeObject } from "../kube.ts";
24
24
  import { activity, result, type Io } from "../output.ts";
25
25
  import { findRoot } from "../root.ts";
26
+ import { parseCommand } from "../command-table.ts";
26
27
 
27
28
  /** A package as the report names it: the number and the real path, so the path says WHICH copy. */
28
29
  export type Copy = { version: string; path: string };
29
30
 
30
31
  /** The image map as `jr2 up` wrote it and pods read it (ADR-0038). */
31
- type Images = Partial<ImageRefs & { operator?: string }>;
32
+ type Images = Partial<ImageRefs & { operator?: string; orchestrator?: string }>;
32
33
 
33
34
  /** The comparison between the local kit line and the orchestrator line — the verdict the two
34
35
  * halves exist to produce. `unknown` when either side is missing. */
@@ -72,17 +73,13 @@ export type VersionReport = {
72
73
  skew?: Skew;
73
74
  };
74
75
 
75
- /** What the pod answers on `/healthz`, plus what the Deployment records — two facts, because a
76
- * rollout that has not landed leaves them disagreeing. Or why neither could be read. */
77
- type Orchestrator = { version?: string; hash?: string; deploymentHash?: string; image?: string } | { error: string };
76
+ /** What the pod answers on `/healthz` — its Kit version and the hash of its current Assembly
77
+ * (ADR-0069), absent while it holds none — plus the image the Deployment runs. Or why neither
78
+ * could be read. */
79
+ type Orchestrator = { version?: string; hash?: string; image?: string } | { error: string };
78
80
 
79
81
  export async function version(args: string[], io: Io): Promise<number> {
80
- const { values } = parseArgs({
81
- args,
82
- allowPositionals: true,
83
- strict: false,
84
- options: { ...TARGET_ARGS, local: { type: "boolean" }, json: { type: "boolean" } },
85
- });
82
+ const { values } = parseCommand("version", args);
86
83
  const opts = targetOptions(values);
87
84
  const report = await versionReport(io, opts, { local: values.local === true });
88
85
  if (values.json === true) result(io, report);
@@ -191,7 +188,7 @@ async function probeCluster(io: Io, opts: TargetOptions, namespace: string): Pro
191
188
  }
192
189
 
193
190
  const orch = await admin.getJson({ kind: "deployment", name: ORCHESTRATOR_SERVICE, namespace, ...ctx });
194
- const recorded = { deploymentHash: orch?.metadata.labels?.[LABEL_HASH], image: firstImage(orch) };
191
+ const recorded = { image: firstImage(orch) };
195
192
  let fwd: { url: string; close: () => void } | undefined;
196
193
  try {
197
194
  fwd = await kube.portForward({ namespace, service: ORCHESTRATOR_SERVICE, port: ORCHESTRATOR_PORT, ...ctx });
@@ -256,7 +253,7 @@ const copy = (c: Copy): string => `${c.version.padEnd(8)} ${c.path}`;
256
253
  export function render(r: VersionReport): string {
257
254
  const rows: Row[] = [["cli", copy(r.cli)]];
258
255
  if (r.global) rows.push(["global", copy(r.global)]);
259
- if (r.kit) rows.push(["kit", renderKit(r.kit)]);
256
+ if (r.kit) rows.push(["kit", renderKit(r.kit, r.cli)]);
260
257
  if (r.instance) {
261
258
  const i = r.instance;
262
259
  const mode = i.mode === "checkout" ? `kit checkout at ${i.kitCheckout}` : `installed kit → ${i.kitRegistry}`;
@@ -271,14 +268,15 @@ export function render(r: VersionReport): string {
271
268
  return rows.map(([label, value]) => (label ? `${`${label}:`.padEnd(width)}${value}` : "")).join("\n") + "\n";
272
269
  }
273
270
 
274
- function renderKit(k: NonNullable<VersionReport["kit"]>): string {
271
+ function renderKit(k: NonNullable<VersionReport["kit"]>, cli: Copy): string {
275
272
  const pinned = k.pinned !== undefined ? `; package.json pins ${k.pinned} — reinstall` : "";
276
273
  if (k.check === "unresolved") return `not installed${pinned}`;
277
274
  const head = copy({ version: k.version!, path: k.path! });
278
275
  if (k.check === "ok") return `${head} ok${pinned}`;
276
+ // The CLI's own number is named: one copy at another version is a mismatch too (ADR-0056).
279
277
  const against = k.against
280
- ? `this jr2 runs against ${k.against.version} (${k.against.path})`
281
- : "this jr2 has no @jr2/orchestrator beside it";
278
+ ? `this jr2 (${cli.version}) runs against ${k.against.version} (${k.against.path})`
279
+ : `this jr2 (${cli.version}) has no @jr2/orchestrator beside it`;
282
280
  return `${head} MISMATCH — ${against}${pinned}`;
283
281
  }
284
282
 
@@ -289,10 +287,9 @@ function deployedRows(d: NonNullable<VersionReport["deployed"]>, r: VersionRepor
289
287
  else if ("error" in o) rows.push(["orchestrator", o.error]);
290
288
  else {
291
289
  const parts = [o.version ?? "(no version reported)"];
292
- if (o.hash) parts.push(`hash ${o.hash}`);
293
- if (o.deploymentHash && o.hash && o.deploymentHash !== o.hash) {
294
- parts.push(`(rollout incomplete: deployment records ${o.deploymentHash})`);
295
- }
290
+ // The current Assembly (ADR-0069). None is a state the Orchestrator serves in: volume loss, or
291
+ // a Kit image rolled before its first publish.
292
+ parts.push(o.hash ? `assembly ${o.hash}` : "no Assembly — `jr2 up` publishes");
296
293
  if (o.image) parts.push(o.image);
297
294
  rows.push(["orchestrator", parts.join(" ")]);
298
295
  }
@@ -323,7 +320,11 @@ function skewSentence(r: VersionReport): string | undefined {
323
320
  );
324
321
  case "same":
325
322
  return r.instance?.mode === "checkout"
326
- ? "same version; checkout builds are content-addressed — compare the hash against `jr2 up`'s line"
323
+ ? // A kit source edit moves the Kit image's ref and not the Assembly hash: the Assembly
324
+ // strips the Kit closure (ADR-0069). So the image is what shows kit skew here.
325
+ "same version; checkout builds are content-addressed — compare the orchestrator image against " +
326
+ "`jr2 up`'s `orchestrator image:` line for the kit, and the assembly hash against its `assembly:` " +
327
+ "line for the Instance"
327
328
  : undefined;
328
329
  default:
329
330
  return undefined;