diff --git a/src/code/.vs/Microsoft.PowerShell.PSResourceGet/v17/.suo b/src/code/.vs/Microsoft.PowerShell.PSResourceGet/v17/.suo new file mode 100644 index 000000000..05b0bcf91 Binary files /dev/null and b/src/code/.vs/Microsoft.PowerShell.PSResourceGet/v17/.suo differ diff --git a/src/code/FindHelper.cs b/src/code/FindHelper.cs index 043736822..4418b5acd 100644 --- a/src/code/FindHelper.cs +++ b/src/code/FindHelper.cs @@ -637,11 +637,23 @@ private IEnumerable SearchByNames(ServerApiCall currentServer, R continue; } + // Check to see if version falls within version range PSResourceInfo foundPkg = currentResult.returnedObject; - parentPkgs.Add(foundPkg); - _pkgsLeftToFind.Remove(foundPkg.Name); - pkgsFound.Add(String.Format("{0}{1}", foundPkg.Name, foundPkg.Version.ToString())); - yield return foundPkg; + string versionStr = $"{foundPkg.Version}"; + if (foundPkg.IsPrerelease) + { + versionStr += $"-{foundPkg.Prerelease}"; + } + + if (NuGetVersion.TryParse(versionStr, out NuGetVersion version) + && _versionRange.Satisfies(version)) + { + parentPkgs.Add(foundPkg); + _pkgsLeftToFind.Remove(foundPkg.Name); + pkgsFound.Add(String.Format("{0}{1}", foundPkg.Name, foundPkg.Version.ToString())); + + yield return foundPkg; + } } } } @@ -699,7 +711,7 @@ internal IEnumerable FindDependencyPackages( { PSResourceInfo depPkg = null; - if (dep.VersionRange == VersionRange.All) + if (dep.VersionRange.Equals(VersionRange.All)) { FindResults responses = currentServer.FindName(dep.Name, _prerelease, _type, out ErrorRecord errRecord); if (errRecord != null) @@ -768,9 +780,31 @@ internal IEnumerable FindDependencyPackages( continue; } - depPkg = currentResult.returnedObject; + // Check to see if version falls within version range + PSResourceInfo foundDep = currentResult.returnedObject; + string depVersionStr = $"{foundDep.Version}"; + if (foundDep.IsPrerelease) { + depVersionStr += $"-{foundDep.Prerelease}"; + } + + if (NuGetVersion.TryParse(depVersionStr, out NuGetVersion depVersion) + && dep.VersionRange.Satisfies(depVersion)) + { + depPkg = foundDep; + } } + if (depPkg == null) + { + _cmdletPassedIn.WriteError(new ErrorRecord( + new PackageNotFoundException($"Dependency package '{dep.Name}' with version range '{dep.VersionRange}' could not be found"), + "DependencyPackageNotFound", + ErrorCategory.ObjectNotFound, + this)); + + yield return null; + } + string pkgHashKey = String.Format("{0}{1}", depPkg.Name, depPkg.Version.ToString()); if (!foundPkgs.Contains(pkgHashKey)) diff --git a/src/code/InstallHelper.cs b/src/code/InstallHelper.cs index 8ede5fd39..09bd6dbcd 100644 --- a/src/code/InstallHelper.cs +++ b/src/code/InstallHelper.cs @@ -626,20 +626,47 @@ private Hashtable InstallPackage( break; } - PSResourceResult currentResult = currentResponseUtil.ConvertToPSResourceResult(responseResults: responses).FirstOrDefault(); + PSResourceInfo pkgToInstall = null; + foreach (PSResourceResult currentResult in currentResponseUtil.ConvertToPSResourceResult(responses)) + { + if (currentResult.exception != null && !currentResult.exception.Message.Equals(string.Empty)) + { + // V2Server API calls will return non-empty response when package is not found but fail at conversion time + _cmdletPassedIn.WriteError(new ErrorRecord( + new InvalidOrEmptyResponse($"Package '{pkgNameToInstall}' could not be installed", currentResult.exception), + "InstallPackageFailure", + ErrorCategory.InvalidData, + this)); + } + else if (searchVersionType == VersionType.VersionRange) + { + // Check to see if version falls within version range + PSResourceInfo foundPkg = currentResult.returnedObject; + string versionStr = $"{foundPkg.Version}"; + if (foundPkg.IsPrerelease) + { + versionStr += $"-{foundPkg.Prerelease}"; + } + + if (NuGetVersion.TryParse(versionStr, out NuGetVersion version) + && _versionRange.Satisfies(version)) + { + pkgToInstall = foundPkg; + + break; + } + } else { + pkgToInstall = currentResult.returnedObject; + + break; + } + } - if (currentResult.exception != null && !currentResult.exception.Message.Equals(string.Empty)) + if (pkgToInstall == null) { - // V2Server API calls will return non-empty response when package is not found but fail at conversion time - _cmdletPassedIn.WriteError(new ErrorRecord( - new InvalidOrEmptyResponse($"Package '{pkgNameToInstall}' could not be installed", currentResult.exception), - "InstallPackageFailure", - ErrorCategory.InvalidData, - this)); return packagesHash; } - PSResourceInfo pkgToInstall = currentResult.returnedObject; pkgToInstall.RepositorySourceLocation = repository.Uri.ToString(); pkgToInstall.AdditionalMetadata.TryGetValue("NormalizedVersion", out string pkgVersion); diff --git a/src/code/V2ServerAPICalls.cs b/src/code/V2ServerAPICalls.cs index f08d6d9ea..89d6990df 100644 --- a/src/code/V2ServerAPICalls.cs +++ b/src/code/V2ServerAPICalls.cs @@ -797,7 +797,16 @@ private string FindVersionGlobbing(string packageName, VersionRange versionRange if (versionRange.MaxVersion != null) { string operation = versionRange.IsMaxInclusive ? "le" : "lt"; - maxPart = String.Format(format, operation, $"'{versionRange.MaxVersion.ToNormalizedString()}'"); + // Adding 1 because we want to retrieve all the prerelease versions for the max version and PSGallery views prerelease as higher than its stable + // eg 3.0.0-prerelease > 3.0.0 + string maxString = $"{versionRange.MaxVersion.Major}.{versionRange.MaxVersion.Minor + 1}"; + if (NuGetVersion.TryParse(maxString, out NuGetVersion maxVersion)) + { + maxPart = String.Format(format, operation, $"'{maxVersion.ToNormalizedString()}'"); + } + else { + maxPart = String.Format(format, operation, $"'{versionRange.MaxVersion.ToNormalizedString()}'"); + } } string versionFilterParts = String.Empty; @@ -833,8 +842,7 @@ private string FindVersionGlobbing(string packageName, VersionRange versionRange filterQuery += $"{andOperator}{versionFilterParts}"; } - string topParam = getOnlyLatest ? "$top=1" : "$top=100"; // only need 1 package if interested in latest - string paginationParam = $"$inlinecount=allpages&$skip={skip}&{topParam}"; + string paginationParam = $"$inlinecount=allpages&$skip={skip}"; filterQuery = filterQuery.EndsWith("=") ? string.Empty : filterQuery; var requestUrlV2 = $"{Repository.Uri}/FindPackagesById()?id='{packageName}'&$orderby=NormalizedVersion desc&{paginationParam}{filterQuery}"; diff --git a/test/FindPSResourceTests/FindPSResourceV2Server.Tests.ps1 b/test/FindPSResourceTests/FindPSResourceV2Server.Tests.ps1 index 456c70205..8c50efe3e 100644 --- a/test/FindPSResourceTests/FindPSResourceV2Server.Tests.ps1 +++ b/test/FindPSResourceTests/FindPSResourceV2Server.Tests.ps1 @@ -140,7 +140,7 @@ Describe 'Test HTTP Find-PSResource for V2 Server Protocol' -tags 'CI' { elseif ($pkg.Name -eq "TestModuleWithDependencyB") { $foundDepB = $true - $foundDepBCorrectVersion = [System.Version]$pkg.Version -ge [System.Version]"3.0" + $foundDepBCorrectVersion = [System.Version]$pkg.Version -ge [System.Version]"1.0" } elseif ($pkg.Name -eq "TestModuleWithDependencyD") { @@ -372,6 +372,12 @@ Describe 'Test HTTP Find-PSResource for V2 Server Protocol' -tags 'CI' { $res.Count | Should -Be 1 $res.Repository | Should -Be $PSGalleryName } + + It "find all resources within a version range, including prereleases" { + $res = Find-PSResource -Name "PSReadLine" -Version "(2.0,2.1)" -Prerelease -Repository $PSGalleryName + $res | Should -Not -BeNullOrEmpty + $res.Count | Should -Be 7 + } } Describe 'Test HTTP Find-PSResource for V2 Server Protocol' -tags 'ManualValidationOnly' { diff --git a/test/InstallPSResourceTests/InstallPSResourceV2Server.Tests.ps1 b/test/InstallPSResourceTests/InstallPSResourceV2Server.Tests.ps1 index 682662c33..ad341c160 100644 --- a/test/InstallPSResourceTests/InstallPSResourceV2Server.Tests.ps1 +++ b/test/InstallPSResourceTests/InstallPSResourceV2Server.Tests.ps1 @@ -141,11 +141,11 @@ Describe 'Test Install-PSResource for V2 Server scenarios' -tags 'CI' { It "Install a module with a dependency" { Uninstall-PSResource -Name "TestModuleWithDependency*" -Version "*" -SkipDependencyCheck -ErrorAction SilentlyContinue - Install-PSResource -Name "TestModuleWithDependencyC" -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository + Install-PSResource -Name "TestModuleWithDependencyC" -Version "5.0.0.0" -Repository $PSGalleryName -TrustRepository $pkg = Get-InstalledPSResource "TestModuleWithDependencyC" $pkg.Name | Should -Be "TestModuleWithDependencyC" - $pkg.Version | Should -Be "1.0" + $pkg.Version | Should -Be "5.0" $pkg = Get-InstalledPSResource "TestModuleWithDependencyB" $pkg.Name | Should -Be "TestModuleWithDependencyB" @@ -153,7 +153,7 @@ Describe 'Test Install-PSResource for V2 Server scenarios' -tags 'CI' { $pkg = Get-InstalledPSResource "TestModuleWithDependencyD" $pkg.Name | Should -Be "TestModuleWithDependencyD" - $pkg.Version | Should -Be "1.0" + $pkg.Version | Should -Be "2.0" } It "Install a module with a dependency and skip installing the dependency" {