Migrating to Pinned Installs
Show the whole prompt
Read https://intentius.io/chant/lexicons/helm/migration/.
Move the unpinned release I name onto a pinned render: render it with `HelmRender` under the release name it already runs under, persist the render, and change the `helmInstall` call to `{ name, contentDigest, renderStoreRoot }` with `chart`, `chartVersion`, `values` and `set` removed.
Run `npx chant build` and `npx chant lint` until both are clean, and open a pull request with the change and the content digest.
Print the deploy command and the `helm history <release>` check for a person to run; do not run them.
Never deploy to a real cloud account or shared environment (with `chant run`, `chant components` or a platform's own CLI), approve (a pull request review, `chant approve` or `chant run approve`), override a policy denial (`chant approve policy-override`), or merge; never cut or publish a release (`chant release cut --yes` or `chant release publish`), push or delete a `ci/*` tag (`chant ci tick` pushes them), or change the value of an existing flag; never touch `.chant/allowed_signers` or `chant/lifecycle`.Optional: hand this page to your coding agent
Read https://intentius.io/chant/lexicons/helm/migration/.Move the unpinned release I name onto a pinned render: render it with `HelmRender` under the release name it already runs under, persist the render, and change the `helmInstall` call to `{ name, contentDigest, renderStoreRoot }` with `chart`, `chartVersion`, `values` and `set` removed.Run `npx chant build` and `npx chant lint` until both are clean, and open a pull request with the change and the content digest.Print the deploy command and the `helm history <release>` check for a person to run; do not run them.Never deploy to a real cloud account or shared environment (with `chant run`, `chant components` or a platform's own CLI), approve (a pull request review, `chant approve` or `chant run approve`), override a policy denial (`chant approve policy-override`), or merge; never cut or publish a release (`chant release cut --yes` or `chant release publish`), push or delete a `ci/*` tag (`chant ci tick` pushes them), or change the value of an existing flag; never touch `.chant/allowed_signers` or `chant/lifecycle`.A release deployed by the unpinned path can move onto a pinned render with an ordinary helm upgrade --install, the same command the pinned path already runs. The release upgrades in place, with no uninstall and no recreated resources.
The one rule
Section titled “The one rule”Deploy the pinned render under the same release name the unpinned release already runs under.
// Was:await helmInstall({ name: "web", chart: "./chart", chartVersion: "1.2.0" });
// Migrates to:await helmInstall({ name: "web", contentDigest, renderStoreRoot });helm upgrade --install targets a release by name. When a release named web already exists, it upgrades that release in place: same object, next revision, helm history web continuous across the switch. The release already exists, so this is just its next revision.
The wrapper chart the pinned path materializes keeps the source chart’s name and version (RoutedRender.chart / chartVersion; see Helm Concepts), so helm history shows the same chart throughout.
Why a new release name fails
Section titled “Why a new release name fails”Installing the pinned render under a different name looks like a safer, more explicit migration: a fresh helm install web-v2 <wrapper> next to the existing web. It fails instead, with an ownership-metadata error: the resources web already owns (their meta.helm.sh/release-name / meta.helm.sh/release-namespace annotations) belong to web, and helm refuses to let a second release claim them.
helmInstall refuses this earlier and offline, before any helm command runs. A contentDigest is rendered for exactly one release name (RenderManifest.releaseName; .Release.Name is baked into the recorded bytes, labels and resource names included), and a deploy that targets a different name throws PinnedInstallInputError naming both:
pinned install refused: render sha256:... was rendered for release "web" but the deploytargets release "other". `.Release.Name` is baked into the recorded bytes (labels, resourcenames), so installing them under another name deploys bytes that claim a different release.Deploy under "web", or render for "other". Nothing was deployed.Render the migration target under the release’s own name (HelmRender with releaseName: "web"), then deploy that digest. Never deploy a digest rendered for a different name.
What stays, what changes
Section titled “What stays, what changes”- CRDs stay untouched. Helm applies
crds/only onhelm install, never onhelm upgrade(see Best Practices). Migrating an existing release is an upgrade of that release, so its already-installed CRDs (including ones a subchart shipped) are left exactly as they are. The wrapper still carries them incrds/, uninstall-safe, for the release’s next fresh install. - Hooks still fire. Hook documents route separately from the main manifest but ship through the same wrapper templates, so helm registers and runs them on the upgrade exactly as it would for any other revision.
- Unrelated resources are unchanged. A ConfigMap (or any other resource) whose rendered bytes did not change between the unpinned and pinned renders reaches the cluster byte-identical; the pinned path installs the render store’s recorded bytes verbatim, never re-templating them.
- The revision advances by one, same as any other
helm upgrade. Nothing is deleted and recreated to make the switch.
Checklist
Section titled “Checklist”- Render the chart for pinning with
HelmRender, passing the exactreleaseNamethe live release already runs under. - Persist the render and note its
contentDigest. - Deploy with
helmInstall({ name: <same release name>, contentDigest, ... }). Dropchart,chartVersion,valuesandset, which the pinned path refuses alongsidecontentDigest. - Confirm with
helm history <release>: the revision after the deploy is one more than before, same chart name, no gap.