Skip to content

Commit 27b0cd3

Browse files
committed
chore(agents): fix version normalization for lockfiles
1 parent 05ff6d8 commit 27b0cd3

File tree

2 files changed

+49
-23
lines changed

2 files changed

+49
-23
lines changed

pkg/agentfs/sdk_version_check.go

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,7 @@ func isVersionSatisfied(version, minVersion string, sourceType SourceType) (bool
660660
case SourceTypeLock:
661661
// For lock files, we have the exact version that was installed
662662
// Check if this exact version is >= the minimum version
663-
normalizedVersion := normalizeVersion(version)
663+
normalizedVersion := normalizeVersion(version, sourceType)
664664
v, err := semver.NewVersion(normalizedVersion)
665665
if err != nil {
666666
return false, fmt.Errorf("invalid version format: %s", version)
@@ -735,16 +735,22 @@ func isVersionSatisfied(version, minVersion string, sourceType SourceType) (bool
735735
}
736736
}
737737

738-
// normalizeVersion normalizes version strings for semver parsing
739-
func normalizeVersion(version string) string {
740-
// Remove common prefixes and suffixes
738+
// normalizeVersion cleans up version strings for parsing
739+
func normalizeVersion(version string, sourceType SourceType) string {
740+
// Remove whitespace and quotes
741741
version = strings.TrimSpace(version)
742-
version = strings.Trim(version, " \"'")
742+
version = strings.Trim(version, `"'`)
743743

744-
// Handle npm version ranges (^ and ~ are npm-specific, not semver constraints)
745-
if strings.HasPrefix(version, "^") || strings.HasPrefix(version, "~") {
746-
version = version[1:]
744+
// For lock files, we want the exact version, so remove npm-style prefixes
745+
// For package files, we want to preserve the constraint operators
746+
if sourceType == SourceTypeLock {
747+
// Handle npm version ranges (^ and ~ are npm-specific, not semver constraints)
748+
if strings.HasPrefix(version, "^") || strings.HasPrefix(version, "~") {
749+
version = version[1:]
750+
}
747751
}
752+
// For SourceTypePackage, we preserve all operators including ^ and ~
753+
// as they are valid semver constraints that the semver library can parse
748754

749755
return version
750756
}

pkg/agentfs/sdk_version_check_test.go

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -251,25 +251,45 @@ func TestIsVersionSatisfied(t *testing.T) {
251251

252252
func TestNormalizeVersion(t *testing.T) {
253253
tests := []struct {
254-
input string
255-
expected string
254+
input string
255+
sourceType SourceType
256+
expected string
257+
description string
256258
}{
257-
{"1.5.0", "1.5.0"},
258-
{"^1.5.0", "1.5.0"},
259-
{"~1.5.0", "1.5.0"},
260-
{">=1.5.0", ">=1.5.0"},
261-
{"<2.0.0", "<2.0.0"},
262-
{"==1.5.0", "==1.5.0"},
263-
{" 1.5.0 ", "1.5.0"},
264-
{`"1.5.0"`, "1.5.0"},
265-
{`'1.5.0'`, "1.5.0"},
266-
{"*", "*"},
267-
{"latest", "latest"},
259+
// Lock file tests (should remove ^ and ~)
260+
{"1.5.0", SourceTypeLock, "1.5.0", "exact version"},
261+
{"^1.5.0", SourceTypeLock, "1.5.0", "npm caret removed for lock file"},
262+
{"~1.5.0", SourceTypeLock, "1.5.0", "npm tilde removed for lock file"},
263+
{">=1.5.0", SourceTypeLock, ">=1.5.0", "semver operators preserved for lock file"},
264+
{"<2.0.0", SourceTypeLock, "<2.0.0", "semver operators preserved for lock file"},
265+
{"==1.5.0", SourceTypeLock, "==1.5.0", "semver operators preserved for lock file"},
266+
{" 1.5.0 ", SourceTypeLock, "1.5.0", "whitespace removed"},
267+
{`"1.5.0"`, SourceTypeLock, "1.5.0", "quotes removed"},
268+
{`'1.5.0'`, SourceTypeLock, "1.5.0", "quotes removed"},
269+
{"*", SourceTypeLock, "*", "wildcard preserved"},
270+
{"latest", SourceTypeLock, "latest", "latest preserved"},
271+
272+
// Package file tests (should preserve ^ and ~)
273+
{"1.5.0", SourceTypePackage, "1.5.0", "exact version"},
274+
{"^1.5.0", SourceTypePackage, "^1.5.0", "npm caret preserved for package file"},
275+
{"~1.5.0", SourceTypePackage, "~1.5.0", "npm tilde preserved for package file"},
276+
{">=1.5.0", SourceTypePackage, ">=1.5.0", "semver operators preserved for package file"},
277+
{"<2.0.0", SourceTypePackage, "<2.0.0", "semver operators preserved for package file"},
278+
{"==1.5.0", SourceTypePackage, "==1.5.0", "semver operators preserved for package file"},
279+
{" 1.5.0 ", SourceTypePackage, "1.5.0", "whitespace removed"},
280+
{`"1.5.0"`, SourceTypePackage, "1.5.0", "quotes removed"},
281+
{`'1.5.0'`, SourceTypePackage, "1.5.0", "quotes removed"},
282+
{"*", SourceTypePackage, "*", "wildcard preserved"},
283+
{"latest", SourceTypePackage, "latest", "latest preserved"},
268284
}
269285

270286
for _, tt := range tests {
271-
t.Run(tt.input, func(t *testing.T) {
272-
result := normalizeVersion(tt.input)
287+
sourceTypeStr := "Package"
288+
if tt.sourceType == SourceTypeLock {
289+
sourceTypeStr = "Lock"
290+
}
291+
t.Run(fmt.Sprintf("%s_%s_%s", tt.input, sourceTypeStr, tt.description), func(t *testing.T) {
292+
result := normalizeVersion(tt.input, tt.sourceType)
273293
if result != tt.expected {
274294
t.Errorf("Expected %s but got %s", tt.expected, result)
275295
}

0 commit comments

Comments
 (0)