@intentius/chant 0.54.2 → 0.54.3

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.
@@ -275,8 +275,22 @@ export interface CrossEnvComparison {
275
275
  envB: string;
276
276
  digestA?: string;
277
277
  digestB?: string;
278
- /** True only when both envs have a recorded digest and they match. */
278
+ /**
279
+ * True only when both envs have a recorded digest and their *comparable*
280
+ * identities match. For a record carrying `inputDigest` (a pinned helm
281
+ * deploy, chant#1242/#1243), that field is the comparable identity —
282
+ * `ReleaseRecord.inputDigest`'s own contract: profiles are per cluster, two
283
+ * environments legitimately render to different bytes, so cross-environment
284
+ * "is prod running what staging tested" joins on the input side while
285
+ * `digest` proves the exact bytes each cluster got. Records without it
286
+ * compare on `digest` exactly as before.
287
+ */
279
288
  same: boolean;
289
+ /** The identities `same` was decided on (`inputDigest ?? digest` per side), when both sides had one and they differ from the digests shown. */
290
+ comparedOn?: {
291
+ a: string;
292
+ b: string;
293
+ };
280
294
  }
281
295
  /** Compare the latest recorded digest for `component` between two environments' release records. */
282
296
  export declare function compareAcrossEnvironments(component: string, envA: {
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/lifecycle/status.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/lifecycle/status.ts"],"names":[],"mappings":"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"}
@@ -8,7 +8,10 @@ export interface OpDiscoveryResult {
8
8
  errors: string[];
9
9
  }
10
10
  /**
11
- * Discover all Op definitions from *.op.ts files under the git root.
11
+ * Discover all Op definitions from `*.op.ts` files under the nearest chant
12
+ * project root (the directory holding `chant.config.ts`/`.json`, walking up
13
+ * from `cwd`), or under the git root when no config exists — see
14
+ * {@link findDiscoveryRoot} (#2058).
12
15
  */
13
16
  export declare function discoverOps(opts?: {
14
17
  cwd?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../../src/op/discover.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,QAAQ,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAC/B,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAiCD;;GAEG;AACH,wBAAsB,WAAW,CAAC,IAAI,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAuCrF"}
1
+ {"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../../src/op/discover.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,QAAQ,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAC/B,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AA0DD;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,IAAI,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAuCrF"}
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/terraform/manifest.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/terraform/manifest.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant",
3
- "version": "0.54.2",
3
+ "version": "0.54.3",
4
4
  "description": "Declarative infrastructure-as-code toolkit — TypeScript on Node.js",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://intentius.io/chant",
@@ -1,5 +1,5 @@
1
1
  import { describe, test, expect } from "vitest";
2
- import { mkdtempSync, writeFileSync, rmSync, readFileSync, existsSync } from "fs";
2
+ import { mkdtempSync, mkdirSync, writeFileSync, rmSync, readFileSync, existsSync } from "fs";
3
3
  import { tmpdir } from "os";
4
4
  import { join } from "path";
5
5
  import { carveApply, formatCarveApply } from "./carve-apply";
@@ -108,6 +108,36 @@ describe("carveApply", () => {
108
108
  });
109
109
  });
110
110
 
111
+ test("a RELATIVE --output still records stampedFiles relative to the manifest's directory, not the cwd (#2059)", async () => {
112
+ if (!parserAvailable) return;
113
+ await withEstate(async (dir) => {
114
+ // The behold carve-demo shape: run FROM the project dir with
115
+ // `--output carveout`. The bug: resolveManifestFilePath joined against
116
+ // the relative outDir, producing a cwd-relative path ("carveout/…")
117
+ // that normalize-on-write passed through — two spellings in one manifest.
118
+ const out = join(dir, "carveout");
119
+ const manifest = manifestFor("aws_s3_bucket.assets", dir);
120
+ manifest.emit = { source: "tfstate", files: [join("src", "assets.ts")], at: "t" };
121
+ writeCarveManifest(out, manifest);
122
+ mkdirSync(join(out, "src"), { recursive: true });
123
+ writeFileSync(join(out, "src", "assets.ts"), 'export const assets = new Bucket({\n BucketName: "b",\n});\n');
124
+
125
+ const prevCwd = process.cwd();
126
+ process.chdir(dir);
127
+ try {
128
+ const res = await carveApply({ from: ".", output: "carveout", env: "prod", stack: "assets", writeSource: true });
129
+ expect(res.ok).toBe(true);
130
+ const m = readCarveManifest(res.manifestPath!)!;
131
+ // One spelling: manifest-dir-relative, same as emit.files — never
132
+ // "carveout/src/assets.ts" (relative to wherever the command ran).
133
+ expect(m.apply!.stampedFiles).toEqual([join("src", "assets.ts")]);
134
+ expect(m.emit!.files).toEqual([join("src", "assets.ts")]);
135
+ } finally {
136
+ process.chdir(prevCwd);
137
+ }
138
+ });
139
+ });
140
+
111
141
  test("--write-source without an emit record in the manifest is a clear error", async () => {
112
142
  if (!parserAvailable) return;
113
143
  await withEstate(async (dir) => {
@@ -413,6 +413,44 @@ describe("status", () => {
413
413
  expect(result.digestB).toBe("sha256:abc");
414
414
  expect(result.same).toBe(false);
415
415
  });
416
+
417
+ test("pinned helm records compare on inputDigest — per-cluster bytes legitimately differ (ReleaseRecord.inputDigest's own contract)", () => {
418
+ // Same chart+values rendered against two clusters' capability profiles:
419
+ // contentDigests (recorded as `digest`) differ, inputDigests match —
420
+ // prod IS running what staging tested.
421
+ const result = compareAcrossEnvironments(
422
+ "web",
423
+ { name: "staging", records: [record({ component: "web", env: "staging", digest: "sha256:staging-bytes", inputDigest: "sha256:inputs" })] },
424
+ { name: "prod", records: [record({ component: "web", env: "prod", digest: "sha256:prod-bytes", inputDigest: "sha256:inputs" })] },
425
+ );
426
+ expect(result.same).toBe(true);
427
+ expect(result.comparedOn).toEqual({ a: "sha256:inputs", b: "sha256:inputs" });
428
+ // The exact bytes each cluster got stay visible.
429
+ expect(result.digestA).toBe("sha256:staging-bytes");
430
+ expect(result.digestB).toBe("sha256:prod-bytes");
431
+ });
432
+
433
+ test("differing inputDigests are a real difference, whatever the bytes say", () => {
434
+ const result = compareAcrossEnvironments(
435
+ "web",
436
+ { name: "staging", records: [record({ component: "web", env: "staging", digest: "sha256:same-bytes", inputDigest: "sha256:inputs-v2" })] },
437
+ { name: "prod", records: [record({ component: "web", env: "prod", digest: "sha256:same-bytes", inputDigest: "sha256:inputs-v1" })] },
438
+ );
439
+ expect(result.same).toBe(false);
440
+ });
441
+
442
+ test("one side input-identified, the other content-identified: compared honestly on what each carries", () => {
443
+ // Mixed deploys (a pinned helm release in prod, an unpinned one in
444
+ // staging where the record's digest IS the input digest): staging's
445
+ // digest against prod's inputDigest is the only meaningful join.
446
+ const result = compareAcrossEnvironments(
447
+ "web",
448
+ { name: "staging", records: [record({ component: "web", env: "staging", digest: "sha256:inputs" })] },
449
+ { name: "prod", records: [record({ component: "web", env: "prod", digest: "sha256:prod-bytes", inputDigest: "sha256:inputs" })] },
450
+ );
451
+ expect(result.same).toBe(true);
452
+ expect(result.comparedOn).toEqual({ a: "sha256:inputs", b: "sha256:inputs" });
453
+ });
416
454
  });
417
455
 
418
456
  describe("mergeLiveEvidence (#57 — per-component stack presence overlay)", () => {
@@ -478,8 +478,19 @@ export interface CrossEnvComparison {
478
478
  envB: string;
479
479
  digestA?: string;
480
480
  digestB?: string;
481
- /** True only when both envs have a recorded digest and they match. */
481
+ /**
482
+ * True only when both envs have a recorded digest and their *comparable*
483
+ * identities match. For a record carrying `inputDigest` (a pinned helm
484
+ * deploy, chant#1242/#1243), that field is the comparable identity —
485
+ * `ReleaseRecord.inputDigest`'s own contract: profiles are per cluster, two
486
+ * environments legitimately render to different bytes, so cross-environment
487
+ * "is prod running what staging tested" joins on the input side while
488
+ * `digest` proves the exact bytes each cluster got. Records without it
489
+ * compare on `digest` exactly as before.
490
+ */
482
491
  same: boolean;
492
+ /** The identities `same` was decided on (`inputDigest ?? digest` per side), when both sides had one and they differ from the digests shown. */
493
+ comparedOn?: { a: string; b: string };
483
494
  }
484
495
 
485
496
  /** Compare the latest recorded digest for `component` between two environments' release records. */
@@ -490,12 +501,16 @@ export function compareAcrossEnvironments(
490
501
  ): CrossEnvComparison {
491
502
  const latestA = latestPerComponent(envA.records).get(component);
492
503
  const latestB = latestPerComponent(envB.records).get(component);
504
+ const idA = latestA ? latestA.inputDigest ?? latestA.digest : undefined;
505
+ const idB = latestB ? latestB.inputDigest ?? latestB.digest : undefined;
506
+ const comparedOnInputs = (latestA?.inputDigest ?? latestB?.inputDigest) !== undefined;
493
507
  return {
494
508
  component,
495
509
  envA: envA.name,
496
510
  envB: envB.name,
497
511
  digestA: latestA?.digest,
498
512
  digestB: latestB?.digest,
499
- same: !!latestA && !!latestB && latestA.digest === latestB.digest,
513
+ same: !!idA && !!idB && idA === idB,
514
+ ...(comparedOnInputs && idA && idB ? { comparedOn: { a: idA, b: idB } } : {}),
500
515
  };
501
516
  }
@@ -0,0 +1,68 @@
1
+ import { describe, test, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { mkdtempSync, mkdirSync, writeFileSync, rmSync, realpathSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { discoverOps } from "./discover";
6
+
7
+ /**
8
+ * #2058 — Op discovery scoped to the project root, the way entity discovery
9
+ * is. The repro: a chant project nested in a larger checkout (behold's
10
+ * committed `example-carve/app` beside `example-writes`, `example-k8s`)
11
+ * discovered every sibling project's `*.op.ts` as its own, because the scan
12
+ * root was the git root unconditionally.
13
+ *
14
+ * The fake git root is a temp tree; the runtime mock answers `git rev-parse`
15
+ * with whatever the current test set (`realpath`ed, since macOS's tmpdir is a
16
+ * symlink and the walk compares resolved paths).
17
+ */
18
+ let fakeGitRoot = "";
19
+ vi.mock("../runtime-adapter", () => ({
20
+ getRuntime: () => ({
21
+ spawn: async (cmd: string[]) =>
22
+ cmd[0] === "git" && cmd[1] === "rev-parse"
23
+ ? { stdout: fakeGitRoot, stderr: "", exitCode: 0 }
24
+ : { stdout: "", stderr: "", exitCode: 0 },
25
+ }),
26
+ }));
27
+
28
+ /** A minimal default export shaped the way discovery validates (entity.props = OpConfig). */
29
+ const OP_FILE = (name: string): string =>
30
+ `export default { props: { name: ${JSON.stringify(name)}, overview: "t", phases: [{ name: "Run", steps: [] }] } };\n`;
31
+
32
+ describe("discoverOps — the scan root is the project, not the checkout (#2058)", () => {
33
+ let checkout: string;
34
+ beforeEach(() => {
35
+ checkout = realpathSync(mkdtempSync(join(tmpdir(), "chant-op-root-")));
36
+ fakeGitRoot = checkout;
37
+ // The monorepo shape from the issue: two sibling chant projects, each
38
+ // with its own config and ops, nested in one git checkout.
39
+ mkdirSync(join(checkout, "app", "ops"), { recursive: true });
40
+ writeFileSync(join(checkout, "app", "chant.config.json"), JSON.stringify({ lexicons: ["aws"] }));
41
+ writeFileSync(join(checkout, "app", "ops", "mine.op.ts"), OP_FILE("mine"));
42
+ mkdirSync(join(checkout, "sibling", "ops"), { recursive: true });
43
+ writeFileSync(join(checkout, "sibling", "chant.config.json"), JSON.stringify({ lexicons: ["aws"] }));
44
+ writeFileSync(join(checkout, "sibling", "ops", "theirs.op.ts"), OP_FILE("theirs"));
45
+ // A configless dir at the checkout root, with an op of its own.
46
+ mkdirSync(join(checkout, "ops"), { recursive: true });
47
+ writeFileSync(join(checkout, "ops", "root.op.ts"), OP_FILE("root"));
48
+ });
49
+ afterEach(() => rmSync(checkout, { recursive: true, force: true }));
50
+
51
+ test("from inside a project, only that project's Ops are discovered — never a sibling's", async () => {
52
+ const { ops, errors } = await discoverOps({ cwd: join(checkout, "app") });
53
+ expect(errors).toEqual([]);
54
+ expect([...ops.keys()]).toEqual(["mine"]);
55
+ });
56
+
57
+ test("the walk finds the config from a subdirectory of the project too (ops/ beside src/, #1675)", async () => {
58
+ const { ops } = await discoverOps({ cwd: join(checkout, "app", "ops") });
59
+ expect([...ops.keys()]).toEqual(["mine"]);
60
+ });
61
+
62
+ test("with no chant config anywhere up to the git root, the git root stands — #1675's original scope", async () => {
63
+ rmSync(join(checkout, "app", "chant.config.json"));
64
+ rmSync(join(checkout, "sibling", "chant.config.json"));
65
+ const { ops } = await discoverOps({ cwd: join(checkout, "app") });
66
+ expect([...ops.keys()].sort()).toEqual(["mine", "root", "theirs"]);
67
+ });
68
+ });
@@ -1,6 +1,7 @@
1
1
  import { getRuntime } from "../runtime-adapter";
2
2
  import { readdir } from "node:fs/promises";
3
- import { join } from "node:path";
3
+ import { existsSync } from "node:fs";
4
+ import { dirname, join, resolve } from "node:path";
4
5
  import type { OpConfig } from "./types";
5
6
 
6
7
  export interface DiscoveredOp {
@@ -20,6 +21,31 @@ async function findGitRoot(cwd?: string): Promise<string> {
20
21
  return result.stdout.trim();
21
22
  }
22
23
 
24
+ /**
25
+ * The root the `*.op.ts` scan starts from (#2058): the nearest chant project
26
+ * root — the directory holding `chant.config.ts`/`chant.config.json`, walking
27
+ * up from `cwd` and never past the git root. #1675's convention keeps Op
28
+ * files OUTSIDE `sourceDir` (`ops/` beside `src/`), which is why discovery
29
+ * walks up at all — but "up" used to mean the git root unconditionally, so a
30
+ * chant project nested in a larger checkout (a monorepo, behold's committed
31
+ * examples) discovered every SIBLING project's Ops as its own. The config
32
+ * file is the project boundary, the same one entity discovery respects.
33
+ * With no config anywhere up to the git root, the git root stands —
34
+ * #1675's original scope, kept for configless layouts.
35
+ */
36
+ async function findDiscoveryRoot(cwd?: string): Promise<string> {
37
+ const gitRoot = resolve(await findGitRoot(cwd));
38
+ let dir = resolve(cwd ?? process.cwd());
39
+ for (;;) {
40
+ if (existsSync(join(dir, "chant.config.ts")) || existsSync(join(dir, "chant.config.json"))) return dir;
41
+ if (dir === gitRoot) break;
42
+ const parent = dirname(dir);
43
+ if (parent === dir) break; // filesystem root — cwd was outside the git root
44
+ dir = parent;
45
+ }
46
+ return gitRoot;
47
+ }
48
+
23
49
  async function collectOpFiles(dir: string): Promise<string[]> {
24
50
  const files: string[] = [];
25
51
  let entries;
@@ -45,14 +71,17 @@ async function collectOpFiles(dir: string): Promise<string[]> {
45
71
  }
46
72
 
47
73
  /**
48
- * Discover all Op definitions from *.op.ts files under the git root.
74
+ * Discover all Op definitions from `*.op.ts` files under the nearest chant
75
+ * project root (the directory holding `chant.config.ts`/`.json`, walking up
76
+ * from `cwd`), or under the git root when no config exists — see
77
+ * {@link findDiscoveryRoot} (#2058).
49
78
  */
50
79
  export async function discoverOps(opts?: { cwd?: string }): Promise<OpDiscoveryResult> {
51
80
  const errors: string[] = [];
52
81
  const ops = new Map<string, DiscoveredOp>();
53
82
 
54
- const gitRoot = await findGitRoot(opts?.cwd);
55
- const files = await collectOpFiles(gitRoot);
83
+ const root = await findDiscoveryRoot(opts?.cwd);
84
+ const files = await collectOpFiles(root);
56
85
 
57
86
  const nameToFile = new Map<string, string>();
58
87
 
@@ -9,7 +9,7 @@
9
9
  */
10
10
 
11
11
  import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "fs";
12
- import { isAbsolute, join, relative } from "path";
12
+ import { isAbsolute, join, relative, resolve } from "path";
13
13
  import type { CarveReport } from "./carve";
14
14
  import type { OwnershipMarker } from "../ownership";
15
15
 
@@ -100,10 +100,14 @@ export function carveManifestPath(outDir: string, target: string): string {
100
100
  /**
101
101
  * Relative-to-`outDir` spelling of a recorded file path. Absolute entries
102
102
  * (the run-time spelling every step naturally produces, and every version-1
103
- * manifest on disk) are rewritten; already-relative entries pass through.
103
+ * manifest on disk) are rewritten against the RESOLVED outDir — a caller's
104
+ * relative `--output` must not change what "relative to the manifest's
105
+ * directory" means (#2059). Already-relative entries pass through: they can
106
+ * only have come from a reader honouring the contract, and re-basing them
107
+ * against an unknowable original cwd would corrupt, not normalize.
104
108
  */
105
109
  function relativizePath(outDir: string, p: string): string {
106
- return isAbsolute(p) ? relative(outDir, p) : p;
110
+ return isAbsolute(p) ? relative(resolve(outDir), p) : p;
107
111
  }
108
112
 
109
113
  /**
@@ -136,7 +140,11 @@ function normalizeManifest(outDir: string, manifest: CarveManifest): CarveManife
136
140
  * on-disk spellings read the same.
137
141
  */
138
142
  export function resolveManifestFilePath(manifestDir: string, p: string): string {
139
- return isAbsolute(p) ? p : join(manifestDir, p);
143
+ // Always ABSOLUTE (#2059): `join` with a relative manifestDir produced a
144
+ // cwd-relative path, which `relativizePath` then passed through untouched —
145
+ // so `carve apply --output carveout` recorded `stampedFiles` relative to
146
+ // wherever the command happened to run, two spellings in one manifest.
147
+ return isAbsolute(p) ? p : resolve(manifestDir, p);
140
148
  }
141
149
 
142
150
  /** Write (or overwrite) a manifest into the output dir. Returns the path. */