@intentius/chant-lexicon-helm 0.46.0 → 0.49.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 +18 -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 +1 -1
- 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/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 +98 -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/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 +47 -0
- 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,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wrapper-chart materialization (#1242).
|
|
3
|
+
*
|
|
4
|
+
* The wrapper is what the pinned install path hands to `helm upgrade
|
|
5
|
+
* --install`, so the tests pin its two safety properties: CRD-origin
|
|
6
|
+
* documents land verbatim in `crds/` (uninstall-safe, epic finding 4), and
|
|
7
|
+
* every other document ships byte-for-byte through a `.Files.Get` shim —
|
|
8
|
+
* never placed directly in `templates/`, where helm would re-template
|
|
9
|
+
* recorded bytes (alertmanager-style `{{ $labels }}` content is the live
|
|
10
|
+
* case).
|
|
11
|
+
*/
|
|
12
|
+
import { describe, test, expect, beforeEach, afterEach } from "vitest";
|
|
13
|
+
import { mkdtempSync, readFileSync, rmSync, existsSync, readdirSync } from "node:fs";
|
|
14
|
+
import { tmpdir } from "node:os";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
|
|
17
|
+
import { routeRender } from "./render-wrapper";
|
|
18
|
+
import { materializeWrapperChart, wrapperChartName } from "./wrapper-chart";
|
|
19
|
+
|
|
20
|
+
let dir: string;
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
dir = mkdtempSync(join(tmpdir(), "chant-wrapper-chart-"));
|
|
23
|
+
});
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
rmSync(dir, { recursive: true, force: true });
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const stream = [
|
|
29
|
+
"---",
|
|
30
|
+
"# Source: umb/charts/kid/crds/kidcrd.yaml",
|
|
31
|
+
"apiVersion: apiextensions.k8s.io/v1",
|
|
32
|
+
"kind: CustomResourceDefinition",
|
|
33
|
+
"metadata:",
|
|
34
|
+
" name: kidthings.example.com",
|
|
35
|
+
"spec:",
|
|
36
|
+
" group: example.com",
|
|
37
|
+
" names:",
|
|
38
|
+
" kind: KidThing",
|
|
39
|
+
"---",
|
|
40
|
+
"# Source: umb/templates/hook-job.yaml",
|
|
41
|
+
"apiVersion: batch/v1",
|
|
42
|
+
"kind: Job",
|
|
43
|
+
"metadata:",
|
|
44
|
+
" name: setup",
|
|
45
|
+
" annotations:",
|
|
46
|
+
' "helm.sh/hook": pre-install',
|
|
47
|
+
"---",
|
|
48
|
+
"# Source: umb/templates/cm.yaml",
|
|
49
|
+
"apiVersion: v1",
|
|
50
|
+
"kind: ConfigMap",
|
|
51
|
+
"metadata:",
|
|
52
|
+
" name: alerts",
|
|
53
|
+
"data:",
|
|
54
|
+
' template: "summary: {{ $labels.instance }} down"',
|
|
55
|
+
].join("\n");
|
|
56
|
+
|
|
57
|
+
describe("wrapperChartName", () => {
|
|
58
|
+
test("keeps a bare chart name, sanitizes a local path, never returns empty", () => {
|
|
59
|
+
expect(wrapperChartName("cert-manager")).toBe("cert-manager");
|
|
60
|
+
expect(wrapperChartName("./charts/My_Web App")).toBe("my-web-app");
|
|
61
|
+
expect(wrapperChartName("../")).toBe("pinned-chart");
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe("materializeWrapperChart", () => {
|
|
66
|
+
test("routes CRDs to crds/ verbatim and ships template docs through .Files.Get shims", () => {
|
|
67
|
+
const routed = routeRender(stream, { chart: "umb", chartVersion: "0.1.0" });
|
|
68
|
+
const wrapper = materializeWrapperChart(routed, dir);
|
|
69
|
+
|
|
70
|
+
expect(wrapper.chartName).toBe("umb");
|
|
71
|
+
expect(wrapper.chartVersion).toBe("0.1.0");
|
|
72
|
+
|
|
73
|
+
const chartYaml = readFileSync(join(dir, "Chart.yaml"), "utf8");
|
|
74
|
+
expect(chartYaml).toContain("name: umb");
|
|
75
|
+
expect(chartYaml).toContain("version: 0.1.0");
|
|
76
|
+
|
|
77
|
+
// CRD: verbatim bytes in crds/, where uninstall leaves them alone.
|
|
78
|
+
expect(wrapper.crdFiles).toHaveLength(1);
|
|
79
|
+
const crd = readFileSync(join(dir, wrapper.crdFiles[0]), "utf8");
|
|
80
|
+
expect(crd).toBe(routed.crds[0].text + "\n");
|
|
81
|
+
|
|
82
|
+
// Non-CRD docs: exact bytes in manifests/, one shim each in templates/.
|
|
83
|
+
expect(wrapper.manifestFiles).toHaveLength(2);
|
|
84
|
+
const manifestTexts = wrapper.manifestFiles.map((rel) => readFileSync(join(dir, rel), "utf8"));
|
|
85
|
+
for (const doc of [...routed.main, ...routed.hooks]) {
|
|
86
|
+
expect(manifestTexts).toContain(doc.text + "\n");
|
|
87
|
+
}
|
|
88
|
+
const shims = readdirSync(join(dir, "templates"));
|
|
89
|
+
expect(shims).toHaveLength(2);
|
|
90
|
+
for (const shim of shims) {
|
|
91
|
+
const text = readFileSync(join(dir, "templates", shim), "utf8");
|
|
92
|
+
expect(text).toMatch(/^\{\{ \.Files\.Get "manifests\/[a-z0-9-]+\.yaml" \}\}\n$/);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// The template-looking bytes survive untouched in the manifest file —
|
|
96
|
+
// the reason the shim indirection exists.
|
|
97
|
+
expect(manifestTexts.join("")).toContain("{{ $labels.instance }}");
|
|
98
|
+
expect(wrapper.hookCount).toBe(1);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("a CRD-only render writes no templates/ or manifests/ directories", () => {
|
|
102
|
+
const crdOnly = stream.split("---").slice(0, 2).join("---");
|
|
103
|
+
const routed = routeRender(crdOnly, { chart: "ops", chartVersion: null });
|
|
104
|
+
const wrapper = materializeWrapperChart(routed, dir);
|
|
105
|
+
expect(wrapper.chartVersion).toBe("0.0.0"); // helm needs a version; a version-less local render falls back
|
|
106
|
+
expect(wrapper.manifestFiles).toHaveLength(0);
|
|
107
|
+
expect(existsSync(join(dir, "templates"))).toBe(false);
|
|
108
|
+
expect(existsSync(join(dir, "manifests"))).toBe(false);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wrapper-chart materialization for the pinned install path (#1242, epic
|
|
3
|
+
* #1228 Phase 4).
|
|
4
|
+
*
|
|
5
|
+
* Turns a `RoutedRender` (#1239) into an installable helm chart on disk.
|
|
6
|
+
* The wrapper preserves the recorded bytes exactly:
|
|
7
|
+
*
|
|
8
|
+
* - CRD-group documents are written verbatim into the wrapper's `crds/`,
|
|
9
|
+
* which is what keeps `helm uninstall` from deleting them — the A/B test
|
|
10
|
+
* behind epic finding 4 showed a flat wrap converts uninstall from safe
|
|
11
|
+
* to data-destroying.
|
|
12
|
+
* - Every other document (main and hook groups) is written verbatim into
|
|
13
|
+
* `manifests/`, and each gets a one-line shim in `templates/` that reads
|
|
14
|
+
* it back with `.Files.Get`. The shim exists because rendered output can
|
|
15
|
+
* legitimately contain template-looking text — a ConfigMap carrying
|
|
16
|
+
* alertmanager notification templates renders `{{ $labels }}` as literal
|
|
17
|
+
* bytes — and a document placed directly in `templates/` would be run
|
|
18
|
+
* through the template engine a second time, mangling or failing on
|
|
19
|
+
* exactly those bytes. `.Files.Get` returns file content untemplated, so
|
|
20
|
+
* what helm installs is what the render store recorded.
|
|
21
|
+
* - Hook documents go through the same shim; their `helm.sh/hook`
|
|
22
|
+
* annotations are part of the recorded bytes, so helm registers and runs
|
|
23
|
+
* them from the rendered output unchanged (epic finding 5).
|
|
24
|
+
*
|
|
25
|
+
* The wrapper inherits the source chart's name and version (epic
|
|
26
|
+
* Decisions), keeping `helm history` continuous across the pinned
|
|
27
|
+
* migration. A local chart path becomes its sanitized basename — helm
|
|
28
|
+
* requires a bare chart name — and a render recorded without a version
|
|
29
|
+
* falls back to `0.0.0`.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
33
|
+
import { join } from "node:path";
|
|
34
|
+
|
|
35
|
+
import type { RoutedDocument, RoutedRender } from "./render-wrapper";
|
|
36
|
+
|
|
37
|
+
/** What `materializeWrapperChart` wrote, with the counts the install result reports. */
|
|
38
|
+
export interface MaterializedWrapperChart {
|
|
39
|
+
/** Chart root directory — the `helm upgrade --install` argument. */
|
|
40
|
+
dir: string;
|
|
41
|
+
/** Wrapper chart name (sanitized from the source chart reference). */
|
|
42
|
+
chartName: string;
|
|
43
|
+
/** Wrapper chart version (`0.0.0` when the render recorded none). */
|
|
44
|
+
chartVersion: string;
|
|
45
|
+
/** Files written under `crds/`, chart-relative. */
|
|
46
|
+
crdFiles: string[];
|
|
47
|
+
/** Verbatim document files written under `manifests/`, chart-relative, in emit order. */
|
|
48
|
+
manifestFiles: string[];
|
|
49
|
+
/** Number of hook documents among `manifestFiles`. */
|
|
50
|
+
hookCount: number;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* A valid helm chart name from a chart reference that may be a local path
|
|
55
|
+
* (`./charts/web`) or a repo-qualified name. Takes the last path segment,
|
|
56
|
+
* lowercases it, and collapses anything outside `[a-z0-9-]` to `-` — helm
|
|
57
|
+
* chart names are DNS-label-shaped. Falls back to `pinned-chart` when
|
|
58
|
+
* nothing survives.
|
|
59
|
+
*/
|
|
60
|
+
export function wrapperChartName(chart: string): string {
|
|
61
|
+
const base = chart.split("/").filter((s) => s.length > 0).pop() ?? "";
|
|
62
|
+
const sanitized = base
|
|
63
|
+
.toLowerCase()
|
|
64
|
+
.replace(/[^a-z0-9-]+/g, "-")
|
|
65
|
+
.replace(/^-+|-+$/g, "");
|
|
66
|
+
return sanitized.length > 0 ? sanitized : "pinned-chart";
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** `doc-0007-my-deployment` — a stable, ordered, filesystem-safe stem for one routed document. */
|
|
70
|
+
function fileStem(prefix: string, index: number, doc: RoutedDocument): string {
|
|
71
|
+
const label = (doc.name ?? doc.kind ?? "document")
|
|
72
|
+
.toLowerCase()
|
|
73
|
+
.replace(/[^a-z0-9-]+/g, "-")
|
|
74
|
+
.replace(/^-+|-+$/g, "")
|
|
75
|
+
.slice(0, 40);
|
|
76
|
+
return `${prefix}-${String(index).padStart(4, "0")}${label ? `-${label}` : ""}`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Write `routed` as an installable wrapper chart under `dir` (which must
|
|
81
|
+
* exist and should be empty). Document text is written byte-for-byte; the
|
|
82
|
+
* only authored files are `Chart.yaml` and the `.Files.Get` shims.
|
|
83
|
+
*/
|
|
84
|
+
export function materializeWrapperChart(routed: RoutedRender, dir: string): MaterializedWrapperChart {
|
|
85
|
+
const chartName = wrapperChartName(routed.chart);
|
|
86
|
+
const chartVersion = routed.chartVersion ?? "0.0.0";
|
|
87
|
+
|
|
88
|
+
writeFileSync(
|
|
89
|
+
join(dir, "Chart.yaml"),
|
|
90
|
+
[
|
|
91
|
+
"apiVersion: v2",
|
|
92
|
+
`name: ${chartName}`,
|
|
93
|
+
`version: ${chartVersion}`,
|
|
94
|
+
"description: chant pinned render wrapper (epic #1228) — installs recorded bytes; templates are .Files.Get shims and are never re-rendered",
|
|
95
|
+
"",
|
|
96
|
+
].join("\n"),
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const crdFiles: string[] = [];
|
|
100
|
+
if (routed.crds.length > 0) {
|
|
101
|
+
mkdirSync(join(dir, "crds"), { recursive: true });
|
|
102
|
+
routed.crds.forEach((doc, i) => {
|
|
103
|
+
const rel = `crds/${fileStem("crd", i, doc)}.yaml`;
|
|
104
|
+
writeFileSync(join(dir, rel), doc.text.endsWith("\n") ? doc.text : `${doc.text}\n`);
|
|
105
|
+
crdFiles.push(rel);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const manifestFiles: string[] = [];
|
|
110
|
+
const docs = [...routed.main, ...routed.hooks];
|
|
111
|
+
if (docs.length > 0) {
|
|
112
|
+
mkdirSync(join(dir, "manifests"), { recursive: true });
|
|
113
|
+
mkdirSync(join(dir, "templates"), { recursive: true });
|
|
114
|
+
docs.forEach((doc, i) => {
|
|
115
|
+
const stem = fileStem("doc", i, doc);
|
|
116
|
+
const rel = `manifests/${stem}.yaml`;
|
|
117
|
+
writeFileSync(join(dir, rel), doc.text.endsWith("\n") ? doc.text : `${doc.text}\n`);
|
|
118
|
+
writeFileSync(join(dir, `templates/${stem}.yaml`), `{{ .Files.Get "${rel}" }}\n`);
|
|
119
|
+
manifestFiles.push(rel);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return { dir, chartName, chartVersion, crdFiles, manifestFiles, hookCount: routed.hooks.length };
|
|
124
|
+
}
|