@intentius/chant-lexicon-helm 0.46.0 → 0.50.0
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/archive-render.d.ts +94 -0
- package/dist/archive-render.d.ts.map +1 -0
- package/dist/commands.d.ts +54 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/config.d.ts +135 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/deep-observe-hooks.d.ts +2 -0
- package/dist/deep-observe-hooks.d.ts.map +1 -0
- package/dist/deep-observe.d.ts +54 -0
- package/dist/deep-observe.d.ts.map +1 -0
- package/dist/describe-resources.d.ts +17 -0
- package/dist/describe-resources.d.ts.map +1 -0
- package/dist/index.d.ts +19 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/integrity.json +5 -3
- package/dist/lint/audit-catalog.d.ts.map +1 -1
- package/dist/lint/post-synth/index.d.ts.map +1 -1
- package/dist/lint/post-synth/whm503.d.ts +34 -0
- package/dist/lint/post-synth/whm503.d.ts.map +1 -0
- package/dist/lint/post-synth/whm504.d.ts +20 -0
- package/dist/lint/post-synth/whm504.d.ts.map +1 -0
- package/dist/manifest.json +25 -13
- package/dist/okf/index.md +2 -0
- package/dist/okf/rules/WHM503.md +15 -0
- package/dist/okf/rules/WHM504.md +11 -0
- package/dist/okf/types/Values.md +1 -0
- package/dist/op/activities/cluster-probe.d.ts +44 -0
- package/dist/op/activities/cluster-probe.d.ts.map +1 -0
- package/dist/op/activities/helm.d.ts +179 -5
- package/dist/op/activities/helm.d.ts.map +1 -1
- package/dist/op/activities/index.d.ts +4 -2
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/builders.d.ts +47 -0
- package/dist/op/builders.d.ts.map +1 -0
- package/dist/pinnability/actions.d.ts +76 -0
- package/dist/pinnability/actions.d.ts.map +1 -0
- package/dist/pinnability/classify.d.ts +129 -0
- package/dist/pinnability/classify.d.ts.map +1 -0
- package/dist/pinnability/conditions.d.ts +109 -0
- package/dist/pinnability/conditions.d.ts.map +1 -0
- package/dist/pinnability/index.d.ts +17 -0
- package/dist/pinnability/index.d.ts.map +1 -0
- package/dist/pinnability/localize.d.ts +124 -0
- package/dist/pinnability/localize.d.ts.map +1 -0
- package/dist/pinnability/render-stream.d.ts +27 -0
- package/dist/pinnability/render-stream.d.ts.map +1 -0
- package/dist/pinnability/values.d.ts +69 -0
- package/dist/pinnability/values.d.ts.map +1 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/release-observe.d.ts +109 -0
- package/dist/release-observe.d.ts.map +1 -0
- package/dist/render-diff.d.ts +192 -0
- package/dist/render-diff.d.ts.map +1 -0
- package/dist/render-digest.d.ts +135 -0
- package/dist/render-digest.d.ts.map +1 -0
- package/dist/render-store.d.ts +310 -0
- package/dist/render-store.d.ts.map +1 -0
- package/dist/render-wrapper.d.ts +140 -0
- package/dist/render-wrapper.d.ts.map +1 -0
- package/dist/render.d.ts +81 -0
- package/dist/render.d.ts.map +1 -1
- package/dist/rules/whm503.ts +101 -0
- package/dist/rules/whm504.ts +52 -0
- package/dist/skills/chant-helm.md +7 -0
- package/dist/values-probe.d.ts +177 -0
- package/dist/values-probe.d.ts.map +1 -0
- package/dist/wrapper-chart.d.ts +61 -0
- package/dist/wrapper-chart.d.ts.map +1 -0
- package/package.json +10 -4
- package/src/archive-render.test.ts +201 -0
- package/src/archive-render.ts +119 -0
- package/src/commands.test.ts +487 -0
- package/src/commands.ts +445 -0
- package/src/config.test.ts +130 -0
- package/src/config.ts +220 -0
- package/src/deep-observe-hooks.ts +20 -0
- package/src/deep-observe.test.ts +165 -0
- package/src/deep-observe.ts +121 -0
- package/src/describe-resources.test.ts +255 -0
- package/src/describe-resources.ts +198 -0
- package/src/index.ts +106 -2
- package/src/lifecycle-integration.test.ts +289 -0
- package/src/lint/audit-catalog.ts +2 -0
- package/src/lint/post-synth/index.ts +4 -0
- package/src/lint/post-synth/whm503.test.ts +134 -0
- package/src/lint/post-synth/whm503.ts +101 -0
- package/src/lint/post-synth/whm504.test.ts +105 -0
- package/src/lint/post-synth/whm504.ts +52 -0
- package/src/op/activities/cluster-probe.test.ts +66 -0
- package/src/op/activities/cluster-probe.ts +148 -0
- package/src/op/activities/helm.test.ts +824 -0
- package/src/op/activities/helm.ts +628 -5
- package/src/op/activities/index.ts +17 -2
- package/src/op/builders.test.ts +70 -0
- package/src/op/builders.ts +66 -0
- package/src/pinnability/actions.ts +263 -0
- package/src/pinnability/classify.ts +579 -0
- package/src/pinnability/conditions.ts +0 -0
- package/src/pinnability/index.ts +77 -0
- package/src/pinnability/localize.test.ts +234 -0
- package/src/pinnability/localize.ts +644 -0
- package/src/pinnability/pinnability.test.ts +560 -0
- package/src/pinnability/render-stream.ts +59 -0
- package/src/pinnability/values.ts +205 -0
- package/src/plugin.test.ts +3 -1
- package/src/plugin.ts +97 -12
- package/src/release-observe.ts +392 -0
- package/src/render-diff.test.ts +366 -0
- package/src/render-diff.ts +407 -0
- package/src/render-digest.test.ts +186 -0
- package/src/render-digest.ts +207 -0
- package/src/render-store.test.ts +412 -0
- package/src/render-store.ts +621 -0
- package/src/render-wrapper.test.ts +259 -0
- package/src/render-wrapper.ts +274 -0
- package/src/render.test.ts +383 -4
- package/src/render.ts +319 -8
- package/src/skills/chant-helm.md +7 -0
- package/src/values-probe.test.ts +400 -0
- package/src/values-probe.ts +627 -0
- package/src/wrapper-chart.test.ts +110 -0
- package/src/wrapper-chart.ts +124 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chart model and value coalescing for the pinnability classifier (#1234,
|
|
3
|
+
* epic #1228).
|
|
4
|
+
*
|
|
5
|
+
* Pinnability is a property of `(chart, values)`, not of the chart alone —
|
|
6
|
+
* so the classifier needs each subchart instance's EFFECTIVE values to ask
|
|
7
|
+
* whether a gate is open. This module walks `Chart.yaml` dependencies
|
|
8
|
+
* (aliases and `condition:` included — finding 12: dependency conditions are
|
|
9
|
+
* values-driven and therefore closed inputs) and approximates helm's
|
|
10
|
+
* coalescing: child defaults, overridden by the parent's `<alias-or-name>`
|
|
11
|
+
* subtree, with `global` propagated down.
|
|
12
|
+
*
|
|
13
|
+
* The approximation is deliberate and bounded. `import-values`, `tags`, and
|
|
14
|
+
* `tpl`-computed values are not modeled; a gate they would decide resolves
|
|
15
|
+
* as UNKNOWN and the classifier refuses rather than pins (see
|
|
16
|
+
* conditions.ts). The build-time values probe (#1251) replaces this with
|
|
17
|
+
* helm's own coalescing.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
21
|
+
import { join } from "node:path";
|
|
22
|
+
import yaml from "js-yaml";
|
|
23
|
+
|
|
24
|
+
export interface ChartDependency {
|
|
25
|
+
name: string;
|
|
26
|
+
alias?: string;
|
|
27
|
+
/** Comma-separated values paths, evaluated against the ROOT values. */
|
|
28
|
+
condition?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ChartMeta {
|
|
32
|
+
name: string;
|
|
33
|
+
dependencies: ChartDependency[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function readChartMeta(chartDir: string): ChartMeta {
|
|
37
|
+
const raw = readYamlFile(join(chartDir, "Chart.yaml")) as
|
|
38
|
+
| { name?: unknown; dependencies?: unknown }
|
|
39
|
+
| undefined;
|
|
40
|
+
const deps: ChartDependency[] = [];
|
|
41
|
+
if (Array.isArray(raw?.dependencies)) {
|
|
42
|
+
for (const d of raw.dependencies) {
|
|
43
|
+
if (d === null || typeof d !== "object") continue;
|
|
44
|
+
const dep = d as Record<string, unknown>;
|
|
45
|
+
if (typeof dep.name !== "string") continue;
|
|
46
|
+
deps.push({
|
|
47
|
+
name: dep.name,
|
|
48
|
+
alias: typeof dep.alias === "string" ? dep.alias : undefined,
|
|
49
|
+
condition: typeof dep.condition === "string" ? dep.condition : undefined,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
name: typeof raw?.name === "string" ? raw.name : "",
|
|
55
|
+
dependencies: deps,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function readYamlFile(path: string): unknown {
|
|
60
|
+
if (!existsSync(path)) return undefined;
|
|
61
|
+
return yaml.load(readFileSync(path, "utf8"));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function isPlainObject(v: unknown): v is Record<string, unknown> {
|
|
65
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Helm-style deep merge: `override` wins, maps merge recursively, a `null`
|
|
70
|
+
* override deletes the key, arrays replace wholesale.
|
|
71
|
+
*/
|
|
72
|
+
export function mergeValues(base: unknown, override: unknown): unknown {
|
|
73
|
+
if (override === undefined) return base;
|
|
74
|
+
if (!isPlainObject(base) || !isPlainObject(override)) return override;
|
|
75
|
+
const out: Record<string, unknown> = { ...base };
|
|
76
|
+
for (const [k, v] of Object.entries(override)) {
|
|
77
|
+
if (v === null) {
|
|
78
|
+
delete out[k];
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
out[k] = k in out ? mergeValues(out[k], v) : v;
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Resolve a dot path against a values tree; undefined when absent. */
|
|
87
|
+
export function valueAtPath(values: unknown, path: string[]): unknown {
|
|
88
|
+
let v = values;
|
|
89
|
+
for (const seg of path) {
|
|
90
|
+
if (!isPlainObject(v)) return undefined;
|
|
91
|
+
v = v[seg];
|
|
92
|
+
}
|
|
93
|
+
return v;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* One chart in the tree, with the values IT sees. An aliased dependency
|
|
98
|
+
* yields two instances over the same directory — each with its own value
|
|
99
|
+
* scope, which is why findings are evaluated per instance but reported per
|
|
100
|
+
* file.
|
|
101
|
+
*/
|
|
102
|
+
export interface ChartInstance {
|
|
103
|
+
/** Directory of the (sub)chart. */
|
|
104
|
+
dir: string;
|
|
105
|
+
/** Directory relative to the root chart ("" for the root). */
|
|
106
|
+
relDir: string;
|
|
107
|
+
/** Values scope from the root, e.g. ["grafana"] or ["kidtwo"]; [] for root. */
|
|
108
|
+
scope: readonly string[];
|
|
109
|
+
/** Effective values as this instance sees them. */
|
|
110
|
+
values: unknown;
|
|
111
|
+
/**
|
|
112
|
+
* When a `condition:` on the dependency chain disables this instance under
|
|
113
|
+
* the supplied values: the first condition path that resolved false, in
|
|
114
|
+
* root coordinates. Constructs inside a disabled instance are gated by it.
|
|
115
|
+
*/
|
|
116
|
+
disabledBy?: string;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Build the instance tree for a chart directory and the values the render
|
|
121
|
+
* was given. `suppliedValues` merges over the root chart's `values.yaml`
|
|
122
|
+
* exactly like `helm template --values`.
|
|
123
|
+
*/
|
|
124
|
+
export function buildChartInstances(
|
|
125
|
+
chartDir: string,
|
|
126
|
+
suppliedValues?: unknown,
|
|
127
|
+
): { instances: ChartInstance[]; warnings: string[] } {
|
|
128
|
+
const warnings: string[] = [];
|
|
129
|
+
const rootDefaults = readYamlFile(join(chartDir, "values.yaml")) ?? {};
|
|
130
|
+
const rootValues = mergeValues(rootDefaults, suppliedValues);
|
|
131
|
+
const instances: ChartInstance[] = [];
|
|
132
|
+
|
|
133
|
+
const visit = (
|
|
134
|
+
dir: string,
|
|
135
|
+
relDir: string,
|
|
136
|
+
scope: readonly string[],
|
|
137
|
+
values: unknown,
|
|
138
|
+
disabledBy: string | undefined,
|
|
139
|
+
): void => {
|
|
140
|
+
instances.push({ dir, relDir, scope, values, disabledBy });
|
|
141
|
+
const meta = readChartMeta(dir);
|
|
142
|
+
const chartsDir = join(dir, "charts");
|
|
143
|
+
const childDirs = existsSync(chartsDir)
|
|
144
|
+
? readdirSync(chartsDir).filter((e) => {
|
|
145
|
+
const p = join(chartsDir, e);
|
|
146
|
+
return statSync(p).isDirectory() && existsSync(join(p, "Chart.yaml"));
|
|
147
|
+
})
|
|
148
|
+
: [];
|
|
149
|
+
const declared = new Set<string>();
|
|
150
|
+
|
|
151
|
+
const visitChild = (dep: ChartDependency): void => {
|
|
152
|
+
const key = dep.alias ?? dep.name;
|
|
153
|
+
const childDir = join(chartsDir, dep.name);
|
|
154
|
+
if (!existsSync(join(childDir, "Chart.yaml"))) {
|
|
155
|
+
warnings.push(
|
|
156
|
+
`dependency ${dep.name} of ${relDir === "" ? "the chart" : relDir} is not vendored under charts/ — not scanned`,
|
|
157
|
+
);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
declared.add(dep.name);
|
|
161
|
+
// Condition paths are resolved against the TOP parent's values
|
|
162
|
+
// (helm's documented semantics); first present path wins, absent
|
|
163
|
+
// conditions leave the dependency enabled.
|
|
164
|
+
let childDisabledBy = disabledBy;
|
|
165
|
+
if (childDisabledBy === undefined && dep.condition !== undefined) {
|
|
166
|
+
for (const rawPath of dep.condition.split(",")) {
|
|
167
|
+
const path = rawPath.trim();
|
|
168
|
+
if (path === "") continue;
|
|
169
|
+
const v = valueAtPath(rootValues, path.split("."));
|
|
170
|
+
if (v === undefined) continue;
|
|
171
|
+
if (v === false) childDisabledBy = path;
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
const childDefaults = readYamlFile(join(childDir, "values.yaml")) ?? {};
|
|
176
|
+
const parentSlice = isPlainObject(values) ? values[key] : undefined;
|
|
177
|
+
let childValues = mergeValues(childDefaults, parentSlice);
|
|
178
|
+
// Globals propagate into every subchart tree.
|
|
179
|
+
const parentGlobal = isPlainObject(values) ? values.global : undefined;
|
|
180
|
+
if (parentGlobal !== undefined && isPlainObject(childValues)) {
|
|
181
|
+
childValues = {
|
|
182
|
+
...childValues,
|
|
183
|
+
global: mergeValues(childValues.global, parentGlobal),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
visit(
|
|
187
|
+
childDir,
|
|
188
|
+
relDir === "" ? `charts/${dep.name}` : `${relDir}/charts/${dep.name}`,
|
|
189
|
+
[...scope, key],
|
|
190
|
+
childValues,
|
|
191
|
+
childDisabledBy,
|
|
192
|
+
);
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
for (const dep of meta.dependencies) visitChild(dep);
|
|
196
|
+
// charts/ directories not declared in Chart.yaml are implicit,
|
|
197
|
+
// always-enabled dependencies.
|
|
198
|
+
for (const name of childDirs) {
|
|
199
|
+
if (!declared.has(name)) visitChild({ name });
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
visit(chartDir, "", [], rootValues, undefined);
|
|
204
|
+
return { instances, warnings };
|
|
205
|
+
}
|
package/src/plugin.test.ts
CHANGED
|
@@ -38,12 +38,14 @@ describe("helmPlugin", () => {
|
|
|
38
38
|
|
|
39
39
|
test("provides post-synth checks", () => {
|
|
40
40
|
const checks = helmPlugin.postSynthChecks!();
|
|
41
|
-
expect(checks.length).toBe(
|
|
41
|
+
expect(checks.length).toBe(23);
|
|
42
42
|
const ids = checks.map((c) => c.id);
|
|
43
43
|
expect(ids).toContain("WHM005");
|
|
44
44
|
expect(ids).toContain("WHM101");
|
|
45
45
|
expect(ids).toContain("WHM105");
|
|
46
46
|
expect(ids).toContain("WHM301");
|
|
47
|
+
expect(ids).toContain("WHM503");
|
|
48
|
+
expect(ids).toContain("WHM504");
|
|
47
49
|
});
|
|
48
50
|
|
|
49
51
|
test("detectTemplate identifies Chart.yaml data", () => {
|
package/src/plugin.ts
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { LexiconPlugin, IntrinsicDef, InitTemplateSet } from "@intentius/chant/lexicon";
|
|
9
|
+
import type { CommandGroup } from "@intentius/chant/cli/command-group";
|
|
10
|
+
import { helmCommandGroup } from "./commands";
|
|
9
11
|
import { detectTemplate } from "./detect";
|
|
10
12
|
import { discoverLintRules } from "@intentius/chant/lint/discover";
|
|
11
13
|
import { postSynthChecks as postSynthCheckList } from "./lint/post-synth";
|
|
@@ -16,12 +18,35 @@ import { fileURLToPath } from "url";
|
|
|
16
18
|
import { helmSerializer } from "./serializer";
|
|
17
19
|
import { helmCompletions } from "./lsp/completions";
|
|
18
20
|
import { helmHover } from "./lsp/hover";
|
|
21
|
+
import { helmConfigSchema } from "./config";
|
|
22
|
+
import { helmDeepNormalizationHooks } from "./deep-observe-hooks";
|
|
23
|
+
import { LABEL_OWNERSHIP_KEYS } from "@intentius/chant/ownership";
|
|
19
24
|
|
|
20
25
|
export const helmPlugin: LexiconPlugin = {
|
|
21
26
|
name: "helm",
|
|
27
|
+
// The `helm` namespace in chant.config.ts — capability profiles (#1235).
|
|
28
|
+
// Declaring the schema makes core validate the namespace at load, so a
|
|
29
|
+
// typo'd profile field fails the build instead of silently unpinning.
|
|
30
|
+
configSchema: helmConfigSchema,
|
|
31
|
+
// #1246 — helm resolves real ownership verdicts on the thin read (every row
|
|
32
|
+
// is release-scoped, so helm-managed = owned via release identity), and the
|
|
33
|
+
// deep read (#1247) delegates to the k8s reader, which resolves the shared
|
|
34
|
+
// label channel (`app.kubernetes.io/managed-by` + `chant.intentius.io/*`)
|
|
35
|
+
// on the live objects.
|
|
36
|
+
ownershipChannel: { keys: LABEL_OWNERSHIP_KEYS, reads: ["describeResources", "observeResourcesDeep"] },
|
|
22
37
|
auditCatalog: () => helmAuditCatalog,
|
|
23
38
|
serializer: helmSerializer,
|
|
24
39
|
|
|
40
|
+
// #1248 (epic #1228 Phase 6, via the #1078 seam) — `chant helm` mounts the
|
|
41
|
+
// pinned-render surface: the pinnability gate (#1234), the double-render
|
|
42
|
+
// localizer (#1236), and the recorded-render listing with digests (#1237).
|
|
43
|
+
// See ./commands.ts for the verb list; this plugin only mounts it. The
|
|
44
|
+
// diff verbs (#1249 render-to-render, #1250 render-to-live, both mounted on
|
|
45
|
+
// "diff") land there.
|
|
46
|
+
commands(): CommandGroup {
|
|
47
|
+
return helmCommandGroup();
|
|
48
|
+
},
|
|
49
|
+
|
|
25
50
|
lintRules() {
|
|
26
51
|
const rulesDir = join(dirname(fileURLToPath(import.meta.url)), "lint", "rules");
|
|
27
52
|
return discoverLintRules(rulesDir, import.meta.url);
|
|
@@ -37,25 +62,63 @@ export const helmPlugin: LexiconPlugin = {
|
|
|
37
62
|
completionProvider: helmCompletions,
|
|
38
63
|
hoverProvider: helmHover,
|
|
39
64
|
|
|
65
|
+
// chant #1174 — `foldsAsCall: true` opts an intrinsic's PLAIN-CALL form
|
|
66
|
+
// into `chant build --fold` (chant #1044's mechanism, previously unused by
|
|
67
|
+
// this lexicon: every helm intrinsic is a plain call — helm has no tagged
|
|
68
|
+
// templates at all — so before this PR NONE of them folded, and a single
|
|
69
|
+
// `include(...)`/`printf(...)` anywhere in a chart's resources forced the
|
|
70
|
+
// whole file back to the run path).
|
|
71
|
+
//
|
|
72
|
+
// Audited one at a time against `IntrinsicDef.foldsAsCall`'s criterion
|
|
73
|
+
// (../../../packages/core/src/lexicon.ts): "calling the intrinsic is a
|
|
74
|
+
// pure function of its arguments that builds a deterministic data
|
|
75
|
+
// envelope". Every function below (`include`/`required`/`helmDefault`/
|
|
76
|
+
// `toYaml`/`quote`/`printf`/`tpl`/`lookup`/`filesGet`/`filesGlob`/
|
|
77
|
+
// `filesAsConfig`/`filesAsSecrets`, ./intrinsics.ts) does exactly that: it
|
|
78
|
+
// builds a `HelmTpl` (or, for `lookup`, a string) by interpolating its
|
|
79
|
+
// arguments into a Go template expression string — no I/O, no mutation, no
|
|
80
|
+
// dependency on anything but the arguments. `lookup`'s own side effect
|
|
81
|
+
// (a live cluster read) happens when HELM renders the resulting template
|
|
82
|
+
// at deploy time, not when this JS function runs at `chant build` time, so
|
|
83
|
+
// it is just as pure here as the rest.
|
|
84
|
+
//
|
|
85
|
+
// Left OUT, each for a specific reason rather than an oversight:
|
|
86
|
+
// - `values`/`Release`/`ChartRef`/`Capabilities`/`Template` are proxy
|
|
87
|
+
// OBJECTS, never called (`values(...)` isn't valid source) — nothing
|
|
88
|
+
// to opt in.
|
|
89
|
+
// - `withOrder`/`argoWave` return a PLAIN record meant to be spread
|
|
90
|
+
// (`{...withOrder(1)}`), not a `HelmTpl`. `fold()`'s object-spread
|
|
91
|
+
// handling (`Object.assign(obj, src)`, ../fold/fold.ts) runs
|
|
92
|
+
// SYNCHRONOUSLY on the immediately-folded value, before the async
|
|
93
|
+
// revival that would turn a folded call into its real result — opting
|
|
94
|
+
// these in would spread the folded `{__intrinsic, args}` envelope's
|
|
95
|
+
// own keys into the object instead of the real record. A real
|
|
96
|
+
// soundness hazard, not a "not yet audited."
|
|
97
|
+
// - `If`/`ElseIf`/`Range`/`With` take an arbitrary `body`/`elseBody`
|
|
98
|
+
// that is often an arrow function (`Range(list, (item) => ({...}))`)
|
|
99
|
+
// or a nested Declarable — `fold()` already rejects a function used as
|
|
100
|
+
// a value on its own terms, so opting these in buys nothing measured
|
|
101
|
+
// on this corpus and is left for a future PR to size against real
|
|
102
|
+
// usage rather than widened on speculation.
|
|
40
103
|
intrinsics(): IntrinsicDef[] {
|
|
41
104
|
return [
|
|
42
105
|
{ name: "values", description: "Proxy accessor for {{ .Values.x }} references", isTag: false },
|
|
43
106
|
{ name: "Release", description: "Built-in Release object (Name, Namespace, etc.)", isTag: false },
|
|
44
107
|
{ name: "ChartRef", description: "Built-in Chart object (Name, Version, AppVersion)", isTag: false },
|
|
45
|
-
{ name: "include", description: 'Include a named template: {{ include "name" . }}', isTag: false },
|
|
46
|
-
{ name: "required", description: 'Require a value: {{ required "msg" .Values.x }}', isTag: false },
|
|
47
|
-
{ name: "helmDefault", description: 'Default value: {{ default "def" .Values.x }}', isTag: false },
|
|
48
|
-
{ name: "toYaml", description: "Convert to YAML: {{ toYaml .Values.x | nindent N }}", isTag: false },
|
|
49
|
-
{ name: "quote", description: "Quote a value: {{ .Values.x | quote }}", isTag: false },
|
|
50
|
-
{ name: "printf", description: 'Format string: {{ printf "%s" .Values.x }}', isTag: false },
|
|
51
|
-
{ name: "tpl", description: "Evaluate template: {{ tpl .Values.x . }}", isTag: false },
|
|
52
|
-
{ name: "lookup", description: 'Lookup resource: {{ lookup "v1" "Secret" "ns" "name" }}', isTag: false },
|
|
108
|
+
{ name: "include", description: 'Include a named template: {{ include "name" . }}', isTag: false, foldsAsCall: true },
|
|
109
|
+
{ name: "required", description: 'Require a value: {{ required "msg" .Values.x }}', isTag: false, foldsAsCall: true },
|
|
110
|
+
{ name: "helmDefault", description: 'Default value: {{ default "def" .Values.x }}', isTag: false, foldsAsCall: true },
|
|
111
|
+
{ name: "toYaml", description: "Convert to YAML: {{ toYaml .Values.x | nindent N }}", isTag: false, foldsAsCall: true },
|
|
112
|
+
{ name: "quote", description: "Quote a value: {{ .Values.x | quote }}", isTag: false, foldsAsCall: true },
|
|
113
|
+
{ name: "printf", description: 'Format string: {{ printf "%s" .Values.x }}', isTag: false, foldsAsCall: true },
|
|
114
|
+
{ name: "tpl", description: "Evaluate template: {{ tpl .Values.x . }}", isTag: false, foldsAsCall: true },
|
|
115
|
+
{ name: "lookup", description: 'Lookup resource: {{ lookup "v1" "Secret" "ns" "name" }}', isTag: false, foldsAsCall: true },
|
|
53
116
|
{ name: "Capabilities", description: "Built-in Capabilities object (KubeVersion, APIVersions, HelmVersion)", isTag: false },
|
|
54
117
|
{ name: "Template", description: "Built-in Template object (Name, BasePath)", isTag: false },
|
|
55
|
-
{ name: "filesGet", description: '.Files.Get: {{ .Files.Get "path" }}', isTag: false },
|
|
56
|
-
{ name: "filesGlob", description: '.Files.Glob: {{ .Files.Glob "pattern" }}', isTag: false },
|
|
57
|
-
{ name: "filesAsConfig", description: ".Files.Glob.AsConfig for ConfigMap data", isTag: false },
|
|
58
|
-
{ name: "filesAsSecrets", description: ".Files.Glob.AsSecrets for Secret data", isTag: false },
|
|
118
|
+
{ name: "filesGet", description: '.Files.Get: {{ .Files.Get "path" }}', isTag: false, foldsAsCall: true },
|
|
119
|
+
{ name: "filesGlob", description: '.Files.Glob: {{ .Files.Glob "pattern" }}', isTag: false, foldsAsCall: true },
|
|
120
|
+
{ name: "filesAsConfig", description: ".Files.Glob.AsConfig for ConfigMap data", isTag: false, foldsAsCall: true },
|
|
121
|
+
{ name: "filesAsSecrets", description: ".Files.Glob.AsSecrets for Secret data", isTag: false, foldsAsCall: true },
|
|
59
122
|
{ name: "ElseIf", description: "Else-if chaining: {{- else if .Values.x }}...{{- end }}", isTag: false },
|
|
60
123
|
{ name: "If", description: "Conditional: {{- if .Values.x }}...{{- end }}", isTag: false },
|
|
61
124
|
{ name: "Range", description: "Range loop: {{- range .Values.x }}...{{- end }}", isTag: false },
|
|
@@ -383,6 +446,28 @@ export const service = new Service({
|
|
|
383
446
|
return listArtifacts(options);
|
|
384
447
|
},
|
|
385
448
|
|
|
449
|
+
// #1246 — per-resource observation of what each declared chart's release
|
|
450
|
+
// holds (`helm get manifest` + `helm get hooks`, both channels), with total
|
|
451
|
+
// tri-state verdicts. This is what gives `lifecycle diff --live` helm rows.
|
|
452
|
+
async describeResources(options) {
|
|
453
|
+
const { describeResources } = await import("./describe-resources");
|
|
454
|
+
return describeResources(options);
|
|
455
|
+
},
|
|
456
|
+
|
|
457
|
+
// #1247 — deep property-level trees for the release's resources, read live
|
|
458
|
+
// through the k8s lexicon's typed client and normalized by its hooks.
|
|
459
|
+
// Dynamic import for the same reason k8s's is: the reader reaches
|
|
460
|
+
// `@intentius/chant-k8s-client`, which must stay off the build path.
|
|
461
|
+
async observeResourcesDeep(options) {
|
|
462
|
+
const { observeResourcesDeepHelm } = await import("./deep-observe");
|
|
463
|
+
return observeResourcesDeepHelm(options);
|
|
464
|
+
},
|
|
465
|
+
|
|
466
|
+
// The k8s lexicon's hooks by reference, not a copy (#1247) — the objects a
|
|
467
|
+
// release deploys are Kubernetes objects, and core normalizes the declared
|
|
468
|
+
// tree with the same rules the reader used.
|
|
469
|
+
deepNormalizationHooks: helmDeepNormalizationHooks,
|
|
470
|
+
|
|
386
471
|
// #1495 piece 4 — the deploy-unit observer for helm-upgrade units: a
|
|
387
472
|
// release's presence and native status, read back by the same name the
|
|
388
473
|
// capability deploys under. Dynamic import mirrors listArtifacts.
|