Changelog

1. 1.2.1

1.1. Dependencies

  • Template engine moved to gotmpl4j 1.3.0 (from 1.2.0). Picks up a Go-parity correctness fix — variadic method calls on values now resolve instead of silently returning nil — plus custom action delimiters, a thread-safe template cache, and the 1.3.0 engine perf batch (shareable function registry, cached method/field dispatch, cheaper scope handling). Additive and non-breaking; no jhelm API or rendering change (chart-parity suite unchanged).

1.2. Performance

The render hot path was profiled (JFR / jvmlens) and tightened. Every change is byte-for-byte identical to Helm across the full chart-parity suite (547 charts, 0 diffs):

  • Function registry reused across renders — the ServiceLoader result and the template-independent Sprig function set (~260 functions), plus their reflection/dispatch caches, are built once per Engine and shared by every render instead of rebuilt each call (#717).

  • Render thread reused instead of spawned per render — rendering runs on a single reused daemon executor rather than creating and joining a fresh large-stack thread every call. On the JMH render harness this is ~1.6x throughput with far less GC churn, and it also closes a pre-existing data race on shared engine state under concurrent renders (#721).

  • toYaml quote-stripping scans in one pass — the YAML post-processor no longer splits the document into a String[] and rebuilds it; it scans in place, allocating only for the lines it actually rewrites (#720).

  • Render values boxed once per render — numeric leaf coercion runs a single time and the boxed leaves are reused, instead of deep-copying the value tree twice (#718).

  • Template parse cache on by default — a new Engine() now keeps a 256-entry parsed-template cache, so re-rendering a chart skips re-parsing unchanged templates (#726).

1.3. Fixes

  • Library-chart define`s are no longer served from a partial parse-cache entry — with the parse cache enabled, a global `define registered by an earlier template could be served from an incomplete cached node-delta when a second chart reused the same cache key, dropping the definition (include: Template '…​' not found). Templates carrying a define now always parse in full, keeping the cache safe for library charts such as Bitnami Common (#726).

  • install/upgrade no longer choke on --- inside a YAML comment — a rendered manifest with a decorative comment such as # ---------------- Postgres ---------------- failed to apply with a SnakeYAML parse error, because the manifest document splitter matched the --- inside the comment as a document separator and produced an invalid fragment (jhelm template was unaffected; the bug was only in the apply/diff/get path that re-splits the manifest). The splitter is now line-anchored — only a line that is exactly --- (optionally with trailing whitespace or a comment) starts a new document — via a shared ManifestDocuments.split reused by hook stripping, manifest diffing, and get. #713.

2. 1.2.0

Closing CLI flag-parity gaps with Helm across the template, install, and query commands, so more helm scripts run unchanged against jhelm. See CLI Flag Parity.

2.1. Helm parity

  • Machine-readable output on more commands — -o table\|json\|yaml now covers status, history, install, upgrade, repo list, and search hub, emitting Helm-shaped output (nested info, snake_case keys) for scripts and agents (#678).

  • template source markers — rendered manifests carry a Helm-style # Source: <chart>/templates/<file> marker before each document (#679).

  • template render-control flags — -s/--show-only, --output-dir, --skip-tests, --include-crds, and --is-upgrade (#679).

  • install --generate-name — omit the release name and pass -g/--generate-name to auto-generate <chart>-<timestamp>, as Helm does (#680).

  • install --description / --labels — store a custom release description and custom labels on the release Secret (#680).

  • upgrade --description / --labels — set the new revision’s description and labels; without --labels, the previous release’s labels are carried forward, matching Helm (#680).

  • --set-literal — on install, upgrade, template, and lint: set a value taken fully literally (no type coercion, no comma or escape interpretation), applied at the highest precedence in the --set family (#680).

  • --dependency-update — on install, upgrade, and template: update a local chart’s charts/ from its Chart.yaml dependencies (like helm dependency update) before the operation. The update flow is now a reusable DependencyUpdateAction shared with the dependency update command (#680).

  • list filtering & pagination — list gains -l/--selector (label selector, key=value / key!=value, comma-ANDed), --filter (regex on the release name), and --offset/-m/--max (paginate; default max 256). Results are sorted by name (#681).

  • list status filters — -a/--all shows every status, and --deployed/--failed/--pending/ --uninstalled/--uninstalling/--superseded restrict to those states. The default view hides uninstalled and superseded releases, matching Helm (#681).

  • list -A/--all-namespaces — list releases across every namespace, backed by a new KubeService.listAllReleases() (lists release Secrets cluster-wide). Releases are keyed by namespace+name so same-named releases in different namespaces stay distinct (#681).

  • install/upgrade from a repository — pass a chart name with --version and an ad-hoc --repo <url> (plus --username/--password and TLS --cert-file/--key-file/--ca-file/ --insecure-skip-tls-verify/--pass-credentials) to fetch the chart directly, matching helm install/upgrade --repo. A repo/chart or oci:// reference is also resolved via the registered repositories. Local chart paths still work unchanged (#682).

  • uninstall wait/cascade semantics — --dry-run (report without deleting), --wait/--timeout (block until the resources are gone, backed by a new KubeService.waitForDeleted), --cascade background\|foreground\|orphan (Kubernetes deletion propagation), and --description (stored on the release when --keep-history is set). Mirrored on REST (DELETE query params) and MCP (#683).

  • rollback force/cleanup semantics — --dry-run, --force (delete-and-recreate), --cleanup-on-fail (delete resources created during a failed rollback), --recreate-pods (rolling restart of the release’s workloads via a restart annotation), and --wait/--wait-for-jobs/--timeout. The rollback readiness wait now lives in the action so REST and MCP honor it too (#683).

  • pull unpack & provenance — --untar/--untardir unpack the fetched chart (and drop the .tgz), and --verify/--prov/--keyring fetch the chart’s detached .prov and check its PGP provenance before use (reusing the verify layer). REST POST /repos/pull gains a verify flag. --devel (prerelease resolution) remains the one open pull gap — it needs semver latest-version resolution jhelm does not have yet (#684).

  • repo add auth, TLS & update control — --username/--password/--cert-file/--key-file/--ca-file/--insecure-skip-tls-verify/--pass-credentials are persisted into repositories.yaml and honored (host-gated) on every index refresh and chart pull; --force-update replaces an existing repo (a duplicate name otherwise fails) and --no-update skips the immediate index fetch. Mirrored on REST POST /repos (#685).

  • Validating registry login — registry login now follows Helm’s flow: it validates the credentials against the registry (a /v2/ ping and, on a Bearer challenge, a token exchange) before storing them, and honors the login-time transport flags --insecure, --plain-http, --ca-file, --cert-file, and --key-file. As in Helm, those transport options apply only to the handshake and are not persisted — only the credentials are stored. A wrong password, TLS failure, or unreachable registry now fails the login instead of silently saving. Completes #708.

  • Global persistent flags — Helm’s cluster/config flags are now accepted on every command (before or after the subcommand): --kubeconfig, --kube-context, --kube-apiserver, --registry-config, --repository-config, --repository-cache, and --debug. They are resolved before the application context is built (they configure startup beans) and take precedence over the equivalent jhelm.* properties/env (#708).

  • Misc command flags — package --version/--app-version (rewrites the Chart.yaml inside the archive) and -u/--dependency-update; show chart\|values\|readme\|crds\|all --version/--repo/--username/--password + TLS (show a chart from a repository without a prior pull); lint --quiet/--with-subcharts/--kube-version; and version --template (Go-templates the version output). show --devel remains a documented gap (#687).

  • REST releases API now returns Helm-shaped JSON — the /releases list/status/history and install/upgrade responses now use Helm’s -o json schema (snake_case, nested info) instead of jhelm’s internal DTO, so REST consumers see the same shape as helm …​ -o json and the CLI (#686). Breaking: clients reading the old fields must migrate (e.g. status moves from $.status to $.info.status; list chartName becomes chart; history version becomes revision).

  • MCP tools return structured JSON — helm_list, helm_status, and helm_history now return Helm-shaped JSON (array of rows / nested info object) instead of prose text, so agents can parse them directly (#686).

  • Install options on the API — the REST install endpoints and the MCP helm_install tool accept description and labels, mirroring the CLI’s --description/--labels (#686).

  • Template render controls on the API — the REST template endpoints and the MCP helm_template tool accept showOnly, skipTests, includeCrds, and isUpgrade, mirroring the CLI’s --show-only/--skip-tests/--include-crds/--is-upgrade (#686).

3. 1.1.0

The first release to extend beyond Helm parity: a Spring-Boot-style value-management layer — profiles, a Spring Cloud Config Server source, and encrypted secret values — layered on top of the Helm-compatible core. Every addition is opt-in; charts that don’t use these features render byte-for-byte as Go Helm.

3.1. Features

  • Value profiles — keep environment-specific values (dev, staging, prod, …) in one place and activate them at render/install/upgrade time with -P/--profile, mirroring Spring Boot. Profiles come from ----separated documents gated by spring.config.activate.on-profile or from values-<profile>.yaml sidecars, and apply to a chart’s own values.yaml and to -f files (#670).

  • Values from a Spring Cloud Config Server — fetch values from a central, git-backed Spring Cloud Config Server over jhelm’s SSRF-guarded HTTP path, reusing the active profiles for the requested profile and slotting into the override order the way Spring Boot’s config-data model does. Disabled by default (#671).

  • Encrypted values — {cipher}<hex> secret tokens in any values source are decrypted at render time when jhelm.encrypt.key is set, byte-compatible with Spring Cloud Config’s encryption. Ships a jhelm encrypt / jhelm decrypt CLI tool and a runnable, secured jhelm-config-server-sample module as a DevOps starting point and test fixture. Inert unless a key is configured (#674).

3.2. Fixes

  • jhelm test no longer hangs on a completed pod — terminal pod phases (Succeeded/Failed) are handled instead of being waited on indefinitely (#668).

  • Manifests apply/delete as unstructured maps — resources are applied and deleted as unstructured maps rather than typed models, fixing kinds the typed path mishandled (#666).

4. 1.0.2

A maintenance release fixing two bugs found in triage. No public API changes.

4.1. Fixes

  • lookup returns the full resource and queries the live cluster — the lookup template function now returns the whole object (including Secret/ConfigMap data as base64), and is wired to the live Kubernetes API in the CLI and any jhelm-kube-backed app, as Helm does during install/upgrade. Previously it silently returned the empty stub (and, even when reached, only the resource’s metadata), which regenerated the reuse-existing-password idiom index $existing.data "key" \| b64dec on every upgrade and corrupted bundled-database passwords (#663).

  • CLI reports the real build version — jhelm --version, -V, jhelm version and jhelm env now report the actual build version (sourced from Spring Boot build-info, the same source Actuator’s /actuator/info uses) instead of a hardcoded 0.0.1. The REST OpenAPI document reports the same (#662).

5. 1.0.1

A maintenance release: bug fixes across the Kubernetes layer and the CLI, plus CLI startup and ergonomics improvements. No public API changes.

5.1. Fixes

  • Cluster-scoped resources — kinds such as ClusterRole, ClusterRoleBinding, CustomResourceDefinition, Namespace, StorageClass and PriorityClass are now applied and deleted at the cluster scope instead of being forced through the namespaced API path, which the API server rejected (#650).

  • CLI exit codes — commands exit non-zero on failure (for example a --wait readiness timeout), so scripts and CI can detect a failed operation instead of seeing a spurious success (#647).

5.2. CLI security

  • The CLI now honors jhelm.security.mode and defaults to FULL (read-write), matching helm — the local kubeconfig and cluster RBAC are the trust boundary. Setting jhelm.security.mode=READ_ONLY refuses the cluster-mutating commands (install, upgrade, uninstall, rollback, test) with a non-zero exit. The API key stays a REST/MCP concern and is not required to unlock the CLI (#653, #654, #657).

5.3. CLI ergonomics

  • Faster startup through lazy bean initialization and trimming auto-configurations a one-shot CLI never uses (#649, #651).

  • Quieted a benign picocli-spring INFO line ("Unable to get bean of class interface java.util.List …​") that surfaced on multi-value options such as --set and read like a stacktrace (#656).

5.4. Docs and build

  • The release workflow passes the GPG passphrase via MAVEN_GPG_PASSPHRASE rather than the deprecated -Dgpg.passphrase (#646).

  • Corrected the jhelm-kube description: releases are stored as Helm-compatible Secrets, not ConfigMaps (#655); fixed cross-project documentation links.

6. 1.0.0

The first stable release. 1.0 draws a supported public API surface, hardens security and provenance, verifies Helm interoperability against the real helm binary, and makes the chart-parity claims honest about exactly what is measured.

6.1. Security

  • SSRF controls — an opt-in server-mode block of private / link-local / site-local targets (jhelm.security.block-private-networks, default off) applied through the repository and OCI HTTP clients and a DNS-resolver guard, so a hosted jhelm cannot be steered at internal addresses.

  • Credential and archive hardening — repository credentials are host-gated (not sent on a redirect to a different host), chart un-tar is capped against decompression bombs, OCI reference URLs are validated, and the written registry-credentials file gets owner-only permissions.

6.2. Provenance

  • Key usability checks — verify now rejects a revoked or expired signing key, so a retired or compromised provenance key can no longer validate a chart even with an intact signature.

  • Clear-sign correctness — OpenPGP dash-escaping (RFC 4880 §7.1) is reversed before verifying, so a helm/GnuPG-produced .prov whose signed body contains a --leading line verifies correctly.

6.3. Real cluster capabilities

  • .Capabilities from the cluster — .Capabilities.KubeVersion and .APIVersions are now populated from the connected cluster for install/upgrade; template accepts --kube-version and -a/--api-versions to pin them offline (defaulting to a fixed Kubernetes version for deterministic rendering).

6.4. Helm interoperability

  • Shared release storage — the release record (including the embedded chart) is now byte-schema-compatible with Helm 3’s sh.helm.release.v1 Secret format, so a release installed by one tool is listable, upgradable, roll-back-able, and removable by the other. Covered by a two-way helmjhelm integration suite that runs against a real cluster and the real helm binary in CI. See Interoperability with Helm.

  • Shared configuration — jhelm honors Helm’s HELM_* environment variables and reads/writes Helm’s own repositories.yaml and registry/config.json at Helm’s locations. See Configuration.

6.5. CLI parity

  • New commands / flags — repo update; list -o json|yaml; pull --repo <URL> with inline auth/TLS flags; upgrade --force (delete-and-recreate); --dry-run=client|server|none with real server-side dry-run and --wait-for-jobs; and an enriched env that reports the effective Helm config locations.

  • See the CLI flag parity reference for the full helm ↔ jhelm map.

6.6. REST & MCP

  • OpenAPI — jhelm-rest ships a well-documented springdoc OpenAPI document.

  • Upgrade value strategy — the upgrade endpoints expose valueStrategy (DEFAULT/RESET/REUSE/RESET_THEN_REUSE), mirroring the CLI’s value-retention flags.

6.7. API surface & stability

  • Published API surface — a documented, supported public API with a stability statement (Supported API); the jhelm-kube service implementation moved behind an .internal package, and the KubernetesProvider SPI plus the 1.0 model mutability contract are documented.

  • Metrics — action-layer and chart-pull metrics in JhelmMetrics.

6.8. Correctness & release quality

  • Honest parity — the chart-parity suite compares against helm template with default values and a pinned --kube-version, byte-for-byte apart from a documented set of non-semantic comparison-ignores; over-emitting a resource helm does not is now a hard test failure rather than a warning. The docs state precisely what parity does and does not cover.

  • Packaging — the CLI fat-jar is excluded from the Maven Central bundle; Javadoc doclint is enforced on build.

7. 0.4.0

The pre-1.0 API freeze release, plus an MCP server, a REST library/starter split, and the upgrade value-handling fixes (including the value-retention flags listed as a known limitation in 0.2.0).

7.1. API freeze (breaking, pre-1.0)

  • Unchecked exceptions — the action layer and KubeService now throw the unchecked JhelmException hierarchy instead of throws Exception.

  • No leaked client types — the kubernetes-client ApiException/ApiClient are wrapped behind KubeClient, and the BouncyCastle PGP types behind SigningKey / VerificationKeyring.

  • Options objects — the action API now takes immutable InstallOptions, UpgradeOptions, UninstallOptions, and RollbackOptions objects instead of long parameter lists.

  • Immutable result models — Release, ReleaseInfo, and MapConfig are now immutable; release status is a typed ReleaseStatus enum and the lifecycle phase a LifecyclePhase enum.

7.2. MCP server

  • jhelm-mcp + jhelm-mcp-starter — a new Model Context Protocol server exposing jhelm operations as tools for AI agents, built on Spring AI 2.0 and mirroring the REST operation set. A READ_ONLY/FULL access mode (jhelm.mcp.mode, default READ_ONLY) gates the cluster-mutating tools.

7.3. REST library/starter split

  • Pure library — jhelm-rest is now a pure library; the new jhelm-rest-starter carries the auto-configuration (Spring-Boot-Admin style).

  • Access mode — a READ_ONLY/FULL mode (jhelm.rest.mode, default FULL) gates the cluster-mutating endpoints. Authentication is the embedding application’s responsibility (Spring Security).

7.4. Upgrade value handling

  • User values persisted — user-supplied values are now persisted as the release config, fixing a data-loss bug (and get values).

  • Value-retention flags implemented — --reset-values, --reuse-values, and --reset-then-reuse-values are now implemented. These were listed as a known limitation in 0.2.0.

7.5. New flags

  • Value flags — --set now type-coerces like Helm (int/bool/null; floats and leading-zero values stay strings), joined by --set-string, --set-file, and --set-json.

  • Lifecycle flags — --no-hooks (install/upgrade/uninstall/rollback), --history-max (release-history pruning, default 10), and --create-namespace.

7.6. Engine

  • Loud render failures — recursive / too-deep template rendering now fails with a TemplateRenderException instead of baking an ERROR: string into the manifest.

  • gotmpl4j 1.1.0 — the engine dependency moves to gotmpl4j 1.1.0 (perf wave: double formatting without BigDecimal, cached property accessors, an allocation-free lexer).

7.7. CLI

  • New commands — jhelm version and jhelm env.

  • Clean stdout — command output is now clean on stdout (banner and logs go to stderr), so jhelm template … > out.yaml produces valid YAML.

8. 0.2.0

Helm template-function conformance improvements, grinding jhelm-gotemplate-helm against Helm’s own pkg/engine/funcs_test.go suite (the divergence backlog drops from 37 cases to 1).

  • Helm 4 duration helpers — implemented mustToDuration, durationSeconds, durationMinutes, durationHours, durationDays, durationWeeks, durationMilliseconds, durationMicroseconds, durationNanoseconds, durationRoundTo, and durationTruncateTo, as faithful ports of Go’s time.Duration (parsing, rounding, and String() formatting).

  • from error reporting* — fromJson/fromYaml and fromJsonArray/fromYamlArray now return Helm’s map[Error:<message>] / [<message>] with Go’s json.Unmarshal type-mismatch text when handed valid input of the wrong shape, instead of silently returning an empty container.

  • toToml table output — now emits Go BurntSushi-style [table] / headers with the matching key ordering and indentation, instead of Jackson’s dotted keys.

All 346 reference charts continue to render byte-identically to helm template.

8.1. Known limitations

  • The helm upgrade value-retention flags (--reset-values, --reuse-values, --reset-then-reuse-values) remain unimplemented.

  • One fromToml conformance case (a malformed-input syntax error) still differs from Helm in the exact error wording, an inherent difference between the Jackson and Go (BurntSushi) TOML parsers.

9. 0.1.0 — first public prerelease

The first published release of jhelm, on Maven Central (marked pre-release).

  • Helm-faithful rendering — chart templates render byte-for-byte identically to helm template across a CI-enforced suite of 346 real-world charts (Bitnami, Grafana, GitLab, Kubernetes-monitoring, and others).

  • Go template engine — the full Go text/template language plus Helm’s Sprig function set, provided by the standalone gotmpl4j library (gotmpl4j 0.3.0). Renders absent/nil values as the empty string (missingkey=zero), matching Helm.

  • Helm template functions — include, tpl, required, lookup, toYaml/fromYaml, toJson/fromJson, toToml, and the chart/capabilities/files helpers.

  • Chart lifecycle — install, upgrade, rollback, uninstall, list, status, history, test.

  • Repositories & registries — classic HTTP repositories and OCI registries, with digest verification and content-based caching.

  • Dependency management — list, update, and build chart dependencies; recursive value coalescing across subcharts.

  • Packaging & provenance — package charts to .tgz; PGP sign and verify.

  • JSON Schema validation — chart values validated against values.schema.json (Draft-07).

  • Distribution surfaces — embeddable library, Spring Boot starter (auto-configured action beans), REST API (jhelm-rest, with OpenAPI docs), Maven packaging plugin (jhelm-plugin), and a 22-command CLI.

  • Built on Java 21, Spring Boot 4.0.7, Kubernetes Client 26.0.0, Picocli 4.7.7.

9.1. Known limitations

The following are out of scope for this first prerelease and are planned for 0.2.0:

  • helm upgrade value-retention flags — --reset-values, --reuse-values, and --reset-then-reuse-values are not yet implemented. upgrade applies the chart defaults merged with the values you pass on the command line.

  • Helm function fidelity — a small number of functions have documented partial-fidelity behaviour: the Helm 4 duration helpers, the fromYaml/fromJson error-map format, and nested toToml tables. See Function Coverage.

  • Subchart value coalescing — deeply-nested umbrella charts (GitLab-scale) may differ from Helm in rare value-coalesce edge cases; the common cases render byte-identically (the 346-chart parity suite).