Status (updated)
Implementation is up in #15218, with an end-to-end verification repo (runs the branch against real OCI charts across all strategies) and a companion docs PR github/docs#44584. The three "Open questions" in the original proposal below are addressed in the PR (per-ecosystem Helm::Requirement + RequirementsUpdater, used explicitly rather than globally registered; OCI resolution verified end-to-end).
One design decision is now in front of maintainers (details here): the original proposal below called increase "today's exact-pin behavior," but those turn out to be different things — helm's current exact-pin (^1.0.0 → 1.0.5) diverges from the documented, operator-preserving increase (^1.0.0 → ^1.0.5). Whether the unset default should change for existing users is the open call. Prior art (Helm's own best-practices docs, Renovate, and every other Dependabot ecosystem) favors operator-preserving, but the backward-compat decision is the maintainers'. Original proposal preserved below for context.
Summary
The helm ecosystem always rewrites Chart.yaml dependency versions to the exact resolved version, ignoring the authored constraint. There's no way to preserve a range like ^2.0.0 and only bump it when the new version falls outside it.
This proposes wiring versioning-strategy into helm as it works for the npm-style ecosystems (e.g. npm, Bundler, pip, Composer): increase, increase-if-necessary, and widen. (See the Status note above: increase follows the documented operator-preserving semantics, which differs from helm's current exact-pin — whether the unset default should change is the open decision.)
Motivation
Exact-pin rewriting is fine when the same team authors and consumes a chart. It breaks down for shared internal charts: one platform team owns a base chart, many product repos consume it via Chart.yaml. Every patch the producer ships opens N PRs across N repos owned by the people with the least context on the change — so PRs sit stale (CVE fixes included), get rubber-stamped, or the producer does drive-by merges in other teams' repos.
Semver ranges exist to prevent exactly this: ^2.0.0 is the producer asserting "anything in 2.x is non-breaking." Range-preserving updates honor that:
- in-range patch/minor → no PR; resolved on the next
helm dependency update.
- out-of-range (major) → PR opens — the producer's explicit "re-validate the integration" signal, and that review belongs on the consumer side.
Example
dependencies:
- name: hr-cronjob
version: ^1.0.0
- Producer ships
1.0.5 → satisfied by ^1.0.0 → no PR (today: one PR per consumer repo, every patch).
- Producer ships
2.0.0 → not satisfied → PR bumps the constraint to ^2.0.0.
Prior art
Renovate's rangeStrategy has done range-preserving Helm updates since 2019 (replace ≈ increase-if-necessary, widen ≈ widen). Closing this gap removes a common reason to reach for Renovate.
Approach
- Honor the
requirements_update_strategy already passed to the update checker (the versioning-strategy → strategy mapping happens upstream of core).
- Skip the update when the strategy is range-preserving and the new version already satisfies the constraint; otherwise rewrite the constraint per strategy instead of pinning.
- One correctness note: Helm constraints are SemVer ranges (
^, ~, ||), but helm currently registers a Gem-style requirement class and treats versions as opaque exact strings — so this needs a SemVer-aware requirement parser, modeled on npm's.
lockfile-only is N/A (Chart.lock is generated, not authored).
- Docs follow-up: add
helm to the versioning-strategy reference, which omits it today.
Happy to put up a draft PR once there's directional agreement.
Open questions
- Any architectural reason
helm rewrites exact versions rather than reusing the shared versioning-strategy plumbing — i.e. is there a shared abstraction I should plug into rather than mirror per-ecosystem?
- Preference on duplicating the requirements-updater per-ecosystem (as today) vs. extracting a shared helper as part of this?
- Any concern about interaction with the existing OCI registry resolution path?
Summary
The
helmecosystem always rewritesChart.yamldependency versions to the exact resolved version, ignoring the authored constraint. There's no way to preserve a range like^2.0.0and only bump it when the new version falls outside it.This proposes wiring
versioning-strategyintohelmas it works for the npm-style ecosystems (e.g. npm, Bundler, pip, Composer):increase,increase-if-necessary, andwiden. (See the Status note above:increasefollows the documented operator-preserving semantics, which differs from helm's current exact-pin — whether the unset default should change is the open decision.)Motivation
Exact-pin rewriting is fine when the same team authors and consumes a chart. It breaks down for shared internal charts: one platform team owns a base chart, many product repos consume it via
Chart.yaml. Every patch the producer ships opens N PRs across N repos owned by the people with the least context on the change — so PRs sit stale (CVE fixes included), get rubber-stamped, or the producer does drive-by merges in other teams' repos.Semver ranges exist to prevent exactly this:
^2.0.0is the producer asserting "anything in2.xis non-breaking." Range-preserving updates honor that:helm dependency update.Example
1.0.5→ satisfied by^1.0.0→ no PR (today: one PR per consumer repo, every patch).2.0.0→ not satisfied → PR bumps the constraint to^2.0.0.Prior art
Renovate's
rangeStrategyhas done range-preserving Helm updates since 2019 (replace≈increase-if-necessary,widen≈widen). Closing this gap removes a common reason to reach for Renovate.Approach
requirements_update_strategyalready passed to the update checker (theversioning-strategy→ strategy mapping happens upstream of core).^,~,||), buthelmcurrently registers a Gem-style requirement class and treats versions as opaque exact strings — so this needs a SemVer-aware requirement parser, modeled on npm's.lockfile-onlyis N/A (Chart.lockis generated, not authored).helmto theversioning-strategyreference, which omits it today.Happy to put up a draft PR once there's directional agreement.
Open questions
helmrewrites exact versions rather than reusing the sharedversioning-strategyplumbing — i.e. is there a shared abstraction I should plug into rather than mirror per-ecosystem?