@intentius/chant-lexicon-helm 0.124.31 → 0.124.33

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.
Files changed (111) hide show
  1. package/dist/archive-render.js +76 -0
  2. package/dist/codegen/docs-cli.js +3 -0
  3. package/dist/codegen/docs.js +113 -0
  4. package/dist/codegen/generate-cli.js +19 -0
  5. package/dist/codegen/generate.js +210 -0
  6. package/dist/codegen/naming.js +29 -0
  7. package/dist/codegen/package.js +44 -0
  8. package/dist/codegen/values-type.js +87 -0
  9. package/dist/commands.js +413 -0
  10. package/dist/components/builders.js +9 -0
  11. package/dist/components/capability-plugin.js +17 -0
  12. package/dist/components/helm-upgrade.js +109 -0
  13. package/dist/components/index.js +11 -0
  14. package/dist/composites/catalog.js +1119 -0
  15. package/dist/composites/helm-batch-job.js +148 -0
  16. package/dist/composites/helm-crd-lifecycle.js +145 -0
  17. package/dist/composites/helm-cron-job.js +130 -0
  18. package/dist/composites/helm-daemon-set.js +125 -0
  19. package/dist/composites/helm-external-secret.js +63 -0
  20. package/dist/composites/helm-library.js +24 -0
  21. package/dist/composites/helm-microservice.js +260 -0
  22. package/dist/composites/helm-monitored-service.js +188 -0
  23. package/dist/composites/helm-namespace-env.js +116 -0
  24. package/dist/composites/helm-secure-ingress.js +86 -0
  25. package/dist/composites/helm-stateful-service.js +160 -0
  26. package/dist/composites/helm-web-app.js +201 -0
  27. package/dist/composites/helm-worker.js +164 -0
  28. package/dist/composites/index.js +13 -0
  29. package/dist/config.js +161 -0
  30. package/dist/coverage.js +37 -0
  31. package/dist/deep-observe-hooks.js +19 -0
  32. package/dist/deep-observe.js +55 -0
  33. package/dist/describe-resources.js +119 -0
  34. package/dist/describe-stack-status.js +66 -0
  35. package/dist/detect.js +11 -0
  36. package/dist/generated/index.js +11 -0
  37. package/dist/helpers.js +84 -0
  38. package/dist/import/generator.js +292 -0
  39. package/dist/import/parser.js +141 -0
  40. package/dist/import/template-stripper.js +86 -0
  41. package/dist/index.js +55 -0
  42. package/dist/integrity.json +2 -2
  43. package/dist/intrinsics.js +482 -0
  44. package/dist/lint/audit-catalog.js +33 -0
  45. package/dist/lint/audit-lineage.js +53 -0
  46. package/dist/lint/post-synth/helm-helpers.js +60 -0
  47. package/dist/lint/post-synth/index.js +48 -0
  48. package/dist/lint/post-synth/whm005-no-empty-wrapper.js +47 -0
  49. package/dist/lint/post-synth/whm101.js +38 -0
  50. package/dist/lint/post-synth/whm102.js +26 -0
  51. package/dist/lint/post-synth/whm103.js +51 -0
  52. package/dist/lint/post-synth/whm104.js +29 -0
  53. package/dist/lint/post-synth/whm105.js +23 -0
  54. package/dist/lint/post-synth/whm201.js +29 -0
  55. package/dist/lint/post-synth/whm202.js +42 -0
  56. package/dist/lint/post-synth/whm203.js +32 -0
  57. package/dist/lint/post-synth/whm204.js +51 -0
  58. package/dist/lint/post-synth/whm301.js +31 -0
  59. package/dist/lint/post-synth/whm302.js +33 -0
  60. package/dist/lint/post-synth/whm401.js +52 -0
  61. package/dist/lint/post-synth/whm402.js +38 -0
  62. package/dist/lint/post-synth/whm403.js +38 -0
  63. package/dist/lint/post-synth/whm404.js +30 -0
  64. package/dist/lint/post-synth/whm405.js +47 -0
  65. package/dist/lint/post-synth/whm406.js +27 -0
  66. package/dist/lint/post-synth/whm407.js +72 -0
  67. package/dist/lint/post-synth/whm501.js +89 -0
  68. package/dist/lint/post-synth/whm502.js +85 -0
  69. package/dist/lint/post-synth/whm503.js +82 -0
  70. package/dist/lint/post-synth/whm504.js +47 -0
  71. package/dist/lint/rules/chart-metadata.js +53 -0
  72. package/dist/lint/rules/no-hardcoded-image.js +50 -0
  73. package/dist/lint/rules/values-no-helm-tpl.js +78 -0
  74. package/dist/lint/rules/values-no-secrets.js +65 -0
  75. package/dist/list-artifacts.js +134 -0
  76. package/dist/lsp/completions.d.ts.map +1 -1
  77. package/dist/lsp/completions.js +18 -0
  78. package/dist/lsp/hover.d.ts.map +1 -1
  79. package/dist/lsp/hover.js +40 -0
  80. package/dist/manifest.json +1 -1
  81. package/dist/op/activities/cluster-probe.js +116 -0
  82. package/dist/op/activities/helm.js +449 -0
  83. package/dist/op/activities/index.js +10 -0
  84. package/dist/op/builders.js +45 -0
  85. package/dist/package-cli.js +21 -0
  86. package/dist/pinnability/actions.js +207 -0
  87. package/dist/pinnability/classify.js +416 -0
  88. package/dist/pinnability/conditions.js +0 -0
  89. package/dist/pinnability/index.js +16 -0
  90. package/dist/pinnability/localize.js +518 -0
  91. package/dist/pinnability/render-stream.js +53 -0
  92. package/dist/pinnability/values.js +151 -0
  93. package/dist/plugin.js +458 -0
  94. package/dist/project-codegen.js +215 -0
  95. package/dist/release-observe.js +287 -0
  96. package/dist/render-diff.js +279 -0
  97. package/dist/render-digest.js +0 -0
  98. package/dist/render-records.js +29 -0
  99. package/dist/render-store.js +401 -0
  100. package/dist/render-wrapper.js +164 -0
  101. package/dist/render.js +394 -0
  102. package/dist/resources.js +109 -0
  103. package/dist/serializer.js +903 -0
  104. package/dist/validate-cli.js +17 -0
  105. package/dist/validate.js +30 -0
  106. package/dist/values-probe.js +504 -0
  107. package/dist/wrapper-chart.js +95 -0
  108. package/package.json +11 -11
  109. package/src/codegen/package.ts +2 -2
  110. package/src/lsp/completions.ts +2 -1
  111. package/src/lsp/hover.ts +2 -1
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Live introspection of Helm releases via `helm list -A -o json`.
3
+ *
4
+ * The Helm lexicon's chant entities describe chart-authoring primitives
5
+ * (Chart.yaml, templates/, values.yaml). The runtime concept — a Helm
6
+ * release installed in a kubeconfig context — is created by `helm install`
7
+ * outside chant's entity model. This implementation reports those releases
8
+ * as artifacts so `state diff --live` / `WatchOp` can detect manual
9
+ * installs/upgrades/rollbacks that slip past CI.
10
+ *
11
+ * Helm-not-installed (binary missing) → returns `{}` cleanly so other
12
+ * lexicons' snapshots aren't blocked.
13
+ */
14
+ import { exec } from "node:child_process";
15
+ import { promisify } from "node:util";
16
+ import { loadChantConfigUpward } from "@intentius/chant/config";
17
+ import { resolveClusterTarget } from "@intentius/chant/kubectl-context";
18
+ import { readReleaseLedger, latestPerComponent } from "@intentius/chant/lifecycle/release-ledger";
19
+ const execAsync = promisify(exec);
20
+ /**
21
+ * The render identity a chant deploy recorded for a release (#2031). `helm
22
+ * list` cannot know which bytes a release was installed from — but the
23
+ * release ledger already records it on deploy (#1243): the input digest for
24
+ * an unpinned deploy, contentDigest + inputDigest for a pinned one. This
25
+ * reads that record back, so an observed release joins `helm renders` /
26
+ * `helm diff` by digest instead of by chart-name prefix matching.
27
+ *
28
+ * The join key is the ledger record's `component`, which the helm deploy
29
+ * activity defaults to the release name (`args.component ?? args.name`,
30
+ * ./op/activities/helm.ts). Deliberately best-effort and honest about
31
+ * absence:
32
+ *
33
+ * - a release deployed outside chant has no record, and reports nothing;
34
+ * - a ledger that cannot be read (no repo, no lifecycle branch) joins
35
+ * nothing rather than failing the snapshot;
36
+ * - two observed releases sharing one name (different namespaces) are
37
+ * ambiguous — the record does not carry a namespace — so neither joins;
38
+ * - a record whose `component` was overridden away from the release name
39
+ * does not join (the honest miss, same as today).
40
+ */
41
+ function renderIdentityOf(record) {
42
+ if (!record)
43
+ return {};
44
+ if (record.inputDigest) {
45
+ // Pinned deploy (#1242): `digest` is the render's contentDigest, the
46
+ // input identity rides alongside.
47
+ return { inputDigest: record.inputDigest, contentDigest: record.digest };
48
+ }
49
+ // Unpinned deploy: the record is keyed by the input digest itself
50
+ // (`ReleaseRecord.inputDigest` is "absent when digest is already input-side").
51
+ return { inputDigest: record.digest };
52
+ }
53
+ function pruneUndefined(obj) {
54
+ const out = {};
55
+ for (const [k, v] of Object.entries(obj)) {
56
+ if (v !== undefined)
57
+ out[k] = v;
58
+ }
59
+ return out;
60
+ }
61
+ export async function listArtifacts(options) {
62
+ const result = {};
63
+ // The same cluster binding every other helm read resolves (chant#1488 —
64
+ // `k8s.profiles.<env>.context`, shared with the k8s lexicon). Listing on
65
+ // the AMBIENT context read whichever cluster the operator's shell last
66
+ // pointed at: verified live on kubemicrovm-ops, whose releases came back
67
+ // `{}` because the shell's current-context was an unrelated EKS cluster.
68
+ // Unresolvable → ambient, which is chant's own fallback when no profile is
69
+ // declared.
70
+ let context;
71
+ try {
72
+ const { config } = await loadChantConfigUpward(process.cwd());
73
+ context = (await resolveClusterTarget(config, options.environment, "helm")).context;
74
+ }
75
+ catch {
76
+ context = undefined;
77
+ }
78
+ let stdout;
79
+ try {
80
+ ({ stdout } = await execAsync(`helm list -A -o json${context ? ` --kube-context '${context.replace(/'/g, "'\\''")}'` : ""}`));
81
+ }
82
+ catch {
83
+ // Binary not installed, no kubeconfig, or some other error — return
84
+ // empty rather than blocking the whole snapshot.
85
+ return result;
86
+ }
87
+ let entries;
88
+ try {
89
+ entries = JSON.parse(stdout);
90
+ }
91
+ catch {
92
+ return result;
93
+ }
94
+ // The environment's latest release record per component (#2031) — the
95
+ // deploy-side identity read back. Best-effort: an unreadable ledger joins
96
+ // nothing rather than blocking the snapshot.
97
+ let recorded = new Map();
98
+ try {
99
+ const { records } = await readReleaseLedger(options.environment);
100
+ recorded = latestPerComponent(records);
101
+ }
102
+ catch {
103
+ recorded = new Map();
104
+ }
105
+ // A name observed in several namespaces is ambiguous against a record that
106
+ // carries no namespace — neither joins.
107
+ const nameCounts = new Map();
108
+ for (const entry of entries) {
109
+ if (entry.name)
110
+ nameCounts.set(entry.name, (nameCounts.get(entry.name) ?? 0) + 1);
111
+ }
112
+ for (const entry of entries) {
113
+ if (!entry.name || !entry.namespace)
114
+ continue;
115
+ const key = `release/${entry.namespace}/${entry.name}`;
116
+ const record = nameCounts.get(entry.name) === 1 ? recorded.get(entry.name) : undefined;
117
+ const { inputDigest, contentDigest } = renderIdentityOf(record);
118
+ result[key] = {
119
+ type: "Helm::Release",
120
+ physicalId: `${entry.namespace}/${entry.name}`,
121
+ status: entry.status ?? "unknown",
122
+ lastUpdated: entry.updated,
123
+ attributes: pruneUndefined({
124
+ chart: entry.chart,
125
+ revision: entry.revision,
126
+ appVersion: entry.app_version,
127
+ namespace: entry.namespace,
128
+ inputDigest,
129
+ contentDigest,
130
+ }),
131
+ };
132
+ }
133
+ return result;
134
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"completions.d.ts","sourceRoot":"","sources":["../../src/lsp/completions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAapF;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,iBAAiB,GAAG,cAAc,EAAE,CAExE"}
1
+ {"version":3,"file":"completions.d.ts","sourceRoot":"","sources":["../../src/lsp/completions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAapF;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,iBAAiB,GAAG,cAAc,EAAE,CAExE"}
@@ -0,0 +1,18 @@
1
+ import { createRequire } from "module";
2
+ import { moduleSourceUrl } from "@intentius/chant/module-source";
3
+ import { LexiconIndex, lexiconCompletions } from "@intentius/chant/lsp/lexicon-providers";
4
+ const require = createRequire(moduleSourceUrl(import.meta.url));
5
+ let cachedIndex = null;
6
+ function getIndex() {
7
+ if (cachedIndex)
8
+ return cachedIndex;
9
+ const data = require("../generated/lexicon-helm.json");
10
+ cachedIndex = new LexiconIndex(data);
11
+ return cachedIndex;
12
+ }
13
+ /**
14
+ * Provide Helm resource completions based on context.
15
+ */
16
+ export function helmCompletions(ctx) {
17
+ return lexiconCompletions(ctx, getIndex(), "Helm chart resource");
18
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"hover.d.ts","sourceRoot":"","sources":["../../src/lsp/hover.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAa1E;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,YAAY,GAAG,SAAS,GAAG,SAAS,CAElE"}
1
+ {"version":3,"file":"hover.d.ts","sourceRoot":"","sources":["../../src/lsp/hover.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAa1E;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,YAAY,GAAG,SAAS,GAAG,SAAS,CAElE"}
@@ -0,0 +1,40 @@
1
+ import { createRequire } from "module";
2
+ import { moduleSourceUrl } from "@intentius/chant/module-source";
3
+ import { LexiconIndex, lexiconHover } from "@intentius/chant/lsp/lexicon-providers";
4
+ const require = createRequire(moduleSourceUrl(import.meta.url));
5
+ let cachedIndex = null;
6
+ function getIndex() {
7
+ if (cachedIndex)
8
+ return cachedIndex;
9
+ const data = require("../generated/lexicon-helm.json");
10
+ cachedIndex = new LexiconIndex(data);
11
+ return cachedIndex;
12
+ }
13
+ /**
14
+ * Provide hover information for Helm resource types.
15
+ */
16
+ export function helmHover(ctx) {
17
+ return lexiconHover(ctx, getIndex(), resourceHover);
18
+ }
19
+ function resourceHover(className, entry) {
20
+ const lines = [];
21
+ lines.push(`**${className}**`);
22
+ lines.push("");
23
+ lines.push(`Helm type: \`${entry.resourceType}\``);
24
+ if (entry.kind === "resource") {
25
+ lines.push("");
26
+ lines.push("*Resource — serialized as a top-level Helm chart artifact*");
27
+ }
28
+ else {
29
+ lines.push("");
30
+ lines.push("*Property — used as a nested value in Helm resources*");
31
+ }
32
+ if (entry.attrs && Object.keys(entry.attrs).length > 0) {
33
+ lines.push("");
34
+ lines.push("**Attributes:**");
35
+ for (const [key, value] of Object.entries(entry.attrs)) {
36
+ lines.push(`- \`${key}\` \u2192 \`${value}\``);
37
+ }
38
+ }
39
+ return { contents: lines.join("\n") };
40
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "helm",
3
- "version": "0.124.31",
3
+ "version": "0.124.33",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "Helm",
6
6
  "intrinsics": [
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Live-cluster capability probe for the deploy-time profile assertion
3
+ * (#1244, epic #1228 phase 4).
4
+ *
5
+ * A render pinned for one capability profile and deployed to a cluster with
6
+ * another is silently skewed: the bytes were computed against a declared
7
+ * `kubeVersion` and `apiVersions` set, and nothing at deploy time checked
8
+ * the target actually matches. This module reads the target cluster's real
9
+ * capabilities — `kubectl version -o json` for the server version,
10
+ * `kubectl api-versions` for the served API set — through the same
11
+ * exec-and-ambient-kubeconfig convention `helmInstall` already uses for the
12
+ * helm binary, and names every divergence from a declared profile.
13
+ *
14
+ * The comparison tolerance is deliberate: kubeVersion matches on
15
+ * major.minor. A profile declaring `1.33.6` matches a live `v1.33.4+k3s1` —
16
+ * the capability surface helm renders against (`.Capabilities.KubeVersion`)
17
+ * is a major.minor fact, and patch skew within a minor changes no API
18
+ * surface a chart can probe. Declared apiVersions are exact-match: each one
19
+ * the profile names must be served by the cluster.
20
+ */
21
+ import { exec } from "node:child_process";
22
+ import { promisify } from "node:util";
23
+ const execAsync = promisify(exec);
24
+ /**
25
+ * Thrown when the cluster's capabilities cannot be read at all — kubectl
26
+ * missing, cluster unreachable, or unparseable output. A deploy that
27
+ * declared a profile cannot verify its guarantee through this error, so the
28
+ * caller must refuse to proceed rather than deploy blind.
29
+ */
30
+ export class ClusterProbeError extends Error {
31
+ constructor(message) {
32
+ super(message);
33
+ this.name = "ClusterProbeError";
34
+ }
35
+ }
36
+ /** `major.minor` from a declared or live version string (`1.33`, `1.33.6`, `v1.33.6+k3s1`). Undefined when the string has no parseable major.minor. */
37
+ export function majorMinorOf(version) {
38
+ const match = /^v?(\d+)\.(\d+)/.exec(version.trim());
39
+ return match ? `${match[1]}.${match[2]}` : undefined;
40
+ }
41
+ function parseServerVersion(raw) {
42
+ let parsed;
43
+ try {
44
+ parsed = JSON.parse(raw);
45
+ }
46
+ catch {
47
+ throw new ClusterProbeError(`kubectl version -o json produced unparseable output: ${raw.slice(0, 200)}`);
48
+ }
49
+ const server = parsed.serverVersion;
50
+ if (!server) {
51
+ throw new ClusterProbeError("kubectl version -o json reported no serverVersion — the cluster did not answer");
52
+ }
53
+ const gitVersion = typeof server.gitVersion === "string" ? server.gitVersion : "";
54
+ // major/minor are the authoritative fields; minor can carry a vendor
55
+ // suffix (GKE reports "33+"), so digits are extracted rather than trusted.
56
+ const major = typeof server.major === "string" ? /\d+/.exec(server.major)?.[0] : undefined;
57
+ const minor = typeof server.minor === "string" ? /\d+/.exec(server.minor)?.[0] : undefined;
58
+ const majorMinor = major && minor ? `${major}.${minor}` : gitVersion ? majorMinorOf(gitVersion) : undefined;
59
+ if (!majorMinor) {
60
+ throw new ClusterProbeError(`kubectl version -o json reported no usable server version (gitVersion: ${gitVersion || "<absent>"})`);
61
+ }
62
+ return { kubeVersion: gitVersion || majorMinor, majorMinor };
63
+ }
64
+ /**
65
+ * Read the target cluster's capabilities. Uses whatever kubeconfig/context
66
+ * the process environment provides — the same ambient target `helm upgrade`
67
+ * itself will act on, so the probe and the deploy see one cluster.
68
+ */
69
+ export async function probeClusterCapabilities(signal) {
70
+ let versionRaw;
71
+ let apiVersionsRaw;
72
+ try {
73
+ const [versionResult, apiResult] = await Promise.all([
74
+ execAsync("kubectl version -o json", { signal }),
75
+ execAsync("kubectl api-versions", { signal }),
76
+ ]);
77
+ versionRaw = versionResult.stdout;
78
+ apiVersionsRaw = apiResult.stdout;
79
+ }
80
+ catch (err) {
81
+ throw new ClusterProbeError(`could not read the target cluster's capabilities: ${err instanceof Error ? err.message : String(err)}`);
82
+ }
83
+ const { kubeVersion, majorMinor } = parseServerVersion(versionRaw);
84
+ const apiVersions = apiVersionsRaw
85
+ .split("\n")
86
+ .map((line) => line.trim())
87
+ .filter((line) => line.length > 0);
88
+ return { kubeVersion, majorMinor, apiVersions };
89
+ }
90
+ /**
91
+ * Every way `live` diverges from what `profile` declares, one message per
92
+ * divergence, each naming the declared and the live value. Empty when the
93
+ * cluster matches the profile.
94
+ *
95
+ * kubeVersion compares on major.minor (see module doc). apiVersions is a
96
+ * subset check: everything the profile declares must be served; extra API
97
+ * versions on the cluster are not a divergence, since a render only ever
98
+ * probed the declared set.
99
+ */
100
+ export function compareCapabilityProfile(profile, live) {
101
+ const divergences = [];
102
+ if (profile.kubeVersion) {
103
+ const declared = majorMinorOf(profile.kubeVersion);
104
+ if (declared !== live.majorMinor) {
105
+ divergences.push(`kubeVersion: profile declares ${profile.kubeVersion} (${declared ?? "unparseable"}), ` +
106
+ `cluster runs ${live.kubeVersion} (${live.majorMinor})`);
107
+ }
108
+ }
109
+ const served = new Set(live.apiVersions);
110
+ for (const apiVersion of profile.apiVersions ?? []) {
111
+ if (!served.has(apiVersion)) {
112
+ divergences.push(`apiVersion ${apiVersion}: declared by the profile, not served by the cluster`);
113
+ }
114
+ }
115
+ return divergences;
116
+ }