From 0e5b2314175063dccd4e05027ade32836272326e Mon Sep 17 00:00:00 2001 From: Andrew Shoell Date: Fri, 13 Feb 2026 08:38:48 -0700 Subject: [PATCH 1/8] docs: adding HIP-28 to expose release history during template rendering Signed-off-by: Andrew Shoell --- hips/hip-0028.md | 287 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 287 insertions(+) create mode 100644 hips/hip-0028.md diff --git a/hips/hip-0028.md b/hips/hip-0028.md new file mode 100644 index 00000000..3cd14cd0 --- /dev/null +++ b/hips/hip-0028.md @@ -0,0 +1,287 @@ +--- +hip: 9999 +title: "Expose Release History During Template Rendering" +authors: ["Andrew Shoell "] +created: "2025-11-12" +type: "feature" +status: "draft" +--- + +## Abstract + +This HIP proposes exposing release history metadata during template rendering. Currently, Helm templates have access to `.Chart` for the chart being installed but no equivalent access to deployed release history. This forces chart authors to use complex workarounds like post-renderers, pre-upgrade hooks, or manual values conventions to implement version-aware upgrade logic. + +The proposal introduces `.Release.History` (array of historical releases) available in template contexts, populated during `helm upgrade` and `helm rollback` operations when the `--release-history-max` flag is provided. The flag controls how many historical releases to retrieve (default: 0), requiring explicit opt-in. Chart authors can check `len .Release.History` to determine if sufficient historical data is available and use `.Release.Revision` (or compare `len .Release.History` to expected minimum) to detect upgrade scenarios requiring migration logic. + +## Motivation + +### Current Limitations + +Helm provides comprehensive chart metadata through `.Chart` but offers no native way to access deployed release metadata during template evaluation. Chart developers must resort to problematic workarounds: + +**Post-Renderers:** External tools that query the cluster, parse manifests, and make version-aware modifications. This moves upgrade logic outside the chart, requires additional tooling, and breaks Helm's self-contained design. + +**Pre-Upgrade Hooks:** Store version metadata in ConfigMaps via hooks, creating ordering dependencies and potential failure points. + +**Manual Values:** Require users to specify previous versions in values files—error-prone and defeats Helm's release tracking. + +### Real-World Impact + +This limitation prevents or complicates legitimate use cases: + +- **Breaking Changes:** No clean migration path for renamed resources or changed structures +- **Conditional Resources:** Cannot create migration Jobs based on version deltas +- **Smart Defaults:** Cannot distinguish fresh installs from upgrades for intelligent defaults +- **Advanced Deployments:** Blue-green and similar strategies require external orchestration + +Post-rendering solutions violate Helm's design philosophy that template rendering should be deterministic and self-contained. Making deployed chart metadata available at template time keeps upgrade logic in the chart itself, maintaining Helm's portability, testability, and transparency. + +## Rationale + +### Naming: `.Release.History` + +`.Release.History` extends the existing `.Release` built-in object with a history array, making it immediately intuitive and discoverable for Helm users. This follows the established pattern of `.Release.Name`, `.Release.Namespace`, `.Release.Revision`, etc., and feels like a natural part of Helm's API. + +The history array is ordered in reverse chronological order (index 0 is most recent deployed release). This provides ergonomic access to the most recent release while enabling multi-version migrations when needed. + +Alternatives considered and rejected: + +- `.PreviousChart` - Ambiguous during rollbacks +- `.DeployedChart`/`.DeployedCharts` - Less discoverable, doesn't extend existing objects +- `.InstalledChart` - Confusing with current installation +- `.CurrentChart` - Ambiguous which is "current" +- `.Release.Deployed.Chart` - Unnecessarily nested + +### Always Available as Template Object + +`.Release.History` (empty array or populated) is always present to ensure consistent template behavior, prevent undefined variable errors, and enable testing with `helm template`. + +### Populated Only During Upgrades/Rollbacks + +`.Release.History` contains release metadata only during `helm upgrade` and `helm rollback` when deployed releases exist and `--release-history-max` is greater than 0. During rollback, the history reflects releases deployed before the rollback target. It's empty for: + +- `helm install` - No deployed release +- `helm template` / dry-runs - No cluster context +- When `--release-history-max 0` is used (default) + +### Filtered Release Data + +This proposal exposes a filtered subset of release data to balance utility with performance and security: + +**Included by default:** + +- Chart metadata (Name, Version, AppVersion, and other Chart.yaml fields) +- Release metadata (Name, Namespace, Revision, Status) +- Timestamps (FirstDeployed, LastDeployed) + +**Excluded by default (opt-in via flag):** + +- Values (may contain secrets; use `--include-history-values` to include) +- Manifests (can be very large; future consideration) +- Templates (can be very large; no clear use case - templates are static per chart version) +- Hooks (implementation detail) + +The conservative default excludes potentially sensitive or large data. Users who need historical values for complex migration scenarios can opt-in explicitly with `--include-history-values`, accepting the security and performance tradeoffs. See Security Implications section for detailed rationale. + +**Future consideration:** Historical manifests could be made available via `--include-history-manifests` if demand exists, though manifests can be quite large and increase memory/performance overhead significantly. + +### Max Control Flag + +The `--release-history-max` flag controls how many historical releases to retrieve (default: 0, requiring explicit opt-in). This conservative default protects users from accidental performance impact. Setting `--release-history-max 0` explicitly disables the feature (though this is already the default). Higher max values may impact performance and should only be used for specific multi-version migration scenarios. + +### Design Decisions + +- **Different Chart Names:** Still populates `.Release.History` even if chart names differ—templates can detect and handle this +- **Helm's Record:** Reflects Helm's stored release record, not actual cluster state (use `lookup()` for that) +- **Dry-Run/Template:** Always empty array to maintain cluster-agnostic, deterministic behavior +- **Opt-In by Default:** Default max of 0 requires explicit user choice, preventing accidental performance impact + +## Specification + +### New Template Objects + +**`.Release.History`**: Array of release objects in reverse chronological order (most recent first). Empty array if no deployed releases exist or `--release-history-max 0` is used (default). + +**Note:** Use `.Release.Revision` to detect if this is an upgrade (revision > 1) and `len .Release.History` to check how much historical data is available. + +**Release Object Structure:** Each release object contains: + +```go +type HistoricalRelease struct { + Name string // Release name + Namespace string // Release namespace + Revision int // Revision number + Status string // Release status (deployed, superseded, failed, etc.) + Chart Chart // Chart metadata (same structure as .Chart) + FirstDeployed time.Time // When this release was first deployed + LastDeployed time.Time // When this release was last deployed +} +``` + +**Usage Examples:** + +```yaml +# Check if upgrading from a version that needs migration +{{- if gt (len .Release.History) 0 }} +{{- $lastRelease := index .Release.History 0 }} +{{- if and (semverCompare ">=2.0.0" .Chart.Version) (semverCompare "<2.0.0" $lastRelease.Chart.Version) }} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "mychart.fullname" . }}-migration + annotations: + "helm.sh/hook": pre-upgrade +spec: + template: + spec: + containers: + - name: migrate + image: myapp/migrator:{{ .Chart.AppVersion }} + command: ["migrate", "v1-to-v2"] +{{- end }} +{{- end }} + +# Require minimum depth for safe upgrades +{{- if lt .Release.HistoryDepth 3 }} + {{- fail "This chart requires --release-history-depth=3 for safe upgrades from v2.x" }} +{{- end }} + +# Multi-version migration: handle complex upgrade paths +{{- range .Release.History }} +{{- if and (eq .Status "deployed") (semverCompare "<1.5.0" .Chart.Version) }} +# Run migration for successfully deployed versions before 1.5.0 +{{- end }} +{{- end }} + +# Check if last deployment was successful +{{- if gt (len .Release.History) 0 }} +{{- $lastRelease := index .Release.History 0 }} +{{- if ne $lastRelease.Status "deployed" }} + {{- fail (printf "Previous release was %s - manual intervention required" $lastRelease.Status) }} +{{- end }} +{{- end }} +``` + +### Command-Line Flag + +```bash +# Default: no history retrieved (max 0) +helm upgrade myrelease mychart + +# Retrieve latest deployed release +helm upgrade myrelease mychart --release-history-max 1 + +# Retrieve last 3 releases +helm upgrade myrelease mychart --release-history-max 3 + +# Explicitly disable (same as default) +helm upgrade myrelease mychart --release-history-max 0 +``` + +### Behavior Matrix + +The following table shows what values are available in template context for different operations: + +| Operation | `.Release.History` | `.Release.Revision` | +| ------------------------------------------------- | ------------------------------------- | ------------------- | +| `helm install` | `[]` | 1 | +| `helm upgrade` (first) | `[]` (default, no flag) | 2 | +| `helm upgrade --release-history-max 1` (first) | Populated with 1 release | 2 | +| `helm upgrade --release-history-max N` | Up to N releases | varies | +| `helm rollback --release-history-max N` | Populated with releases before target | varies | +| `helm template` / dry-runs | `[]` | 1 | + +**Note:** Use `.Release.Revision` to distinguish installs (revision=1) from upgrades (revision>1). + +## Backwards Compatibility + +Fully backwards compatible. The `.Release.History` field is purely additive—existing charts work unchanged. Go templates handle empty arrays safely; the recommended `{{ if gt (len .Release.History) 0 }}` pattern works in all scenarios. Default max of 0 means existing behavior is unchanged unless users explicitly opt in. + +## Security Implications + +**Not Exposed:** Previous values (may contain secrets) or previous manifests (may contain sensitive data). Only filtered release metadata is exposed (chart metadata, release status, timestamps, revision numbers). + +**Considerations:** Chart authors should not store sensitive data in Chart.yaml. The default `--release-history-depth 0` provides opt-out by default. Higher depth values increase data exposure; use the minimum required. Release status and metadata are already stored in cluster secrets by Helm, so this doesn't expose data that isn't already persisted. + +## How to Teach This + +### Documentation Additions + +1. **Template Objects Reference:** Add `.Release.History` to built-in objects documentation with availability details and usage patterns with `.Release.Revision` +2. **Upgrade Guide:** "Implementing Version-Aware Upgrades" covering empty array checks, version comparisons, status checking, and best practices +3. **Migration Examples:** Show replacement of post-renderers and pre-upgrade hooks, including use of opt-in flag for values when needed, with practical patterns (check last release, check last successful) +4. **Performance Note:** Document that `--release-history-max` should be kept minimal; opt-in by default protects users +5. **Chart Linting:** Update `helm lint` to warn on `.Release.History` usage without empty array checks, and suggest using `.Release.Revision` for upgrade detection +6. **Security Guide:** Document the opt-in flag `--include-history-values` with clear warnings about security implications + +### Key Example Pattern + +```yaml +# Pattern 1: Defensive check before accessing history +{{- if gt (len .Release.History) 0 }} +{{- $lastRelease := index .Release.History 0 }} +{{- if semverCompare "<3.0.0" $lastRelease.Chart.Version }} +# Handle breaking change from versions < 3.0.0 +{{- end }} +{{- end }} + +# Pattern 2: Check only last successful deployment +{{- $lastSuccessful := dict }} +{{- range .Release.History }} + {{- if eq .Status "deployed" }} + {{- $lastSuccessful = . }} + {{- break }} + {{- end }} +{{- end }} +{{- if $lastSuccessful }} + # Use $lastSuccessful for migration logic +{{- end }} + +# Pattern 3: Require history for upgrades (not installs) +{{- if gt .Release.Revision 1 }} + {{- if eq (len .Release.History) 0 }} + {{- fail "Upgrades require --release-history-max=1 for continuity checks" }} + {{- end }} +{{- end }} + +# Pattern 4: Check for sufficient history depth (less common) +{{- if and (gt .Release.Revision 1) (lt (len .Release.History) 2) }} + {{- fail "This complex migration requires --release-history-max=2 for full validation" }} +{{- end }} +``` + +## Reference Implementation + +A future pull request will: + +1. Extend template rendering context to include `.Release.History` +2. Populate `.Release.History` from release records during upgrade/rollback (reverse chronological order) +3. Add `--release-history-max` flag (default: 0) +4. Add opt-in flag: `--include-history-values` +5. Filter release objects by default to include: Chart, Name, Namespace, Revision, Status, FirstDeployed, LastDeployed +6. When opt-in flag is used, include Values in historical releases +7. Include comprehensive unit and integration tests covering flag behavior, filtering, and edge cases + +## Rejected Ideas + +- **Full Release Object:** Security/performance concerns; filtered release metadata sufficient +- **Chart Metadata Only:** Missing release status/revision limits utility for migration logic +- **Only Version Strings:** Inconsistent with `.Chart`; prevents access to other metadata +- **`.DeployedChart`/`.DeployedCharts` Naming:** Less discoverable than extending `.Release` object +- **Default Max of 1:** Opt-in by default (max 0) is more conservative and safer +- **Environment Variable Control:** Less explicit than CLI flag +- **Cluster Query During `helm template`:** Violates cluster-agnostic design principle +- **Mutable Objects:** Violates read-only template model; no clear use case +- **Separate `--disable-release-history` Flag:** Unified `--release-history-max` with 0 value is cleaner +- **Unlimited History:** Performance implications; requiring explicit max prevents accidental overhead +- **Including Values/Manifests by Default:** While historical values could be useful for migration scenarios, and users already have access via `helm get values --revision N` or `lookup()`, making them automatically available in templates creates additional surface area for accidental exposure. The filtered metadata approach with opt-in flag (`--include-history-values`) serves both conservative defaults and advanced use cases. Manifests moved to future consideration (`--include-history-manifests`) as they are very large and less commonly needed. +- **Including Templates:** Templates are static per chart version; if you need old templates, retrieve the old chart version. No flag needed. +- **`.Release.HistoryMax` Field:** Redundant with `len .Release.History` and `.Release.Revision`. Chart authors can use `.Release.Revision > 1` to detect upgrades and `len .Release.History` to check available data. + +## References + +- [Helm Built-in Objects](https://helm.sh/docs/chart_template_guide/builtin_objects/) +- [Helm Chart.yaml](https://helm.sh/docs/topics/charts/#the-chartyaml-file) +- [Go Templates](https://pkg.go.dev/text/template) +- [Semantic Versioning](https://semver.org/) +- [Example of current workaround](https://github.com/helm/community/pull/421#issuecomment-3662769874) From e7f3bf199b427082c6077541e3a161eb43cf6283 Mon Sep 17 00:00:00 2001 From: Andrew Shoell Date: Thu, 9 Apr 2026 20:03:20 -0600 Subject: [PATCH 2/8] docs: addressing reviews, particularly around missed migration points to focus on using the history verbage Signed-off-by: Andrew Shoell --- hips/hip-0028.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/hips/hip-0028.md b/hips/hip-0028.md index 3cd14cd0..ec760a95 100644 --- a/hips/hip-0028.md +++ b/hips/hip-0028.md @@ -58,7 +58,7 @@ Alternatives considered and rejected: ### Populated Only During Upgrades/Rollbacks -`.Release.History` contains release metadata only during `helm upgrade` and `helm rollback` when deployed releases exist and `--release-history-max` is greater than 0. During rollback, the history reflects releases deployed before the rollback target. It's empty for: +`.Release.History` contains release metadata only during `helm upgrade` and `helm rollback` when deployed releases exist and `--release-history-max` is greater than 0. During rollback, history follows Helm's normal revision behavior: rollback creates a new revision and prior revisions remain in history. It's empty for: - `helm install` - No deployed release - `helm template` / dry-runs - No cluster context @@ -89,6 +89,8 @@ The conservative default excludes potentially sensitive or large data. Users who The `--release-history-max` flag controls how many historical releases to retrieve (default: 0, requiring explicit opt-in). This conservative default protects users from accidental performance impact. Setting `--release-history-max 0` explicitly disables the feature (though this is already the default). Higher max values may impact performance and should only be used for specific multi-version migration scenarios. +`--release-history-max` is a retrieval limit, not a guarantee that many revisions exist. Template checks should rely on actual available data (`len .Release.History`). + ### Design Decisions - **Different Chart Names:** Still populates `.Release.History` even if chart names differ—templates can detect and handle this @@ -104,6 +106,10 @@ The `--release-history-max` flag controls how many historical releases to retrie **Note:** Use `.Release.Revision` to detect if this is an upgrade (revision > 1) and `len .Release.History` to check how much historical data is available. +**Note:** Template logic should rely on actual availability (`len .Release.History`), not requested max. Requested max may be larger than existing revisions. + +**Note:** `.Release.Revision` provides an upper bound on possible prior history (`revision - 1`). Chart maintainers can combine `.Release.Revision` with `len .Release.History` for stricter migration checks. + **Release Object Structure:** Each release object contains: ```go @@ -141,9 +147,9 @@ spec: {{- end }} {{- end }} -# Require minimum depth for safe upgrades -{{- if lt .Release.HistoryDepth 3 }} - {{- fail "This chart requires --release-history-depth=3 for safe upgrades from v2.x" }} +# Require minimum history for safe upgrades +{{- if and (gt .Release.Revision 1) (lt (len .Release.History) 3) }} + {{- fail "This chart requires --release-history-max=3 for safe upgrades from v2.x" }} {{- end }} # Multi-version migration: handle complex upgrade paths @@ -201,7 +207,7 @@ Fully backwards compatible. The `.Release.History` field is purely additive—ex **Not Exposed:** Previous values (may contain secrets) or previous manifests (may contain sensitive data). Only filtered release metadata is exposed (chart metadata, release status, timestamps, revision numbers). -**Considerations:** Chart authors should not store sensitive data in Chart.yaml. The default `--release-history-depth 0` provides opt-out by default. Higher depth values increase data exposure; use the minimum required. Release status and metadata are already stored in cluster secrets by Helm, so this doesn't expose data that isn't already persisted. +**Considerations:** Chart authors should not store sensitive data in Chart.yaml. The default `--release-history-max 0` provides opt-out by default. Higher max values increase data exposure; use the minimum required. Release status and metadata are already stored in cluster secrets by Helm, so this doesn't expose data that isn't already persisted. ## How to Teach This @@ -250,6 +256,8 @@ Fully backwards compatible. The `.Release.History` field is purely additive—ex {{- end }} ``` +**Guidance:** Treat `--release-history-max` as a retrieval cap, not a guarantee. Use `.Release.Revision` to detect install vs upgrade and `len .Release.History` to validate actual available history. + ## Reference Implementation A future pull request will: From b53040ec9970134094f701638fcddbcd9fad6615 Mon Sep 17 00:00:00 2001 From: Andrew Shoell Date: Thu, 9 Apr 2026 20:07:53 -0600 Subject: [PATCH 3/8] docs: updating to hip 0029 Signed-off-by: Andrew Shoell --- hips/{hip-0028.md => hip-0029.md} | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) rename hips/{hip-0028.md => hip-0029.md} (94%) diff --git a/hips/hip-0028.md b/hips/hip-0029.md similarity index 94% rename from hips/hip-0028.md rename to hips/hip-0029.md index ec760a95..e693cf12 100644 --- a/hips/hip-0028.md +++ b/hips/hip-0029.md @@ -1,5 +1,5 @@ --- -hip: 9999 +hip: 0029 title: "Expose Release History During Template Rendering" authors: ["Andrew Shoell "] created: "2025-11-12" @@ -188,14 +188,14 @@ helm upgrade myrelease mychart --release-history-max 0 The following table shows what values are available in template context for different operations: -| Operation | `.Release.History` | `.Release.Revision` | -| ------------------------------------------------- | ------------------------------------- | ------------------- | -| `helm install` | `[]` | 1 | -| `helm upgrade` (first) | `[]` (default, no flag) | 2 | -| `helm upgrade --release-history-max 1` (first) | Populated with 1 release | 2 | -| `helm upgrade --release-history-max N` | Up to N releases | varies | -| `helm rollback --release-history-max N` | Populated with releases before target | varies | -| `helm template` / dry-runs | `[]` | 1 | +| Operation | `.Release.History` | `.Release.Revision` | +| ---------------------------------------------- | ------------------------------------- | ------------------- | +| `helm install` | `[]` | 1 | +| `helm upgrade` (first) | `[]` (default, no flag) | 2 | +| `helm upgrade --release-history-max 1` (first) | Populated with 1 release | 2 | +| `helm upgrade --release-history-max N` | Up to N releases | varies | +| `helm rollback --release-history-max N` | Populated with releases before target | varies | +| `helm template` / dry-runs | `[]` | 1 | **Note:** Use `.Release.Revision` to distinguish installs (revision=1) from upgrades (revision>1). From ab6bca958ec3e37503940e3b44d07de7b0a91f9d Mon Sep 17 00:00:00 2001 From: Andrew Shoell Date: Thu, 9 Apr 2026 20:52:40 -0600 Subject: [PATCH 4/8] docs: adding the link for the new hip Signed-off-by: Andrew Shoell --- hips/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/hips/README.md b/hips/README.md index a48bb746..0cac393b 100644 --- a/hips/README.md +++ b/hips/README.md @@ -37,3 +37,4 @@ restricted markdown format and can be found in the - [hip-0025: Better Support for Resource Creation Sequencing](hip-0025.md) - [hip-0026: H4HIP: Wasm plugin system](hip-0026.md) - [hip-0027: Bring .helmignore to parity with .gitignore file targeting syntax](hip-0027.md) +- [hip-0029: Expose Release History During Template Rendering](hip-0029.md) From 1017509d1a0c10c948d41d6bbb17555a3a2e7f1c Mon Sep 17 00:00:00 2001 From: Andrew Shoell Date: Thu, 23 Apr 2026 11:28:08 -0600 Subject: [PATCH 5/8] docs: Update hips/hip-0029.md Co-authored-by: Evans Mungai Signed-off-by: Andrew Shoell --- hips/hip-0029.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hips/hip-0029.md b/hips/hip-0029.md index e693cf12..6039160c 100644 --- a/hips/hip-0029.md +++ b/hips/hip-0029.md @@ -106,7 +106,7 @@ The `--release-history-max` flag controls how many historical releases to retrie **Note:** Use `.Release.Revision` to detect if this is an upgrade (revision > 1) and `len .Release.History` to check how much historical data is available. -**Note:** Template logic should rely on actual availability (`len .Release.History`), not requested max. Requested max may be larger than existing revisions. +**Note:** Template logic should rely on actual availability (`len .Release.History`), not requested max. Requested max may be larger or smaller than existing revisions. **Note:** `.Release.Revision` provides an upper bound on possible prior history (`revision - 1`). Chart maintainers can combine `.Release.Revision` with `len .Release.History` for stricter migration checks. From a6368f91160b02f155f09ed5e3327024b9d291f9 Mon Sep 17 00:00:00 2001 From: Andrew Shoell Date: Thu, 23 Apr 2026 11:56:05 -0600 Subject: [PATCH 6/8] docs: addressing feedback on how to handle --dry-run=server and explicitly calling out that the list in not persisted Signed-off-by: Andrew Shoell --- hips/hip-0029.md | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/hips/hip-0029.md b/hips/hip-0029.md index 6039160c..18e9d748 100644 --- a/hips/hip-0029.md +++ b/hips/hip-0029.md @@ -11,7 +11,7 @@ status: "draft" This HIP proposes exposing release history metadata during template rendering. Currently, Helm templates have access to `.Chart` for the chart being installed but no equivalent access to deployed release history. This forces chart authors to use complex workarounds like post-renderers, pre-upgrade hooks, or manual values conventions to implement version-aware upgrade logic. -The proposal introduces `.Release.History` (array of historical releases) available in template contexts, populated during `helm upgrade` and `helm rollback` operations when the `--release-history-max` flag is provided. The flag controls how many historical releases to retrieve (default: 0), requiring explicit opt-in. Chart authors can check `len .Release.History` to determine if sufficient historical data is available and use `.Release.Revision` (or compare `len .Release.History` to expected minimum) to detect upgrade scenarios requiring migration logic. +The proposal introduces `.Release.History` (array of historical releases) available in template contexts, populated during `helm upgrade` and `helm rollback` operations (including `--dry-run=server`) when the `--release-history-max` flag is provided. The flag controls how many historical releases to retrieve (default: 0), requiring explicit opt-in. Chart authors can check `len .Release.History` to determine if sufficient historical data is available and use `.Release.Revision` (or compare `len .Release.History` to expected minimum) to detect upgrade scenarios requiring migration logic. ## Motivation @@ -56,12 +56,14 @@ Alternatives considered and rejected: `.Release.History` (empty array or populated) is always present to ensure consistent template behavior, prevent undefined variable errors, and enable testing with `helm template`. -### Populated Only During Upgrades/Rollbacks +### Populated During Upgrades/Rollbacks (and Server Dry-Runs) -`.Release.History` contains release metadata only during `helm upgrade` and `helm rollback` when deployed releases exist and `--release-history-max` is greater than 0. During rollback, history follows Helm's normal revision behavior: rollback creates a new revision and prior revisions remain in history. It's empty for: +`.Release.History` contains release metadata during `helm upgrade` and `helm rollback` when deployed releases exist and `--release-history-max` is greater than 0. During rollback, history follows Helm's normal revision behavior: rollback creates a new revision and prior revisions remain in history. + +For `--dry-run=server`, behavior matches the corresponding live command because cluster context is available (for example, `helm upgrade --dry-run=server --release-history-max N` can populate `.Release.History`). It's empty for: - `helm install` - No deployed release -- `helm template` / dry-runs - No cluster context +- `helm template` and `--dry-run=client` - No cluster context - When `--release-history-max 0` is used (default) ### Filtered Release Data @@ -95,7 +97,7 @@ The `--release-history-max` flag controls how many historical releases to retrie - **Different Chart Names:** Still populates `.Release.History` even if chart names differ—templates can detect and handle this - **Helm's Record:** Reflects Helm's stored release record, not actual cluster state (use `lookup()` for that) -- **Dry-Run/Template:** Always empty array to maintain cluster-agnostic, deterministic behavior +- **Dry-Run Behavior:** `helm template` and `--dry-run=client` always return an empty array (cluster-agnostic). `--dry-run=server` follows live command behavior and can populate history when available. - **Opt-In by Default:** Default max of 0 requires explicit user choice, preventing accidental performance impact ## Specification @@ -110,6 +112,8 @@ The `--release-history-max` flag controls how many historical releases to retrie **Note:** `.Release.Revision` provides an upper bound on possible prior history (`revision - 1`). Chart maintainers can combine `.Release.Revision` with `len .Release.History` for stricter migration checks. +**Note:** `HistoricalRelease` is a runtime projection created for template rendering from Helm's stored release records. It is not a new persisted release schema. + **Release Object Structure:** Each release object contains: ```go @@ -195,7 +199,10 @@ The following table shows what values are available in template context for diff | `helm upgrade --release-history-max 1` (first) | Populated with 1 release | 2 | | `helm upgrade --release-history-max N` | Up to N releases | varies | | `helm rollback --release-history-max N` | Populated with releases before target | varies | -| `helm template` / dry-runs | `[]` | 1 | +| `helm upgrade --dry-run=server --release-history-max N` | Same as live `upgrade` | varies | +| `helm rollback --dry-run=server --release-history-max N` | Same as live `rollback` | varies | +| `helm install --dry-run=server` | `[]` | 1 | +| `helm template` / `--dry-run=client` | `[]` | 1 | **Note:** Use `.Release.Revision` to distinguish installs (revision=1) from upgrades (revision>1). @@ -263,7 +270,7 @@ Fully backwards compatible. The `.Release.History` field is purely additive—ex A future pull request will: 1. Extend template rendering context to include `.Release.History` -2. Populate `.Release.History` from release records during upgrade/rollback (reverse chronological order) +2. Populate `.Release.History` from release records during upgrade/rollback, including `--dry-run=server` parity (reverse chronological order) 3. Add `--release-history-max` flag (default: 0) 4. Add opt-in flag: `--include-history-values` 5. Filter release objects by default to include: Chart, Name, Namespace, Revision, Status, FirstDeployed, LastDeployed From 22f112ff27c9ec18982d4897b9a0f5e17e3f6074 Mon Sep 17 00:00:00 2001 From: Andrew Shoell Date: Thu, 23 Apr 2026 12:02:07 -0600 Subject: [PATCH 7/8] docs: calling out the relationship between "helm history" and ".Release.History" and showing that the values are there and only included when the flag is set Signed-off-by: Andrew Shoell --- hips/hip-0029.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hips/hip-0029.md b/hips/hip-0029.md index 18e9d748..a85453b7 100644 --- a/hips/hip-0029.md +++ b/hips/hip-0029.md @@ -97,6 +97,7 @@ The `--release-history-max` flag controls how many historical releases to retrie - **Different Chart Names:** Still populates `.Release.History` even if chart names differ—templates can detect and handle this - **Helm's Record:** Reflects Helm's stored release record, not actual cluster state (use `lookup()` for that) +- **Relationship to `helm history`:** `.Release.History` is derived from the same release records used by `helm history `, but exposed as a reverse-chronological, template-friendly projection with field filtering and `--release-history-max` limits. - **Dry-Run Behavior:** `helm template` and `--dry-run=client` always return an empty array (cluster-agnostic). `--dry-run=server` follows live command behavior and can populate history when available. - **Opt-In by Default:** Default max of 0 requires explicit user choice, preventing accidental performance impact @@ -123,6 +124,7 @@ type HistoricalRelease struct { Revision int // Revision number Status string // Release status (deployed, superseded, failed, etc.) Chart Chart // Chart metadata (same structure as .Chart) + Values map[string]interface{} // Optional: only included when --include-history-values is set FirstDeployed time.Time // When this release was first deployed LastDeployed time.Time // When this release was last deployed } @@ -206,6 +208,8 @@ The following table shows what values are available in template context for diff **Note:** Use `.Release.Revision` to distinguish installs (revision=1) from upgrades (revision>1). +**Note:** `.Release.History` and `helm history ` both come from Helm's stored release records. For template ergonomics, `.Release.History` is reverse chronological (most recent first), filtered to HIP-defined fields, and capped by `--release-history-max`. During rollback, Helm creates a new revision; prior revisions remain in history. + ## Backwards Compatibility Fully backwards compatible. The `.Release.History` field is purely additive—existing charts work unchanged. Go templates handle empty arrays safely; the recommended `{{ if gt (len .Release.History) 0 }}` pattern works in all scenarios. Default max of 0 means existing behavior is unchanged unless users explicitly opt in. From d3703a3d8b5f900881b9e0c1104baaf36667277a Mon Sep 17 00:00:00 2001 From: Andrew Shoell Date: Thu, 23 Apr 2026 12:05:58 -0600 Subject: [PATCH 8/8] docs: fixing formatting Signed-off-by: Andrew Shoell --- hips/hip-0029.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/hips/hip-0029.md b/hips/hip-0029.md index a85453b7..fd3b62fa 100644 --- a/hips/hip-0029.md +++ b/hips/hip-0029.md @@ -194,17 +194,17 @@ helm upgrade myrelease mychart --release-history-max 0 The following table shows what values are available in template context for different operations: -| Operation | `.Release.History` | `.Release.Revision` | -| ---------------------------------------------- | ------------------------------------- | ------------------- | -| `helm install` | `[]` | 1 | -| `helm upgrade` (first) | `[]` (default, no flag) | 2 | -| `helm upgrade --release-history-max 1` (first) | Populated with 1 release | 2 | -| `helm upgrade --release-history-max N` | Up to N releases | varies | -| `helm rollback --release-history-max N` | Populated with releases before target | varies | -| `helm upgrade --dry-run=server --release-history-max N` | Same as live `upgrade` | varies | -| `helm rollback --dry-run=server --release-history-max N` | Same as live `rollback` | varies | -| `helm install --dry-run=server` | `[]` | 1 | -| `helm template` / `--dry-run=client` | `[]` | 1 | +| Operation | `.Release.History` | `.Release.Revision` | +| -------------------------------------------------------- | ------------------------------------- | ------------------- | +| `helm install` | `[]` | 1 | +| `helm upgrade` (first) | `[]` (default, no flag) | 2 | +| `helm upgrade --release-history-max 1` (first) | Populated with 1 release | 2 | +| `helm upgrade --release-history-max N` | Up to N releases | varies | +| `helm rollback --release-history-max N` | Populated with releases before target | varies | +| `helm upgrade --dry-run=server --release-history-max N` | Same as live `upgrade` | varies | +| `helm rollback --dry-run=server --release-history-max N` | Same as live `rollback` | varies | +| `helm install --dry-run=server` | `[]` | 1 | +| `helm template` / `--dry-run=client` | `[]` | 1 | **Note:** Use `.Release.Revision` to distinguish installs (revision=1) from upgrades (revision>1).