diff --git a/pkg/repo/v1/index.go b/pkg/repo/v1/index.go index 7969d64e9..0655e2e8d 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 517457dc4..47ea9e849 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) + } + }) + } +}