@intentius/chant-lexicon-helm 0.0.18 → 0.0.24
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/integrity.json +5 -5
- package/dist/manifest.json +1 -1
- package/dist/skills/chant-helm.md +447 -0
- package/package.json +21 -3
- package/src/codegen/docs.test.ts +14 -0
- package/src/codegen/generate.test.ts +71 -0
- package/src/codegen/package.test.ts +36 -0
- package/src/composites/composites.test.ts +116 -110
- package/src/composites/helm-batch-job.ts +33 -19
- package/src/composites/helm-crd-lifecycle.ts +37 -24
- package/src/composites/helm-cron-job.ts +25 -13
- package/src/composites/helm-daemon-set.ts +26 -14
- package/src/composites/helm-external-secret.ts +21 -12
- package/src/composites/helm-library.ts +21 -7
- package/src/composites/helm-microservice.ts +46 -29
- package/src/composites/helm-monitored-service.ts +75 -51
- package/src/composites/helm-namespace-env.ts +80 -52
- package/src/composites/helm-secure-ingress.ts +66 -50
- package/src/composites/helm-stateful-service.ts +29 -16
- package/src/composites/helm-web-app.ts +37 -22
- package/src/composites/helm-worker.ts +34 -20
- package/src/import/roundtrip.test.ts +144 -0
- package/src/lint/post-synth/whm101.test.ts +69 -0
- package/src/lint/post-synth/whm102.test.ts +57 -0
- package/src/lint/post-synth/whm103.test.ts +58 -0
- package/src/lint/post-synth/whm104.test.ts +57 -0
- package/src/lint/post-synth/whm105.test.ts +41 -0
- package/src/lint/post-synth/whm201.test.ts +59 -0
- package/src/lint/post-synth/whm202.test.ts +62 -0
- package/src/lint/post-synth/whm203.test.ts +58 -0
- package/src/lint/post-synth/whm204.test.ts +56 -0
- package/src/lint/post-synth/whm301.test.ts +49 -0
- package/src/lint/post-synth/whm302.test.ts +58 -0
- package/src/lint/post-synth/whm401.test.ts +59 -0
- package/src/lint/post-synth/whm402.test.ts +58 -0
- package/src/lint/post-synth/whm403.test.ts +50 -0
- package/src/lint/post-synth/whm404.test.ts +50 -0
- package/src/lint/post-synth/whm405.test.ts +60 -0
- package/src/lint/post-synth/whm406.test.ts +43 -0
- package/src/lint/post-synth/whm407.test.ts +60 -0
- package/src/lint/post-synth/whm501.test.ts +70 -0
- package/src/lint/post-synth/whm502.test.ts +72 -0
- package/src/lint/rules/chart-metadata.test.ts +45 -0
- package/src/lint/rules/no-hardcoded-image.test.ts +41 -0
- package/src/lint/rules/values-no-secrets.test.ts +48 -0
- package/src/plugin.test.ts +3 -3
- package/src/plugin.ts +190 -19
- package/src/resources.ts +29 -0
- package/src/skills/chant-helm.md +447 -0
- package/dist/skills/chant-helm-create-chart.md +0 -211
- package/src/skills/create-chart.md +0 -211
- /package/dist/skills/{chant-helm-chart-patterns.md → chant-helm-patterns.md} +0 -0
- /package/dist/skills/{chant-helm-chart-security-patterns.md → chant-helm-security.md} +0 -0
- /package/src/skills/{chart-patterns.md → chant-helm-patterns.md} +0 -0
- /package/src/skills/{chart-security-patterns.md → chant-helm-security.md} +0 -0
package/dist/integrity.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"algorithm": "xxhash64",
|
|
3
3
|
"artifacts": {
|
|
4
|
-
"manifest.json": "
|
|
4
|
+
"manifest.json": "2557e415419da552",
|
|
5
5
|
"meta.json": "b7aab243e162dfaf",
|
|
6
6
|
"types/index.d.ts": "e8011da51963f058",
|
|
7
7
|
"rules/no-hardcoded-image.ts": "c75aa9c33016c3b9",
|
|
@@ -28,9 +28,9 @@
|
|
|
28
28
|
"rules/whm405.ts": "ec82442f9120e5e0",
|
|
29
29
|
"rules/whm202.ts": "dbe1cbb3237be84f",
|
|
30
30
|
"rules/whm501.ts": "e9a9f2b4e034a51f",
|
|
31
|
-
"skills/chant-helm
|
|
32
|
-
"skills/chant-helm-
|
|
33
|
-
"skills/chant-helm-
|
|
31
|
+
"skills/chant-helm.md": "8ccf71feddc9536c",
|
|
32
|
+
"skills/chant-helm-patterns.md": "7a2163247f44bf6d",
|
|
33
|
+
"skills/chant-helm-security.md": "a7b950513dac7d37"
|
|
34
34
|
},
|
|
35
|
-
"composite": "
|
|
35
|
+
"composite": "43682c36509928ac"
|
|
36
36
|
}
|
package/dist/manifest.json
CHANGED
|
@@ -0,0 +1,447 @@
|
|
|
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 compiler** — it compiles TypeScript source files into a complete Helm chart directory (Chart.yaml, values.yaml, templates/, etc.). `chant build` does not call the Helm CLI; synthesis is pure and deterministic. Your job as an agent is to bridge synthesis and deployment:
|
|
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
|
+
### Build the chart
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
chant build # synthesize TypeScript → dist/
|
|
84
|
+
chant build --watch # rebuild on source changes
|
|
85
|
+
chant build --output my-chart/ # write to a custom directory
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`chant build` produces a complete Helm chart directory:
|
|
89
|
+
- `dist/Chart.yaml` — chart metadata (name, version, apiVersion v2)
|
|
90
|
+
- `dist/values.yaml` — default values extracted from `values` proxy usage
|
|
91
|
+
- `dist/templates/*.yaml` — rendered K8s manifests with Go template directives
|
|
92
|
+
- `dist/templates/_helpers.tpl` — named templates (fullname, labels, etc.)
|
|
93
|
+
- `dist/templates/NOTES.txt` — post-install instructions
|
|
94
|
+
|
|
95
|
+
### Lint and validate
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# Lint the TypeScript (pre-synth rules)
|
|
99
|
+
chant lint
|
|
100
|
+
|
|
101
|
+
# Validate the generated chart (post-synth checks)
|
|
102
|
+
chant check
|
|
103
|
+
|
|
104
|
+
# Validate with helm CLI
|
|
105
|
+
helm lint dist/
|
|
106
|
+
helm template test dist/
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### What each step catches
|
|
110
|
+
|
|
111
|
+
| Step | Catches | When to run |
|
|
112
|
+
|------|---------|-------------|
|
|
113
|
+
| `chant lint` | Pre-synth: missing chart metadata, bare secrets in values, hardcoded images | Every edit |
|
|
114
|
+
| `chant check` | Post-synth: unbalanced template braces, missing _helpers.tpl, deprecated APIs, security issues | After build |
|
|
115
|
+
| `helm lint dist/` | Chart structure validation, values schema, template rendering errors | Before publish |
|
|
116
|
+
| `helm template test dist/` | Full template rendering with default values — catches nil pointer panics in Go templates | Before publish |
|
|
117
|
+
| `helm template test dist/ -f values-prod.yaml` | Template rendering with environment-specific overrides | Before deploy |
|
|
118
|
+
|
|
119
|
+
## Common patterns
|
|
120
|
+
|
|
121
|
+
### Deployment with parameterized image
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
export const deployment = new Deployment({
|
|
125
|
+
metadata: {
|
|
126
|
+
name: include("my-app.fullname"),
|
|
127
|
+
labels: include("my-app.labels"),
|
|
128
|
+
},
|
|
129
|
+
spec: {
|
|
130
|
+
replicas: values.replicaCount,
|
|
131
|
+
template: {
|
|
132
|
+
spec: {
|
|
133
|
+
containers: [{
|
|
134
|
+
name: "my-app",
|
|
135
|
+
image: printf("%s:%s", values.image.repository, values.image.tag),
|
|
136
|
+
resources: toYaml(values.resources),
|
|
137
|
+
}],
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Composites for common patterns
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
import { HelmWebApp, HelmMicroservice, HelmDaemonSet, HelmWorker } from "@intentius/chant-lexicon-helm";
|
|
148
|
+
|
|
149
|
+
// Quick scaffold: Deployment + Service + Ingress + HPA + ServiceAccount
|
|
150
|
+
const result = HelmWebApp({ name: "my-app", port: 3000, replicas: 3 });
|
|
151
|
+
|
|
152
|
+
// Full microservice: + PDB + ConfigMap + health probes + resource limits
|
|
153
|
+
const msvc = HelmMicroservice({ name: "api", port: 8080 });
|
|
154
|
+
|
|
155
|
+
// DaemonSet for node-level agents (logging, monitoring)
|
|
156
|
+
const agent = HelmDaemonSet({ name: "log-agent", imageRepository: "fluent/fluent-bit" });
|
|
157
|
+
|
|
158
|
+
// Worker for background processors (no Service, exec probes, queue config)
|
|
159
|
+
const worker = HelmWorker({ name: "job-processor", replicas: 4 });
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Composites reference
|
|
163
|
+
|
|
164
|
+
| Need | Composite | Resources |
|
|
165
|
+
|------|-----------|-----------|
|
|
166
|
+
| Stateless web app | **HelmWebApp** | Deployment + Service + optional Ingress + HPA + ServiceAccount |
|
|
167
|
+
| Production microservice | **HelmMicroservice** | Deployment + Service + HPA + PDB + ConfigMap + probes + limits |
|
|
168
|
+
| Stateful database/cache | **HelmStatefulService** | StatefulSet + headless Service + PVC + optional PDB |
|
|
169
|
+
| Background workers | **HelmWorker** | Deployment + optional HPA (no Service, exec probes) |
|
|
170
|
+
| Node-level agent | **HelmDaemonSet** | DaemonSet + ServiceAccount + RBAC |
|
|
171
|
+
| Scheduled jobs | **HelmCronJob** | CronJob + ServiceAccount + RBAC |
|
|
172
|
+
| One-shot batch jobs | **HelmBatchJob** | Job + ServiceAccount + optional RBAC |
|
|
173
|
+
| App with Prometheus monitoring | **HelmMonitoredService** | Deployment + Service + ServiceMonitor + optional PrometheusRule |
|
|
174
|
+
| TLS Ingress (cert-manager) | **HelmSecureIngress** | Ingress + optional Certificate |
|
|
175
|
+
| External secrets (Vault, AWS SM) | **HelmExternalSecret** | ExternalSecret + SecretStore ref |
|
|
176
|
+
| CRD lifecycle hooks | **HelmCRDLifecycle** | pre-install/upgrade Job for CRD apply |
|
|
177
|
+
| Library chart (no templates) | **HelmLibrary** | Chart.yaml with `type: library` + _helpers.tpl |
|
|
178
|
+
| Namespace with quotas | **HelmNamespaceEnv** | Namespace + ResourceQuota + LimitRange + NetworkPolicy |
|
|
179
|
+
|
|
180
|
+
### Secret management with ExternalSecret
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
import { HelmExternalSecret } from "@intentius/chant-lexicon-helm";
|
|
184
|
+
|
|
185
|
+
const secrets = HelmExternalSecret({
|
|
186
|
+
name: "app-secrets",
|
|
187
|
+
secretStoreName: "vault",
|
|
188
|
+
data: {
|
|
189
|
+
DB_PASSWORD: "secret/data/db-password",
|
|
190
|
+
API_KEY: "secret/data/api-key",
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Resource ordering
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
import { withOrder, argoWave } from "@intentius/chant-lexicon-helm";
|
|
199
|
+
|
|
200
|
+
// Helm hook ordering (lower weight = runs first)
|
|
201
|
+
metadata: { annotations: { ...withOrder(-5) } }
|
|
202
|
+
|
|
203
|
+
// Argo CD sync waves
|
|
204
|
+
metadata: { annotations: { ...argoWave(2) } }
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### CRD lifecycle management
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
import { HelmCRDLifecycle } from "@intentius/chant-lexicon-helm";
|
|
211
|
+
|
|
212
|
+
// Managed CRD lifecycle via Helm hooks (solves Helm's CRD limitation)
|
|
213
|
+
const lifecycle = HelmCRDLifecycle({
|
|
214
|
+
name: "my-operator",
|
|
215
|
+
crdContent: crdYaml,
|
|
216
|
+
});
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Lint rules
|
|
220
|
+
|
|
221
|
+
| Rule | Description |
|
|
222
|
+
|------|-------------|
|
|
223
|
+
| WHM001 | Chart must have name, version, apiVersion |
|
|
224
|
+
| WHM002 | Values should not contain bare secrets |
|
|
225
|
+
| WHM003 | Container images should use values references |
|
|
226
|
+
| WHM101 | Chart.yaml has valid apiVersion (v2) |
|
|
227
|
+
| WHM102 | values.schema.json present when Values used |
|
|
228
|
+
| WHM103 | Go template syntax valid (balanced braces) |
|
|
229
|
+
| WHM104 | NOTES.txt exists for application charts |
|
|
230
|
+
| WHM105 | _helpers.tpl exists |
|
|
231
|
+
| WHM201 | Resources have standard Helm labels |
|
|
232
|
+
| WHM301 | At least one test for application charts |
|
|
233
|
+
| WHM302 | Resource limits set |
|
|
234
|
+
| WHM401 | Image uses :latest tag or no tag |
|
|
235
|
+
| WHM402 | runAsNonRoot not set |
|
|
236
|
+
| WHM403 | readOnlyRootFilesystem not set |
|
|
237
|
+
| WHM404 | privileged: true detected |
|
|
238
|
+
| WHM405 | Resource spec missing cpu/memory |
|
|
239
|
+
| WHM406 | CRD lifecycle limitation |
|
|
240
|
+
| WHM407 | Secret with inline data |
|
|
241
|
+
| WHM501 | Unused values keys |
|
|
242
|
+
| WHM502 | Deprecated K8s API versions |
|
|
243
|
+
|
|
244
|
+
## OCI registry workflow
|
|
245
|
+
|
|
246
|
+
Helm 3.8+ supports OCI registries as first-class chart repositories.
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
# Login to an OCI registry
|
|
250
|
+
helm registry login ghcr.io -u <user>
|
|
251
|
+
helm registry login <account>.dkr.ecr.<region>.amazonaws.com --username AWS --password $(aws ecr get-login-password)
|
|
252
|
+
|
|
253
|
+
# Package the chart
|
|
254
|
+
helm package dist/
|
|
255
|
+
|
|
256
|
+
# Push to OCI registry
|
|
257
|
+
helm push my-app-1.0.0.tgz oci://ghcr.io/my-org/charts
|
|
258
|
+
helm push my-app-1.0.0.tgz oci://<account>.dkr.ecr.<region>.amazonaws.com/charts
|
|
259
|
+
|
|
260
|
+
# Pull from OCI registry
|
|
261
|
+
helm pull oci://ghcr.io/my-org/charts/my-app --version 1.0.0
|
|
262
|
+
|
|
263
|
+
# Install directly from OCI
|
|
264
|
+
helm install my-app oci://ghcr.io/my-org/charts/my-app --version 1.0.0
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Chart testing
|
|
268
|
+
|
|
269
|
+
### helm test
|
|
270
|
+
|
|
271
|
+
Helm tests are pods defined in `templates/tests/` with the `helm.sh/hook: test` annotation. They run after install/upgrade.
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
helm install my-app dist/
|
|
275
|
+
helm test my-app # run test pods
|
|
276
|
+
helm test my-app --logs # show test pod logs
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### chart-testing (ct)
|
|
280
|
+
|
|
281
|
+
The `ct` tool validates charts against a set of rules and optionally installs them in a Kind cluster.
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
# Lint all changed charts
|
|
285
|
+
ct lint --charts dist/
|
|
286
|
+
|
|
287
|
+
# Lint + install (requires a running cluster)
|
|
288
|
+
ct install --charts dist/
|
|
289
|
+
|
|
290
|
+
# CI-oriented: lint and install only changed charts
|
|
291
|
+
ct lint-and-install --chart-dirs dist/
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Deploy lifecycle
|
|
295
|
+
|
|
296
|
+
### Install a release
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
# Build the chart first
|
|
300
|
+
chant build
|
|
301
|
+
|
|
302
|
+
# Install with default values
|
|
303
|
+
helm install my-app dist/
|
|
304
|
+
|
|
305
|
+
# Install with overrides
|
|
306
|
+
helm install my-app dist/ -f values-prod.yaml
|
|
307
|
+
helm install my-app dist/ --set replicaCount=3 --set image.tag=v2.0.0
|
|
308
|
+
|
|
309
|
+
# Install in a specific namespace (create if missing)
|
|
310
|
+
helm install my-app dist/ -n production --create-namespace
|
|
311
|
+
|
|
312
|
+
# Dry run (renders templates, validates against cluster)
|
|
313
|
+
helm install my-app dist/ --dry-run
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Upgrade a release
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
# Upgrade with new chart
|
|
320
|
+
chant build
|
|
321
|
+
helm upgrade my-app dist/
|
|
322
|
+
|
|
323
|
+
# Upgrade with value overrides
|
|
324
|
+
helm upgrade my-app dist/ -f values-prod.yaml --set image.tag=v2.1.0
|
|
325
|
+
|
|
326
|
+
# Atomic upgrade (auto-rollback on failure)
|
|
327
|
+
helm upgrade my-app dist/ --atomic --timeout 5m
|
|
328
|
+
|
|
329
|
+
# Install or upgrade (idempotent)
|
|
330
|
+
helm upgrade --install my-app dist/ -f values-prod.yaml
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Rollback a release
|
|
334
|
+
|
|
335
|
+
```bash
|
|
336
|
+
# View release history
|
|
337
|
+
helm history my-app
|
|
338
|
+
|
|
339
|
+
# Rollback to previous revision
|
|
340
|
+
helm rollback my-app
|
|
341
|
+
|
|
342
|
+
# Rollback to a specific revision
|
|
343
|
+
helm rollback my-app 3
|
|
344
|
+
|
|
345
|
+
# Rollback with timeout
|
|
346
|
+
helm rollback my-app 3 --timeout 5m
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### Uninstall
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
helm uninstall my-app
|
|
353
|
+
helm uninstall my-app --keep-history # preserve history for rollback
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
## Troubleshooting
|
|
357
|
+
|
|
358
|
+
### Common errors
|
|
359
|
+
|
|
360
|
+
#### "apiVersion must be v2"
|
|
361
|
+
Helm 3 requires `apiVersion: v2` in Chart.yaml. Update your Chart metadata.
|
|
362
|
+
|
|
363
|
+
#### "unbalanced template braces"
|
|
364
|
+
A Go template expression has mismatched `{{` / `}}`. Check your intrinsic usage.
|
|
365
|
+
|
|
366
|
+
#### "hardcoded image tag"
|
|
367
|
+
Use `printf("%s:%s", values.image.repository, values.image.tag)` instead of literal strings for container images.
|
|
368
|
+
|
|
369
|
+
#### "UPGRADE FAILED: another operation is in progress"
|
|
370
|
+
A previous install/upgrade was interrupted. Fix with:
|
|
371
|
+
```bash
|
|
372
|
+
helm history my-app
|
|
373
|
+
helm rollback my-app <last-good-revision>
|
|
374
|
+
```
|
|
375
|
+
If stuck in `pending-install`, uninstall and reinstall:
|
|
376
|
+
```bash
|
|
377
|
+
helm uninstall my-app
|
|
378
|
+
helm install my-app dist/
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
#### "rendered manifests contain a resource that already exists"
|
|
382
|
+
Another release or kubectl owns the resource. Add `--force` or use `kubectl annotate` to transfer ownership:
|
|
383
|
+
```bash
|
|
384
|
+
kubectl annotate <resource> meta.helm.sh/release-name=my-app --overwrite
|
|
385
|
+
kubectl annotate <resource> meta.helm.sh/release-namespace=default --overwrite
|
|
386
|
+
kubectl label <resource> app.kubernetes.io/managed-by=Helm --overwrite
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Troubleshooting decision tree
|
|
390
|
+
|
|
391
|
+
```
|
|
392
|
+
helm install/upgrade failed?
|
|
393
|
+
├─ "INSTALLATION FAILED: cannot re-use a name"
|
|
394
|
+
│ └─ Release name in use → helm uninstall <name> or pick a new name
|
|
395
|
+
├─ "UPGRADE FAILED: another operation is in progress"
|
|
396
|
+
│ └─ Stuck release → helm rollback to last good revision
|
|
397
|
+
├─ "rendered manifests contain a resource that already exists"
|
|
398
|
+
│ └─ Ownership conflict → annotate existing resources with Helm metadata
|
|
399
|
+
├─ Template rendering error
|
|
400
|
+
│ ├─ "nil pointer evaluating interface"
|
|
401
|
+
│ │ └─ Missing values → add defaults in values.yaml or guard with {{- if }}
|
|
402
|
+
│ ├─ "function X not defined"
|
|
403
|
+
│ │ └─ Missing _helpers.tpl → ensure include() references defined templates
|
|
404
|
+
│ └─ "wrong type for value"
|
|
405
|
+
│ └─ Type mismatch → check values.yaml types match template expectations
|
|
406
|
+
├─ Pods not starting after deploy
|
|
407
|
+
│ └─ See kubectl debugging: kubectl get pods, describe, logs --previous
|
|
408
|
+
└─ Helm test failing
|
|
409
|
+
├─ Test pod CrashLoopBackOff → check test script, endpoint connectivity
|
|
410
|
+
└─ Timeout → increase --timeout or fix service readiness
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
## Quick reference
|
|
414
|
+
|
|
415
|
+
```bash
|
|
416
|
+
# Build
|
|
417
|
+
chant build # synthesize chart
|
|
418
|
+
chant lint # pre-synth lint
|
|
419
|
+
chant check # post-synth checks
|
|
420
|
+
|
|
421
|
+
# Validate
|
|
422
|
+
helm lint dist/ # chart structure
|
|
423
|
+
helm template test dist/ # render templates
|
|
424
|
+
helm template test dist/ -f values-prod.yaml # render with overrides
|
|
425
|
+
|
|
426
|
+
# Deploy
|
|
427
|
+
helm install my-app dist/ # install
|
|
428
|
+
helm upgrade my-app dist/ # upgrade
|
|
429
|
+
helm upgrade --install my-app dist/ # idempotent install/upgrade
|
|
430
|
+
helm rollback my-app # rollback to previous
|
|
431
|
+
|
|
432
|
+
# Inspect
|
|
433
|
+
helm list # list releases
|
|
434
|
+
helm history my-app # release history
|
|
435
|
+
helm get values my-app # current values
|
|
436
|
+
helm get manifest my-app # current manifests
|
|
437
|
+
|
|
438
|
+
# Test
|
|
439
|
+
helm test my-app --logs # run chart tests
|
|
440
|
+
|
|
441
|
+
# Package and publish
|
|
442
|
+
helm package dist/ # create .tgz
|
|
443
|
+
helm push my-app-1.0.0.tgz oci://registry/charts # push to OCI
|
|
444
|
+
|
|
445
|
+
# Cleanup
|
|
446
|
+
helm uninstall my-app # remove release
|
|
447
|
+
```
|
package/package.json
CHANGED
|
@@ -1,7 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/chant-lexicon-helm",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.24",
|
|
4
|
+
"description": "Helm chart lexicon for chant — declarative IaC in TypeScript",
|
|
4
5
|
"license": "Apache-2.0",
|
|
6
|
+
"homepage": "https://intentius.io/chant",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/intentius/chant.git",
|
|
10
|
+
"directory": "lexicons/helm"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/intentius/chant/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"infrastructure-as-code",
|
|
17
|
+
"iac",
|
|
18
|
+
"typescript",
|
|
19
|
+
"helm",
|
|
20
|
+
"kubernetes",
|
|
21
|
+
"chant"
|
|
22
|
+
],
|
|
5
23
|
"type": "module",
|
|
6
24
|
"files": [
|
|
7
25
|
"src/",
|
|
@@ -25,8 +43,8 @@
|
|
|
25
43
|
"prepack": "bun run generate && bun run bundle && bun run validate"
|
|
26
44
|
},
|
|
27
45
|
"dependencies": {
|
|
28
|
-
"@intentius/chant": "0.0.
|
|
29
|
-
"@intentius/chant-lexicon-k8s": "0.0.
|
|
46
|
+
"@intentius/chant": "0.0.22",
|
|
47
|
+
"@intentius/chant-lexicon-k8s": "0.0.22"
|
|
30
48
|
},
|
|
31
49
|
"devDependencies": {
|
|
32
50
|
"typescript": "^5.9.3"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
|
|
3
|
+
describe("Helm docs generation", () => {
|
|
4
|
+
test("docs module is importable", async () => {
|
|
5
|
+
const mod = await import("./docs");
|
|
6
|
+
expect(mod.generateDocs).toBeFunction();
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
test("generateDocs function signature accepts options", async () => {
|
|
10
|
+
const mod = await import("./docs");
|
|
11
|
+
// Verify the function exists and has the expected shape
|
|
12
|
+
expect(typeof mod.generateDocs).toBe("function");
|
|
13
|
+
});
|
|
14
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import { generate } from "./generate";
|
|
3
|
+
|
|
4
|
+
describe("Helm generate pipeline", () => {
|
|
5
|
+
test("generate module is importable", async () => {
|
|
6
|
+
const mod = await import("./generate");
|
|
7
|
+
expect(mod.generate).toBeFunction();
|
|
8
|
+
expect(mod.writeGeneratedFiles).toBeFunction();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test("generates lexicon JSON, types, and index", async () => {
|
|
12
|
+
const result = await generate();
|
|
13
|
+
expect(result.lexiconJSON).toBeTruthy();
|
|
14
|
+
expect(result.typesDTS).toBeTruthy();
|
|
15
|
+
expect(result.indexTS).toBeTruthy();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("lexicon JSON contains known resource types", async () => {
|
|
19
|
+
const result = await generate();
|
|
20
|
+
const registry = JSON.parse(result.lexiconJSON);
|
|
21
|
+
expect(registry.Chart).toBeDefined();
|
|
22
|
+
expect(registry.Chart.resourceType).toBe("Helm::Chart");
|
|
23
|
+
expect(registry.Chart.kind).toBe("resource");
|
|
24
|
+
expect(registry.Values).toBeDefined();
|
|
25
|
+
expect(registry.HelmHook).toBeDefined();
|
|
26
|
+
expect(registry.HelmHook.kind).toBe("property");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("types DTS contains class declarations", async () => {
|
|
30
|
+
const result = await generate();
|
|
31
|
+
expect(result.typesDTS).toContain("export interface ChartProps");
|
|
32
|
+
expect(result.typesDTS).toContain("export declare const Chart");
|
|
33
|
+
expect(result.typesDTS).toContain("export interface ValuesProps");
|
|
34
|
+
expect(result.typesDTS).toContain("export declare const Values");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("runtime index contains createResource calls", async () => {
|
|
38
|
+
const result = await generate();
|
|
39
|
+
expect(result.indexTS).toContain('createResource("Helm::Chart"');
|
|
40
|
+
expect(result.indexTS).toContain('createResource("Helm::Values"');
|
|
41
|
+
expect(result.indexTS).toContain('createProperty("Helm::Hook"');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("resource and property counts are correct", async () => {
|
|
45
|
+
const result = await generate();
|
|
46
|
+
expect(result.resources).toBeGreaterThan(0);
|
|
47
|
+
expect(result.properties).toBeGreaterThan(0);
|
|
48
|
+
expect(result.enums).toBe(0);
|
|
49
|
+
expect(result.warnings).toHaveLength(0);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("lexicon JSON contains all Helm types", async () => {
|
|
53
|
+
const result = await generate();
|
|
54
|
+
const registry = JSON.parse(result.lexiconJSON);
|
|
55
|
+
expect(registry.HelmTest).toBeDefined();
|
|
56
|
+
expect(registry.HelmNotes).toBeDefined();
|
|
57
|
+
expect(registry.HelmDependency).toBeDefined();
|
|
58
|
+
expect(registry.HelmMaintainer).toBeDefined();
|
|
59
|
+
expect(registry.HelmCRD).toBeDefined();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("Chart props include required fields", async () => {
|
|
63
|
+
const result = await generate();
|
|
64
|
+
const registry = JSON.parse(result.lexiconJSON);
|
|
65
|
+
const chartProps = registry.Chart.props;
|
|
66
|
+
expect(chartProps.apiVersion).toBeDefined();
|
|
67
|
+
expect(chartProps.apiVersion.required).toBe(true);
|
|
68
|
+
expect(chartProps.name.required).toBe(true);
|
|
69
|
+
expect(chartProps.version.required).toBe(true);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import { packageLexicon } from "./package";
|
|
3
|
+
|
|
4
|
+
describe("Helm package pipeline", () => {
|
|
5
|
+
test("packageLexicon is importable", async () => {
|
|
6
|
+
const mod = await import("./package");
|
|
7
|
+
expect(mod.packageLexicon).toBeFunction();
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test("packageLexicon returns a valid result", async () => {
|
|
11
|
+
const result = await packageLexicon({ verbose: false });
|
|
12
|
+
expect(result).toBeDefined();
|
|
13
|
+
expect(result.spec).toBeDefined();
|
|
14
|
+
expect(result.spec.manifest).toBeDefined();
|
|
15
|
+
expect(result.spec.manifest.name).toBe("helm");
|
|
16
|
+
expect(result.spec.manifest.namespace).toBe("Helm");
|
|
17
|
+
expect(result.spec.manifest.chantVersion).toBeTruthy();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("manifest contains intrinsics", async () => {
|
|
21
|
+
const result = await packageLexicon();
|
|
22
|
+
const intrinsics = result.spec.manifest.intrinsics;
|
|
23
|
+
expect(intrinsics).toBeDefined();
|
|
24
|
+
expect(intrinsics!.length).toBeGreaterThan(0);
|
|
25
|
+
const names = intrinsics!.map((i: { name: string }) => i.name);
|
|
26
|
+
expect(names).toContain("values");
|
|
27
|
+
expect(names).toContain("Release");
|
|
28
|
+
expect(names).toContain("include");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("stats have resource and property counts", async () => {
|
|
32
|
+
const result = await packageLexicon();
|
|
33
|
+
expect(result.stats.resources).toBeGreaterThan(0);
|
|
34
|
+
expect(result.stats.properties).toBeGreaterThan(0);
|
|
35
|
+
});
|
|
36
|
+
});
|