Sprig's toPrettyJson uses json.MarshalIndent which HTML-escapes &, <, >
to \u0026, \u003c, \u003e. This breaks URLs and other strings containing
these characters when used in Helm chart templates.
* fix(engine): introduce toPrettyRawJson instead of overriding toPrettyJson
Per maintainer review, do not override Sprig's toPrettyJson/mustToPrettyJson
(which HTML-escape &, <, >), since existing charts may rely on the escaped
output. Instead add new toPrettyRawJson/mustToPrettyRawJson functions that
produce indented JSON with HTML characters unescaped — the indented
counterpart to Sprig's toRawJson — preserving backwards compatibility.
Add a regression test asserting toPrettyJson still escapes HTML.
* fix(engine): gofmt alignment in funcMap
* fix(engine): resolve leftover merge conflict in funcs_test.go
The merge of main left unresolved conflict markers in the mustFuncsTests
table. Keep main's brace and indentation style for the slice literal, and
re-apply the toPrettyRawJson/mustToPrettyRawJson cases on top of it.
Signed-off-by: piotr.laczykowski <piotr.laczykowski@example.com>
Dependabot bumps that pull in a dependency requiring Go 1.27 (#32786,
#32755) raise the go directive in go.mod, which in turn activates the
modernize analyzer's embedlit and stringscut checks. Those checks then
fire on pre-existing code and fail golangci-lint, so every such bump is
blocked on a cleanup it does not own.
Do the cleanup once, here, together with the directive bump that makes
it legal: neither fix compiles at go 1.26.0 (promoted-field keys in
composite literals and strings.CutLast are both Go 1.27).
Changes are the modernize autofix, reviewed:
- embedlit: drop the embedded field type from struct literals, mostly
ObjectMeta/TypeMeta in tests, plus a few in production code.
- stringscut: parseTemplateSimpleErrorString now uses strings.CutLast
instead of strings.LastIndex with manual slicing.
CI already builds with Go 1.27 (GOLANG_VERSION in .github/env), so this
only aligns the module's declared language version with the toolchain.
Signed-off-by: Terry Howe <thowe@nvidia.com>
* ci: run e2e registry jobs from one matrix
Replace the per-registry dockerhub and gitlab jobs with a single matrix
job. Each registry is configured by the same E2E_<ID>_* settings, and
the registry host moves to a repository variable, so adding a registry
is a matrix entry plus its settings.
Signed-off-by: Terry Howe <terrylhowe@gmail.com>
* ci: drop kind cluster from e2e registry jobs
The Kubernetes install test exercises the same pull path as the
registry tests and already runs per-PR against kind in e2e.yml.
Signed-off-by: Terry Howe <terrylhowe@gmail.com>
* ci: read e2e registry usernames from secrets
Signed-off-by: Terry Howe <terrylhowe@gmail.com>
---------
Signed-off-by: Terry Howe <terrylhowe@gmail.com>
NewTransport cloned http.DefaultTransport only when that held an
*http.Transport, and otherwise returned the shared value as is. A
RoundTripper installed by an imported package cannot be cloned, and
ensureTLSConfig cannot apply Helm's TLS settings to it either, so the
TLS options of a registry client silently had nowhere to go and
registry login panicked on the resulting error.
Return a transport built from the standard library defaults in that
case, and log that the replacement is not used, so the transport a
client gets is always one Helm owns and can configure.
Signed-off-by: yuwk <1729065730@qq.com>
`helm list --help` said --max 0 returns "the server's default". The
action only truncates when Limit > 0 (pkg/action/list.go:216), so 0 or a
negative value returns every release.
The root help listed HELM_COLOR as "(default: never)"; envColorMode()
in pkg/cli/environment.go falls back to "auto", which --color also
reports as its default.
Signed-off-by: ZainnQureshii <43629888+ZainnQureshii@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Add a gitlab job to the e2e-registries workflow, configured from
vars.E2E_GITLAB_USERNAME, vars.E2E_GITLAB_NAMESPACE,
vars.E2E_GITLAB_REPOSITORY and secrets.E2E_GITLAB_PASSWORD. GitLab nests
repository paths under the project, so the suite runs in its default
layout. Its token endpoint only implements the distribution GET flow,
which covers the OAuth2 POST fallback that Docker Hub does not exercise.
Document running the suite against GitLab with a deploy token.
Signed-off-by: Terry Howe <terrylhowe@gmail.com>
* feature: automated OCI end-to-end tests
Signed-off-by: Terry Howe <terrylhowe@gmail.com>
* test: address review feedback on OCI e2e tests
Move the OCI end-to-end tests out of package cmd into a dedicated
test/e2e package behind an 'e2e' build tag, and drive a real helm binary
rather than executeActionCommand.
- Parameterize the registry, namespace, and credentials so the tests run
against any OCI registry, not a hard-coded personal GHCR namespace.
- Isolate each run's repositories and namespace with a random run ID.
- Fail rather than skip when required configuration is missing, since the
build tag is already an explicit opt-in.
- Pass the registry password over stdin so credentials never reach argv
or test output.
- Assert an authentication-specific failure in the invalid credential
test instead of accepting any error.
- Install through a real helm binary against the ambient kubecontext, so
the Kubernetes test no longer runs against the fake kube client.
- Stop dereferencing the os.Stat result after a stat error.
- Add a 'make test-e2e' target and test/e2e/README.md.
Signed-off-by: Terry Howe <thowe@nvidia.com>
* test: address second round of review feedback on OCI e2e tests
- Verify pulled chart contents against the source fixture instead of only
checking that a path of the expected name exists, so a registry serving a
different artifact under the same tag fails the test.
- Skip the invalid-credential test against a registry that serves anonymous
access, where there is no authentication to exercise.
- Delete the namespace the Kubernetes test creates, and wait for it to go,
so runs do not accumulate namespaces. A caller-supplied
HELM_E2E_NAMESPACE is left in place.
Signed-off-by: Terry Howe <thowe@nvidia.com>
* test: run OCI e2e tests across registry implementations
The suite has no registry-specific behavior, so covering a new registry
is a matter of pointing it at one. Add the CI wiring to do that, and make
the repository paths suitable for shared registries.
- Repository paths are now stable rather than per-run. The fixtures are
immutable, so re-running overwrites each tag with identical content.
This bounds the repositories a shared registry accumulates and lets
registries that require repositories to exist before a push (ECR) have
them created ahead of time. HELM_E2E_ISOLATE restores per-run paths.
- Add HELM_E2E_REPO to set the repository path under the registry.
- Add e2e.yml: local authenticated registry plus kind, no secrets, so it
runs on pull requests from forks.
- Add e2e-registries.yml: nightly matrix over GHCR, Quay, and ECR with
fail-fast disabled. A registry with no secrets configured is skipped,
so they can be wired up one at a time.
Signed-off-by: Terry Howe <thowe@nvidia.com>
* test: address review feedback on e2e harness and workflow
- Build the helm binary by relative package path instead of the v4 module
path, so the harness also works on branches where the module carries a
different major version suffix.
- Scope Kubernetes release names to the run. A caller-supplied
HELM_E2E_NAMESPACE may already hold releases, and a fixed name could
collide with one that cleanup would then uninstall.
- Fail a partially configured registry leg in the nightly workflow
instead of reporting it as configured and failing later in the harness
or the AWS CLI. A leg with no registry set is still skipped.
Signed-off-by: Terry Howe <thowe@nvidia.com>
* test: honor HELM_KUBECONTEXT when deleting the e2e namespace
Helm subprocesses inherit HELM_KUBECONTEXT from the environment, but the
cleanup client loaded the kubeconfig's current-context. When the two
differ, releases are installed into one cluster while the namespace
delete is sent to another, where it returns NotFound and is treated as
success, leaking the namespace it was meant to remove.
Apply the same context override when building the cleanup client.
Signed-off-by: Terry Howe <thowe@nvidia.com>
* test: build e2e cleanup client from helm's own settings
Overriding only HELM_KUBECONTEXT still left the cleanup client able to
diverge from the helm subprocesses, which also honor HELM_KUBEAPISERVER,
HELM_KUBETOKEN, HELM_KUBECAFILE, HELM_KUBETLS_SERVER_NAME and
HELM_KUBEINSECURE_SKIP_TLS_VERIFY. Any of those could send the namespace
delete to a different endpoint, where NotFound reads as success and the
namespace leaks.
Resolve the client through cli.New().RESTClientGetter() instead, so it is
built exactly the way the subprocesses build theirs and stays in step
without mirroring each variable by hand.
Signed-off-by: Terry Howe <thowe@nvidia.com>
* test: run hosted e2e against docker hub in single-repository mode
Replace the GHCR, Quay and ECR legs of e2e-registries with a Docker Hub
job using the E2E_DOCKER_* secrets. Docker Hub does not nest repository
paths and a scoped token can only write to one repository, so add
HELM_E2E_SINGLE_REPO: each fixture is repackaged under that repository's
name with its own prerelease version, e.g. 0.1.0-unicode-chart.
Signed-off-by: Terry Howe <terrylhowe@gmail.com>
* ci: read non-secret docker hub e2e settings from repository variables
Signed-off-by: Terry Howe <terrylhowe@gmail.com>
* ci: run e2e workflow on main only
Signed-off-by: Terry Howe <terrylhowe@gmail.com>
* ci: update kind, registry, httpd, checkout and setup-go in e2e workflows
Signed-off-by: Terry Howe <terrylhowe@gmail.com>
* ci: serialize e2e-registries runs that share docker hub tags
Signed-off-by: Terry Howe <terrylhowe@gmail.com>
---------
Signed-off-by: Terry Howe <terrylhowe@gmail.com>
Signed-off-by: Terry Howe <thowe@nvidia.com>
Resolves govulncheck failures for GO-2026-6603, GO-2026-6610 and
related HTTP/2 vulnerabilities fixed in golang.org/x/net v0.60.0.
golang.org/x/net is an indirect dependency, so Dependabot version
updates do not cover it.
Signed-off-by: Terry Howe <terrylhowe@gmail.com>
The golangci-lint job now gets Go 1.27.2 from setup-go, and
golangci-lint v2.13.1 cannot read its export data, so typecheck fails
on files that were not changed:
could not load export data: internal error in importing
"internal/goarch" (cannot decode "internal/goarch", export data
version 5 is greater than maximum supported version 4)
v2.14.0 works with Go 1.27.2 and reports no issues on main.
Signed-off-by: Ruslan Shaydullin <shaydullin.r.d@outlook.com>
* fix(action): wait for CRD establishment under HookOnlyStrategy
Motivation:
installCRDs() selected its CRD-establishment waiter using the
release's WaitStrategy. Under kube.HookOnlyStrategy, that waiter's
Wait() is a no-op by design, intended to skip waiting on general
chart resources such as Deployments. That no-op also silently
skipped the unrelated CRD establishment check, which exists so
custom resources depending on freshly-installed CRDs aren't
processed before the API server recognizes the new CRD types. Helm 3
performed this CRD establishment wait unconditionally, regardless of
the release's wait flag/strategy. Under HookOnlyStrategy this can
intermittently produce an install failure such as:
no matches for kind "ServiceMonitor" in version "monitoring.coreos.com/v1"
when a chart installs CRDs and dependent custom resources in the
same release, if the CRDs have not finished being established by the
time Helm processes the custom resources.
Approach:
In installCRDs(), compute a local crdWaitStrategy that substitutes
kube.StatusWatcherStrategy for the CRD-establishment wait only when
i.WaitStrategy is HookOnlyStrategy. i.WaitStrategy itself is left
untouched, so every other waiter-selection call site (the
general-resource wait later in Install.RunWithContext, and the
equivalents in rollback.go/upgrade.go) keeps HookOnlyStrategy's
existing no-op behavior for general resources. This mirrors the
promotion to StatusWatcherStrategy already used elsewhere in this
file for HookOnlyStrategy combined with RollbackOnFailure.
Validation:
- go build ./...
- go test ./pkg/action/... ./pkg/kube/...
- golangci-lint run ./pkg/action/... ./pkg/kube/... (0 issues)
- go vet ./pkg/action/... ./pkg/kube/... (clean)
- go mod tidy -diff (empty)
- make build && make test-coverage PKG="./pkg/action/... ./pkg/kube/..."
- Added TestInstallCRDs_HookOnlyStrategyStillWaitsForEstablishment, a
targeted unit test with a fake kube.Interface that mimics the real
no-op hookOnlyWaiter versus a genuine wait attempt. Verified via
git stash that this test fails on the pre-fix code ("An error is
expected but got nil") and passes after the fix.
This was validated at the unit level with a fake Kubernetes client,
not against a live cluster.
Report: https://github.com/helm/helm/issues/32671
Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
Assisted-by: claude-sonnet-5 (via Claude Code)
* test(action): reuse FailingKubeClient for the CRD wait strategy test
Drop the test-only strategyAwareWaitKubeClient/strategyAwareWaiter fakes
and use kubefake.FailingKubeClient instead. FailingKubeClient now records
the WaitStrategy passed to GetWaiter/GetWaiterWithOptions in
RecordedWaitStrategies, alongside the existing RecordedWaitOptions, so the
test can assert installCRDs() asks for a StatusWatcherStrategy waiter
under HookOnlyStrategy.
Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
Assisted-by: claude-opus-5-5 (via Claude Code)
---------
Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
Co-authored-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
In (*Client).Login(), credentials.ServerAddressFromHostname(key) was
originally added in commit 6ab7aa361 as a workaround for oras-go v2.6.0
where ServerAddressFromRegistry("registry-1.docker.io") did not return
the canonical https://index.docker.io/v1/.
Since oras-go was bumped to v2.6.2 (which incorporates oras-project/oras-go#966),
ServerAddressFromRegistry already normalizes both "docker.io" and
"registry-1.docker.io" properly, making this second call redundant.
Closes#32681
Signed-off-by: abdouladieng <diengabdoulaziz110@gmail.com>
otel/log v0.21.0 removed log.Kind, log.Value and log.KeyValue, so the
otlploghttp and stdoutlog exporters must move to the matching release
or they fail to compile.
Signed-off-by: Terry Howe <thowe@nvidia.com>
* feat(storage): add internal/storage/driver for release v2
Copy of pkg/storage/driver operating on internal/release/v2, with the
storage object Type field set to helm.sh/release.v2. The
sh.helm.release.v1 name prefix is unchanged.
Signed-off-by: Evans Mungai <mbuevans@gmail.com>
* feat(storage): mark configmap release schema with a label
ConfigMaps have no Type field to record it in, unlike Secrets. Absence
of the label means v1. Also documents why the sh.helm.release.v1 name
prefix is decoupled from Type.
Signed-off-by: Evans Mungai <mbuevans@gmail.com>
* Update internal/storage/driver/sql.go
Co-authored-by: Terry Howe <terrylhowe@gmail.com>
Signed-off-by: Evans Mungai <mbuevans@gmail.com>
* Update test
Signed-off-by: Evans Mungai <mbuevans@gmail.com>
* PR comment fixes
- Add comment to explain sql driver update behaviour when it comes to updating custom labels
- Apply custom labels in memory driver
Signed-off-by: Evans Mungai <mbuevans@gmail.com>
* Update internal/storage/driver/records.go
Co-authored-by: Terry Howe <terrylhowe@gmail.com>
Signed-off-by: Evans Mungai <mbuevans@gmail.com>
---------
Signed-off-by: Evans Mungai <mbuevans@gmail.com>
Co-authored-by: Terry Howe <terrylhowe@gmail.com>