@intentius/chant 0.41.19 → 0.42.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.
- package/dist/cli/build-params-cli.d.ts +7 -4
- package/dist/cli/build-params-cli.d.ts.map +1 -1
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/carve-apply.d.ts +9 -0
- package/dist/cli/commands/carve-apply.d.ts.map +1 -1
- package/dist/cli/commands/carve-bridge.d.ts +7 -0
- package/dist/cli/commands/carve-bridge.d.ts.map +1 -1
- package/dist/cli/commands/carve-emit.d.ts +7 -0
- package/dist/cli/commands/carve-emit.d.ts.map +1 -1
- package/dist/cli/commands/import.d.ts +16 -0
- package/dist/cli/commands/import.d.ts.map +1 -1
- package/dist/cli/commands/init.d.ts +2 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/handlers/carve-apply.d.ts.map +1 -1
- package/dist/cli/handlers/components.d.ts.map +1 -1
- package/dist/cli/handlers/explain.d.ts +11 -0
- package/dist/cli/handlers/explain.d.ts.map +1 -0
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/misc.d.ts.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/mcp/tools/explain.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +4 -0
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/codegen/docs-rule-scanning.d.ts +11 -0
- package/dist/codegen/docs-rule-scanning.d.ts.map +1 -1
- package/dist/codegen/okf-lexicon.d.ts +36 -0
- package/dist/codegen/okf-lexicon.d.ts.map +1 -0
- package/dist/codegen/package.d.ts +2 -1
- package/dist/codegen/package.d.ts.map +1 -1
- package/dist/components/deploy-units.d.ts.map +1 -1
- package/dist/components/verbs/sbom.d.ts +1 -1
- package/dist/components/verbs/sbom.d.ts.map +1 -1
- package/dist/components/verbs/vuln-gate.d.ts +15 -4
- package/dist/components/verbs/vuln-gate.d.ts.map +1 -1
- package/dist/components/verbs/vuln-scan.d.ts +18 -6
- package/dist/components/verbs/vuln-scan.d.ts.map +1 -1
- package/dist/config.d.ts +12 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/deep-observation.d.ts +13 -0
- package/dist/deep-observation.d.ts.map +1 -1
- package/dist/governance.d.ts +37 -0
- package/dist/governance.d.ts.map +1 -0
- package/dist/lexicon.d.ts +3 -0
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/observe.d.ts.map +1 -1
- package/dist/lifecycle/snapshot.d.ts.map +1 -1
- package/dist/okf.d.ts +57 -0
- package/dist/okf.d.ts.map +1 -0
- package/dist/reconcile.d.ts +15 -0
- package/dist/reconcile.d.ts.map +1 -1
- package/dist/terraform/__fixtures__/build-graph.d.ts +13 -0
- package/dist/terraform/__fixtures__/build-graph.d.ts.map +1 -0
- package/dist/terraform/adopt-state.d.ts +33 -1
- package/dist/terraform/adopt-state.d.ts.map +1 -1
- package/dist/terraform/bridge.d.ts +7 -1
- package/dist/terraform/bridge.d.ts.map +1 -1
- package/dist/terraform/carve.d.ts +12 -0
- package/dist/terraform/carve.d.ts.map +1 -1
- package/dist/terraform/excise.d.ts +31 -0
- package/dist/terraform/excise.d.ts.map +1 -0
- package/dist/terraform/graduate.d.ts +12 -0
- package/dist/terraform/graduate.d.ts.map +1 -1
- package/dist/terraform/graph.d.ts +35 -5
- package/dist/terraform/graph.d.ts.map +1 -1
- package/dist/terraform/manifest.d.ts +89 -0
- package/dist/terraform/manifest.d.ts.map +1 -0
- package/dist/terraform/parse.d.ts +9 -4
- package/dist/terraform/parse.d.ts.map +1 -1
- package/dist/terraform/types.d.ts +7 -0
- package/dist/terraform/types.d.ts.map +1 -1
- package/dist/terraform/unified-diff.d.ts +11 -0
- package/dist/terraform/unified-diff.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/__snapshots__/okf.test.ts.snap +381 -0
- package/src/cli/build-params-cli.test.ts +47 -3
- package/src/cli/build-params-cli.ts +22 -5
- package/src/cli/commands/build.test.ts +44 -0
- package/src/cli/commands/build.ts +15 -2
- package/src/cli/commands/carve-apply.test.ts +94 -2
- package/src/cli/commands/carve-apply.ts +62 -11
- package/src/cli/commands/carve-bridge.test.ts +82 -4
- package/src/cli/commands/carve-bridge.ts +79 -11
- package/src/cli/commands/carve-emit-state.test.ts +110 -1
- package/src/cli/commands/carve-emit.ts +184 -8
- package/src/cli/commands/import.ts +53 -5
- package/src/cli/commands/init.ts +1 -1
- package/src/cli/handlers/build.test.ts +3 -1
- package/src/cli/handlers/carve-apply.ts +1 -0
- package/src/cli/handlers/components.ts +4 -2
- package/src/cli/handlers/explain.test.ts +93 -0
- package/src/cli/handlers/explain.ts +60 -0
- package/src/cli/handlers/lifecycle.ts +9 -7
- package/src/cli/handlers/misc.ts +40 -1
- package/src/cli/handlers/run.test.ts +3 -1
- package/src/cli/main.ts +25 -5
- package/src/cli/mcp/server.test.ts +17 -0
- package/src/cli/mcp/tools/explain.ts +15 -3
- package/src/cli/registry.ts +4 -0
- package/src/codegen/__snapshots__/okf-lexicon.test.ts.snap +170 -0
- package/src/codegen/docs-rule-scanning.ts +35 -14
- package/src/codegen/okf-lexicon.test.ts +248 -0
- package/src/codegen/okf-lexicon.ts +303 -0
- package/src/codegen/package.ts +29 -4
- package/src/codegen/publish-order.test.ts +1 -1
- package/src/codegen/release-wiring.test.ts +7 -3
- package/src/components/config-defaults.test.ts +30 -0
- package/src/components/deploy-units.ts +3 -0
- package/src/components/verbs/__fixtures__/grype-with-kev-epss.json +1 -0
- package/src/components/verbs/__fixtures__/trivy-with-kev-epss.json +631 -0
- package/src/components/verbs/exploitability-roundtrip.test.ts +190 -0
- package/src/components/verbs/sbom.ts +1 -1
- package/src/components/verbs/vuln-gate.test.ts +144 -1
- package/src/components/verbs/vuln-gate.ts +61 -10
- package/src/components/verbs/vuln-scan.test.ts +94 -0
- package/src/components/verbs/vuln-scan.ts +44 -9
- package/src/config.test.ts +21 -0
- package/src/config.ts +18 -0
- package/src/deep-observation.test.ts +19 -0
- package/src/deep-observation.ts +32 -13
- package/src/governance.test.ts +72 -0
- package/src/governance.ts +54 -0
- package/src/lexicon.ts +3 -0
- package/src/lifecycle/observe.test.ts +18 -0
- package/src/lifecycle/observe.ts +10 -3
- package/src/lifecycle/snapshot.ts +5 -4
- package/src/okf.test.ts +169 -0
- package/src/okf.ts +308 -0
- package/src/reconcile.ts +32 -1
- package/src/terraform/__fixtures__/build-graph.ts +25 -0
- package/src/terraform/adopt-state.test.ts +34 -1
- package/src/terraform/adopt-state.ts +69 -2
- package/src/terraform/bridge.test.ts +38 -14
- package/src/terraform/bridge.ts +48 -14
- package/src/terraform/carve.test.ts +9 -7
- package/src/terraform/carve.ts +0 -0
- package/src/terraform/excise.test.ts +92 -0
- package/src/terraform/excise.ts +139 -0
- package/src/terraform/graduate.test.ts +73 -6
- package/src/terraform/graduate.ts +49 -0
- package/src/terraform/graph.test.ts +56 -10
- package/src/terraform/graph.ts +112 -45
- package/src/terraform/manifest.test.ts +105 -0
- package/src/terraform/manifest.ts +162 -0
- package/src/terraform/parse.test.ts +39 -1
- package/src/terraform/parse.ts +36 -9
- package/src/terraform/score.test.ts +8 -8
- package/src/terraform/state.test.ts +4 -4
- package/src/terraform/types.ts +7 -0
- package/src/terraform/unified-diff.test.ts +77 -0
- package/src/terraform/unified-diff.ts +146 -0
|
@@ -32,6 +32,7 @@ import { affectedStacks } from "../../lifecycle/affected";
|
|
|
32
32
|
import { rollbackToRevision } from "../../lifecycle/rollback";
|
|
33
33
|
import { loadChantConfig, environmentNames } from "../../config";
|
|
34
34
|
import { applyLiveEndpoint } from "../../live-endpoint";
|
|
35
|
+
import { isResourceDeclarable } from "../../declarable";
|
|
35
36
|
import { formatError, formatWarning, formatSuccess, formatBold } from "../format";
|
|
36
37
|
import type { CommandContext } from "../registry";
|
|
37
38
|
import type { LifecycleSnapshot } from "../../lifecycle/types";
|
|
@@ -758,17 +759,18 @@ async function runLifecycleDiffLive(args: LiveDiffArgs): Promise<LiveDiffOutcome
|
|
|
758
759
|
continue;
|
|
759
760
|
}
|
|
760
761
|
|
|
761
|
-
// Build per-lexicon entity index
|
|
762
|
+
// Build per-lexicon entity index. Resource declarables only — outputs,
|
|
763
|
+
// parameters and serializer directives have no live counterpart, and a
|
|
764
|
+
// declared name the reader can never resolve reads as missing (see
|
|
765
|
+
// lifecycle/observe.ts).
|
|
762
766
|
const declared = new Set<string>();
|
|
763
767
|
const entities = new Map<string, { entityType: string; props: Record<string, unknown> }>();
|
|
764
768
|
for (const [name, entity] of args.buildResult.entities) {
|
|
765
|
-
if (entity.lexicon === lexiconName) {
|
|
769
|
+
if (entity.lexicon === lexiconName && isResourceDeclarable(entity)) {
|
|
766
770
|
declared.add(name);
|
|
767
771
|
entities.set(name, {
|
|
768
772
|
entityType: entity.entityType,
|
|
769
|
-
props: (
|
|
770
|
-
? entity.props
|
|
771
|
-
: {}) as Record<string, unknown>,
|
|
773
|
+
props: (entity.props != null ? entity.props : {}) as Record<string, unknown>,
|
|
772
774
|
});
|
|
773
775
|
}
|
|
774
776
|
}
|
|
@@ -1119,11 +1121,11 @@ export async function runLifecyclePlan(ctx: CommandContext): Promise<number> {
|
|
|
1119
1121
|
const declared = new Set<string>();
|
|
1120
1122
|
const entities = new Map<string, { entityType: string; props: Record<string, unknown> }>();
|
|
1121
1123
|
for (const [name, entity] of buildResult.entities) {
|
|
1122
|
-
if (entity.lexicon === lexiconName) {
|
|
1124
|
+
if (entity.lexicon === lexiconName && isResourceDeclarable(entity)) {
|
|
1123
1125
|
declared.add(name);
|
|
1124
1126
|
entities.set(name, {
|
|
1125
1127
|
entityType: entity.entityType,
|
|
1126
|
-
props: (
|
|
1128
|
+
props: (entity.props != null ? entity.props : {}) as Record<string, unknown>,
|
|
1127
1129
|
});
|
|
1128
1130
|
}
|
|
1129
1131
|
}
|
package/src/cli/handlers/misc.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { listCommand, printListResult } from "../commands/list";
|
|
2
2
|
import { describeCommand, printDescribeResult } from "../commands/describe";
|
|
3
|
-
import { importCommand, importFromLive, importFromLiveStacks, printImportResult } from "../commands/import";
|
|
3
|
+
import { importCommand, importFromContent, importFromLive, importFromLiveStacks, printImportResult } from "../commands/import";
|
|
4
4
|
import { loadChantConfig } from "../../config";
|
|
5
5
|
import { resolve } from "path";
|
|
6
6
|
import { auditCommand, printAuditResult, type AuditFormat, type AuditTier, type AuditFailOn } from "../commands/audit";
|
|
@@ -171,6 +171,45 @@ export async function runDescribe(ctx: CommandContext): Promise<number> {
|
|
|
171
171
|
export async function runImport(ctx: CommandContext): Promise<number> {
|
|
172
172
|
const { args } = ctx;
|
|
173
173
|
|
|
174
|
+
// `--kustomize <dir>` (#1548): render the overlay and import the output
|
|
175
|
+
// through the k8s template parser — the flag NAMES the lexicon, so no JSON
|
|
176
|
+
// detection. `kustomize build`, falling back to kubectl's vendored
|
|
177
|
+
// kustomize when the standalone binary is absent; a big overlay renders
|
|
178
|
+
// megabytes, hence the buffer bound.
|
|
179
|
+
if (args.kustomize) {
|
|
180
|
+
const { exec } = await import("node:child_process");
|
|
181
|
+
const { promisify } = await import("node:util");
|
|
182
|
+
const execAsync = promisify(exec);
|
|
183
|
+
const run = (cmd: string) => execAsync(cmd, { maxBuffer: 64 * 1024 * 1024 });
|
|
184
|
+
const quoted = `'${args.kustomize.replace(/'/g, "'\\''")}'`;
|
|
185
|
+
let rendered: string;
|
|
186
|
+
try {
|
|
187
|
+
({ stdout: rendered } = await run(`kustomize build ${quoted}`));
|
|
188
|
+
} catch (err) {
|
|
189
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
190
|
+
if (!/ENOENT|not found|command not found|127/.test(message)) {
|
|
191
|
+
console.error(formatError({ message: `kustomize build failed: ${message.split("\n")[0]}` }));
|
|
192
|
+
return 1;
|
|
193
|
+
}
|
|
194
|
+
try {
|
|
195
|
+
({ stdout: rendered } = await run(`kubectl kustomize ${quoted}`));
|
|
196
|
+
} catch (err2) {
|
|
197
|
+
console.error(formatError({
|
|
198
|
+
message: `neither kustomize nor kubectl could render ${args.kustomize}: ${err2 instanceof Error ? err2.message.split("\n")[0] : String(err2)}`,
|
|
199
|
+
}));
|
|
200
|
+
return 1;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const result = await importFromContent({
|
|
204
|
+
content: rendered,
|
|
205
|
+
lexicon: "k8s",
|
|
206
|
+
output: args.output,
|
|
207
|
+
force: args.force,
|
|
208
|
+
});
|
|
209
|
+
printImportResult(result);
|
|
210
|
+
return result.success ? 0 : 1;
|
|
211
|
+
}
|
|
212
|
+
|
|
174
213
|
// `--from <env>` switches import from a template file to a live source.
|
|
175
214
|
if (args.migrateFrom) {
|
|
176
215
|
const selector: ResourceSelector | undefined =
|
|
@@ -951,7 +951,9 @@ describe("runOpComponents", () => {
|
|
|
951
951
|
expect(runComponentsMock).toHaveBeenCalledWith(expect.any(String), "svc", expect.objectContaining({
|
|
952
952
|
buildParams: [{ name: "tier", value: "light", source: "default" }],
|
|
953
953
|
}));
|
|
954
|
-
|
|
954
|
+
// A parameter at its default collapses into the count line now; the
|
|
955
|
+
// provenance forwarded above is where its value is asserted.
|
|
956
|
+
expect(stderr.join("\n")).toContain("at their defaults");
|
|
955
957
|
});
|
|
956
958
|
|
|
957
959
|
test("--param overrides a declared default and is threaded through to runComponents", async () => {
|
package/src/cli/main.ts
CHANGED
|
@@ -26,6 +26,7 @@ import { runCarveApply } from "./handlers/carve-apply";
|
|
|
26
26
|
import { runLifecycleSnapshot, runLifecycleShow, runLifecycleDiff, runLifecycleRollback, runLifecyclePlan, runLifecycleAffected, runLifecycleLog, runLifecycleUnknown } from "./handlers/lifecycle";
|
|
27
27
|
import { runComponentsStatus, runComponentsReleaseRecord, runComponentsUnknown } from "./handlers/components";
|
|
28
28
|
import { runGraph } from "./handlers/graph";
|
|
29
|
+
import { runExplain } from "./handlers/explain";
|
|
29
30
|
import { runSearch } from "./handlers/search";
|
|
30
31
|
import { runOp, runOpList, runOpStatus, runOpSignal, runOpCancel, runOpLog } from "./handlers/run";
|
|
31
32
|
import { runEmulator } from "./handlers/emulator";
|
|
@@ -188,6 +189,10 @@ export function parseArgs(args: string[]): ParsedArgs {
|
|
|
188
189
|
// Shared by `migrate --from <lexicon>` and `import --from <env>`; the
|
|
189
190
|
// two commands never run together, so one field carries both.
|
|
190
191
|
result.migrateFrom = args[++i];
|
|
192
|
+
} else if (arg === "--kustomize") {
|
|
193
|
+
// `chant import --kustomize <dir>` (#1548): render the overlay, import
|
|
194
|
+
// the output through the k8s template parser.
|
|
195
|
+
result.kustomize = args[++i];
|
|
191
196
|
} else if (arg === "--type") {
|
|
192
197
|
result.selectType = args[++i];
|
|
193
198
|
} else if (arg === "--name") {
|
|
@@ -208,6 +213,8 @@ export function parseArgs(args: string[]): ParsedArgs {
|
|
|
208
213
|
result.carveStack = args[++i];
|
|
209
214
|
} else if (arg === "--write") {
|
|
210
215
|
result.write = true;
|
|
216
|
+
} else if (arg === "--write-source") {
|
|
217
|
+
result.writeSource = true;
|
|
211
218
|
} else if (arg === "--to") {
|
|
212
219
|
result.migrateTo = args[++i];
|
|
213
220
|
} else if (arg === "--emit") {
|
|
@@ -381,6 +388,10 @@ Commands:
|
|
|
381
388
|
lint Check specifications for issues
|
|
382
389
|
list List discovered entities
|
|
383
390
|
describe Show the effective config for one component
|
|
391
|
+
explain Summarize discovered entities (--format markdown|json|okf;
|
|
392
|
+
okf emits an OKF v0.2 knowledge bundle — one markdown
|
|
393
|
+
concept per entity + index.md; -o <dir> writes the
|
|
394
|
+
bundle tree, otherwise JSON path→content on stdout)
|
|
384
395
|
vendor Pull pinned, checksummed patterns into your repo
|
|
385
396
|
import Import external template into TypeScript
|
|
386
397
|
audit [path|url] Audit a repo's CI YAML for security issues
|
|
@@ -398,14 +409,22 @@ Commands:
|
|
|
398
409
|
--select <addr> (recommended for TF-managed resources); --env
|
|
399
410
|
--state|--env <env> adopts via live cloud import (--live-name
|
|
400
411
|
<logical-id> narrows a multi-resource stack).
|
|
412
|
+
Persists a carve manifest bridge/apply compose
|
|
413
|
+
with; scaffolds the output dir into a buildable
|
|
414
|
+
chant project (src/ + config + package.json).
|
|
401
415
|
carve bridge Generate the surviving-TF patch (data sources + rewired
|
|
402
|
-
--from <tf-dir> refs) + deferred inputs + reversible runbook
|
|
403
|
-
--select <addr>
|
|
404
|
-
|
|
416
|
+
--from <tf-dir> refs) + deferred inputs + reversible runbook,
|
|
417
|
+
[--select <addr>] plus one git-applyable .patch for the whole
|
|
418
|
+
edit. Writes proposals for review;
|
|
419
|
+
--apply-rewrites edits the .tf in place.
|
|
420
|
+
--select is optional when the output dir
|
|
421
|
+
holds one carve manifest.
|
|
405
422
|
carve apply Apply graduation: ownership marker + finalized apply
|
|
406
423
|
--from <tf-dir> runbook (dial-turn observe→apply). BYOL —
|
|
407
|
-
--select <addr>
|
|
408
|
-
--env <env>
|
|
424
|
+
[--select <addr>] no cloud call; --write saves the doc. --select
|
|
425
|
+
--env <env> is optional with a carve manifest present.
|
|
426
|
+
--write-source stamps the ownership marker
|
|
427
|
+
into the emitted chant source.
|
|
409
428
|
|
|
410
429
|
Ops:
|
|
411
430
|
run <name> Start an Op workflow (spawns worker + submits to Temporal)
|
|
@@ -701,6 +720,7 @@ const registry: CommandDef[] = [
|
|
|
701
720
|
{ name: "lint", handler: runLint },
|
|
702
721
|
{ name: "list", handler: runList },
|
|
703
722
|
{ name: "describe", handler: runDescribe },
|
|
723
|
+
{ name: "explain", handler: runExplain },
|
|
704
724
|
{ name: "search", handler: runSearch },
|
|
705
725
|
{ name: "import", handler: runImport },
|
|
706
726
|
{ name: "audit", handler: runAudit },
|
|
@@ -327,6 +327,23 @@ describe("McpServer", () => {
|
|
|
327
327
|
expect(parsed.sourceFiles).toBeDefined();
|
|
328
328
|
});
|
|
329
329
|
|
|
330
|
+
test("calls explain tool with okf format (#1058)", async () => {
|
|
331
|
+
const response = await server.handleRequest({
|
|
332
|
+
jsonrpc: "2.0",
|
|
333
|
+
id: 1,
|
|
334
|
+
method: "tools/call",
|
|
335
|
+
params: { name: "explain", arguments: { path: testDir, format: "okf" } },
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
expect(response.error).toBeUndefined();
|
|
339
|
+
const result = response.result as { content: Array<{ text: string }> };
|
|
340
|
+
const parsed = JSON.parse(result.content[0].text);
|
|
341
|
+
expect(parsed.okf_version).toBe("0.2");
|
|
342
|
+
// Even an empty project yields a bundle with a root index.md.
|
|
343
|
+
expect(Object.keys(parsed.files)).toEqual(["index.md"]);
|
|
344
|
+
expect(parsed.files["index.md"]).toContain("okf_version: '0.2'");
|
|
345
|
+
});
|
|
346
|
+
|
|
330
347
|
test("calls scaffold tool with generic fallback", async () => {
|
|
331
348
|
const response = await server.handleRequest({
|
|
332
349
|
jsonrpc: "2.0",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolve } from "path";
|
|
2
2
|
import { discover } from "../../../discovery/index";
|
|
3
|
+
import { buildOkfBundle, OKF_VERSION } from "../../../okf";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Explain tool definition for MCP
|
|
@@ -16,8 +17,8 @@ export const explainTool = {
|
|
|
16
17
|
},
|
|
17
18
|
format: {
|
|
18
19
|
type: "string",
|
|
19
|
-
enum: ["markdown", "json"],
|
|
20
|
-
description: "Output format (default: markdown)",
|
|
20
|
+
enum: ["markdown", "json", "okf"],
|
|
21
|
+
description: "Output format (default: markdown). okf: an OKF v0.2 knowledge bundle — one markdown concept per entity plus an index.md (#1058)",
|
|
21
22
|
},
|
|
22
23
|
},
|
|
23
24
|
required: ["path"],
|
|
@@ -29,11 +30,22 @@ export const explainTool = {
|
|
|
29
30
|
*/
|
|
30
31
|
export async function handleExplain(params: Record<string, unknown>): Promise<unknown> {
|
|
31
32
|
const path = params.path as string;
|
|
32
|
-
const format = (params.format as "markdown" | "json") ?? "markdown";
|
|
33
|
+
const format = (params.format as "markdown" | "json" | "okf") ?? "markdown";
|
|
33
34
|
|
|
34
35
|
const infraPath = resolve(path);
|
|
35
36
|
const result = await discover(infraPath);
|
|
36
37
|
|
|
38
|
+
// OKF knowledge bundle (#1058): one concept document per entity plus a root
|
|
39
|
+
// index.md, returned as bundle-relative path → content. The CLI path writes
|
|
40
|
+
// the same files to a directory.
|
|
41
|
+
if (format === "okf") {
|
|
42
|
+
return {
|
|
43
|
+
okf_version: OKF_VERSION,
|
|
44
|
+
files: Object.fromEntries(buildOkfBundle(result, infraPath).map((f) => [f.path, f.content])),
|
|
45
|
+
errors: result.errors.map((e) => e.message),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
37
49
|
// Group entities by lexicon and kind
|
|
38
50
|
const byLexicon = new Map<string, { resources: string[]; properties: string[] }>();
|
|
39
51
|
|
package/src/cli/registry.ts
CHANGED
|
@@ -31,6 +31,8 @@ export interface ParsedArgs {
|
|
|
31
31
|
live: boolean;
|
|
32
32
|
/** `chant migrate --from <name>` (default "github") */
|
|
33
33
|
migrateFrom?: string;
|
|
34
|
+
/** `chant import --kustomize <dir>` — render the kustomization, then import (#1548) */
|
|
35
|
+
kustomize?: string;
|
|
34
36
|
/** `chant carve advise --state <path>` — opt-in .tfstate for accurate instance counts */
|
|
35
37
|
statePath?: string;
|
|
36
38
|
/** `chant carve emit --select <tf-address>` — the Terraform resource to carve */
|
|
@@ -43,6 +45,8 @@ export interface ParsedArgs {
|
|
|
43
45
|
carveStack?: string;
|
|
44
46
|
/** `chant carve apply --write` — save the graduation doc */
|
|
45
47
|
write?: boolean;
|
|
48
|
+
/** `chant carve apply --write-source` — stamp the ownership marker into the emitted source */
|
|
49
|
+
writeSource?: boolean;
|
|
46
50
|
/** `chant migrate --to <name>` (default "gitlab") */
|
|
47
51
|
migrateTo?: string;
|
|
48
52
|
/** `chant migrate --emit yaml|ts` */
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`buildLexiconOkfBundle > registry-style and dts-style bundles snapshot stably 1`] = `
|
|
4
|
+
[
|
|
5
|
+
{
|
|
6
|
+
"content": "---
|
|
7
|
+
okf_version: '0.2'
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Resource types
|
|
11
|
+
|
|
12
|
+
* [Service](/types/Service.md) - Mock::Compose::Service
|
|
13
|
+
* [Volume](/types/Volume.md) - Mock::Compose::Volume
|
|
14
|
+
|
|
15
|
+
# Rules
|
|
16
|
+
|
|
17
|
+
* [MCK001](/rules/MCK001.md) - Avoid :latest image references on a Service
|
|
18
|
+
* [MCK010](/rules/MCK010.md) - A declared Volume must be mounted somewhere
|
|
19
|
+
",
|
|
20
|
+
"path": "index.md",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"content": "---
|
|
24
|
+
type: lint-rule
|
|
25
|
+
title: MCK001
|
|
26
|
+
description: Avoid :latest image references on a Service
|
|
27
|
+
id: MCK001
|
|
28
|
+
severity: warning
|
|
29
|
+
category: correctness
|
|
30
|
+
lexicon: mockdocker
|
|
31
|
+
docs: https://intentius.io/chant/lexicons/mockdocker/rules/
|
|
32
|
+
---
|
|
33
|
+
Avoid :latest image references on a Service
|
|
34
|
+
|
|
35
|
+
## Applies to
|
|
36
|
+
|
|
37
|
+
- [Service](/types/Service.md)
|
|
38
|
+
",
|
|
39
|
+
"path": "rules/MCK001.md",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"content": "---
|
|
43
|
+
type: post-synth-check
|
|
44
|
+
title: MCK010
|
|
45
|
+
description: A declared Volume must be mounted somewhere
|
|
46
|
+
id: MCK010
|
|
47
|
+
severity: error
|
|
48
|
+
category: post-synth
|
|
49
|
+
lexicon: mockdocker
|
|
50
|
+
docs: https://intentius.io/chant/lexicons/mockdocker/rules/
|
|
51
|
+
---
|
|
52
|
+
A declared Volume must be mounted somewhere
|
|
53
|
+
|
|
54
|
+
## Applies to
|
|
55
|
+
|
|
56
|
+
- [Volume](/types/Volume.md)
|
|
57
|
+
",
|
|
58
|
+
"path": "rules/MCK010.md",
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"content": "---
|
|
62
|
+
type: resource-type
|
|
63
|
+
title: Service
|
|
64
|
+
description: A containerized service definition
|
|
65
|
+
name: Service
|
|
66
|
+
lexicon: mockdocker
|
|
67
|
+
resource_type: Mock::Compose::Service
|
|
68
|
+
---
|
|
69
|
+
A containerized service definition
|
|
70
|
+
|
|
71
|
+
## Properties
|
|
72
|
+
|
|
73
|
+
- \`image\` (\`string\`): Container image to use
|
|
74
|
+
- \`ports\` (\`string[]\`): Published ports
|
|
75
|
+
|
|
76
|
+
## Governed by
|
|
77
|
+
|
|
78
|
+
- [MCK001](/rules/MCK001.md): Avoid :latest image references on a Service
|
|
79
|
+
",
|
|
80
|
+
"path": "types/Service.md",
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"content": "---
|
|
84
|
+
type: resource-type
|
|
85
|
+
title: Volume
|
|
86
|
+
description: A named volume
|
|
87
|
+
name: Volume
|
|
88
|
+
lexicon: mockdocker
|
|
89
|
+
resource_type: Mock::Compose::Volume
|
|
90
|
+
---
|
|
91
|
+
A named volume
|
|
92
|
+
|
|
93
|
+
## Properties
|
|
94
|
+
|
|
95
|
+
- \`driver\` (\`string\`): Volume driver
|
|
96
|
+
|
|
97
|
+
## Governed by
|
|
98
|
+
|
|
99
|
+
- [MCK010](/rules/MCK010.md): A declared Volume must be mounted somewhere
|
|
100
|
+
",
|
|
101
|
+
"path": "types/Volume.md",
|
|
102
|
+
},
|
|
103
|
+
]
|
|
104
|
+
`;
|
|
105
|
+
|
|
106
|
+
exports[`buildLexiconOkfBundle > registry-style and dts-style bundles snapshot stably 2`] = `
|
|
107
|
+
[
|
|
108
|
+
{
|
|
109
|
+
"content": "---
|
|
110
|
+
okf_version: '0.2'
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
# Resource types
|
|
114
|
+
|
|
115
|
+
* [Bucket](/types/Bucket.md) - Mock::S3::Bucket
|
|
116
|
+
|
|
117
|
+
# Rules
|
|
118
|
+
|
|
119
|
+
* [MAW006](/rules/MAW006.md) - Detects Bucket creation without encryption
|
|
120
|
+
",
|
|
121
|
+
"path": "index.md",
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"content": "---
|
|
125
|
+
type: lint-rule
|
|
126
|
+
title: MAW006
|
|
127
|
+
description: Detects Bucket creation without encryption
|
|
128
|
+
id: MAW006
|
|
129
|
+
severity: warning
|
|
130
|
+
category: security
|
|
131
|
+
lexicon: mockaws
|
|
132
|
+
docs: https://intentius.io/chant/lexicons/mockaws/rules/
|
|
133
|
+
---
|
|
134
|
+
Detects Bucket creation without encryption
|
|
135
|
+
|
|
136
|
+
## Applies to
|
|
137
|
+
|
|
138
|
+
- [Bucket](/types/Bucket.md)
|
|
139
|
+
",
|
|
140
|
+
"path": "rules/MAW006.md",
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"content": "---
|
|
144
|
+
type: resource-type
|
|
145
|
+
title: Bucket
|
|
146
|
+
description: mockaws resource type Mock::S3::Bucket
|
|
147
|
+
name: Bucket
|
|
148
|
+
lexicon: mockaws
|
|
149
|
+
resource_type: Mock::S3::Bucket
|
|
150
|
+
---
|
|
151
|
+
\`Mock::S3::Bucket\`, a resource type of the mockaws lexicon.
|
|
152
|
+
|
|
153
|
+
## Properties
|
|
154
|
+
|
|
155
|
+
- \`BucketName\` (\`string\`, optional): A name for the bucket.
|
|
156
|
+
- \`VersioningConfiguration\` (\`Bucket_VersioningConfiguration\`, optional): The versioning state.
|
|
157
|
+
|
|
158
|
+
## Attributes
|
|
159
|
+
|
|
160
|
+
- \`Arn\`
|
|
161
|
+
- \`DomainName\`
|
|
162
|
+
|
|
163
|
+
## Governed by
|
|
164
|
+
|
|
165
|
+
- [MAW006](/rules/MAW006.md): Detects Bucket creation without encryption
|
|
166
|
+
",
|
|
167
|
+
"path": "types/Bucket.md",
|
|
168
|
+
},
|
|
169
|
+
]
|
|
170
|
+
`;
|
|
@@ -11,12 +11,27 @@ import { join } from "path";
|
|
|
11
11
|
import { escapeMdx } from "./docs-file-markers";
|
|
12
12
|
import type { DocsConfig, RuleMeta } from "./docs-types";
|
|
13
13
|
|
|
14
|
+
/** A scanned rule: its extracted metadata plus the source it came from. */
|
|
15
|
+
export interface ScannedRule {
|
|
16
|
+
meta: RuleMeta;
|
|
17
|
+
source: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
14
20
|
/**
|
|
15
21
|
* Scan lint rule and post-synth check source files to extract metadata.
|
|
16
22
|
* Uses regex to find id, severity, category, and description from source.
|
|
17
23
|
*/
|
|
18
24
|
export function scanRules(srcDir: string): RuleMeta[] {
|
|
19
|
-
|
|
25
|
+
return scanRulesWithSources(srcDir).map((r) => r.meta);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Like {@link scanRules}, but keeps each rule's source text alongside its
|
|
30
|
+
* metadata — the OKF bundle builder (#1060) reads the source to associate a
|
|
31
|
+
* rule with the resource types it mentions.
|
|
32
|
+
*/
|
|
33
|
+
export function scanRulesWithSources(srcDir: string): ScannedRule[] {
|
|
34
|
+
const rules: ScannedRule[] = [];
|
|
20
35
|
|
|
21
36
|
// Scan lint rules
|
|
22
37
|
scanDir(join(srcDir, "lint", "rules"), "lint", rules);
|
|
@@ -27,7 +42,7 @@ export function scanRules(srcDir: string): RuleMeta[] {
|
|
|
27
42
|
return rules;
|
|
28
43
|
}
|
|
29
44
|
|
|
30
|
-
function scanDir(dir: string, type: "lint" | "post-synth", out:
|
|
45
|
+
function scanDir(dir: string, type: "lint" | "post-synth", out: ScannedRule[]): void {
|
|
31
46
|
let entries: string[];
|
|
32
47
|
try {
|
|
33
48
|
entries = readdirSync(dir);
|
|
@@ -62,13 +77,16 @@ function scanDir(dir: string, type: "lint" | "post-synth", out: RuleMeta[]): voi
|
|
|
62
77
|
);
|
|
63
78
|
|
|
64
79
|
out.push({
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
80
|
+
meta: {
|
|
81
|
+
id: idMatch[1],
|
|
82
|
+
severity: severityMatch?.[1] ?? "warning",
|
|
83
|
+
category: categoryMatch?.[1] ?? "general",
|
|
84
|
+
description:
|
|
85
|
+
descMatch?.[1]?.trim() ??
|
|
86
|
+
extractDescriptionFromComment(content, idMatch[1]),
|
|
87
|
+
type: "lint",
|
|
88
|
+
},
|
|
89
|
+
source: content,
|
|
72
90
|
});
|
|
73
91
|
}
|
|
74
92
|
} else {
|
|
@@ -78,11 +96,14 @@ function scanDir(dir: string, type: "lint" | "post-synth", out: RuleMeta[]): voi
|
|
|
78
96
|
|
|
79
97
|
if (idMatch) {
|
|
80
98
|
out.push({
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
99
|
+
meta: {
|
|
100
|
+
id: idMatch[1],
|
|
101
|
+
severity: "error",
|
|
102
|
+
category: "post-synth",
|
|
103
|
+
description: descMatch?.[1] ?? idMatch[1],
|
|
104
|
+
type: "post-synth",
|
|
105
|
+
},
|
|
106
|
+
source: content,
|
|
86
107
|
});
|
|
87
108
|
}
|
|
88
109
|
}
|