From 1e145ee2b243ef97bce0622cf55da8aa17ebb65f Mon Sep 17 00:00:00 2001 From: Benoit Tigeot Date: Fri, 23 Jan 2026 15:25:09 +0100 Subject: [PATCH 1/4] fix: prevent warning when using version range constraints When using version ranges like ^1 or ~1.10, Helm incorrectly showed warnings about falling back to closest version. Only show the warning when an exact version is requested but not found. Fixes: https://github.com/helm/helm/issues/31757 Signed-off-by: Benoit Tigeot --- pkg/repo/v1/index.go | 8 +++++++- pkg/repo/v1/index_test.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/pkg/repo/v1/index.go b/pkg/repo/v1/index.go index 3dbdf7dfc..461416a59 100644 --- a/pkg/repo/v1/index.go +++ b/pkg/repo/v1/index.go @@ -175,6 +175,12 @@ func (i IndexFile) SortEntries() { } } +// isVersionRange checks if the version string is a range constraint (e.g., "^1", "~1.10") +// rather than an exact version (e.g., "1.10.0"). +func isVersionRange(version string) bool { + return strings.ContainsAny(version, "^~<>=!*xX") || strings.Contains(version, " || ") || strings.Contains(version, " - ") +} + // Get returns the ChartVersion for the given name. // // If version is empty, this will return the chart with the latest stable version, @@ -215,7 +221,7 @@ func (i IndexFile) Get(name, version string) (*ChartVersion, error) { } if constraint.Check(test) { - if len(version) != 0 { + if len(version) != 0 && !isVersionRange(version) { slog.Warn("unable to find exact version requested; falling back to closest available version", "chart", name, "requested", version, "selected", ver.Version) } return ver, nil diff --git a/pkg/repo/v1/index_test.go b/pkg/repo/v1/index_test.go index 550c8e82c..b01aa5a7d 100644 --- a/pkg/repo/v1/index_test.go +++ b/pkg/repo/v1/index_test.go @@ -718,3 +718,37 @@ func TestLoadIndex_DuplicateChartDeps(t *testing.T) { }) } } + +func TestIsVersionRange(t *testing.T) { + tests := []struct { + version string + expected bool + }{ + {"1.0.0", false}, + {"1.0.0+metadata", false}, + {"^1", true}, + {"^1.2.3", true}, + {"~1.10", true}, + {"~1.10.0", true}, + {">= 1.0.0", true}, + {"> 1.0.0", true}, + {"< 2.0.0", true}, + {"<= 2.0.0", true}, + {"!= 1.0.0", true}, + {"1.*", true}, + {"1.x", true}, + {"1.X", true}, + {"1.0.0 - 2.0.0", true}, + {"^1.0.0 || ^2.0.0", true}, + {">=1.0.0 <2.0.0", true}, + } + + for _, tt := range tests { + t.Run(tt.version, func(t *testing.T) { + got := isVersionRange(tt.version) + if got != tt.expected { + t.Errorf("isVersionRange(%q) = %v, want %v", tt.version, got, tt.expected) + } + }) + } +} From bf78b876c74ee4359a6cebb54e0b97183cdfe129 Mon Sep 17 00:00:00 2001 From: Benoit Tigeot Date: Fri, 23 Jan 2026 17:48:35 +0100 Subject: [PATCH 2/4] feat: report in debug the version we select with version range arg Signed-off-by: Benoit Tigeot --- pkg/repo/v1/index.go | 2 ++ pkg/repo/v1/index_test.go | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/pkg/repo/v1/index.go b/pkg/repo/v1/index.go index 461416a59..ccec82b3e 100644 --- a/pkg/repo/v1/index.go +++ b/pkg/repo/v1/index.go @@ -223,6 +223,8 @@ func (i IndexFile) Get(name, version string) (*ChartVersion, error) { if constraint.Check(test) { if len(version) != 0 && !isVersionRange(version) { slog.Warn("unable to find exact version requested; falling back to closest available version", "chart", name, "requested", version, "selected", ver.Version) + } else if len(version) != 0 && isVersionRange(version) { + slog.Debug("selected version matching constraint", "chart", name, "constraint", version, "selected", ver.Version) } return ver, nil } diff --git a/pkg/repo/v1/index_test.go b/pkg/repo/v1/index_test.go index b01aa5a7d..7a3120c89 100644 --- a/pkg/repo/v1/index_test.go +++ b/pkg/repo/v1/index_test.go @@ -726,6 +726,8 @@ func TestIsVersionRange(t *testing.T) { }{ {"1.0.0", false}, {"1.0.0+metadata", false}, + {"v1.19.2", false}, + {"v1", false}, {"^1", true}, {"^1.2.3", true}, {"~1.10", true}, @@ -738,6 +740,8 @@ func TestIsVersionRange(t *testing.T) { {"1.*", true}, {"1.x", true}, {"1.X", true}, + {"v1.x", true}, + {"v1.X", true}, {"1.0.0 - 2.0.0", true}, {"^1.0.0 || ^2.0.0", true}, {">=1.0.0 <2.0.0", true}, From b79d7f18813796f101f66eff6c513ded25edf0ad Mon Sep 17 00:00:00 2001 From: Benoit Tigeot Date: Wed, 4 Feb 2026 22:02:19 +0100 Subject: [PATCH 3/4] fix(version): version range || can has no space From Matt's comment > The check for " || " should remove the spaces and have "||". Spaces around the || aren't required. Signed-off-by: Benoit Tigeot --- pkg/repo/v1/index.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/repo/v1/index.go b/pkg/repo/v1/index.go index ccec82b3e..1ed7c32b6 100644 --- a/pkg/repo/v1/index.go +++ b/pkg/repo/v1/index.go @@ -178,7 +178,7 @@ func (i IndexFile) SortEntries() { // isVersionRange checks if the version string is a range constraint (e.g., "^1", "~1.10") // rather than an exact version (e.g., "1.10.0"). func isVersionRange(version string) bool { - return strings.ContainsAny(version, "^~<>=!*xX") || strings.Contains(version, " || ") || strings.Contains(version, " - ") + return strings.ContainsAny(version, "^~<>=!*xX") || strings.Contains(version, "||") || strings.Contains(version, " - ") } // Get returns the ChartVersion for the given name. From 740174a2b12074f7ca506ff330a918a4ff335c39 Mon Sep 17 00:00:00 2001 From: Benoit Tigeot Date: Fri, 24 Apr 2026 09:23:11 +0200 Subject: [PATCH 4/4] fix(version): avoid false range detection on prerelease x/X `isVersionRange` checked for `x`/`X` across the entire version string, misclassifying exact versions like `1.0.0-fix`, `2.0.0-next`, or `1.0.0+exp` as ranges. Signed-off-by: Benoit Tigeot --- pkg/repo/v1/index.go | 9 ++++++++- pkg/repo/v1/index_test.go | 4 ++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/pkg/repo/v1/index.go b/pkg/repo/v1/index.go index 1ed7c32b6..f9829ec7f 100644 --- a/pkg/repo/v1/index.go +++ b/pkg/repo/v1/index.go @@ -178,7 +178,14 @@ func (i IndexFile) SortEntries() { // isVersionRange checks if the version string is a range constraint (e.g., "^1", "~1.10") // rather than an exact version (e.g., "1.10.0"). func isVersionRange(version string) bool { - return strings.ContainsAny(version, "^~<>=!*xX") || strings.Contains(version, "||") || strings.Contains(version, " - ") + if strings.ContainsAny(version, "^~<>=!*") || strings.Contains(version, "||") || strings.Contains(version, " - ") { + return true + } + core := version + if idx := strings.IndexAny(version, "-+"); idx != -1 { + core = version[:idx] + } + return strings.ContainsAny(core, "xX") } // Get returns the ChartVersion for the given name. diff --git a/pkg/repo/v1/index_test.go b/pkg/repo/v1/index_test.go index 7a3120c89..a86efe1e3 100644 --- a/pkg/repo/v1/index_test.go +++ b/pkg/repo/v1/index_test.go @@ -745,6 +745,10 @@ func TestIsVersionRange(t *testing.T) { {"1.0.0 - 2.0.0", true}, {"^1.0.0 || ^2.0.0", true}, {">=1.0.0 <2.0.0", true}, + // Exact versions with 'x'/'X' in prerelease or build metadata + {"1.0.0-fix", false}, + {"2.0.0-next", false}, + {"1.0.0+exp", false}, } for _, tt := range tests {