@@ -66,7 +66,7 @@ func CheckSDKVersion(dir string, projectType ProjectType, settingsMap map[string
66
66
// Find the best result (prefer lock files over source files)
67
67
bestResult := findBestResult (results )
68
68
if bestResult == nil {
69
- return fmt .Errorf ("package %s not found in any project files" , getTargetPackageName ( projectType ))
69
+ return fmt .Errorf ("package %s not found in any project files" , projectType . TargetPackageName ( ))
70
70
}
71
71
72
72
if ! bestResult .Satisfied {
@@ -173,8 +173,8 @@ func parsePythonPackageVersion(line string) (string, bool) {
173
173
// clean up the version string if it contains multiple constraints
174
174
// handle comma-separated version constraints like ">=1.2.5,<2"
175
175
if strings .Contains (version , "," ) {
176
- parts := strings .Split (version , "," )
177
- for _ , part := range parts {
176
+ parts := strings .SplitSeq (version , "," )
177
+ for part := range parts {
178
178
trimmed := strings .TrimSpace (part )
179
179
if regexp .MustCompile (`\d` ).MatchString (trimmed ) {
180
180
if strings .ContainsAny (trimmed , "=~><" ) {
@@ -596,23 +596,37 @@ func checkUvLock(filePath, minVersion string) VersionCheckResult {
596
596
return VersionCheckResult {Error : err }
597
597
}
598
598
599
- // Look for livekit-agents in the lock file
600
- pattern := regexp .MustCompile (`(?m)^\s*livekit-agents\s*=\s*"([^"]+)"` )
601
- matches := pattern .FindStringSubmatch (string (content ))
602
- if matches != nil {
603
- version := matches [1 ]
604
- satisfied , err := isVersionSatisfied (version , minVersion , SourceTypeLock )
605
- return VersionCheckResult {
606
- PackageInfo : PackageInfo {
607
- Name : "livekit-agents" ,
608
- Version : version ,
609
- FoundInFile : filePath ,
610
- ProjectType : ProjectTypePythonUV ,
611
- Ecosystem : "pypi" ,
612
- },
613
- MinVersion : minVersion ,
614
- Satisfied : satisfied ,
615
- Error : err ,
599
+ type uvLockPackage struct {
600
+ Name string `toml:"name"`
601
+ Version string `toml:"version"`
602
+ }
603
+
604
+ type uvLockFile struct {
605
+ Packages []uvLockPackage `toml:"package"`
606
+ }
607
+
608
+ var uvLock uvLockFile
609
+ if err := toml .Unmarshal (content , & uvLock ); err != nil {
610
+ return VersionCheckResult {Error : err }
611
+ }
612
+
613
+ // Check for livekit-agents in the packages
614
+ for _ , pkg := range uvLock .Packages {
615
+ if pkg .Name == "livekit-agents" {
616
+ version := pkg .Version
617
+ satisfied , err := isVersionSatisfied (version , minVersion , SourceTypeLock )
618
+ return VersionCheckResult {
619
+ PackageInfo : PackageInfo {
620
+ Name : "livekit-agents" ,
621
+ Version : version ,
622
+ FoundInFile : filePath ,
623
+ ProjectType : ProjectTypePythonUV ,
624
+ Ecosystem : "pypi" ,
625
+ },
626
+ MinVersion : minVersion ,
627
+ Satisfied : satisfied ,
628
+ Error : err ,
629
+ }
616
630
}
617
631
}
618
632
@@ -660,10 +674,10 @@ func isVersionSatisfied(version, minVersion string, sourceType SourceType) (bool
660
674
case SourceTypeLock :
661
675
// For lock files, we have the exact version that was installed
662
676
// Check if this exact version is >= the minimum version
663
- normalizedVersion := normalizeVersion (version )
677
+ normalizedVersion := normalizeVersion (version , sourceType )
664
678
v , err := semver .NewVersion (normalizedVersion )
665
679
if err != nil {
666
- return false , fmt .Errorf ("invalid version format: %s" , version )
680
+ return false , fmt .Errorf ("failed to extract base version for %s: %w " , version , err )
667
681
}
668
682
669
683
min , err := semver .NewVersion (minVersion )
@@ -675,81 +689,54 @@ func isVersionSatisfied(version, minVersion string, sourceType SourceType) (bool
675
689
return ! v .LessThan (min ), nil
676
690
677
691
case SourceTypePackage :
678
- // For package files, we have a constraint that will be resolved at install time
679
- // Check if this constraint would allow installing a version that satisfies the minimum requirement
680
- packageConstraint , err := semver .NewConstraint (version )
692
+ // For package files, we may have a constraint that will be resolved at install time.
693
+
694
+ // First, we check if the normalized version is greater than or equal to the minimum version
695
+ // This is safe because in < and <= checks, the newest version will always be installed and in
696
+ // ^, ~ and >= checks, if the lower bound is greater than the minimum SDK version, we're good.
697
+ normalizedVersion := normalizeVersion (version , sourceType )
698
+ baseVersion , err := semver .NewVersion (normalizedVersion )
681
699
if err != nil {
682
- return false , fmt .Errorf ("invalid package constraint format: %s " , version )
700
+ return false , fmt .Errorf ("failed to extract base version for %s: %w " , version , err )
683
701
}
684
702
685
703
min , err := semver .NewVersion (minVersion )
686
704
if err != nil {
687
705
return false , fmt .Errorf ("invalid minimum version format: %s" , minVersion )
688
706
}
689
707
690
- // Check if the package constraint would allow installing a version >= minimum
691
- // We do this by checking if there exists a version >= minimum that satisfies the package constraint
692
- if packageConstraint .Check (min ) {
693
- // The minimum version satisfies the package constraint, so it would be installable
708
+ if baseVersion .GreaterThanEqual (min ) {
694
709
return true , nil
695
710
}
696
711
697
- // Check if the package constraint allows any version >= minimum
698
- // This handles cases like ">=1.5.0" where 1.0.0 doesn't satisfy it, but it would install 1.5.0+ which > 1.0.0
699
- // We'll test a few strategic versions to see if any satisfy the package constraint and are >= minimum
700
- testVersions := []string {
701
- minVersion , // The minimum version itself
702
- fmt .Sprintf ("%d.%d.%d" , min .Major ()+ 1 , 0 , 0 ),
703
- fmt .Sprintf ("%d.%d.%d" , min .Major (), min .Minor ()+ 1 , 0 ),
704
- fmt .Sprintf ("%d.%d.%d" , min .Major (), min .Minor (), min .Patch ()+ 1 ),
705
- }
706
-
707
- // Add more versions to cover edge cases
708
- if min .Major () > 0 {
709
- testVersions = append (testVersions , fmt .Sprintf ("%d.0.0" , min .Major ()))
710
- }
711
- if min .Minor () > 0 {
712
- testVersions = append (testVersions , fmt .Sprintf ("%d.%d.0" , min .Major (), min .Minor ()))
712
+ // Next, we check if min itself satisfies the package constraint. This resolves
713
+ // cases in which the range includes min, like ~1.0 when min is 1.0.0.
714
+ packageConstraint , err := semver .NewConstraint (version )
715
+ if err != nil {
716
+ return false , fmt .Errorf ("invalid package constraint format: %s" , version )
713
717
}
714
-
715
- // Add some specific versions that might be common in constraints
716
- testVersions = append (testVersions ,
717
- fmt .Sprintf ("%d.%d.0" , min .Major (), min .Minor ()+ 2 ),
718
- fmt .Sprintf ("%d.%d.0" , min .Major (), min .Minor ()+ 5 ),
719
- fmt .Sprintf ("%d.%d.0" , min .Major (), min .Minor ()+ 10 ),
720
- )
721
-
722
- for _ , testVersion := range testVersions {
723
- if v , err := semver .NewVersion (testVersion ); err == nil {
724
- // Check if this version is >= minimum and satisfies the package constraint
725
- if ! v .LessThan (min ) && packageConstraint .Check (v ) {
726
- return true , nil
727
- }
728
- }
718
+ if packageConstraint .Check (min ) {
719
+ return true , nil
729
720
}
730
-
721
+
722
+ // Finally, we need to check if the package constraint allows any version >= minimum.
723
+
731
724
return false , nil
732
725
733
726
default :
734
727
return false , fmt .Errorf ("unknown source type: %d" , sourceType )
735
728
}
736
729
}
737
730
738
- // normalizeVersion normalizes version strings for semver parsing
739
- func normalizeVersion (version string ) string {
740
- // Remove common prefixes and suffixes
731
+ // Cleans up version strings for parsing
732
+ func normalizeVersion (version string , sourceType SourceType ) string {
733
+ // Remove whitespace, quotes, and version range specifiers
741
734
version = strings .TrimSpace (version )
742
- version = strings .Trim (version , " \" '" )
743
-
744
- // Handle npm version ranges (^ and ~ are npm-specific, not semver constraints)
745
- if strings .HasPrefix (version , "^" ) || strings .HasPrefix (version , "~" ) {
746
- version = version [1 :]
747
- }
748
-
735
+ version = strings .Trim (version , `"'^~><=` )
749
736
return version
750
737
}
751
738
752
- // findBestResult finds the best result from multiple package checks
739
+ // Finds the best possible source for version checks
753
740
func findBestResult (results []VersionCheckResult ) * VersionCheckResult {
754
741
if len (results ) == 0 {
755
742
return nil
@@ -792,15 +779,3 @@ func findBestResult(results []VersionCheckResult) *VersionCheckResult {
792
779
793
780
return bestResult
794
781
}
795
-
796
- // getTargetPackageName returns the target package name for the project type
797
- func getTargetPackageName (projectType ProjectType ) string {
798
- switch projectType {
799
- case ProjectTypePythonPip , ProjectTypePythonUV :
800
- return "livekit-agents"
801
- case ProjectTypeNode :
802
- return "@livekit/agents"
803
- default :
804
- return ""
805
- }
806
- }
0 commit comments