@intentius/chant-lexicon-terraform 0.72.2 → 0.72.4

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "algorithm": "sha256",
3
3
  "artifacts": {
4
- "manifest.json": "75ad743a7a6085f25e3f89df1e38fe44229828d84dc590ed6f55be617c3f250a",
4
+ "manifest.json": "edf1bdf88b99a6e3bfb8c4bf8fef6730eb723baffe1dec18afc76c233a446450",
5
5
  "meta.json": "ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356",
6
6
  "types/index.d.ts": "022927e6a7b351cc4cf52d5f973f60ccd140e5810e17b32fb8868965aa1db161",
7
7
  "rules/plan-before-apply.ts": "31b1b3176799fd365b2583fe3947edd187c2a863e3dc0094ed9e91ece2464c99",
@@ -37,5 +37,5 @@
37
37
  "rules/tf028.ts": "81ef95659c40ad1774c83e663473ed769143d894a4ba0517c3defa31f00ba723",
38
38
  "skills/chant-terraform.md": "d9ca7b7c28aed0038ce775a3491150298492eed2858fd3651ed9bfca0e577ae8"
39
39
  },
40
- "composite": "2ce680f930edfc780fa11a0a59c10759c4acb5305961fcd7abaa8a2444af1bd9"
40
+ "composite": "2d1981f2808b7a245a5bfd923589681e75a8de4fcbbad82d0d22bdbe344b7635"
41
41
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "terraform",
3
- "version": "0.72.2",
3
+ "version": "0.72.4",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "Terraform"
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-lexicon-terraform",
3
- "version": "0.72.2",
3
+ "version": "0.72.4",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "src/",
@@ -56,7 +56,7 @@
56
56
  },
57
57
  "peerDependencies": {
58
58
  "zod": "^4.3.6",
59
- "@intentius/chant": "^0.72.2",
59
+ "@intentius/chant": "^0.72.4",
60
60
  "typescript": "^5.9.3"
61
61
  },
62
62
  "description": "terraform lexicon for chant, existing Terraform root modules as declarable entities",
@@ -0,0 +1,147 @@
1
+ import { describe, test, expect } from "vitest";
2
+ import { readFileSync } from "node:fs";
3
+ import { fileURLToPath } from "node:url";
4
+ import { load } from "js-yaml";
5
+ import { generateGithubOpPipeline } from "@intentius/chant-lexicon-github/components/generate-op-pipeline";
6
+
7
+ /**
8
+ * chant#2331 — the published sample of generated output is checked against the
9
+ * generator.
10
+ *
11
+ * `ops.mdx` publishes the gated-apply workflow `generateGithubOpPipeline`
12
+ * emits. Nothing checked it, and it went stale twice in a single day: #2299
13
+ * added `shell: bash` and the sample still showed the pre-fix YAML — the exact
14
+ * shape #2299 says cannot start under `container:`, so a reader copying it
15
+ * reproduced the bug that had just been fixed. #2321 then removed
16
+ * `set -o pipefail` and `shell: bash` again, and the sample #2322 had just
17
+ * hand-corrected was stale a second time.
18
+ *
19
+ * A published sample of generated output is a promise about what the generator
20
+ * does. Unguarded it drifts toward documenting bugs that were already fixed,
21
+ * which is worse than showing nothing, because it reads as authoritative.
22
+ *
23
+ * ## Why this compares structurally rather than byte for byte
24
+ *
25
+ * The issue asks for a regenerate-and-diff. That cannot be literal here,
26
+ * because the sample deliberately ELIDES two long scripts:
27
+ *
28
+ * node -e '...set gated/op/gate/approve on $GITHUB_OUTPUT...'
29
+ * ...find the merged PR for $GITHUB_SHA, post or edit the marker comment...
30
+ *
31
+ * Abbreviating them is right: a reader wants the workflow's shape, not forty
32
+ * lines of embedded JavaScript. So the rule this enforces is not "the sample is
33
+ * the output" but the weaker and more useful one: **the sample may abbreviate,
34
+ * and may not misstate.** A string containing `...` is a wildcard; everything
35
+ * else must match the generator exactly.
36
+ *
37
+ * Both staleness events would have failed here. `shell: bash` is a key, not an
38
+ * elision, and so is the `run:` line that carried `set -o pipefail`.
39
+ */
40
+ const DOC = fileURLToPath(new URL("../docs/pages/ops.mdx", import.meta.url));
41
+
42
+ /** The spec the prose immediately above the sample declares, transcribed. */
43
+ const assumeRole = (roleVariable: string) => [
44
+ {
45
+ uses: "aws-actions/configure-aws-credentials@v6",
46
+ with: { "role-to-assume": `\${{ vars.${roleVariable} }}`, "aws-region": "eu-west-1" },
47
+ },
48
+ ];
49
+
50
+ /**
51
+ * The sample references `installTerraform` without ever defining it, which is
52
+ * its own small defect in the page. Transcribed here from the `run:` line the
53
+ * sample publishes, so this test pins what the doc shows rather than inventing
54
+ * a step the doc does not.
55
+ */
56
+ const installTerraform =
57
+ "curl -fsSL https://releases.hashicorp.com/terraform/1.13.3/terraform_1.13.3_linux_amd64.zip " +
58
+ "-o /tmp/terraform.zip && unzip -q -o /tmp/terraform.zip -d /usr/local/bin && terraform version";
59
+
60
+ /** Pull the fenced yaml block whose first line names `file`. */
61
+ function publishedYaml(doc: string, file: string): string {
62
+ const fence = new RegExp("```yaml\\n# " + file + "\\n([\\s\\S]*?)\\n```");
63
+ const found = fence.exec(doc);
64
+ if (!found) throw new Error(`no \`\`\`yaml block for "${file}" in ops.mdx`);
65
+ return found[1];
66
+ }
67
+
68
+ /**
69
+ * Compare `documented` against `generated`, treating any documented string
70
+ * containing `...` as an elision that matches whatever the generator produced.
71
+ */
72
+ function mismatches(documented: unknown, generated: unknown, path = ""): string[] {
73
+ if (typeof documented === "string" && documented.includes("...")) return [];
74
+ if (Array.isArray(documented) || Array.isArray(generated)) {
75
+ if (!Array.isArray(documented) || !Array.isArray(generated)) {
76
+ return [`${path}: documented ${JSON.stringify(documented)}, generated ${JSON.stringify(generated)}`];
77
+ }
78
+ if (documented.length !== generated.length) {
79
+ return [`${path}: documented ${documented.length} items, generated ${generated.length}`];
80
+ }
81
+ return documented.flatMap((d, i) => mismatches(d, generated[i], `${path}[${i}]`));
82
+ }
83
+ if (documented && generated && typeof documented === "object" && typeof generated === "object") {
84
+ const d = documented as Record<string, unknown>;
85
+ const g = generated as Record<string, unknown>;
86
+ const keys = [...new Set([...Object.keys(d), ...Object.keys(g)])];
87
+ return keys.flatMap((k) => {
88
+ const where = path ? `${path}.${k}` : k;
89
+ if (!(k in d)) return [`${where}: the generator emits this and the sample omits it`];
90
+ if (!(k in g)) return [`${where}: the sample shows this and the generator does not emit it`];
91
+ return mismatches(d[k], g[k], where);
92
+ });
93
+ }
94
+ return documented === generated
95
+ ? []
96
+ : [`${path}: documented ${JSON.stringify(documented)}, generated ${JSON.stringify(generated)}`];
97
+ }
98
+
99
+ describe("the ops page's sample matches what the generator emits (chant#2331)", () => {
100
+ const result = generateGithubOpPipeline(
101
+ [
102
+ {
103
+ name: "app-plan",
104
+ trigger: { kind: "pull_request", branches: ["main"] },
105
+ findingMode: "comment",
106
+ setup: assumeRole("AWS_PLAN_ROLE_ARN"),
107
+ permissions: { "id-token": "write" },
108
+ },
109
+ {
110
+ name: "app-apply",
111
+ trigger: { kind: "push", branches: ["main"] },
112
+ setup: assumeRole("AWS_APPLY_ROLE_ARN"),
113
+ permissions: { "id-token": "write" },
114
+ environment: { name: "production" },
115
+ },
116
+ ] as never,
117
+ { beforeScript: [installTerraform] } as never,
118
+ );
119
+
120
+ test("the generator still emits the two files the page describes", () => {
121
+ expect(result.files.map((f) => f.name).sort()).toEqual(["app-apply.yml", "app-plan.yml"]);
122
+ });
123
+
124
+ test("the published app-apply.yml says nothing the generator does not do", () => {
125
+ const doc = readFileSync(DOC, "utf8");
126
+ const documented = load(publishedYaml(doc, "app-apply.yml"));
127
+ const generated = load(result.files.find((f) => f.name === "app-apply.yml")!.yaml);
128
+
129
+ const found = mismatches(documented, generated);
130
+ expect(
131
+ found,
132
+ "lexicons/terraform/docs/pages/ops.mdx publishes a sample of generated output that has " +
133
+ "drifted from the generator. Regenerate the block and resync the derived copy under " +
134
+ "docs/src/content/docs/. An abbreviated script is fine — write `...` inside it — but a " +
135
+ "key or a value that differs is the page promising something the generator does not do.\n\n" +
136
+ found.join("\n"),
137
+ ).toEqual([]);
138
+ });
139
+
140
+ test("an elision is a wildcard, and everything else is not", () => {
141
+ // The comparison's own contract, so a future reader does not weaken it by
142
+ // sprinkling `...` to make a failure go away.
143
+ expect(mismatches({ run: "...anything..." }, { run: "a real script" })).toEqual([]);
144
+ expect(mismatches({ shell: "bash" }, { shell: "sh" })).toHaveLength(1);
145
+ expect(mismatches({ a: 1 }, { a: 1, b: 2 })).toHaveLength(1);
146
+ });
147
+ });