Skip to content

Helm Concepts

Every exported resource declaration becomes part of the generated Helm chart. The serializer handles the translation from typed TypeScript to Helm chart files automatically.

chant generates a standard Helm chart directory:

Output FileChant TypeDescription
Chart.yamlChartChart metadata (name, version, appVersion, dependencies)
values.yamlValuesDefault configuration values
templates/*.yamlResource constructorsKubernetes manifests with template expressions
templates/NOTES.txtHelmNotesPost-install usage instructions
templates/tests/*.yamlHelmTestHelm test pods
crds/*.yamlHelmCRDCustom Resource Definitions

The values proxy is the core abstraction for referencing .Values.* paths in templates. Instead of writing raw template strings, use typed property access:

import { values, Deployment } from "@intentius/chant-lexicon-helm";
export const deployment = new Deployment({
name: "my-app",
replicas: values.replicaCount,
image: values.image.repository,
tag: values.image.tag,
});

This generates template expressions:

replicas: {{ .Values.replicaCount }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"

The proxy supports arbitrarily deep nesting. Every property access appends to the .Values path, so values.global.ingress.tls.enabled becomes {{ .Values.global.ingress.tls.enabled }}.

Helm provides several built-in objects accessible at render time. The lexicon exposes these as typed references:

import { Release } from "@intentius/chant-lexicon-helm";
const labels = {
"app.kubernetes.io/instance": Release.Name,
"app.kubernetes.io/managed-by": Release.Service,
};
// Generates: {{ .Release.Name }}, {{ .Release.Service }}

Available properties: Release.Name, Release.Namespace, Release.Service, Release.Revision, Release.IsUpgrade, Release.IsInstall.

import { ChartRef } from "@intentius/chant-lexicon-helm";
const labels = {
"helm.sh/chart": ChartRef.Name,
"app.kubernetes.io/version": ChartRef.AppVersion,
};
// Generates: {{ .Chart.Name }}, {{ .Chart.AppVersion }}
import { Capabilities } from "@intentius/chant-lexicon-helm";
// Any property path works — `Capabilities` is a proxy, not a fixed set
const kubeVersion = Capabilities.KubeVersion.Version;
// Generates: {{ .Capabilities.KubeVersion.Version }}

Property access is all the proxy supports; the paths are not calls. For a predicate like APIVersions.Has, drop to a raw template expression:

import { HelmTpl } from "@intentius/chant-lexicon-helm";
const usePDB = new HelmTpl('{{ .Capabilities.APIVersions.Has "policy/v1" }}');
import { Template } from "@intentius/chant-lexicon-helm";
const source = Template.BasePath;
// Generates: {{ .Template.BasePath }}

The lexicon provides typed wrappers for Helm/Sprig template functions:

import { include, required, helmDefault, toYaml, quote } from "@intentius/chant-lexicon-helm";
// Named template inclusion — the second arg is the context string, default "."
const labels = include("my-app.labels");
// Generates: {{ include "my-app.labels" . }}
// Required values with error messages
const image = required("image.repository is required", values.image.repository);
// Generates: {{ required "image.repository is required" .Values.image.repository }}
// Default values — the default comes first, mirroring Sprig's `default`
const pullPolicy = helmDefault("IfNotPresent", values.image.pullPolicy);
// Generates: {{ default "IfNotPresent" .Values.image.pullPolicy }}
// YAML serialization for nested objects
const resources = toYaml(values.resources);
// Generates: {{ toYaml .Values.resources }}
// Pass an indent to get the nindent pipe that block scalars need
const indented = toYaml(values.resources, 12);
// Generates: {{ toYaml .Values.resources | nindent 12 }}
// Quoting string values
const name = quote(values.nameOverride);
// Generates: {{ .Values.nameOverride | quote }}

Use If, ElseIf, Range, and With to control template rendering:

import { If, Range, With, values } from "@intentius/chant-lexicon-helm";
// Conditional block
const ingressBlock = If(values.ingress.enabled, {
apiVersion: "networking.k8s.io/v1",
kind: "Ingress",
metadata: { name: values.ingress.hostname },
});
// Iteration
const envVars = Range(values.env, (item) => ({
name: item.name,
value: item.value,
}));
// Scoped context
const tlsBlock = With(values.ingress.tls, (tls) => ({
secretName: tls.secretName,
hosts: tls.hosts,
}));

Helm hooks run at specific points in the release lifecycle:

import { HelmHook } from "@intentius/chant-lexicon-helm";
export const migration = new HelmHook({
name: "db-migrate",
type: "pre-install,pre-upgrade",
weight: -5,
deletePolicy: "before-hook-creation",
image: values.image.repository,
command: ["migrate", "--target", "latest"],
});

This generates a Job manifest with helm.sh/hook annotations. The weight property controls execution order (lower runs first).

Chart dependencies are declared as properties of Chart:

import { Chart, HelmDependency } from "@intentius/chant-lexicon-helm";
export const chart = new Chart({
name: "my-app",
version: "1.0.0",
dependencies: [
new HelmDependency({
name: "postgresql",
version: "12.x",
repository: "https://charts.bitnami.com/bitnami",
condition: "postgresql.enabled",
}),
new HelmDependency({
name: "redis",
version: "17.x",
repository: "https://charts.bitnami.com/bitnami",
condition: "redis.enabled",
}),
],
});