@intentius/chant-lexicon-helm 0.0.22 → 0.1.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.
Files changed (39) hide show
  1. package/dist/integrity.json +7 -5
  2. package/dist/manifest.json +1 -1
  3. package/dist/rules/values-no-helm-tpl.ts +92 -0
  4. package/dist/rules/whm005-no-empty-wrapper.ts +54 -0
  5. package/dist/skills/{chant-helm-chart-patterns.md → chant-helm-patterns.md} +52 -0
  6. package/dist/skills/chant-helm.md +496 -0
  7. package/package.json +6 -3
  8. package/src/codegen/docs.ts +3 -2
  9. package/src/composites/composites.test.ts +116 -110
  10. package/src/composites/helm-batch-job.ts +33 -19
  11. package/src/composites/helm-crd-lifecycle.ts +37 -24
  12. package/src/composites/helm-cron-job.ts +25 -13
  13. package/src/composites/helm-daemon-set.ts +26 -14
  14. package/src/composites/helm-external-secret.ts +21 -12
  15. package/src/composites/helm-library.ts +21 -7
  16. package/src/composites/helm-microservice.ts +46 -29
  17. package/src/composites/helm-monitored-service.ts +75 -51
  18. package/src/composites/helm-namespace-env.ts +80 -52
  19. package/src/composites/helm-secure-ingress.ts +66 -50
  20. package/src/composites/helm-stateful-service.ts +29 -16
  21. package/src/composites/helm-web-app.ts +37 -22
  22. package/src/composites/helm-worker.ts +34 -20
  23. package/src/index.ts +4 -1
  24. package/src/intrinsics.ts +53 -0
  25. package/src/lint/post-synth/post-synth.test.ts +43 -0
  26. package/src/lint/post-synth/whm005-no-empty-wrapper.ts +54 -0
  27. package/src/lint/rules/lint-rules.test.ts +35 -0
  28. package/src/lint/rules/values-no-helm-tpl.ts +92 -0
  29. package/src/plugin.test.ts +7 -5
  30. package/src/plugin.ts +8 -42
  31. package/src/resources.ts +49 -0
  32. package/src/serializer.test.ts +113 -2
  33. package/src/serializer.ts +149 -13
  34. package/src/skills/{chart-patterns.md → chant-helm-patterns.md} +52 -0
  35. package/src/skills/chant-helm.md +496 -0
  36. package/dist/skills/chant-helm-create-chart.md +0 -211
  37. package/src/skills/create-chart.md +0 -211
  38. /package/dist/skills/{chant-helm-chart-security-patterns.md → chant-helm-security.md} +0 -0
  39. /package/src/skills/{chart-security-patterns.md → chant-helm-security.md} +0 -0
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "algorithm": "xxhash64",
3
3
  "artifacts": {
4
- "manifest.json": "21f64ba92bab7858",
4
+ "manifest.json": "8230c04a29b03431",
5
5
  "meta.json": "b7aab243e162dfaf",
6
6
  "types/index.d.ts": "e8011da51963f058",
7
7
  "rules/no-hardcoded-image.ts": "c75aa9c33016c3b9",
8
8
  "rules/values-no-secrets.ts": "213276bfe1fb8ff7",
9
+ "rules/values-no-helm-tpl.ts": "ed23236546936fd0",
9
10
  "rules/chart-metadata.ts": "d0cff2b78fed78d3",
10
11
  "rules/whm103.ts": "a777a13f2eaf1831",
11
12
  "rules/whm104.ts": "e2d644da2a9dc605",
@@ -25,12 +26,13 @@
25
26
  "rules/whm401.ts": "fe72c3c450a12f93",
26
27
  "rules/whm105.ts": "8977ec834713b90c",
27
28
  "rules/whm403.ts": "729d10edb48b0d03",
29
+ "rules/whm005-no-empty-wrapper.ts": "1a1baea985f0bd86",
28
30
  "rules/whm405.ts": "ec82442f9120e5e0",
29
31
  "rules/whm202.ts": "dbe1cbb3237be84f",
30
32
  "rules/whm501.ts": "e9a9f2b4e034a51f",
31
- "skills/chant-helm-chart-patterns.md": "7a2163247f44bf6d",
32
- "skills/chant-helm-chart-security-patterns.md": "a7b950513dac7d37",
33
- "skills/chant-helm-create-chart.md": "52df5756e29c32bf"
33
+ "skills/chant-helm.md": "6476c552b9235085",
34
+ "skills/chant-helm-patterns.md": "cc9bfe5595f22710",
35
+ "skills/chant-helm-security.md": "a7b950513dac7d37"
34
36
  },
35
- "composite": "987b6dfc47cf4f5d"
37
+ "composite": "8872cd0d6c961e4d"
36
38
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "helm",
3
- "version": "0.0.22",
3
+ "version": "0.1.0",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "Helm",
6
6
  "intrinsics": [
@@ -0,0 +1,92 @@
1
+ /**
2
+ * WHM004: HelmTpl Expression Has No Effect in Values Constructor
3
+ *
4
+ * Detects Values constructor props that use `v.xxx` (the `values` proxy)
5
+ * or any HelmTpl-like expression. values.yaml is static YAML — it is NOT
6
+ * processed as a Go template by Helm. These expressions silently become ''.
7
+ *
8
+ * Bad: new Values({ host: v.pgHost })
9
+ * Good: new Values({ host: runtimeSlot("Cloud SQL private IP") })
10
+ */
11
+
12
+ import type { LintRule, LintDiagnostic, LintContext } from "@intentius/chant/lint/rule";
13
+ import * as ts from "typescript";
14
+
15
+ export const valuesNoHelmTplRule: LintRule = {
16
+ id: "WHM004",
17
+ severity: "warning",
18
+ category: "correctness",
19
+ description:
20
+ "HelmTpl expression has no effect in values.yaml — use runtimeSlot() for deploy-time values",
21
+
22
+ check(context: LintContext): LintDiagnostic[] {
23
+ const { sourceFile } = context;
24
+ const diagnostics: LintDiagnostic[] = [];
25
+
26
+ function visit(node: ts.Node): void {
27
+ if (
28
+ ts.isNewExpression(node) &&
29
+ ts.isIdentifier(node.expression) &&
30
+ node.expression.text === "Values" &&
31
+ node.arguments &&
32
+ node.arguments.length > 0
33
+ ) {
34
+ const arg = node.arguments[0];
35
+ if (ts.isObjectLiteralExpression(arg)) {
36
+ checkObjectLiteral(arg, [], sourceFile, diagnostics);
37
+ }
38
+ }
39
+ ts.forEachChild(node, visit);
40
+ }
41
+
42
+ visit(sourceFile);
43
+ return diagnostics;
44
+ },
45
+ };
46
+
47
+ /**
48
+ * Get the root identifier name of a property access / call chain.
49
+ * v.foo → "v"; values.x.pipe("fn") → "values"; runtimeSlot() → "runtimeSlot"
50
+ */
51
+ function getRootIdentifier(node: ts.Node): string | null {
52
+ if (ts.isIdentifier(node)) return node.text;
53
+ if (ts.isPropertyAccessExpression(node)) return getRootIdentifier(node.expression);
54
+ if (ts.isCallExpression(node)) return getRootIdentifier(node.expression);
55
+ return null;
56
+ }
57
+
58
+ function isHelmTplExpr(node: ts.Node): boolean {
59
+ const root = getRootIdentifier(node);
60
+ return root === "v" || root === "values";
61
+ }
62
+
63
+ function checkObjectLiteral(
64
+ obj: ts.ObjectLiteralExpression,
65
+ path: string[],
66
+ sourceFile: ts.SourceFile,
67
+ diagnostics: LintDiagnostic[],
68
+ ): void {
69
+ for (const prop of obj.properties) {
70
+ if (!ts.isPropertyAssignment(prop)) continue;
71
+
72
+ const keyName = ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name)
73
+ ? prop.name.text
74
+ : undefined;
75
+ const propPath = keyName ? [...path, keyName] : path;
76
+
77
+ if (isHelmTplExpr(prop.initializer)) {
78
+ const { line, character } = sourceFile.getLineAndCharacterOfPosition(prop.getStart());
79
+ const pathStr = propPath.join(".");
80
+ diagnostics.push({
81
+ file: sourceFile.fileName,
82
+ line: line + 1,
83
+ column: character + 1,
84
+ ruleId: "WHM004",
85
+ severity: "warning",
86
+ message: `HelmTpl expression has no effect in values.yaml (values.yaml is not a Go template). Use runtimeSlot() for deploy-time values or a static default.${pathStr ? ` (path: ${pathStr})` : ""}`,
87
+ });
88
+ } else if (ts.isObjectLiteralExpression(prop.initializer)) {
89
+ checkObjectLiteral(prop.initializer, propPath, sourceFile, diagnostics);
90
+ }
91
+ }
92
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * WHM005: Chart Has Sub-chart Dependencies But Generates No Templates
3
+ *
4
+ * A chart with HelmDependency entries but no templates/*.yaml files generates
5
+ * an empty templates/ directory. Deploying it requires `helm dependency build`
6
+ * as a non-obvious prerequisite.
7
+ *
8
+ * If you only need value overrides for an upstream chart, deploy it directly:
9
+ * helm upgrade upstream-chart -f values-override.yaml
10
+ * and use ValuesOverride to generate the override file.
11
+ */
12
+
13
+ import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
14
+ import { getChartFiles } from "./helm-helpers";
15
+
16
+ export const whm005: PostSynthCheck = {
17
+ id: "WHM005",
18
+ description:
19
+ "Chart with sub-chart dependencies but no templates should deploy upstream chart directly",
20
+
21
+ check(ctx: PostSynthContext): PostSynthDiagnostic[] {
22
+ const diagnostics: PostSynthDiagnostic[] = [];
23
+
24
+ for (const [, output] of ctx.outputs) {
25
+ const files = getChartFiles(output);
26
+ const chartYaml = files["Chart.yaml"];
27
+ if (!chartYaml) continue;
28
+
29
+ // Check for non-empty dependencies block
30
+ const hasDependencies = /^dependencies:/m.test(chartYaml);
31
+ if (!hasDependencies) continue;
32
+
33
+ // Check for template files (excluding _helpers.tpl and NOTES.txt)
34
+ const hasTemplates = Object.keys(files).some((path) => {
35
+ if (!path.startsWith("templates/")) return false;
36
+ const filename = path.slice("templates/".length);
37
+ if (filename === "_helpers.tpl" || filename === "NOTES.txt") return false;
38
+ return filename.endsWith(".yaml") || filename.endsWith(".yml");
39
+ });
40
+
41
+ if (!hasTemplates) {
42
+ diagnostics.push({
43
+ checkId: "WHM005",
44
+ severity: "warning",
45
+ message:
46
+ "Chart has sub-chart dependencies but generates no templates. Deploying this chart requires 'helm dependency build' first. If you only need value overrides for an upstream chart, deploy it directly with 'helm upgrade upstream-chart -f values-override.yaml' and use ValuesOverride to generate the override file.",
47
+ lexicon: "helm",
48
+ });
49
+ }
50
+ }
51
+
52
+ return diagnostics;
53
+ },
54
+ };
@@ -215,6 +215,58 @@ ChartRef.Name // {{ .Chart.Name }}
215
215
  ChartRef.Version // {{ .Chart.Version }}
216
216
  ```
217
217
 
218
+ ## Deploying Upstream Charts with Value Overrides
219
+
220
+ When you need to deploy an upstream chart (like `gitlab/gitlab`) with custom values, avoid wrapper charts with no templates. Instead:
221
+
222
+ 1. Use `runtimeSlot()` in `new Values({...})` for deploy-time values (DB IPs, bucket names, replicas)
223
+ 2. Use `ValuesOverride` for static values shared across all environments (disabled bundled services, shared secret refs)
224
+ 3. Deploy the upstream chart directly with `-f` flags
225
+
226
+ ```typescript
227
+ import { Values, ValuesOverride, runtimeSlot } from "@intentius/chant-lexicon-helm";
228
+
229
+ // Runtime slots → values-runtime-slots.yaml (deploy-time checklist)
230
+ export const vals = new Values({
231
+ global: {
232
+ psql: { host: runtimeSlot("Cloud SQL private IP") },
233
+ redis: { host: runtimeSlot("Memorystore host") },
234
+ },
235
+ });
236
+
237
+ // Static overrides → values-base.yaml (shared across all deployments)
238
+ export const baseOverride = new ValuesOverride({
239
+ filename: "values-base",
240
+ values: {
241
+ postgresql: { install: false },
242
+ redis: { install: false },
243
+ certmanager: { install: false },
244
+ "nginx-ingress": { enabled: false },
245
+ },
246
+ });
247
+ ```
248
+
249
+ Outputs:
250
+ - `chart-dir/values.yaml` — defaults; runtime slots appear as `''`
251
+ - `chart-dir/values-base.yaml` — generated static overrides
252
+ - `chart-dir/values-runtime-slots.yaml` — deploy-time slots with descriptions as comments
253
+
254
+ Deploy:
255
+ ```bash
256
+ # chant build generates chart-dir/ including values-base.yaml
257
+ chant build
258
+
259
+ # Fill in runtime-slot values (one per environment)
260
+ # values-prod.yaml contains: global.psql.host, global.redis.host, etc.
261
+
262
+ helm upgrade --install my-release upstream/chart \
263
+ -f chart-dir/values-base.yaml \
264
+ -f values-prod.yaml \
265
+ --wait
266
+ ```
267
+
268
+ **WHM005** warns when a chart has `HelmDependency` entries but generates no templates — this is the "empty wrapper" anti-pattern that requires `helm dependency build` as a non-obvious prerequisite.
269
+
218
270
  ## Template Functions
219
271
 
220
272
  ```typescript