@intentius/chant 0.41.19 → 0.41.20
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/import.d.ts +16 -0
- package/dist/cli/commands/import.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/registry.d.ts +2 -0
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/components/deploy-units.d.ts.map +1 -1
- package/dist/governance.d.ts +37 -0
- package/dist/governance.d.ts.map +1 -0
- package/dist/reconcile.d.ts +15 -0
- package/dist/reconcile.d.ts.map +1 -1
- package/package.json +1 -1
- 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/import.ts +53 -5
- package/src/cli/handlers/build.test.ts +3 -1
- package/src/cli/handlers/misc.ts +40 -1
- package/src/cli/handlers/run.test.ts +3 -1
- package/src/cli/main.ts +4 -0
- package/src/cli/registry.ts +2 -0
- package/src/codegen/publish-order.test.ts +1 -1
- package/src/codegen/release-wiring.test.ts +7 -3
- package/src/components/deploy-units.ts +3 -0
- package/src/governance.test.ts +72 -0
- package/src/governance.ts +54 -0
- package/src/reconcile.ts +32 -1
|
@@ -46,10 +46,13 @@ export interface CliBuildParamsResolution {
|
|
|
46
46
|
* chant #1064's acceptance criterion that this never surfaces as a thrown
|
|
47
47
|
* error from inside user source.
|
|
48
48
|
*
|
|
49
|
-
* On success, every
|
|
50
|
-
* `[param] <name> = <value> (<source>)
|
|
51
|
-
*
|
|
52
|
-
*
|
|
49
|
+
* On success, every explicitly-set parameter is logged via `console.error` as
|
|
50
|
+
* `[param] <name> = <value> (<source>)`, and the ones still at their declared
|
|
51
|
+
* defaults collapse to a single count line (`CHANT_PARAM_ECHO=all` lists them
|
|
52
|
+
* individually). Overrides stay unconditionally visible — not gated on
|
|
53
|
+
* `--verbose` — so a build's environment-varying inputs are always in the log,
|
|
54
|
+
* the same way #1022's fold decisions are; the defaults are the part that
|
|
55
|
+
* never varies, which is exactly why they can be a count.
|
|
53
56
|
*/
|
|
54
57
|
export declare function resolveCliBuildParams(buildParamsConfig: BuildParamsConfig | undefined, args: CliBuildParamsArgs): CliBuildParamsResolution;
|
|
55
58
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-params-cli.d.ts","sourceRoot":"","sources":["../../src/cli/build-params-cli.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsB,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAC7E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAG1D;;;;;;;;;;;;;;;GAeG;AAEH,4SAA4S;AAC5S,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAQtF;AAED,qOAAqO;AACrO,MAAM,WAAW,kBAAkB;IACjC,mNAAmN;IACnN,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,gGAAgG;IAChG,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,iHAAiH;IACjH,UAAU,EAAE,oBAAoB,EAAE,CAAC;IACnC,4FAA4F;IAC5F,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED
|
|
1
|
+
{"version":3,"file":"build-params-cli.d.ts","sourceRoot":"","sources":["../../src/cli/build-params-cli.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsB,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAC7E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAG1D;;;;;;;;;;;;;;;GAeG;AAEH,4SAA4S;AAC5S,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAQtF;AAED,qOAAqO;AACrO,MAAM,WAAW,kBAAkB;IACjC,mNAAmN;IACnN,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,gGAAgG;IAChG,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,iHAAiH;IACjH,UAAU,EAAE,oBAAoB,EAAE,CAAC;IACnC,4FAA4F;IAC5F,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,qBAAqB,CACnC,iBAAiB,EAAE,iBAAiB,GAAG,SAAS,EAChD,IAAI,EAAE,kBAAkB,GACvB,wBAAwB,CAgD1B;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,kBAAkB,CACtC,iBAAiB,EAAE,iBAAiB,GAAG,SAAS,EAChD,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9C,OAAO,CAAC,oBAAoB,EAAE,GAAG,SAAS,CAAC,CAU7C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/build.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAoB,MAAM,kBAAkB,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAYnD;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oBAAoB;IACpB,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,2CAA2C;IAC3C,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,8CAA8C;IAC9C,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;IAC1B,8BAA8B;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEhC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,MAAM,EAAE,MAAM,GAAG,SAAS,GACzB;IAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAoB/C;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,kCAAkC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,gCAAgC;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,uBAAuB;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,2JAA2J;IAC3J,WAAW,CAAC,EAAE,OAAO,kBAAkB,EAAE,oBAAoB,EAAE,CAAC;CACjE;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/build.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAoB,MAAM,kBAAkB,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAYnD;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oBAAoB;IACpB,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,2CAA2C;IAC3C,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,8CAA8C;IAC9C,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;IAC1B,8BAA8B;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEhC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,MAAM,EAAE,MAAM,GAAG,SAAS,GACzB;IAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAoB/C;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,kCAAkC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,gCAAgC;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,uBAAuB;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,2JAA2J;IAC3J,WAAW,CAAC,EAAE,OAAO,kBAAkB,EAAE,oBAAoB,EAAE,CAAC;CACjE;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAoc9E;AAiDD;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAIlD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAItD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,YAAY,EACrB,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,GACxC,MAAM,IAAI,CAmCZ"}
|
|
@@ -29,6 +29,22 @@ export interface ImportResult {
|
|
|
29
29
|
* Execute the import command
|
|
30
30
|
*/
|
|
31
31
|
export declare function importCommand(options: ImportOptions): Promise<ImportResult>;
|
|
32
|
+
/**
|
|
33
|
+
* Import from an in-memory template string through a KNOWN plugin — no
|
|
34
|
+
* detection, no JSON assumption (#1548). This is the seam
|
|
35
|
+
* `chant import --kustomize <dir>` drives with `kustomize build` output
|
|
36
|
+
* through the k8s plugin's YAML parser; `importCommand` above is the same
|
|
37
|
+
* pipeline behind file reading + JSON detection.
|
|
38
|
+
*/
|
|
39
|
+
export interface ContentImportOptions {
|
|
40
|
+
/** The raw template content (YAML or JSON — the plugin's parser decides). */
|
|
41
|
+
content: string;
|
|
42
|
+
/** The lexicon whose parser/generator handle it, e.g. "k8s". */
|
|
43
|
+
lexicon: string;
|
|
44
|
+
output?: string;
|
|
45
|
+
force?: boolean;
|
|
46
|
+
}
|
|
47
|
+
export declare function importFromContent(options: ContentImportOptions): Promise<ImportResult>;
|
|
32
48
|
/**
|
|
33
49
|
* Live import options — read from a running cloud/cluster instead of a file.
|
|
34
50
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"import.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/import.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAErE;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,4BAA4B;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,8CAA8C;IAC9C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,+BAA+B;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,sBAAsB;IACtB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,uBAAuB;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uBAAuB;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAmID;;GAEG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"import.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/import.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAErE;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,4BAA4B;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,8CAA8C;IAC9C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,+BAA+B;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,sBAAsB;IACtB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,uBAAuB;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uBAAuB;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAmID;;GAEG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CA4EjF;AAED;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB;IACnC,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,YAAY,CAAC,CA2B5F;AAuED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,yEAAyE;IACzE,WAAW,EAAE,MAAM,CAAC;IACpB;;uBAEmB;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,+EAA+E;IAC/E,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAkBD;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,YAAY,CAAC,CAatF;AAED;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,OAAO,GAAG,QAAQ,CAAC,EACpD,MAAM,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,GAC3C,OAAO,CAAC,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,YAAY,CAAA;CAAE,CAAC,CAAC,CAezD;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,aAAa,EAAE,EACxB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,YAAY,CAAC,CAoFvB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAsB5D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"misc.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/misc.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAgBlD,wBAAsB,QAAQ,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAsDnE;AAED,wBAAsB,OAAO,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAuClE;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA8CtE;AAED,wBAAsB,SAAS,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"misc.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/misc.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAgBlD,wBAAsB,QAAQ,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAsDnE;AAED,wBAAsB,OAAO,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAuClE;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA8CtE;AAED,wBAAsB,SAAS,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA+FpE;AAED,wBAAsB,SAAS,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAKpE;AAED,wBAAsB,SAAS,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAgBpE"}
|
package/dist/cli/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":";AAMA,OAAO,EAAmC,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AA2E9E;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":";AAMA,OAAO,EAAmC,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AA2E9E;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAqRpD;AAmiBD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB5E"}
|
package/dist/cli/registry.d.ts
CHANGED
|
@@ -30,6 +30,8 @@ export interface ParsedArgs {
|
|
|
30
30
|
live: boolean;
|
|
31
31
|
/** `chant migrate --from <name>` (default "github") */
|
|
32
32
|
migrateFrom?: string;
|
|
33
|
+
/** `chant import --kustomize <dir>` — render the kustomization, then import (#1548) */
|
|
34
|
+
kustomize?: string;
|
|
33
35
|
/** `chant carve advise --state <path>` — opt-in .tfstate for accurate instance counts */
|
|
34
36
|
statePath?: string;
|
|
35
37
|
/** `chant carve emit --select <tf-address>` — the Terraform resource to carve */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/cli/registry.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/cli/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deploy-units.d.ts","sourceRoot":"","sources":["../../src/components/deploy-units.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,6EAA6E;AAC7E,MAAM,WAAW,cAAc;IAC7B,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAAS,cAAc,
|
|
1
|
+
{"version":3,"file":"deploy-units.d.ts","sourceRoot":"","sources":["../../src/components/deploy-units.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,6EAA6E;AAC7E,MAAM,WAAW,cAAc;IAC7B,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAAS,cAAc,EAOtD,CAAC;AAEF,kEAAkE;AAClE,MAAM,WAAW,UAAU;IACzB,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,UAAU,EAAE,CA0BzD"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Governance verb vocabulary (#790, epic #787).
|
|
3
|
+
*
|
|
4
|
+
* `resourceType` on a change-set entry is a free provider-specific string
|
|
5
|
+
* ("team", "branch-protection", "protected-tag", …) chosen per cycle. That is
|
|
6
|
+
* right for display but useless for reading two providers' plans as one
|
|
7
|
+
* grammar. The verb is the cross-provider category a cycle reconciles;
|
|
8
|
+
* provider-specific cycle names stay for display.
|
|
9
|
+
*
|
|
10
|
+
* SCM and cloud cycles stamp the same verbs, so a GitHub plan and an AWS plan
|
|
11
|
+
* group the same way:
|
|
12
|
+
*
|
|
13
|
+
* - `org-unit` — the containers and their settings: GitHub org/repo settings,
|
|
14
|
+
* GitLab group/project/instance settings, cloud OUs/folders/accounts.
|
|
15
|
+
* - `policy-guardrail` — rules constraining what may happen: branch
|
|
16
|
+
* protections, rulesets, push rules, MR approvals, security features,
|
|
17
|
+
* dependency hygiene, SCPs, Org Policies.
|
|
18
|
+
* - `membership` — who belongs, directly or via teams: org members, team
|
|
19
|
+
* membership, group members.
|
|
20
|
+
* - `identity-assignment` — non-person and delegated access: tokens, deploy
|
|
21
|
+
* keys, service credentials, SSO/IAM assignments. `membership` is people
|
|
22
|
+
* in containers; this is everything else that can act.
|
|
23
|
+
* - `audit-sink` — where events and evidence flow: webhooks, integrations,
|
|
24
|
+
* CloudTrail/audit-log sinks.
|
|
25
|
+
* - `secret-material` — secrets and CI variables the platform stores.
|
|
26
|
+
*
|
|
27
|
+
* A cycle has exactly one verb. When a cycle plausibly spans two (GitHub
|
|
28
|
+
* `environments` carries both reviewer gates and secrets), stamp the verb of
|
|
29
|
+
* the cycle's primary object — the thing whose create/delete the cycle owns —
|
|
30
|
+
* not of fields it also happens to write.
|
|
31
|
+
*/
|
|
32
|
+
/** The cross-provider governance category a cycle reconciles. */
|
|
33
|
+
export type GovernanceVerb = "org-unit" | "policy-guardrail" | "membership" | "identity-assignment" | "audit-sink" | "secret-material";
|
|
34
|
+
/** Every verb, for exhaustiveness checks ("does each cycle stamp one?"). */
|
|
35
|
+
export declare const GOVERNANCE_VERBS: readonly GovernanceVerb[];
|
|
36
|
+
export declare function isGovernanceVerb(v: unknown): v is GovernanceVerb;
|
|
37
|
+
//# sourceMappingURL=governance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"governance.d.ts","sourceRoot":"","sources":["../src/governance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,iEAAiE;AACjE,MAAM,MAAM,cAAc,GACtB,UAAU,GACV,kBAAkB,GAClB,YAAY,GACZ,qBAAqB,GACrB,YAAY,GACZ,iBAAiB,CAAC;AAEtB,4EAA4E;AAC5E,eAAO,MAAM,gBAAgB,EAAE,SAAS,cAAc,EAOrD,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,cAAc,CAEhE"}
|
package/dist/reconcile.d.ts
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
* are pure and clock-free; `runReconcile` is the orchestration loop and is the
|
|
19
19
|
* only part that drives I/O (through the provider's `Cycle` implementations).
|
|
20
20
|
*/
|
|
21
|
+
import { type GovernanceVerb } from "./governance.js";
|
|
21
22
|
/** A single field-level change: what the old value was and what it will become. */
|
|
22
23
|
export interface FieldChange {
|
|
23
24
|
field: string;
|
|
@@ -31,6 +32,12 @@ export interface ChangeSetEntry {
|
|
|
31
32
|
kind: ChangeKind;
|
|
32
33
|
/** High-level resource category (e.g. "team", "member", "branch-protection"). */
|
|
33
34
|
resourceType: string;
|
|
35
|
+
/**
|
|
36
|
+
* Cross-provider governance category (#790). `resourceType` stays the
|
|
37
|
+
* provider-specific display string; the verb is the shared grammar SCM and
|
|
38
|
+
* cloud plans group by. Stamped by `runReconcile` from the cycle's `verb`.
|
|
39
|
+
*/
|
|
40
|
+
verb?: GovernanceVerb;
|
|
34
41
|
/**
|
|
35
42
|
* Unique key identifying this resource within its type.
|
|
36
43
|
* - For top-level resources: a single name (team slug, member login, …).
|
|
@@ -172,6 +179,12 @@ export declare class BudgetExhaustedError extends Error {
|
|
|
172
179
|
export interface Cycle<TClient, TConfig, TLive, TScope = unknown> {
|
|
173
180
|
/** Human-readable name, e.g. "branch-protection". */
|
|
174
181
|
name: string;
|
|
182
|
+
/**
|
|
183
|
+
* Cross-provider governance category this cycle reconciles (#790). Every
|
|
184
|
+
* SCM warden cycle stamps one; cloud cycles (epic #787 C2) must. Optional
|
|
185
|
+
* only so provider-external Cycle implementations don't break.
|
|
186
|
+
*/
|
|
187
|
+
verb?: GovernanceVerb;
|
|
175
188
|
fetchLive(client: TClient, scopeId: string, scope: TScope, budget: RateBudget): Promise<TLive>;
|
|
176
189
|
buildDesired(config: TConfig, scopeId: string, scope: TScope): TConfig;
|
|
177
190
|
apply(client: TClient, entry: ChangeSetEntry, scopeId: string, scope: TScope, budget: RateBudget): Promise<void>;
|
|
@@ -179,6 +192,8 @@ export interface Cycle<TClient, TConfig, TLive, TScope = unknown> {
|
|
|
179
192
|
/** Per-cycle outcome recorded in the run result. */
|
|
180
193
|
export interface CycleResult {
|
|
181
194
|
name: string;
|
|
195
|
+
/** The cycle's governance verb (#790), when it stamps one. */
|
|
196
|
+
verb?: GovernanceVerb;
|
|
182
197
|
/** Scope id this result is for (e.g. an org login). */
|
|
183
198
|
org: string;
|
|
184
199
|
counts: {
|
package/dist/reconcile.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../src/reconcile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;
|
|
1
|
+
{"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../src/reconcile.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -56,18 +56,62 @@ describe("resolveCliBuildParams", () => {
|
|
|
56
56
|
}
|
|
57
57
|
});
|
|
58
58
|
|
|
59
|
-
test("
|
|
59
|
+
test("a build with no overrides collapses to a single defaults count", () => {
|
|
60
|
+
// The provenance still records every value; the echo just stops naming
|
|
61
|
+
// what nobody set. One line, so silence still never means "no parameters
|
|
62
|
+
// reached us".
|
|
60
63
|
const defs: BuildParamsConfig = { tier: { type: "string", default: "light" } };
|
|
61
64
|
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
62
65
|
try {
|
|
63
|
-
resolveCliBuildParams(defs, {});
|
|
66
|
+
const result = resolveCliBuildParams(defs, {});
|
|
64
67
|
const logged = errorSpy.mock.calls.map((call) => String(call[0]));
|
|
65
|
-
expect(logged.some((line) => line.includes("
|
|
68
|
+
expect(logged.some((line) => line.includes("1 parameters at their defaults"))).toBe(true);
|
|
69
|
+
expect(logged.some((line) => line.includes("[param] tier"))).toBe(false);
|
|
70
|
+
expect(result.provenance).toEqual([{ name: "tier", value: "light", source: "default" }]);
|
|
66
71
|
} finally {
|
|
67
72
|
errorSpy.mockRestore();
|
|
68
73
|
}
|
|
69
74
|
});
|
|
70
75
|
|
|
76
|
+
test("names the overrides and collapses the defaults into a count", () => {
|
|
77
|
+
const defs: BuildParamsConfig = {
|
|
78
|
+
tier: { type: "string", default: "light" },
|
|
79
|
+
target: { type: "string", default: "k3d" },
|
|
80
|
+
host: { type: "string", default: "localhost:4000" },
|
|
81
|
+
};
|
|
82
|
+
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
83
|
+
try {
|
|
84
|
+
resolveCliBuildParams(defs, { cli: { tier: "ha" } });
|
|
85
|
+
const logged = errorSpy.mock.calls.map((call) => String(call[0]));
|
|
86
|
+
expect(logged.some((line) => line.includes("[param] tier") && line.includes("ha") && line.includes("cli"))).toBe(true);
|
|
87
|
+
expect(logged.some((line) => line.includes("2 more at their defaults"))).toBe(true);
|
|
88
|
+
expect(logged.some((line) => line.includes("[param] target"))).toBe(false);
|
|
89
|
+
} finally {
|
|
90
|
+
errorSpy.mockRestore();
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("CHANT_PARAM_ECHO=all restores the full listing", () => {
|
|
95
|
+
const defs: BuildParamsConfig = {
|
|
96
|
+
tier: { type: "string", default: "light" },
|
|
97
|
+
target: { type: "string", default: "k3d" },
|
|
98
|
+
};
|
|
99
|
+
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
100
|
+
const prev = process.env.CHANT_PARAM_ECHO;
|
|
101
|
+
process.env.CHANT_PARAM_ECHO = "all";
|
|
102
|
+
try {
|
|
103
|
+
resolveCliBuildParams(defs, { cli: { tier: "ha" } });
|
|
104
|
+
const logged = errorSpy.mock.calls.map((call) => String(call[0]));
|
|
105
|
+
expect(logged.some((line) => line.includes("[param] tier") && line.includes("ha"))).toBe(true);
|
|
106
|
+
expect(logged.some((line) => line.includes("[param] target") && line.includes("k3d") && line.includes("default"))).toBe(true);
|
|
107
|
+
expect(logged.some((line) => line.includes("more at their defaults"))).toBe(false);
|
|
108
|
+
} finally {
|
|
109
|
+
if (prev === undefined) delete process.env.CHANT_PARAM_ECHO;
|
|
110
|
+
else process.env.CHANT_PARAM_ECHO = prev;
|
|
111
|
+
errorSpy.mockRestore();
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
71
115
|
test("no declared params → success with empty provenance and no logging", () => {
|
|
72
116
|
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
73
117
|
try {
|
|
@@ -62,10 +62,13 @@ export interface CliBuildParamsResolution {
|
|
|
62
62
|
* chant #1064's acceptance criterion that this never surfaces as a thrown
|
|
63
63
|
* error from inside user source.
|
|
64
64
|
*
|
|
65
|
-
* On success, every
|
|
66
|
-
* `[param] <name> = <value> (<source>)
|
|
67
|
-
*
|
|
68
|
-
*
|
|
65
|
+
* On success, every explicitly-set parameter is logged via `console.error` as
|
|
66
|
+
* `[param] <name> = <value> (<source>)`, and the ones still at their declared
|
|
67
|
+
* defaults collapse to a single count line (`CHANT_PARAM_ECHO=all` lists them
|
|
68
|
+
* individually). Overrides stay unconditionally visible — not gated on
|
|
69
|
+
* `--verbose` — so a build's environment-varying inputs are always in the log,
|
|
70
|
+
* the same way #1022's fold decisions are; the defaults are the part that
|
|
71
|
+
* never varies, which is exactly why they can be a count.
|
|
69
72
|
*/
|
|
70
73
|
export function resolveCliBuildParams(
|
|
71
74
|
buildParamsConfig: BuildParamsConfig | undefined,
|
|
@@ -99,9 +102,23 @@ export function resolveCliBuildParams(
|
|
|
99
102
|
return { success: false, provenance: [], errors };
|
|
100
103
|
}
|
|
101
104
|
|
|
102
|
-
|
|
105
|
+
// Echo what the operator SET, one line each; collapse what they did not.
|
|
106
|
+
// A project with two dozen declared parameters printed two dozen grey lines
|
|
107
|
+
// on every build, and the ones that matter — the overrides — drowned in the
|
|
108
|
+
// ones that never vary. Defaults still get named on request
|
|
109
|
+
// (CHANT_PARAM_ECHO=all), and a build with no overrides still says how many
|
|
110
|
+
// parameters resolved, so silence never means "no parameters reached us"
|
|
111
|
+
// (the failure mode warnIfParamsCannotReachProject exists for).
|
|
112
|
+
const explicit = resolution.provenance.filter((p) => p.source !== "default");
|
|
113
|
+
const defaulted = resolution.provenance.length - explicit.length;
|
|
114
|
+
const echoAll = process.env.CHANT_PARAM_ECHO === "all";
|
|
115
|
+
for (const p of echoAll ? resolution.provenance : explicit) {
|
|
103
116
|
console.error(formatInfo(`[param] ${p.name} = ${JSON.stringify(p.value)} (${p.source})`));
|
|
104
117
|
}
|
|
118
|
+
if (!echoAll && defaulted > 0) {
|
|
119
|
+
const more = explicit.length > 0 ? `${defaulted} more` : `${defaulted} parameters`;
|
|
120
|
+
console.error(formatInfo(`[param] ${more} at their defaults (CHANT_PARAM_ECHO=all lists them)`));
|
|
121
|
+
}
|
|
105
122
|
|
|
106
123
|
return { success: true, provenance: resolution.provenance, errors: [] };
|
|
107
124
|
}
|
|
@@ -732,3 +732,47 @@ describe("resolveBuildFormat", () => {
|
|
|
732
732
|
expect(resolveBuildFormat("yaml", "template.yaml")).toEqual({ format: "yaml" });
|
|
733
733
|
});
|
|
734
734
|
});
|
|
735
|
+
|
|
736
|
+
describe("buildCommand error grouping (fountain-ops#62)", () => {
|
|
737
|
+
let testDir: string;
|
|
738
|
+
|
|
739
|
+
beforeEach(async () => {
|
|
740
|
+
testDir = join(tmpdir(), `chant-cli-dedup-${Date.now()}-${Math.random()}`);
|
|
741
|
+
await mkdir(testDir, { recursive: true });
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
afterEach(async () => {
|
|
745
|
+
await rm(testDir, { recursive: true, force: true });
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
test("the same message from three files surfaces once, with a count of the others", async () => {
|
|
749
|
+
// The fountain-ops shape: a guard in a shared params module throws during
|
|
750
|
+
// every resource file's evaluation, so the identical sentence arrives
|
|
751
|
+
// once per file — fourteen times, in the report that named this issue.
|
|
752
|
+
for (const f of ["a", "b", "c"]) {
|
|
753
|
+
await writeFile(
|
|
754
|
+
join(testDir, `${f}.infra.ts`),
|
|
755
|
+
`throw new Error('tier "ha" cannot run on this seam — fix the seam');\n`,
|
|
756
|
+
);
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
const result = await buildCommand({ path: testDir, format: "json", serializers: [] });
|
|
760
|
+
|
|
761
|
+
expect(result.success).toBe(false);
|
|
762
|
+
const matching = result.errors.filter((e) => e.includes("cannot run on this seam"));
|
|
763
|
+
expect(matching).toHaveLength(1);
|
|
764
|
+
expect(matching[0]).toContain("and the same from 2 more files");
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
test("distinct messages keep their own lines", async () => {
|
|
768
|
+
await writeFile(join(testDir, "a.infra.ts"), `throw new Error("first failure");\n`);
|
|
769
|
+
await writeFile(join(testDir, "b.infra.ts"), `throw new Error("second failure");\n`);
|
|
770
|
+
|
|
771
|
+
const result = await buildCommand({ path: testDir, format: "json", serializers: [] });
|
|
772
|
+
|
|
773
|
+
expect(result.success).toBe(false);
|
|
774
|
+
expect(result.errors.some((e) => e.includes("first failure"))).toBe(true);
|
|
775
|
+
expect(result.errors.some((e) => e.includes("second failure"))).toBe(true);
|
|
776
|
+
expect(result.errors.some((e) => e.includes("more files"))).toBe(false);
|
|
777
|
+
});
|
|
778
|
+
});
|
|
@@ -275,13 +275,26 @@ export async function buildCommand(options: BuildOptions): Promise<BuildResult>
|
|
|
275
275
|
}
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
-
// Format errors
|
|
278
|
+
// Format errors — grouped by message, because one refusal can surface once
|
|
279
|
+
// per resource file. A project whose params module throws (a guard like
|
|
280
|
+
// "this seam cannot back that tier") throws in EVERY file that imports it,
|
|
281
|
+
// and fourteen copies of one sentence bury the sentence
|
|
282
|
+
// (INTENTIUS/fountain-ops#62). The first file to hit it is named, the rest
|
|
283
|
+
// become a count; errors with genuinely different messages keep their own
|
|
284
|
+
// lines, and a message that appears once is printed exactly as before.
|
|
285
|
+
const byMessage = new Map<string, { first: (typeof result.errors)[number]; files: number }>();
|
|
279
286
|
for (const error of result.errors) {
|
|
287
|
+
const key = `${error.name ?? ""}:${error.message}`;
|
|
288
|
+
const seen = byMessage.get(key);
|
|
289
|
+
if (seen) seen.files += 1;
|
|
290
|
+
else byMessage.set(key, { first: error, files: 1 });
|
|
291
|
+
}
|
|
292
|
+
for (const { first: error, files } of byMessage.values()) {
|
|
280
293
|
const formatted = formatError({
|
|
281
294
|
file: "file" in error ? (error as unknown as Record<string, unknown>).file as string | undefined : undefined,
|
|
282
295
|
line: "line" in error ? (error as unknown as Record<string, unknown>).line as number | undefined : undefined,
|
|
283
296
|
column: "column" in error ? (error as unknown as Record<string, unknown>).column as number | undefined : undefined,
|
|
284
|
-
message: error.message,
|
|
297
|
+
message: files > 1 ? `${error.message} (and the same from ${files - 1} more files)` : error.message,
|
|
285
298
|
name: error.name,
|
|
286
299
|
});
|
|
287
300
|
errors.push(formatted);
|
|
@@ -241,20 +241,68 @@ export async function importCommand(options: ImportOptions): Promise<ImportResul
|
|
|
241
241
|
|
|
242
242
|
const lexicon = plugin.name;
|
|
243
243
|
|
|
244
|
+
return parseAndWrite(plugin, content, outputDir, options.force, warnings, generatedFiles, lexicon);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Import from an in-memory template string through a KNOWN plugin — no
|
|
249
|
+
* detection, no JSON assumption (#1548). This is the seam
|
|
250
|
+
* `chant import --kustomize <dir>` drives with `kustomize build` output
|
|
251
|
+
* through the k8s plugin's YAML parser; `importCommand` above is the same
|
|
252
|
+
* pipeline behind file reading + JSON detection.
|
|
253
|
+
*/
|
|
254
|
+
export interface ContentImportOptions {
|
|
255
|
+
/** The raw template content (YAML or JSON — the plugin's parser decides). */
|
|
256
|
+
content: string;
|
|
257
|
+
/** The lexicon whose parser/generator handle it, e.g. "k8s". */
|
|
258
|
+
lexicon: string;
|
|
259
|
+
output?: string;
|
|
260
|
+
force?: boolean;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export async function importFromContent(options: ContentImportOptions): Promise<ImportResult> {
|
|
264
|
+
const outputDir = resolve(options.output ?? "./infra/");
|
|
265
|
+
let plugins: LexiconPlugin[];
|
|
266
|
+
try {
|
|
267
|
+
plugins = await loadPlugins([options.lexicon]);
|
|
268
|
+
} catch (err) {
|
|
269
|
+
return {
|
|
270
|
+
success: false,
|
|
271
|
+
generatedFiles: [],
|
|
272
|
+
warnings: [],
|
|
273
|
+
error: `Could not load lexicon "${options.lexicon}": ${err}`,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
const plugin = plugins[0];
|
|
277
|
+
if (!plugin) {
|
|
278
|
+
return { success: false, generatedFiles: [], warnings: [], error: `Lexicon "${options.lexicon}" not available.` };
|
|
279
|
+
}
|
|
244
280
|
if (!plugin.templateParser || !plugin.templateGenerator) {
|
|
245
281
|
return {
|
|
246
282
|
success: false,
|
|
247
283
|
generatedFiles: [],
|
|
248
284
|
warnings: [],
|
|
249
285
|
error: `Lexicon "${plugin.name}" does not support template import.`,
|
|
250
|
-
lexicon,
|
|
286
|
+
lexicon: plugin.name,
|
|
251
287
|
};
|
|
252
288
|
}
|
|
289
|
+
return parseAndWrite(plugin, options.content, outputDir, options.force, [], [], plugin.name);
|
|
290
|
+
}
|
|
253
291
|
|
|
292
|
+
/** The shared tail of every template import: parse → generate → write. */
|
|
293
|
+
function parseAndWrite(
|
|
294
|
+
plugin: LexiconPlugin,
|
|
295
|
+
content: string,
|
|
296
|
+
outputDir: string,
|
|
297
|
+
force: boolean | undefined,
|
|
298
|
+
warnings: string[],
|
|
299
|
+
generatedFiles: string[],
|
|
300
|
+
lexicon: string,
|
|
301
|
+
): ImportResult {
|
|
254
302
|
// Parse template
|
|
255
303
|
let ir: TemplateIR;
|
|
256
304
|
try {
|
|
257
|
-
const parser = plugin.templateParser();
|
|
305
|
+
const parser = plugin.templateParser!();
|
|
258
306
|
ir = parser.parse(content);
|
|
259
307
|
} catch (err) {
|
|
260
308
|
return {
|
|
@@ -265,10 +313,10 @@ export async function importCommand(options: ImportOptions): Promise<ImportResul
|
|
|
265
313
|
};
|
|
266
314
|
}
|
|
267
315
|
|
|
268
|
-
const generator = plugin.templateGenerator();
|
|
316
|
+
const generator = plugin.templateGenerator!();
|
|
269
317
|
|
|
270
318
|
// Check output directory
|
|
271
|
-
if (existsSync(outputDir) && !
|
|
319
|
+
if (existsSync(outputDir) && !force) {
|
|
272
320
|
const files = readdirSync(outputDir);
|
|
273
321
|
if (files.length > 0) {
|
|
274
322
|
warnings.push(`Output directory ${outputDir} is not empty. Use --force to overwrite.`);
|
|
@@ -293,7 +341,7 @@ export async function importCommand(options: ImportOptions): Promise<ImportResul
|
|
|
293
341
|
}
|
|
294
342
|
|
|
295
343
|
// Check for existing file
|
|
296
|
-
if (existsSync(filePath) && !
|
|
344
|
+
if (existsSync(filePath) && !force) {
|
|
297
345
|
warnings.push(`File ${file.path} already exists, skipping`);
|
|
298
346
|
continue;
|
|
299
347
|
}
|
|
@@ -88,7 +88,9 @@ describe("runBuild --components --generate (chant #1108 build-time parameters)",
|
|
|
88
88
|
undefined,
|
|
89
89
|
[{ name: "tier", value: "light", source: "default" }],
|
|
90
90
|
);
|
|
91
|
-
|
|
91
|
+
// A parameter at its default collapses into the count line now; the
|
|
92
|
+
// provenance forwarded above is where its value is asserted.
|
|
93
|
+
expect(stderr.join("\n")).toContain("at their defaults");
|
|
92
94
|
vi.restoreAllMocks();
|
|
93
95
|
});
|
|
94
96
|
|
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
|
@@ -188,6 +188,10 @@ export function parseArgs(args: string[]): ParsedArgs {
|
|
|
188
188
|
// Shared by `migrate --from <lexicon>` and `import --from <env>`; the
|
|
189
189
|
// two commands never run together, so one field carries both.
|
|
190
190
|
result.migrateFrom = args[++i];
|
|
191
|
+
} else if (arg === "--kustomize") {
|
|
192
|
+
// `chant import --kustomize <dir>` (#1548): render the overlay, import
|
|
193
|
+
// the output through the k8s template parser.
|
|
194
|
+
result.kustomize = args[++i];
|
|
191
195
|
} else if (arg === "--type") {
|
|
192
196
|
result.selectType = args[++i];
|
|
193
197
|
} else if (arg === "--name") {
|
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 */
|
|
@@ -48,7 +48,7 @@ describe("publish order", () => {
|
|
|
48
48
|
// same stranding by a different route.
|
|
49
49
|
const all = execFileSync(
|
|
50
50
|
"bash",
|
|
51
|
-
["-c", 'for d in packages/*/ lexicons/*/; do [ -f "$d/package.json" ] && echo "${d%/}"; done'],
|
|
51
|
+
["-c", 'for d in packages/*/ lexicons/*/ wardens/*/; do [ -f "$d/package.json" ] && echo "${d%/}"; done'],
|
|
52
52
|
{ cwd: REPO, encoding: "utf8" },
|
|
53
53
|
)
|
|
54
54
|
.split("\n")
|
|
@@ -117,10 +117,14 @@ function recipeBody(name: string): string {
|
|
|
117
117
|
* which is why this asserts the mechanism, not a shared literal.
|
|
118
118
|
*/
|
|
119
119
|
describe("release wiring: peer ranges stay in lockstep (#1255)", () => {
|
|
120
|
-
it("the whole-repo release rewrites
|
|
120
|
+
it("the whole-repo release rewrites every @intentius peer range", () => {
|
|
121
|
+
// Since #788 the rewrite is generic (`with_entries` over every
|
|
122
|
+
// `@intentius/*` peer) rather than two named keys: the wardens added
|
|
123
|
+
// `@intentius/warden-core` as a third in-repo peer, and a hand-listed
|
|
124
|
+
// rewrite goes stale the same way the hand-listed publish steps did.
|
|
121
125
|
const body = recipeBody("release");
|
|
122
|
-
expect(body).toMatch(
|
|
123
|
-
expect(body).toMatch(/
|
|
126
|
+
expect(body).toMatch(/\.peerDependencies\s*\|=\s*with_entries/);
|
|
127
|
+
expect(body).toMatch(/startswith\("@intentius\/"\)/);
|
|
124
128
|
});
|
|
125
129
|
|
|
126
130
|
it("the single-lexicon release rewrites them too", () => {
|
|
@@ -37,6 +37,9 @@ export interface DeployUnitRule {
|
|
|
37
37
|
export const DEPLOY_UNIT_RULES: readonly DeployUnitRule[] = [
|
|
38
38
|
{ kind: "cfn-deploy", field: "stack", lexicon: "aws" },
|
|
39
39
|
{ kind: "kubectl-apply", field: "stack", lexicon: "k8s" },
|
|
40
|
+
// kustomize renders, then applies through the same stack-labelled pipeline —
|
|
41
|
+
// so its unit is observed by the identical label sweep (#1548).
|
|
42
|
+
{ kind: "kustomize-apply", field: "stack", lexicon: "k8s" },
|
|
40
43
|
{ kind: "helm-upgrade", field: "release", lexicon: "helm" },
|
|
41
44
|
];
|
|
42
45
|
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { GOVERNANCE_VERBS, isGovernanceVerb, type GovernanceVerb } from "./governance.js";
|
|
3
|
+
import { renderChangeSet, runReconcile, type ChangeSet, type Cycle } from "./reconcile.js";
|
|
4
|
+
|
|
5
|
+
describe("governance verbs (#790)", () => {
|
|
6
|
+
it("the runtime list and the type stay in sync", () => {
|
|
7
|
+
// Compile-time: assigning the list's element type to GovernanceVerb and
|
|
8
|
+
// back fails if either side gains a member the other lacks.
|
|
9
|
+
const fromList: GovernanceVerb = GOVERNANCE_VERBS[0];
|
|
10
|
+
const toList: (typeof GOVERNANCE_VERBS)[number] = "audit-sink" satisfies GovernanceVerb;
|
|
11
|
+
expect(isGovernanceVerb(fromList)).toBe(true);
|
|
12
|
+
expect(isGovernanceVerb(toList)).toBe(true);
|
|
13
|
+
expect(isGovernanceVerb("teams")).toBe(false);
|
|
14
|
+
expect(isGovernanceVerb(undefined)).toBe(false);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("runReconcile stamps the cycle's verb onto entries that lack one", async () => {
|
|
18
|
+
const cycle: Cycle<null, Record<string, never>, null> = {
|
|
19
|
+
name: "teams",
|
|
20
|
+
verb: "membership",
|
|
21
|
+
fetchLive: async () => null,
|
|
22
|
+
buildDesired: (c) => c,
|
|
23
|
+
apply: async () => {},
|
|
24
|
+
};
|
|
25
|
+
const result = await runReconcile({
|
|
26
|
+
scopes: { acme: {} },
|
|
27
|
+
client: null,
|
|
28
|
+
cycles: [cycle],
|
|
29
|
+
mode: "apply",
|
|
30
|
+
diff: (org) => ({
|
|
31
|
+
org,
|
|
32
|
+
entries: [
|
|
33
|
+
{ kind: "create", resourceType: "team", key: "platform" },
|
|
34
|
+
// A diff that stamps its own verb wins over the cycle's.
|
|
35
|
+
{ kind: "delete", resourceType: "grant", key: "bot", verb: "identity-assignment" },
|
|
36
|
+
],
|
|
37
|
+
}),
|
|
38
|
+
});
|
|
39
|
+
expect(result.cycles[0].verb).toBe("membership");
|
|
40
|
+
expect(result.cycles[0].applied.map((e) => e.verb)).toEqual(["membership", "identity-assignment"]);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("renderChangeSet groups by verb without changing line format", () => {
|
|
44
|
+
const cs: ChangeSet = {
|
|
45
|
+
org: "acme",
|
|
46
|
+
entries: [
|
|
47
|
+
{ kind: "create", resourceType: "webhook", key: "audit", verb: "audit-sink" },
|
|
48
|
+
{ kind: "create", resourceType: "ou", key: "Security", verb: "org-unit" },
|
|
49
|
+
{ kind: "create", resourceType: "team", key: "platform", verb: "membership" },
|
|
50
|
+
],
|
|
51
|
+
};
|
|
52
|
+
const out = renderChangeSet(cs);
|
|
53
|
+
// Vocabulary order: org-unit before membership before audit-sink.
|
|
54
|
+
expect(out.indexOf("[ou] Security")).toBeLessThan(out.indexOf("[team] platform"));
|
|
55
|
+
expect(out.indexOf("[team] platform")).toBeLessThan(out.indexOf("[webhook] audit"));
|
|
56
|
+
// Line format is untouched — no verb appears in the rendering.
|
|
57
|
+
expect(out).not.toContain("org-unit");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("a verbless change set renders exactly as before", () => {
|
|
61
|
+
const cs: ChangeSet = {
|
|
62
|
+
org: "acme",
|
|
63
|
+
entries: [
|
|
64
|
+
{ kind: "create", resourceType: "b", key: "2" },
|
|
65
|
+
{ kind: "create", resourceType: "a", key: "1" },
|
|
66
|
+
],
|
|
67
|
+
};
|
|
68
|
+
expect(renderChangeSet(cs)).toBe(
|
|
69
|
+
"Plan for acme: 2 to create, 0 to update, 0 to delete\n\nCREATE:\n [b] 2\n [a] 1",
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Governance verb vocabulary (#790, epic #787).
|
|
3
|
+
*
|
|
4
|
+
* `resourceType` on a change-set entry is a free provider-specific string
|
|
5
|
+
* ("team", "branch-protection", "protected-tag", …) chosen per cycle. That is
|
|
6
|
+
* right for display but useless for reading two providers' plans as one
|
|
7
|
+
* grammar. The verb is the cross-provider category a cycle reconciles;
|
|
8
|
+
* provider-specific cycle names stay for display.
|
|
9
|
+
*
|
|
10
|
+
* SCM and cloud cycles stamp the same verbs, so a GitHub plan and an AWS plan
|
|
11
|
+
* group the same way:
|
|
12
|
+
*
|
|
13
|
+
* - `org-unit` — the containers and their settings: GitHub org/repo settings,
|
|
14
|
+
* GitLab group/project/instance settings, cloud OUs/folders/accounts.
|
|
15
|
+
* - `policy-guardrail` — rules constraining what may happen: branch
|
|
16
|
+
* protections, rulesets, push rules, MR approvals, security features,
|
|
17
|
+
* dependency hygiene, SCPs, Org Policies.
|
|
18
|
+
* - `membership` — who belongs, directly or via teams: org members, team
|
|
19
|
+
* membership, group members.
|
|
20
|
+
* - `identity-assignment` — non-person and delegated access: tokens, deploy
|
|
21
|
+
* keys, service credentials, SSO/IAM assignments. `membership` is people
|
|
22
|
+
* in containers; this is everything else that can act.
|
|
23
|
+
* - `audit-sink` — where events and evidence flow: webhooks, integrations,
|
|
24
|
+
* CloudTrail/audit-log sinks.
|
|
25
|
+
* - `secret-material` — secrets and CI variables the platform stores.
|
|
26
|
+
*
|
|
27
|
+
* A cycle has exactly one verb. When a cycle plausibly spans two (GitHub
|
|
28
|
+
* `environments` carries both reviewer gates and secrets), stamp the verb of
|
|
29
|
+
* the cycle's primary object — the thing whose create/delete the cycle owns —
|
|
30
|
+
* not of fields it also happens to write.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/** The cross-provider governance category a cycle reconciles. */
|
|
34
|
+
export type GovernanceVerb =
|
|
35
|
+
| "org-unit"
|
|
36
|
+
| "policy-guardrail"
|
|
37
|
+
| "membership"
|
|
38
|
+
| "identity-assignment"
|
|
39
|
+
| "audit-sink"
|
|
40
|
+
| "secret-material";
|
|
41
|
+
|
|
42
|
+
/** Every verb, for exhaustiveness checks ("does each cycle stamp one?"). */
|
|
43
|
+
export const GOVERNANCE_VERBS: readonly GovernanceVerb[] = [
|
|
44
|
+
"org-unit",
|
|
45
|
+
"policy-guardrail",
|
|
46
|
+
"membership",
|
|
47
|
+
"identity-assignment",
|
|
48
|
+
"audit-sink",
|
|
49
|
+
"secret-material",
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
export function isGovernanceVerb(v: unknown): v is GovernanceVerb {
|
|
53
|
+
return typeof v === "string" && (GOVERNANCE_VERBS as readonly string[]).includes(v);
|
|
54
|
+
}
|
package/src/reconcile.ts
CHANGED
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
* only part that drives I/O (through the provider's `Cycle` implementations).
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
+
import { GOVERNANCE_VERBS, type GovernanceVerb } from "./governance.js";
|
|
23
|
+
|
|
22
24
|
// ---------------------------------------------------------------------------
|
|
23
25
|
// Change-set model
|
|
24
26
|
// ---------------------------------------------------------------------------
|
|
@@ -38,6 +40,12 @@ export interface ChangeSetEntry {
|
|
|
38
40
|
kind: ChangeKind;
|
|
39
41
|
/** High-level resource category (e.g. "team", "member", "branch-protection"). */
|
|
40
42
|
resourceType: string;
|
|
43
|
+
/**
|
|
44
|
+
* Cross-provider governance category (#790). `resourceType` stays the
|
|
45
|
+
* provider-specific display string; the verb is the shared grammar SCM and
|
|
46
|
+
* cloud plans group by. Stamped by `runReconcile` from the cycle's `verb`.
|
|
47
|
+
*/
|
|
48
|
+
verb?: GovernanceVerb;
|
|
41
49
|
/**
|
|
42
50
|
* Unique key identifying this resource within its type.
|
|
43
51
|
* - For top-level resources: a single name (team slug, member login, …).
|
|
@@ -211,8 +219,17 @@ export function renderChangeSet(cs: ChangeSet): string {
|
|
|
211
219
|
|
|
212
220
|
const ORDER: ChangeKind[] = ["create", "update", "delete"];
|
|
213
221
|
for (const kind of ORDER) {
|
|
214
|
-
|
|
222
|
+
let group = byKind[kind];
|
|
215
223
|
if (group.length === 0) continue;
|
|
224
|
+
// Verb-aware grouping (#790): when entries carry governance verbs, order
|
|
225
|
+
// each section by verb (vocabulary order, unverbed entries last) so mixed
|
|
226
|
+
// plans read category-by-category. Stable, and line format is unchanged —
|
|
227
|
+
// a verbless change set renders exactly as before.
|
|
228
|
+
if (group.some((e) => e.verb)) {
|
|
229
|
+
const rank = (e: ChangeSetEntry): number =>
|
|
230
|
+
e.verb ? GOVERNANCE_VERBS.indexOf(e.verb) : GOVERNANCE_VERBS.length;
|
|
231
|
+
group = [...group].sort((a, b) => rank(a) - rank(b));
|
|
232
|
+
}
|
|
216
233
|
lines.push(`\n${kind.toUpperCase()}:`);
|
|
217
234
|
for (const e of group) {
|
|
218
235
|
lines.push(` [${e.resourceType}] ${e.key}`);
|
|
@@ -399,6 +416,12 @@ class MutableRateBudget implements RateBudget {
|
|
|
399
416
|
export interface Cycle<TClient, TConfig, TLive, TScope = unknown> {
|
|
400
417
|
/** Human-readable name, e.g. "branch-protection". */
|
|
401
418
|
name: string;
|
|
419
|
+
/**
|
|
420
|
+
* Cross-provider governance category this cycle reconciles (#790). Every
|
|
421
|
+
* SCM warden cycle stamps one; cloud cycles (epic #787 C2) must. Optional
|
|
422
|
+
* only so provider-external Cycle implementations don't break.
|
|
423
|
+
*/
|
|
424
|
+
verb?: GovernanceVerb;
|
|
402
425
|
fetchLive(client: TClient, scopeId: string, scope: TScope, budget: RateBudget): Promise<TLive>;
|
|
403
426
|
buildDesired(config: TConfig, scopeId: string, scope: TScope): TConfig;
|
|
404
427
|
apply(
|
|
@@ -413,6 +436,8 @@ export interface Cycle<TClient, TConfig, TLive, TScope = unknown> {
|
|
|
413
436
|
/** Per-cycle outcome recorded in the run result. */
|
|
414
437
|
export interface CycleResult {
|
|
415
438
|
name: string;
|
|
439
|
+
/** The cycle's governance verb (#790), when it stamps one. */
|
|
440
|
+
verb?: GovernanceVerb;
|
|
416
441
|
/** Scope id this result is for (e.g. an org login). */
|
|
417
442
|
org: string;
|
|
418
443
|
counts: { create: number; update: number; delete: number };
|
|
@@ -535,6 +560,11 @@ export async function runReconcile<TClient, TConfig, TLive, TScope = unknown>(
|
|
|
535
560
|
}
|
|
536
561
|
|
|
537
562
|
const changeSet = diffFn(scopeId, desired, live, diffOptions);
|
|
563
|
+
// Stamp the cycle's governance verb (#790) onto entries that don't
|
|
564
|
+
// carry one, so provider diffs stay verb-unaware.
|
|
565
|
+
if (cycle.verb) {
|
|
566
|
+
for (const e of changeSet.entries) e.verb ??= cycle.verb;
|
|
567
|
+
}
|
|
538
568
|
const guardrailResult = guardrails(changeSet, live);
|
|
539
569
|
|
|
540
570
|
const counts = { create: 0, update: 0, delete: 0 };
|
|
@@ -542,6 +572,7 @@ export async function runReconcile<TClient, TConfig, TLive, TScope = unknown>(
|
|
|
542
572
|
|
|
543
573
|
const cycleResult: CycleResult = {
|
|
544
574
|
name: cycle.name,
|
|
575
|
+
verb: cycle.verb,
|
|
545
576
|
org: scopeId,
|
|
546
577
|
counts,
|
|
547
578
|
guardrails: guardrailResult,
|