From e8b053d999952c1a80fce234ad9b8ec3662e207b Mon Sep 17 00:00:00 2001 From: Evans Mungai Date: Thu, 28 May 2026 17:27:28 +0100 Subject: [PATCH] fix: avoid importing testing package in release builds internal/version and pkg/chart/common imported "testing" only to call testing.Testing(), pulling the testing package and its dependencies into release binaries. Replace those checks with exported KubeVersionMajorTesting / KubeVersionMinorTesting sentinels that default to zero. A new build-tagged file internal/version/version_helmtest.go seeds them in init() and only compiles under -tags helmtest. The Makefile applies -tags helmtest as a baseline so test-unit and test-coverage work directly or via `make test`. scripts/coverage.sh passes the tag to its raw `go test` call. CONTRIBUTING.md documents that contributors running tests outside the Makefile must pass the tag themselves. Signed-off-by: Evans Mungai --- CONTRIBUTING.md | 14 +++++++++++++ Makefile | 5 +++++ internal/version/version.go | 13 ++++++------ internal/version/version_helmtest.go | 31 ++++++++++++++++++++++++++++ pkg/chart/common/capabilities.go | 10 ++------- scripts/coverage.sh | 2 +- 6 files changed, 59 insertions(+), 16 deletions(-) create mode 100644 internal/version/version_helmtest.go diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7aa19972f..5ffbfaa5a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -232,6 +232,20 @@ guide](https://helm.sh/docs/community/developers/) to get started. Coding conventions and standards are explained in the [official developer docs](https://helm.sh/docs/developers/). +### Running tests + +Use the Makefile targets (`make test`, `make test-unit`, `make test-coverage`) +rather than invoking `go test ./...` directly. The Makefile passes the +`helmtest` build tag, which is required: it activates +`internal/version/version_helmtest.go` to seed testing-version sentinels. Test +binaries have no module info, so without this tag the production code path +attempts to read `k8s.io/client-go`'s version from build info and panics during +package init. + +If you run tests outside the Makefile (IDE test runners, `go test` directly, +custom CI), pass `-tags helmtest`. The tag is omitted from release builds so +the `testing` package and its dependencies stay out of shipped binaries. + ## Pull Requests Like any good open source project, we use Pull Requests (PRs) to track code changes. diff --git a/Makefile b/Makefile index 81b149a68..1254ec374 100644 --- a/Makefile +++ b/Makefile @@ -89,6 +89,11 @@ endif test: test-style test: test-unit +# The `helmtest` build tag activates internal/version/version_helmtest.go, +# which seeds testing-version sentinels so test binaries don't panic reading +# missing module info. Applied to all test invocations. +TESTFLAGS += -tags helmtest + .PHONY: test-unit test-unit: @echo diff --git a/internal/version/version.go b/internal/version/version.go index 007f79f16..dfd58c081 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -17,12 +17,10 @@ limitations under the License. package version import ( - "flag" "fmt" "log/slog" "runtime" "strings" - "testing" "github.com/Masterminds/semver/v3" ) @@ -44,8 +42,9 @@ var ( gitTreeState = "" ) -const ( - kubeClientGoVersionTesting = "v1.20" +var ( + KubeVersionMajorTesting uint64 + KubeVersionMinorTesting uint64 ) // BuildInfo describes the compile time information. @@ -82,8 +81,8 @@ func Get() BuildInfo { // Test builds don't include debug info / module info // (And even if they did, we probably want a stable version during tests anyway) // Return a default value for test builds - if testing.Testing() { - return kubeClientGoVersionTesting + if KubeVersionMajorTesting != 0 && KubeVersionMinorTesting != 0 { + return fmt.Sprintf("v%d.%d", KubeVersionMajorTesting, KubeVersionMinorTesting) } vstr, err := K8sIOClientGoModVersion() @@ -113,7 +112,7 @@ func Get() BuildInfo { } // HACK(bacongobbler): strip out GoVersion during a test run for consistent test output - if flag.Lookup("test.v") != nil { + if KubeVersionMajorTesting != 0 && KubeVersionMinorTesting != 0 { v.GoVersion = "" } return v diff --git a/internal/version/version_helmtest.go b/internal/version/version_helmtest.go new file mode 100644 index 000000000..0354d0985 --- /dev/null +++ b/internal/version/version_helmtest.go @@ -0,0 +1,31 @@ +//go:build helmtest + +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package version + +// This file is only compiled when the `helmtest` build tag is set (applied by +// the Makefile to all test invocations). It seeds the testing-version +// sentinels so that production code paths in this package and in +// pkg/chart/common substitute stable values instead of attempting to read +// build info from a `go test` binary (which has no module info and would +// panic during package init). + +func init() { + KubeVersionMajorTesting = 1 + KubeVersionMinorTesting = 20 +} diff --git a/pkg/chart/common/capabilities.go b/pkg/chart/common/capabilities.go index 20f4953cf..135476107 100644 --- a/pkg/chart/common/capabilities.go +++ b/pkg/chart/common/capabilities.go @@ -20,7 +20,6 @@ import ( "slices" "strconv" "strings" - "testing" "github.com/Masterminds/semver/v3" "k8s.io/client-go/kubernetes/scheme" @@ -32,11 +31,6 @@ import ( helmversion "helm.sh/helm/v4/internal/version" ) -const ( - kubeVersionMajorTesting = 1 - kubeVersionMinorTesting = 20 -) - var ( // DefaultVersionSet is the default version set, which includes only Core V1 ("v1"). DefaultVersionSet = allKnownVersions() @@ -146,8 +140,8 @@ func makeDefaultCapabilities() (*Capabilities, error) { // Test builds don't include debug info / module info // (And even if they did, we probably want stable capabilities for tests anyway) // Return a default value for test builds - if testing.Testing() { - return newCapabilities(kubeVersionMajorTesting, kubeVersionMinorTesting) + if helmversion.KubeVersionMajorTesting != 0 && helmversion.KubeVersionMinorTesting != 0 { + return newCapabilities(helmversion.KubeVersionMajorTesting, helmversion.KubeVersionMinorTesting) } vstr, err := helmversion.K8sIOClientGoModVersion() diff --git a/scripts/coverage.sh b/scripts/coverage.sh index 4a29a68ad..b07d5d7fd 100755 --- a/scripts/coverage.sh +++ b/scripts/coverage.sh @@ -37,7 +37,7 @@ generate_cover_data() { for d in $(go list "$target"); do ( local output="${coverdir}/${d//\//-}.cover" - go test -coverprofile="${output}" -covermode="$covermode" "$d" + go test -tags helmtest -coverprofile="${output}" -covermode="$covermode" "$d" ) done