@intentius/chant-lexicon-helm 0.0.16

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 (110) hide show
  1. package/README.md +22 -0
  2. package/dist/integrity.json +36 -0
  3. package/dist/manifest.json +37 -0
  4. package/dist/meta.json +208 -0
  5. package/dist/rules/chart-metadata.ts +64 -0
  6. package/dist/rules/helm-helpers.ts +64 -0
  7. package/dist/rules/no-hardcoded-image.ts +62 -0
  8. package/dist/rules/values-no-secrets.ts +82 -0
  9. package/dist/rules/whm101.ts +46 -0
  10. package/dist/rules/whm102.ts +33 -0
  11. package/dist/rules/whm103.ts +59 -0
  12. package/dist/rules/whm104.ts +35 -0
  13. package/dist/rules/whm105.ts +30 -0
  14. package/dist/rules/whm201.ts +36 -0
  15. package/dist/rules/whm202.ts +50 -0
  16. package/dist/rules/whm203.ts +39 -0
  17. package/dist/rules/whm204.ts +60 -0
  18. package/dist/rules/whm301.ts +41 -0
  19. package/dist/rules/whm302.ts +40 -0
  20. package/dist/rules/whm401.ts +57 -0
  21. package/dist/rules/whm402.ts +45 -0
  22. package/dist/rules/whm403.ts +45 -0
  23. package/dist/rules/whm404.ts +36 -0
  24. package/dist/rules/whm405.ts +53 -0
  25. package/dist/rules/whm406.ts +34 -0
  26. package/dist/rules/whm407.ts +83 -0
  27. package/dist/rules/whm501.ts +103 -0
  28. package/dist/rules/whm502.ts +94 -0
  29. package/dist/skills/chant-helm-chart-patterns.md +229 -0
  30. package/dist/skills/chant-helm-chart-security-patterns.md +192 -0
  31. package/dist/skills/chant-helm-create-chart.md +211 -0
  32. package/dist/types/index.d.ts +132 -0
  33. package/package.json +34 -0
  34. package/src/codegen/docs-cli.ts +4 -0
  35. package/src/codegen/docs.ts +483 -0
  36. package/src/codegen/generate-cli.ts +28 -0
  37. package/src/codegen/generate.ts +249 -0
  38. package/src/codegen/naming.ts +38 -0
  39. package/src/codegen/package.ts +64 -0
  40. package/src/composites/composites.test.ts +1050 -0
  41. package/src/composites/helm-batch-job.ts +209 -0
  42. package/src/composites/helm-crd-lifecycle.ts +184 -0
  43. package/src/composites/helm-cron-job.ts +177 -0
  44. package/src/composites/helm-daemon-set.ts +169 -0
  45. package/src/composites/helm-external-secret.ts +93 -0
  46. package/src/composites/helm-library.ts +51 -0
  47. package/src/composites/helm-microservice.ts +331 -0
  48. package/src/composites/helm-monitored-service.ts +252 -0
  49. package/src/composites/helm-namespace-env.ts +154 -0
  50. package/src/composites/helm-secure-ingress.ts +114 -0
  51. package/src/composites/helm-stateful-service.ts +213 -0
  52. package/src/composites/helm-web-app.ts +264 -0
  53. package/src/composites/helm-worker.ts +207 -0
  54. package/src/composites/index.ts +38 -0
  55. package/src/coverage.test.ts +21 -0
  56. package/src/coverage.ts +50 -0
  57. package/src/generated/index.d.ts +132 -0
  58. package/src/generated/index.ts +13 -0
  59. package/src/generated/lexicon-helm.json +208 -0
  60. package/src/helpers.test.ts +51 -0
  61. package/src/helpers.ts +100 -0
  62. package/src/import/generator.ts +285 -0
  63. package/src/import/import.test.ts +224 -0
  64. package/src/import/parser.ts +160 -0
  65. package/src/import/template-stripper.ts +123 -0
  66. package/src/index.ts +108 -0
  67. package/src/intrinsics.test.ts +380 -0
  68. package/src/intrinsics.ts +484 -0
  69. package/src/lint/post-synth/helm-helpers.ts +64 -0
  70. package/src/lint/post-synth/post-synth.test.ts +504 -0
  71. package/src/lint/post-synth/whm101.ts +46 -0
  72. package/src/lint/post-synth/whm102.ts +33 -0
  73. package/src/lint/post-synth/whm103.ts +59 -0
  74. package/src/lint/post-synth/whm104.ts +35 -0
  75. package/src/lint/post-synth/whm105.ts +30 -0
  76. package/src/lint/post-synth/whm201.ts +36 -0
  77. package/src/lint/post-synth/whm202.ts +50 -0
  78. package/src/lint/post-synth/whm203.ts +39 -0
  79. package/src/lint/post-synth/whm204.ts +60 -0
  80. package/src/lint/post-synth/whm301.ts +41 -0
  81. package/src/lint/post-synth/whm302.ts +40 -0
  82. package/src/lint/post-synth/whm401.ts +57 -0
  83. package/src/lint/post-synth/whm402.ts +45 -0
  84. package/src/lint/post-synth/whm403.ts +45 -0
  85. package/src/lint/post-synth/whm404.ts +36 -0
  86. package/src/lint/post-synth/whm405.ts +53 -0
  87. package/src/lint/post-synth/whm406.ts +34 -0
  88. package/src/lint/post-synth/whm407.ts +83 -0
  89. package/src/lint/post-synth/whm501.ts +103 -0
  90. package/src/lint/post-synth/whm502.ts +94 -0
  91. package/src/lint/rules/chart-metadata.ts +64 -0
  92. package/src/lint/rules/lint-rules.test.ts +97 -0
  93. package/src/lint/rules/no-hardcoded-image.ts +62 -0
  94. package/src/lint/rules/values-no-secrets.ts +82 -0
  95. package/src/lsp/completions.test.ts +72 -0
  96. package/src/lsp/completions.ts +20 -0
  97. package/src/lsp/hover.test.ts +46 -0
  98. package/src/lsp/hover.ts +46 -0
  99. package/src/package-cli.ts +28 -0
  100. package/src/plugin.test.ts +71 -0
  101. package/src/plugin.ts +206 -0
  102. package/src/resources.ts +77 -0
  103. package/src/serializer.test.ts +930 -0
  104. package/src/serializer.ts +835 -0
  105. package/src/skills/chart-patterns.md +229 -0
  106. package/src/skills/chart-security-patterns.md +192 -0
  107. package/src/skills/create-chart.md +211 -0
  108. package/src/validate-cli.ts +21 -0
  109. package/src/validate.test.ts +37 -0
  110. package/src/validate.ts +36 -0
@@ -0,0 +1,229 @@
1
+ ---
2
+ skill: chant-helm-patterns
3
+ description: Common Helm chart patterns and best practices using chant
4
+ user-invocable: true
5
+ ---
6
+
7
+ # Common Helm Chart Patterns
8
+
9
+ ## Standard Chart Layout
10
+
11
+ A chant Helm project compiles TypeScript into the standard Helm directory structure:
12
+
13
+ ```
14
+ my-chart/
15
+ Chart.yaml ← chart metadata (name, version, apiVersion: v2)
16
+ values.yaml ← default configuration values
17
+ templates/
18
+ deployment.yaml
19
+ service.yaml
20
+ ingress.yaml
21
+ _helpers.tpl ← named templates (fullname, labels, etc.)
22
+ NOTES.txt ← post-install instructions
23
+ charts/ ← subcharts (dependencies)
24
+ ```
25
+
26
+ The source of truth is `src/`. Never edit generated files in `templates/` by hand.
27
+
28
+ ## Parameterization via Values Proxy
29
+
30
+ The `values` proxy turns property access into `{{ .Values.x }}` template directives:
31
+
32
+ ```typescript
33
+ import { values } from "@intentius/chant-lexicon-helm";
34
+
35
+ // Simple access
36
+ values.replicaCount // → {{ .Values.replicaCount }}
37
+
38
+ // Nested access
39
+ values.image.repository // → {{ .Values.image.repository }}
40
+ values.image.tag // → {{ .Values.image.tag }}
41
+
42
+ // Use in resource definitions
43
+ export const deployment = new Deployment({
44
+ spec: {
45
+ replicas: values.replicaCount,
46
+ template: {
47
+ spec: {
48
+ containers: [{
49
+ name: "app",
50
+ image: printf("%s:%s", values.image.repository, values.image.tag),
51
+ }],
52
+ },
53
+ },
54
+ },
55
+ });
56
+ ```
57
+
58
+ Default values are set in the `Values` resource, which becomes `values.yaml`.
59
+
60
+ ## Conditional Resources with If()
61
+
62
+ Wrap resources in `If()` to gate them on a values flag:
63
+
64
+ ```typescript
65
+ import { If, values } from "@intentius/chant-lexicon-helm";
66
+ import { Ingress } from "@intentius/chant-lexicon-k8s";
67
+
68
+ // Only rendered when .Values.ingress.enabled is true
69
+ export const ingress = If(values.ingress.enabled, new Ingress({
70
+ metadata: { name: include("my-app.fullname") },
71
+ spec: {
72
+ rules: [{ host: values.ingress.hostname }],
73
+ },
74
+ }));
75
+ ```
76
+
77
+ `If()` wraps the entire template output in `{{- if .Values.ingress.enabled }}...{{- end }}`.
78
+
79
+ ## Helper Templates with include()
80
+
81
+ Reference named templates from `_helpers.tpl`:
82
+
83
+ ```typescript
84
+ import { include } from "@intentius/chant-lexicon-helm";
85
+
86
+ export const deployment = new Deployment({
87
+ metadata: {
88
+ name: include("my-app.fullname"),
89
+ labels: include("my-app.labels"),
90
+ },
91
+ spec: {
92
+ selector: {
93
+ matchLabels: include("my-app.selectorLabels"),
94
+ },
95
+ },
96
+ });
97
+ ```
98
+
99
+ The composites (`HelmWebApp`, `HelmMicroservice`, etc.) generate `_helpers.tpl` automatically with standard named templates for fullname, labels, and selector labels.
100
+
101
+ ## Dependency Management with HelmDependency
102
+
103
+ Declare subchart dependencies that go into `Chart.yaml`:
104
+
105
+ ```typescript
106
+ import { HelmDependency } from "@intentius/chant-lexicon-helm";
107
+
108
+ export const redisDep = HelmDependency({
109
+ name: "redis",
110
+ version: "17.x",
111
+ repository: "https://charts.bitnami.com/bitnami",
112
+ condition: "redis.enabled",
113
+ });
114
+
115
+ export const postgresqlDep = HelmDependency({
116
+ name: "postgresql",
117
+ version: "12.x",
118
+ repository: "https://charts.bitnami.com/bitnami",
119
+ condition: "postgresql.enabled",
120
+ });
121
+ ```
122
+
123
+ After building, run `helm dependency update` to fetch the subcharts.
124
+
125
+ ## Multi-Environment Configuration
126
+
127
+ Use separate values files per environment and compose them at deploy time:
128
+
129
+ ```typescript
130
+ // src/infra.ts — base chart with parameterized defaults
131
+ export const app = HelmWebApp({
132
+ name: "my-app",
133
+ port: 3000,
134
+ replicas: 1, // default for dev
135
+ imageTag: "latest", // overridden per env
136
+ });
137
+ ```
138
+
139
+ ```bash
140
+ # Deploy with environment-specific overrides
141
+ helm install my-app . -f values.yaml -f values-staging.yaml
142
+ helm install my-app . -f values.yaml -f values-production.yaml
143
+ ```
144
+
145
+ Structure values files as overlays: `values.yaml` (defaults) -> `values-staging.yaml` (overrides) -> `values-production.yaml` (overrides). Later files win.
146
+
147
+ ## Library Charts with HelmLibrary
148
+
149
+ Create reusable chart libraries that other charts can import:
150
+
151
+ ```typescript
152
+ import { HelmLibrary } from "@intentius/chant-lexicon-helm";
153
+
154
+ export const library = HelmLibrary({
155
+ name: "common-templates",
156
+ version: "1.0.0",
157
+ templates: {
158
+ "common.labels": labelsTemplate,
159
+ "common.annotations": annotationsTemplate,
160
+ "common.fullname": fullnameTemplate,
161
+ },
162
+ });
163
+ ```
164
+
165
+ Library charts produce no templates of their own. They are included as dependencies by application charts, which use `include()` to reference the shared named templates.
166
+
167
+ ## Testing Patterns with HelmTest
168
+
169
+ Add Helm test pods that run on `helm test`:
170
+
171
+ ```typescript
172
+ import { HelmTest } from "@intentius/chant-lexicon-helm";
173
+
174
+ export const connectivityTest = HelmTest({
175
+ name: "test-connection",
176
+ image: "busybox:1.36",
177
+ command: ["wget", "--spider", "http://my-app:3000/healthz"],
178
+ });
179
+ ```
180
+
181
+ This generates a pod in `templates/tests/` with the `helm.sh/hook: test` annotation. Run tests after install:
182
+
183
+ ```bash
184
+ helm install my-app .
185
+ helm test my-app
186
+ ```
187
+
188
+ **Lint rule WHM301** fires when an application chart has no test resources.
189
+
190
+ ## Composites Quick Reference
191
+
192
+ | Composite | Resources created |
193
+ |---------------------|-------------------------------------------------------------|
194
+ | `HelmWebApp` | Chart, Values, Deployment, Service, Ingress?, HPA?, SA? |
195
+ | `HelmMicroservice` | Chart, Values, Deployment, Service, SA, ConfigMap?, PDB?, HPA?, Ingress? |
196
+ | `HelmWorker` | Chart, Values, Deployment, SA, HPA?, PDB? |
197
+ | `HelmCronJob` | Chart, Values, CronJob |
198
+ | `HelmStatefulSet` | Chart, Values, StatefulSet, Service |
199
+ | `HelmDaemonSet` | Chart, Values, DaemonSet, SA? |
200
+ | `HelmLibrary` | Chart (type: library), named templates |
201
+
202
+ All composites accept optional `podSecurityContext`, `securityContext`, `nodeSelector`, `tolerations`, `affinity`, and `strategy` fields.
203
+
204
+ ## Built-in Objects
205
+
206
+ Access Helm's built-in objects in templates:
207
+
208
+ ```typescript
209
+ import { Release, ChartRef, Capabilities } from "@intentius/chant-lexicon-helm";
210
+
211
+ Release.Name // {{ .Release.Name }}
212
+ Release.Namespace // {{ .Release.Namespace }}
213
+ Release.IsUpgrade // {{ .Release.IsUpgrade }}
214
+ ChartRef.Name // {{ .Chart.Name }}
215
+ ChartRef.Version // {{ .Chart.Version }}
216
+ ```
217
+
218
+ ## Template Functions
219
+
220
+ ```typescript
221
+ import { include, printf, toYaml, quote, required, helmDefault } from "@intentius/chant-lexicon-helm";
222
+
223
+ include("my-app.fullname") // {{ include "my-app.fullname" . }}
224
+ printf("%s-%s", Release.Name, "worker") // {{ printf "%s-%s" .Release.Name "worker" }}
225
+ toYaml(values.resources, 12) // {{ toYaml .Values.resources | nindent 12 }}
226
+ quote(values.annotations) // {{ quote .Values.annotations }}
227
+ required("image.tag is required", values.image.tag) // {{ required "..." .Values.image.tag }}
228
+ helmDefault(values.replicas, 1) // {{ default 1 .Values.replicas }}
229
+ ```
@@ -0,0 +1,192 @@
1
+ ---
2
+ skill: chant-helm-security
3
+ description: Security best practices for Helm charts built with chant
4
+ user-invocable: true
5
+ ---
6
+
7
+ # Helm Chart Security Patterns
8
+
9
+ ## Pod Security Context
10
+
11
+ Always set pod-level security constraints. The `podSecurityContext` field applies to all containers in the pod.
12
+
13
+ ```typescript
14
+ import { HelmWebApp } from "@intentius/chant-lexicon-helm";
15
+
16
+ const app = HelmWebApp({
17
+ name: "secure-app",
18
+ port: 3000,
19
+ podSecurityContext: {
20
+ runAsNonRoot: true,
21
+ runAsUser: 1000,
22
+ runAsGroup: 1000,
23
+ fsGroup: 1000,
24
+ seccompProfile: { type: "RuntimeDefault" },
25
+ },
26
+ });
27
+ ```
28
+
29
+ **Lint rule WHM402** fires when `runAsNonRoot` is not set on any pod spec.
30
+
31
+ ## Container Security Context
32
+
33
+ Set per-container restrictions to minimize attack surface:
34
+
35
+ ```typescript
36
+ const app = HelmWebApp({
37
+ name: "hardened-app",
38
+ port: 8080,
39
+ securityContext: {
40
+ readOnlyRootFilesystem: true,
41
+ allowPrivilegeEscalation: false,
42
+ capabilities: { drop: ["ALL"] },
43
+ runAsUser: 1000,
44
+ },
45
+ });
46
+ ```
47
+
48
+ - **WHM403**: `readOnlyRootFilesystem` not set
49
+ - **WHM404**: `privileged: true` detected
50
+
51
+ If the application needs to write temporary files, mount a writable `emptyDir` volume at the specific path rather than disabling `readOnlyRootFilesystem`.
52
+
53
+ ## Image Tagging
54
+
55
+ Never use `:latest` or omit the tag entirely. Use semver tags or digests.
56
+
57
+ ```typescript
58
+ // Bad — WHM401 fires
59
+ HelmWebApp({ name: "app", imageTag: "latest", port: 3000 });
60
+
61
+ // Good — pinned semver
62
+ HelmWebApp({ name: "app", imageTag: "v2.4.1", port: 3000 });
63
+
64
+ // Best — pinned digest
65
+ HelmWebApp({ name: "app", imageTag: "sha256:abc123...", port: 3000 });
66
+ ```
67
+
68
+ **Lint rule WHM401** fires when the image uses `:latest` or has no tag at all.
69
+
70
+ ## Secret Management
71
+
72
+ Never inline secrets in `values.yaml`. Use External Secrets Operator or Sealed Secrets.
73
+
74
+ ```typescript
75
+ import { HelmExternalSecret } from "@intentius/chant-lexicon-helm";
76
+
77
+ // External Secrets Operator — fetches secrets from a remote store at runtime
78
+ const secrets = HelmExternalSecret({
79
+ name: "app-secrets",
80
+ secretStoreName: "aws-secretsmanager",
81
+ data: {
82
+ DB_PASSWORD: "prod/db-password",
83
+ API_KEY: "prod/api-key",
84
+ },
85
+ });
86
+ ```
87
+
88
+ **Lint rule WHM407** fires when a Secret resource contains inline `data` or `stringData` values. The fix is always to use an external secret provider.
89
+
90
+ ### What NOT to do
91
+
92
+ ```typescript
93
+ // Bad — WHM407 fires, secret value is in source control
94
+ new Secret({
95
+ metadata: { name: "db-creds" },
96
+ stringData: { password: "hunter2" },
97
+ });
98
+ ```
99
+
100
+ ## RBAC and ServiceAccount
101
+
102
+ Every workload should run under a dedicated ServiceAccount with minimal RBAC:
103
+
104
+ ```typescript
105
+ const app = HelmMicroservice({
106
+ name: "payment-api",
107
+ port: 8080,
108
+ serviceAccount: true, // creates a dedicated SA
109
+ });
110
+ ```
111
+
112
+ When the workload needs API access, bind only the required verbs and resources:
113
+
114
+ ```typescript
115
+ import { Role, RoleBinding } from "@intentius/chant-lexicon-k8s";
116
+
117
+ export const role = new Role({
118
+ metadata: { name: include("payment-api.fullname") },
119
+ rules: [{
120
+ apiGroups: [""],
121
+ resources: ["configmaps"],
122
+ verbs: ["get", "watch"],
123
+ }],
124
+ });
125
+ ```
126
+
127
+ Avoid `ClusterRole` with wildcard resources or verbs. Prefer namespace-scoped `Role` bindings.
128
+
129
+ ## Network Policies
130
+
131
+ Restrict pod-to-pod traffic to only what is required:
132
+
133
+ ```typescript
134
+ import { NetworkPolicy } from "@intentius/chant-lexicon-k8s";
135
+
136
+ export const netpol = new NetworkPolicy({
137
+ metadata: { name: "allow-frontend-to-api" },
138
+ spec: {
139
+ podSelector: { matchLabels: { app: "payment-api" } },
140
+ policyTypes: ["Ingress"],
141
+ ingress: [{
142
+ from: [{ podSelector: { matchLabels: { app: "frontend" } } }],
143
+ ports: [{ protocol: "TCP", port: 8080 }],
144
+ }],
145
+ },
146
+ });
147
+ ```
148
+
149
+ Start with a default-deny policy per namespace, then add allow rules for known traffic patterns.
150
+
151
+ ## Resource Limits and Requests
152
+
153
+ Always set both requests and limits. Without them, a single pod can starve others.
154
+
155
+ ```typescript
156
+ const app = HelmMicroservice({
157
+ name: "api",
158
+ port: 8080,
159
+ // HelmMicroservice sets sensible defaults:
160
+ // requests: { cpu: "250m", memory: "128Mi" }
161
+ // limits: { cpu: "500m", memory: "256Mi" }
162
+ });
163
+ ```
164
+
165
+ **Lint rule WHM405** fires when a container spec is missing `cpu` or `memory` in resources. **WHM302** fires when resource limits are not set at all.
166
+
167
+ ## Security Lint Rules Reference
168
+
169
+ | Rule | What it checks | Severity |
170
+ |--------|---------------------------------------------|----------|
171
+ | WHM401 | Image uses `:latest` tag or no tag | Warning |
172
+ | WHM402 | `runAsNonRoot` not set on pod | Warning |
173
+ | WHM403 | `readOnlyRootFilesystem` not set | Warning |
174
+ | WHM404 | `privileged: true` on a container | Error |
175
+ | WHM405 | Missing cpu/memory in resource spec | Warning |
176
+ | WHM406 | CRD lifecycle limitation (no auto-upgrade) | Info |
177
+ | WHM407 | Secret with inline data in source | Error |
178
+
179
+ ## Checklist
180
+
181
+ When reviewing or building a Helm chart, verify:
182
+
183
+ 1. Pod runs as non-root with a numeric UID (`runAsNonRoot: true`, `runAsUser: 1000`)
184
+ 2. Containers drop all capabilities (`capabilities: { drop: ["ALL"] }`)
185
+ 3. Root filesystem is read-only (`readOnlyRootFilesystem: true`)
186
+ 4. Privilege escalation is blocked (`allowPrivilegeEscalation: false`)
187
+ 5. Images use pinned semver or digest tags, never `:latest`
188
+ 6. Secrets come from an external provider, never inline
189
+ 7. Each workload has a dedicated ServiceAccount
190
+ 8. RBAC is namespace-scoped with minimal verbs
191
+ 9. Network policies restrict ingress/egress to known peers
192
+ 10. CPU and memory requests and limits are set on every container
@@ -0,0 +1,211 @@
1
+ ---
2
+ skill: chant-helm
3
+ description: Build, validate, and deploy Helm charts from a chant project
4
+ user-invocable: true
5
+ ---
6
+
7
+ # Helm Chart Operational Playbook
8
+
9
+ ## How chant and Helm relate
10
+
11
+ chant is a **synthesis-only** tool — it compiles TypeScript source files into a complete Helm chart directory (Chart.yaml, values.yaml, templates/, etc.). chant does NOT call Helm CLI. Your job as an agent is to bridge the two:
12
+
13
+ - Use **chant** for: build, lint, diff (local chart comparison)
14
+ - Use **helm** for: install, upgrade, rollback, test, dependency update
15
+
16
+ The source of truth is the TypeScript in `src/`. The generated chart directory is an intermediate artifact — never edit it by hand.
17
+
18
+ ## Scaffolding a new project
19
+
20
+ ### Initialize with a template
21
+
22
+ ```bash
23
+ chant init --lexicon helm # default: Deployment + Service chart
24
+ ```
25
+
26
+ ### Project structure after init
27
+
28
+ ```
29
+ my-chart/
30
+ src/
31
+ chart.ts ← Chart metadata, Values, K8s resources with Helm intrinsics
32
+ chant.json ← project configuration
33
+ package.json
34
+ ```
35
+
36
+ ## Key concepts
37
+
38
+ ### Values proxy
39
+
40
+ The `values` proxy creates `{{ .Values.x }}` template directives:
41
+
42
+ ```typescript
43
+ import { values } from "@intentius/chant-lexicon-helm";
44
+
45
+ // values.replicaCount → {{ .Values.replicaCount }}
46
+ // values.image.repository → {{ .Values.image.repository }}
47
+ ```
48
+
49
+ ### Template functions
50
+
51
+ ```typescript
52
+ import { include, printf, toYaml, quote, required, helmDefault } from "@intentius/chant-lexicon-helm";
53
+
54
+ include("my-app.fullname") // {{ include "my-app.fullname" . }}
55
+ printf("%s:%s", values.image.repo, values.image.tag) // {{ printf "%s:%s" ... }}
56
+ toYaml(values.resources, 12) // {{ toYaml .Values.resources | nindent 12 }}
57
+ ```
58
+
59
+ ### Conditional resources
60
+
61
+ ```typescript
62
+ import { If, values } from "@intentius/chant-lexicon-helm";
63
+
64
+ // Wrap an entire resource in {{- if .Values.ingress.enabled }}
65
+ export const ingress = If(values.ingress.enabled, new Ingress({ ... }));
66
+ ```
67
+
68
+ ### Built-in objects
69
+
70
+ ```typescript
71
+ import { Release, ChartRef } from "@intentius/chant-lexicon-helm";
72
+
73
+ Release.Name // {{ .Release.Name }}
74
+ Release.Namespace // {{ .Release.Namespace }}
75
+ ChartRef.Version // {{ .Chart.Version }}
76
+ ```
77
+
78
+ ## Build and validate workflow
79
+
80
+ ```bash
81
+ # Build the chart
82
+ chant build
83
+
84
+ # Lint the TypeScript (pre-synth rules)
85
+ chant lint
86
+
87
+ # Validate the generated chart (post-synth checks)
88
+ chant check
89
+
90
+ # Validate with helm CLI
91
+ helm lint dist/
92
+ helm template test dist/
93
+ ```
94
+
95
+ ## Common patterns
96
+
97
+ ### Deployment with parameterized image
98
+
99
+ ```typescript
100
+ export const deployment = new Deployment({
101
+ metadata: {
102
+ name: include("my-app.fullname"),
103
+ labels: include("my-app.labels"),
104
+ },
105
+ spec: {
106
+ replicas: values.replicaCount,
107
+ template: {
108
+ spec: {
109
+ containers: [{
110
+ name: "my-app",
111
+ image: printf("%s:%s", values.image.repository, values.image.tag),
112
+ resources: toYaml(values.resources),
113
+ }],
114
+ },
115
+ },
116
+ },
117
+ });
118
+ ```
119
+
120
+ ### Composites for common patterns
121
+
122
+ ```typescript
123
+ import { HelmWebApp, HelmMicroservice, HelmDaemonSet, HelmWorker } from "@intentius/chant-lexicon-helm";
124
+
125
+ // Quick scaffold: Deployment + Service + Ingress + HPA + ServiceAccount
126
+ const result = HelmWebApp({ name: "my-app", port: 3000, replicas: 3 });
127
+
128
+ // Full microservice: + PDB + ConfigMap + health probes + resource limits
129
+ const msvc = HelmMicroservice({ name: "api", port: 8080 });
130
+
131
+ // DaemonSet for node-level agents (logging, monitoring)
132
+ const agent = HelmDaemonSet({ name: "log-agent", imageRepository: "fluent/fluent-bit" });
133
+
134
+ // Worker for background processors (no Service, exec probes, queue config)
135
+ const worker = HelmWorker({ name: "job-processor", replicas: 4 });
136
+ ```
137
+
138
+ ### Secret management with ExternalSecret
139
+
140
+ ```typescript
141
+ import { HelmExternalSecret } from "@intentius/chant-lexicon-helm";
142
+
143
+ const secrets = HelmExternalSecret({
144
+ name: "app-secrets",
145
+ secretStoreName: "vault",
146
+ data: {
147
+ DB_PASSWORD: "secret/data/db-password",
148
+ API_KEY: "secret/data/api-key",
149
+ },
150
+ });
151
+ ```
152
+
153
+ ### Resource ordering
154
+
155
+ ```typescript
156
+ import { withOrder, argoWave } from "@intentius/chant-lexicon-helm";
157
+
158
+ // Helm hook ordering (lower weight = runs first)
159
+ metadata: { annotations: { ...withOrder(-5) } }
160
+
161
+ // Argo CD sync waves
162
+ metadata: { annotations: { ...argoWave(2) } }
163
+ ```
164
+
165
+ ### CRD lifecycle management
166
+
167
+ ```typescript
168
+ import { HelmCRDLifecycle } from "@intentius/chant-lexicon-helm";
169
+
170
+ // Managed CRD lifecycle via Helm hooks (solves Helm's CRD limitation)
171
+ const lifecycle = HelmCRDLifecycle({
172
+ name: "my-operator",
173
+ crdContent: crdYaml,
174
+ });
175
+ ```
176
+
177
+ ## Lint rules
178
+
179
+ | Rule | Description |
180
+ |------|-------------|
181
+ | WHM001 | Chart must have name, version, apiVersion |
182
+ | WHM002 | Values should not contain bare secrets |
183
+ | WHM003 | Container images should use values references |
184
+ | WHM101 | Chart.yaml has valid apiVersion (v2) |
185
+ | WHM102 | values.schema.json present when Values used |
186
+ | WHM103 | Go template syntax valid (balanced braces) |
187
+ | WHM104 | NOTES.txt exists for application charts |
188
+ | WHM105 | _helpers.tpl exists |
189
+ | WHM201 | Resources have standard Helm labels |
190
+ | WHM301 | At least one test for application charts |
191
+ | WHM302 | Resource limits set |
192
+ | WHM401 | Image uses :latest tag or no tag |
193
+ | WHM402 | runAsNonRoot not set |
194
+ | WHM403 | readOnlyRootFilesystem not set |
195
+ | WHM404 | privileged: true detected |
196
+ | WHM405 | Resource spec missing cpu/memory |
197
+ | WHM406 | CRD lifecycle limitation |
198
+ | WHM407 | Secret with inline data |
199
+ | WHM501 | Unused values keys |
200
+ | WHM502 | Deprecated K8s API versions |
201
+
202
+ ## Troubleshooting
203
+
204
+ ### "apiVersion must be v2"
205
+ Helm 3 requires `apiVersion: v2` in Chart.yaml. Update your Chart metadata.
206
+
207
+ ### "unbalanced template braces"
208
+ A Go template expression has mismatched `{{` / `}}`. Check your intrinsic usage.
209
+
210
+ ### "hardcoded image tag"
211
+ Use `printf("%s:%s", values.image.repository, values.image.tag)` instead of literal strings for container images.
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * CLI entry point for Helm lexicon validation.
4
+ */
5
+
6
+ import { validate } from "./validate";
7
+ import { printValidationResult } from "@intentius/chant/codegen/validate";
8
+
9
+ async function main() {
10
+ const result = await validate();
11
+ printValidationResult(result);
12
+
13
+ if (!result.success) {
14
+ process.exit(1);
15
+ }
16
+ }
17
+
18
+ main().catch((err) => {
19
+ console.error("Validation failed:", err);
20
+ process.exit(1);
21
+ });
@@ -0,0 +1,37 @@
1
+ import { describe, test, expect } from "bun:test";
2
+ import { existsSync } from "fs";
3
+ import { join, dirname } from "path";
4
+ import { fileURLToPath } from "url";
5
+ import { validate } from "./validate";
6
+
7
+ const basePath = dirname(dirname(fileURLToPath(import.meta.url)));
8
+ const generatedDir = join(basePath, "src", "generated");
9
+ const hasGenerated = existsSync(join(generatedDir, "lexicon-helm.json"));
10
+
11
+ describe("validate", () => {
12
+ test("runs validation checks on current artifacts", async () => {
13
+ const result = await validate({ basePath });
14
+ expect(result.checks.length).toBeGreaterThan(0);
15
+ });
16
+
17
+ test.skipIf(!hasGenerated)("checks lexicon JSON exists and parses", async () => {
18
+ const result = await validate({ basePath });
19
+ const jsonCheck = result.checks.find((c) => c.name === "lexicon-json-exists");
20
+ expect(jsonCheck).toBeDefined();
21
+ expect(jsonCheck?.ok).toBe(true);
22
+ });
23
+
24
+ test.skipIf(!hasGenerated)("checks types exist", async () => {
25
+ const result = await validate({ basePath });
26
+ const typesCheck = result.checks.find((c) => c.name === "types-exist");
27
+ expect(typesCheck).toBeDefined();
28
+ expect(typesCheck?.ok).toBe(true);
29
+ });
30
+
31
+ test.skipIf(!hasGenerated)("checks required names are present", async () => {
32
+ const result = await validate({ basePath });
33
+ const requiredCheck = result.checks.find((c) => c.name === "required-names");
34
+ expect(requiredCheck).toBeDefined();
35
+ expect(requiredCheck?.ok).toBe(true);
36
+ });
37
+ });