diff --git a/src/code/ContainerRegistryServerAPICalls.cs b/src/code/ContainerRegistryServerAPICalls.cs index 9c17c0db0..20f084f10 100644 --- a/src/code/ContainerRegistryServerAPICalls.cs +++ b/src/code/ContainerRegistryServerAPICalls.cs @@ -81,6 +81,16 @@ public ContainerRegistryServerAPICalls(PSRepositoryInfo repository, PSCmdlet cmd #region Overridden Methods + public override Task FindVersionAsync(string packageName, string version, ResourceType type) + { + return null; + } + + public override Task FindVersionGlobbingAsync(string packageName, VersionRange versionRange, bool includePrerelease, ResourceType type, bool getOnlyLatest) + { + return null; + } + /// /// Find method which allows for searching for all packages from a repository and returns latest version for each. /// @@ -146,6 +156,12 @@ public override FindResults FindName(string packageName, bool includePrerelease, return new FindResults(stringResponse: new string[] { }, hashtableResponse: pkgResult.ToArray(), responseType: containerRegistryFindResponseType); } + + public override Task FindNameAsync(string packageName, bool includePrerelease, ResourceType type) + { + return null; + } + /// /// Find method which allows for searching for single name and tag and returns latest version. /// Name: no wildcard support diff --git a/src/code/FindHelper.cs b/src/code/FindHelper.cs index 388be9090..6bdd4bacb 100644 --- a/src/code/FindHelper.cs +++ b/src/code/FindHelper.cs @@ -1,16 +1,22 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using Microsoft.PowerShell.PSResourceGet.UtilClasses; -using NuGet.Versioning; using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Management.Automation; using System.Net; using System.Runtime.ExceptionServices; +using System.Runtime.InteropServices; +using System.Security.Cryptography; using System.Text.RegularExpressions; using System.Threading; +using System.Threading.Tasks; +using Azure; +using Microsoft.PowerShell.PSResourceGet.UtilClasses; +using NuGet.Protocol.Core.Types; +using NuGet.Versioning; namespace Microsoft.PowerShell.PSResourceGet.Cmdlets { @@ -35,7 +41,19 @@ internal class FindHelper private bool _includeDependencies = false; private bool _repositoryNameContainsWildcard = true; private NetworkCredential _networkCredential; - private Dictionary> _packagesFound; + + // Gets intantiated each time a cmdlet is run. + // If running 'Install-PSResource Az, TestModule, NewTestModule', it will contain one parent and its dependencies. + private ConcurrentDictionary> _packagesFound; + + // Creates a new instance of depPkgsFound each time FindDependencyPackages() is called. + // This will eventually return the PSResourceInfo object to the main cmdlet class. + private ConcurrentDictionary depPkgsFound; + + // Contains the latest found version of a particular package. + private ConcurrentDictionary _knownLatestPkgVersion; + + ConcurrentDictionary> _cachedNetworkCalls; #endregion @@ -48,7 +66,10 @@ public FindHelper(CancellationToken cancellationToken, PSCmdlet cmdletPassedIn, _cancellationToken = cancellationToken; _cmdletPassedIn = cmdletPassedIn; _networkCredential = networkCredential; - _packagesFound = new Dictionary>(StringComparer.OrdinalIgnoreCase); + _packagesFound = new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase); + _knownLatestPkgVersion = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + _type = ResourceType.None; + _cachedNetworkCalls = new ConcurrentDictionary>(); } #endregion @@ -740,7 +761,6 @@ private IEnumerable SearchByNames(ServerApiCall currentServer, R { parentPkgs.Add(foundPkg); TryAddToPackagesFound(foundPkg); - _cmdletPassedIn.WriteDebug($"Found package '{foundPkg.Name}' version '{foundPkg.Version}'"); yield return foundPkg; } @@ -811,6 +831,8 @@ private IEnumerable SearchByNames(ServerApiCall currentServer, R if (_tag.Length == 0) { responses = currentServer.FindName(pkgName, _prerelease, _type, out errRecord); + _cmdletPassedIn.WriteDebug($"returned back to SearchByName() 834"); + } else { @@ -884,7 +906,20 @@ private IEnumerable SearchByNames(ServerApiCall currentServer, R FindResults responses = null; if (_tag.Length == 0) { - responses = currentServer.FindVersion(pkgName, _nugetVersion.ToNormalizedString(), _type, out errRecord); + ConcurrentDictionary> cachedNetworkCalls = new ConcurrentDictionary>(); + Task response = null; + if (currentServer.Repository.ApiVersion == PSRepositoryInfo.APIVersion.V2) { + string key = $"{pkgName}|{_nugetVersion.ToNormalizedString()}|{_type}"; + response = cachedNetworkCalls.GetOrAdd(key, _ => currentServer.FindVersionAsync(pkgName, _nugetVersion.ToNormalizedString(), _type)); + + responses = response.GetAwaiter().GetResult(); + + } + else { + responses = currentServer.FindVersion(pkgName, _nugetVersion.ToNormalizedString(), _type, out errRecord); + } + + } else { @@ -956,7 +991,17 @@ private IEnumerable SearchByNames(ServerApiCall currentServer, R FindResults responses = null; if (_tag.Length == 0) { - responses = currentServer.FindVersionGlobbing(pkgName, _versionRange, _prerelease, _type, getOnlyLatest: false, out errRecord); + ConcurrentDictionary> cachedNetworkCalls = new ConcurrentDictionary>(); + Task response = null; + if (currentServer.Repository.ApiVersion == PSRepositoryInfo.APIVersion.V2) { + string key = $"{pkgName}|{_versionRange.ToString()}|{_type}"; + response = cachedNetworkCalls.GetOrAdd(key, _ => currentServer.FindVersionGlobbingAsync(pkgName, _versionRange, _prerelease, _type, getOnlyLatest: false)); + + responses = response.GetAwaiter().GetResult(); + } + else { + responses = currentServer.FindVersionGlobbing(pkgName, _versionRange, _prerelease, _type, getOnlyLatest: false, out errRecord); + } } else { @@ -1023,7 +1068,7 @@ private IEnumerable SearchByNames(ServerApiCall currentServer, R { foreach (PSResourceInfo currentPkg in parentPkgs) { - _cmdletPassedIn.WriteDebug($"Finding dependency packages for '{currentPkg.Name}'"); + _cmdletPassedIn.WriteDebug($"Finding dependency packages (SearchByNames) for '{currentPkg.Name}'"); foreach (PSResourceInfo pkgDep in FindDependencyPackages(currentServer, currentResponseUtil, currentPkg, repository)) { yield return pkgDep; @@ -1050,31 +1095,55 @@ private HashSet GetPackageNamesPopulated(string[] pkgNames) return pkgsToDiscover; } - private bool TryAddToPackagesFound(PSResourceInfo foundPkg) - { + { + // This handles prerelease versions as well. bool addedToHash = false; string foundPkgName = foundPkg.Name; - string foundPkgVersion = Utils.GetNormalizedVersionString(foundPkg.Version.ToString(), foundPkg.Prerelease); + string foundPkgVersion = FormatPkgVersionString(foundPkg); if (_packagesFound.ContainsKey(foundPkgName)) { - List pkgVersions = _packagesFound[foundPkgName] as List; + _packagesFound.TryGetValue(foundPkgName, out List pkgVersions); if (!pkgVersions.Contains(foundPkgVersion)) { - pkgVersions.Add(foundPkgVersion); - _packagesFound[foundPkgName] = pkgVersions; + List newPkgVersions = new List(pkgVersions) + { + foundPkgVersion + }; + _packagesFound.TryUpdate(foundPkgName, newPkgVersions, pkgVersions); + addedToHash = true; } } else { - _packagesFound.Add(foundPkg.Name, new List { foundPkgVersion }); + _packagesFound.TryAdd(foundPkg.Name, new List { foundPkgVersion }); addedToHash = true; } - _cmdletPassedIn.WriteDebug($"Found package '{foundPkg.Name}' version '{foundPkg.Version}'"); + return addedToHash; + } + + private bool TryAddToKnownLatestPkgVersion(PSResourceInfo foundPkg) + { + // This handles prerelease versions as well. + bool addedToHash = false; + string foundPkgName = foundPkg.Name; + string foundPkgVersion = FormatPkgVersionString(foundPkg); + + if (_knownLatestPkgVersion.ContainsKey(foundPkgName)) + { + _knownLatestPkgVersion.TryGetValue(foundPkgName, out PSResourceInfo oldPkgVersion); + _knownLatestPkgVersion.TryUpdate(foundPkgName, foundPkg, oldPkgVersion); + addedToHash = true; + } + else + { + _knownLatestPkgVersion.TryAdd(foundPkg.Name, foundPkg); + addedToHash = true; + } return addedToHash; } @@ -1082,12 +1151,10 @@ private bool TryAddToPackagesFound(PSResourceInfo foundPkg) private string FormatPkgVersionString(PSResourceInfo pkg) { string fullPkgVersion = pkg.Version.ToString(); - if (!string.IsNullOrWhiteSpace(pkg.Prerelease)) { fullPkgVersion += $"-{pkg.Prerelease}"; } - _cmdletPassedIn.WriteDebug($"Formatted full package version is: '{fullPkgVersion}'"); return fullPkgVersion; } @@ -1096,250 +1163,290 @@ private string FormatPkgVersionString(PSResourceInfo pkg) #region Internal Client Search Methods - internal IEnumerable FindDependencyPackages( - ServerApiCall currentServer, - ResponseUtil currentResponseUtil, - PSResourceInfo currentPkg, - PSRepositoryInfo repository) + internal IEnumerable FindDependencyPackages(ServerApiCall currentServer, ResponseUtil currentResponseUtil, PSResourceInfo currentPkg, PSRepositoryInfo repository) { + depPkgsFound = new ConcurrentDictionary(); + _cmdletPassedIn.WriteDebug($"In FindHelper::FindDependencyPackages() - {currentPkg.Name}"); + FindDependencyPackagesHelper(currentServer, currentResponseUtil, currentPkg, repository); + + return depPkgsFound.Values.ToList(); + } + + // Method 2 + internal void FindDependencyPackagesHelper(ServerApiCall currentServer, ResponseUtil currentResponseUtil, PSResourceInfo currentPkg, PSRepositoryInfo repository) + { + ConcurrentBag errors = new ConcurrentBag(); if (currentPkg.Dependencies.Length > 0) { - foreach (var dep in currentPkg.Dependencies) + // If finding more than 5 packages, do so concurrently + //const int PARALLEL_THRESHOLD = 5; // TODO: Trottle limit from user, defaults to 5; + int processorCount = Environment.ProcessorCount; + int maxDegreeOfParallelism = processorCount * 4; + if (currentPkg.Dependencies.Length > processorCount) + { + Parallel.ForEach(currentPkg.Dependencies, new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism }, dep => + { + FindDependencyPackageVersion(dep, currentServer, currentResponseUtil, currentPkg, repository, errors); + }); + // TODO: what is perf if parallel.ForEach is always run? + } + else + { + foreach (var dep in currentPkg.Dependencies) + { + FindDependencyPackageVersion(dep, currentServer, currentResponseUtil, currentPkg, repository, errors); + } + } + foreach (ErrorRecord error in errors) { - PSResourceInfo depPkg = null; + _cmdletPassedIn.WriteError(error); + } + } + } + + // Method 3 + private void FindDependencyPackageVersion(Dependency dep, ServerApiCall currentServer, ResponseUtil currentResponseUtil, PSResourceInfo currentPkg, PSRepositoryInfo repository, ConcurrentBag errors) + { + PSResourceInfo depPkg = null; - if (dep.VersionRange.Equals(VersionRange.All)) + if (dep.VersionRange.Equals(VersionRange.All) || !dep.VersionRange.HasUpperBound) + { + // Case 1: No upper bound, eg: "*" or "(1.0.0, )" + // Check if the latest version is cached + if (_knownLatestPkgVersion.TryGetValue(dep.Name, out PSResourceInfo cachedDepPkg)) { - FindResults responses = currentServer.FindName(dep.Name, includePrerelease: true, _type, out ErrorRecord errRecord); - if (errRecord != null) - { - if (errRecord.Exception is ResourceNotFoundException) - { - _cmdletPassedIn.WriteVerbose(errRecord.Exception.Message); - } - else - { - _cmdletPassedIn.WriteError(errRecord); - } - yield return null; - continue; - } + depPkg = cachedDepPkg; + } + else + { + // Find this version from the server + depPkg = FindDependencyWithLowerBound(dep, currentServer, currentResponseUtil, currentPkg, repository, errors); + } + } + else if (dep.VersionRange.HasLowerBound && dep.VersionRange.MinVersion.Equals(dep.VersionRange.MaxVersion)) + { + // Case 2: Exact package version, eg: "1.0.0" or "[1.0.0, 1.0.0]" + // Note: need to check if VersionRange has lower bound because if it does not, MinVersion will be null + // Check if the latest version is cached, and if this latest version is the version we're looking for + if (_knownLatestPkgVersion.TryGetValue(dep.Name, out PSResourceInfo cachedRangePkg) && + NuGetVersion.TryParse(cachedRangePkg.Version?.ToString(), out NuGetVersion cachedPkgVersion) && + dep.VersionRange.Satisfies(cachedPkgVersion)) + { + depPkg = cachedRangePkg; + } + else + { + depPkg = FindDependencyWithSpecificVersion(dep, currentServer, currentResponseUtil, currentPkg, repository, errors); + } + } + else + { + // Case 3: Version range with an upper bound, eg: "(1.0.0, 3.0.0)" + // Check if the latest version is cached, and if this latest version is the version we're looking for + if (_knownLatestPkgVersion.TryGetValue(dep.Name, out PSResourceInfo cachedRangePkg) && + NuGetVersion.TryParse(cachedRangePkg.Version?.ToString(), out NuGetVersion cachedPkgVersion) && + dep.VersionRange.Satisfies(cachedPkgVersion)) + { + depPkg = cachedRangePkg; + } + else + { + depPkg = FindDependencyWithUpperBound(dep, currentServer, currentResponseUtil, currentPkg, repository, errors); + } + } + } - PSResourceResult currentResult = currentResponseUtil.ConvertToPSResourceResult(responses).FirstOrDefault(); - if (currentResult == null) - { - // This scenario may occur when the package version requested is unlisted. - _cmdletPassedIn.WriteError(new ErrorRecord( - new ResourceNotFoundException($"Dependency package with name '{dep.Name}' could not be found in repository '{repository.Name}'"), - "DependencyPackageNotFound", - ErrorCategory.ObjectNotFound, - this)); - yield return null; - continue; - } + // Method 4 + private PSResourceInfo FindDependencyWithSpecificVersion(Dependency dep, ServerApiCall currentServer, ResponseUtil currentResponseUtil, PSResourceInfo currentPkg, PSRepositoryInfo repository, ConcurrentBag errors) + { + PSResourceInfo depPkg = null; + ErrorRecord errRecord = null; + FindResults responses = null; + Task response = null; - if (currentResult.exception != null && !currentResult.exception.Message.Equals(string.Empty)) - { - _cmdletPassedIn.WriteError(new ErrorRecord( - new ResourceNotFoundException($"Dependency package with name '{dep.Name}' could not be found in repository '{repository.Name}'", currentResult.exception), - "DependencyPackageNotFound", - ErrorCategory.ObjectNotFound, - this)); - yield return null; - continue; - } + if (currentServer.Repository.ApiVersion == PSRepositoryInfo.APIVersion.V2) + { + // See if the network call we're making is already cached, if not, call FindNameAsync() and cache results + string key = $"{dep.Name}|{dep.VersionRange.MaxVersion.ToString()}|{_type}"; + response = _cachedNetworkCalls.GetOrAdd(key, _ => currentServer.FindVersionAsync(dep.Name, dep.VersionRange.MaxVersion.ToString(), _type)); + + responses = response.GetAwaiter().GetResult(); + } + else + { + responses = currentServer.FindVersion(dep.Name, dep.VersionRange.MaxVersion.ToString(), _type, out errRecord); + } - depPkg = currentResult.returnedObject; - if (!_packagesFound.ContainsKey(depPkg.Name)) - { - foreach (PSResourceInfo depRes in FindDependencyPackages(currentServer, currentResponseUtil, depPkg, repository)) - { - yield return depRes; - } - } - else - { - List pkgVersions = _packagesFound[depPkg.Name] as List; - // _packagesFound has depPkg.name in it, but the version is not the same - if (!pkgVersions.Contains(FormatPkgVersionString(depPkg))) - { - foreach (PSResourceInfo depRes in FindDependencyPackages(currentServer, currentResponseUtil, depPkg, repository)) - { - yield return depRes; - } - } - } - } - else if(dep.VersionRange.MaxVersion != null && dep.VersionRange.MinVersion != null && dep.VersionRange.MaxVersion.OriginalVersion.Equals(dep.VersionRange.MinVersion.OriginalVersion)) + // Error handling and Convert to PSResource object + if (errRecord != null) + { + errors.Add(new ErrorRecord( + new ResourceNotFoundException($"Dependency package could not be found: '{errRecord.Exception.Message}'"), + "DependencyPackageNotFound", + ErrorCategory.ObjectNotFound, + this)); + } + else + { + PSResourceResult currentResult = currentResponseUtil.ConvertToPSResourceResult(responses).FirstOrDefault(); + if (currentResult == null || currentResult.exception != null && !currentResult.exception.Message.Equals(string.Empty)) + { + errors.Add(new ErrorRecord( + new ResourceNotFoundException($"Dependency package with name '{dep.Name}' and version range '{dep.VersionRange}' could not be found in repository '{repository.Name}'", currentResult?.exception ?? new ItemNotFoundException()), + "DependencyPackageNotFound", + ErrorCategory.ObjectNotFound, + this)); + } + else + { + depPkg = currentResult.returnedObject; + TryAddToKnownLatestPkgVersion(depPkg); + + string pkgVersion = FormatPkgVersionString(depPkg); + string key = $"{depPkg.Name}{pkgVersion}"; + if (!depPkgsFound.ContainsKey(key)) { - string depPkgVersion = dep.VersionRange.MaxVersion.OriginalVersion; - FindResults responses = currentServer.FindVersion(dep.Name, version: dep.VersionRange.MaxVersion.ToNormalizedString(), _type, out ErrorRecord errRecord); - if (errRecord != null) - { - if (errRecord.Exception is ResourceNotFoundException) - { - _cmdletPassedIn.WriteVerbose(errRecord.Exception.Message); - } - else - { - _cmdletPassedIn.WriteError(errRecord); - } - yield return null; - continue; - } + // Add pkg to collection of packages found then find dependencies + // depPkgsFound creates a new instance of depPkgsFound each time FindDependencyPackages() is called. + // This will eventually return the PSResourceInfo object to the main cmdlet class. + depPkgsFound.TryAdd(key, depPkg); + FindDependencyPackagesHelper(currentServer, currentResponseUtil, depPkg, repository); + } + } + } - PSResourceResult currentResult = currentResponseUtil.ConvertToPSResourceResult(responses).FirstOrDefault(); - if (currentResult == null) - { - // This scenario may occur when the package version requested is unlisted. - _cmdletPassedIn.WriteError(new ErrorRecord( - new ResourceNotFoundException($"Dependency package with name '{dep.Name}' and version '{depPkgVersion}' could not be found in repository '{repository.Name}'"), - "DependencyPackageNotFound", - ErrorCategory.ObjectNotFound, - this)); - yield return null; - continue; - } + return depPkg; + } - if (currentResult.exception != null && !currentResult.exception.Message.Equals(string.Empty)) - { - _cmdletPassedIn.WriteError(new ErrorRecord( - new ResourceNotFoundException($"Dependency package with name '{dep.Name}' and version '{depPkgVersion}' could not be found in repository '{repository.Name}'", currentResult.exception), - "DependencyPackageNotFound", - ErrorCategory.ObjectNotFound, - this)); - yield return null; - continue; - } + // Method 5 + private PSResourceInfo FindDependencyWithLowerBound(Dependency dep, ServerApiCall currentServer, ResponseUtil currentResponseUtil, PSResourceInfo currentPkg, PSRepositoryInfo repository, ConcurrentBag errors) + { + PSResourceInfo depPkg = null; + FindResults responses = null; + ErrorRecord errRecord = null; + Task response = null; + + if (currentServer.Repository.ApiVersion == PSRepositoryInfo.APIVersion.V2) + { + // See if the network call we're making is already cached, if not, call FindNameAsync() and cache results + string key = $"{dep.Name}|*|{_type}"; + response = _cachedNetworkCalls.GetOrAdd(key, _ => currentServer.FindNameAsync(dep.Name, includePrerelease: true, _type)); + + responses = response.GetAwaiter().GetResult(); + } + else + { + responses = currentServer.FindName(dep.Name, includePrerelease: true, _type, out errRecord); + } - depPkg = currentResult.returnedObject; + // Error handling and Convert to PSResource object + if (errRecord != null) + { + errors.Add(new ErrorRecord( + new ResourceNotFoundException($"Dependency package could not be found: '{errRecord.Exception.Message}'"), + "DependencyPackageNotFound", + ErrorCategory.ObjectNotFound, + this)); + } + else + { + PSResourceResult currentResult = currentResponseUtil.ConvertToPSResourceResult(responses).FirstOrDefault(); + if (currentResult == null || currentResult.exception != null && !currentResult.exception.Message.Equals(string.Empty)) + { + errors.Add(new ErrorRecord( + new ResourceNotFoundException($"Dependency package with name '{dep.Name}' and version range '{dep.VersionRange}' could not be found in repository '{repository.Name}'", currentResult?.exception ?? new ItemNotFoundException()), + "DependencyPackageNotFound", + ErrorCategory.ObjectNotFound, + this)); + } + else + { + depPkg = currentResult.returnedObject; + TryAddToKnownLatestPkgVersion(depPkg); - if (!_packagesFound.ContainsKey(depPkg.Name)) - { - foreach (PSResourceInfo depRes in FindDependencyPackages(currentServer, currentResponseUtil, depPkg, repository)) - { - yield return depRes; - } - } - else - { - List pkgVersions = _packagesFound[depPkg.Name] as List; - // _packagesFound has depPkg.name in it, but the version is not the same - if (!pkgVersions.Contains(FormatPkgVersionString(depPkg))) - { - foreach (PSResourceInfo depRes in FindDependencyPackages(currentServer, currentResponseUtil, depPkg, repository)) - { - yield return depRes; - } - } - } - } - else + string pkgVersion = FormatPkgVersionString(depPkg); + string key = $"{depPkg.Name}{pkgVersion}"; + if (!depPkgsFound.ContainsKey(key)) { - FindResults responses = currentServer.FindVersionGlobbing(dep.Name, dep.VersionRange, includePrerelease: true, ResourceType.None, getOnlyLatest: true, out ErrorRecord errRecord); - if (errRecord != null) - { - if (errRecord.Exception is ResourceNotFoundException) - { - _cmdletPassedIn.WriteVerbose(errRecord.Exception.Message); - } - else - { - _cmdletPassedIn.WriteError(errRecord); - } - yield return null; - continue; - } - - if (responses.IsFindResultsEmpty()) - { - _cmdletPassedIn.WriteError(new ErrorRecord( - new InvalidOrEmptyResponse($"Dependency package with name {dep.Name} and version range {dep.VersionRange} could not be found in repository '{repository.Name}"), - "FindDepPackagesFindVersionGlobbingFailure", - ErrorCategory.InvalidResult, - this)); - yield return null; - continue; - } + // Add pkg to collection of packages found then find dependencies + // depPkgsFound creates a new instance of depPkgsFound each time FindDependencyPackages() is called. + // This will eventually return the PSResourceInfo object to the main cmdlet class. + depPkgsFound.TryAdd(key, depPkg); + FindDependencyPackagesHelper(currentServer, currentResponseUtil, depPkg, repository); + } + } + } - foreach (PSResourceResult currentResult in currentResponseUtil.ConvertToPSResourceResult(responses)) - { - if (currentResult.exception != null && !currentResult.exception.Message.Equals(string.Empty)) - { - _cmdletPassedIn.WriteError(new ErrorRecord( - new ResourceNotFoundException($"Dependency package with name '{dep.Name}' and version range '{dep.VersionRange}' could not be found in repository '{repository.Name}'", currentResult.exception), - "DependencyPackageNotFound", - ErrorCategory.ObjectNotFound, - this)); + return depPkg; + } - yield return null; - continue; - } + // Method 6 + private PSResourceInfo FindDependencyWithUpperBound(Dependency dep, ServerApiCall currentServer, ResponseUtil currentResponseUtil, PSResourceInfo currentPkg, PSRepositoryInfo repository, ConcurrentBag errors) + { + PSResourceInfo depPkg = null; + ErrorRecord errRecord = null; + FindResults responses = null; + Task response = null; - // Check to see if version falls within version range - PSResourceInfo foundDep = currentResult.returnedObject; - string depVersionStr = $"{foundDep.Version}"; - if (foundDep.IsPrerelease) - { - depVersionStr += $"-{foundDep.Prerelease}"; - } + ConcurrentDictionary> cachedNetworkCalls = new ConcurrentDictionary>(); - if (NuGetVersion.TryParse(depVersionStr, out NuGetVersion depVersion) - && dep.VersionRange.Satisfies(depVersion)) - { - depPkg = foundDep; - } - } + if (currentServer.Repository.ApiVersion == PSRepositoryInfo.APIVersion.V2) + { + // See if the network call we're making is already caced, if not, call FindNameAsync() and cache results + string key = $"{dep.Name}|{dep.VersionRange.MaxVersion.ToString()}|{_type}"; + response = cachedNetworkCalls.GetOrAdd(key, _ => currentServer.FindVersionGlobbingAsync(dep.Name, dep.VersionRange, includePrerelease: true, ResourceType.None, getOnlyLatest: true)); - if (depPkg == null) - { - continue; - } + responses = response.GetAwaiter().GetResult(); - if (!_packagesFound.ContainsKey(depPkg.Name)) - { - foreach (PSResourceInfo depRes in FindDependencyPackages(currentServer, currentResponseUtil, depPkg, repository)) - { - yield return depRes; - } - } - else - { - List pkgVersions = _packagesFound[depPkg.Name] as List; - // _packagesFound has depPkg.name in it, but the version is not the same - if (!pkgVersions.Contains(FormatPkgVersionString(depPkg))) - { - foreach (PSResourceInfo depRes in FindDependencyPackages(currentServer, currentResponseUtil, depPkg, repository)) - { - yield return depRes; - } - } - } - } - } } - - if (!_packagesFound.ContainsKey(currentPkg.Name)) + else { - TryAddToPackagesFound(currentPkg); + responses = currentServer.FindVersionGlobbing(dep.Name, dep.VersionRange, includePrerelease: true, ResourceType.None, getOnlyLatest: true, out errRecord); + } - yield return currentPkg; + // Error handling and Convert to PSResource object + if (errRecord != null) + { + errors.Add(new ErrorRecord( + new ResourceNotFoundException($"Dependency package could not be found: '{errRecord.Exception.Message}'"), + "DependencyPackageNotFound", + ErrorCategory.ObjectNotFound, + this)); } else { - List pkgVersions = _packagesFound[currentPkg.Name] as List; - // _packagesFound has currentPkg.name in it, but the version is not the same - if (!pkgVersions.Contains(FormatPkgVersionString(currentPkg))) + PSResourceResult currentResult = currentResponseUtil.ConvertToPSResourceResult(responses).FirstOrDefault(); + if (currentResult == null || currentResult.exception != null && !currentResult.exception.Message.Equals(string.Empty)) + { + errors.Add(new ErrorRecord( + new ResourceNotFoundException($"Dependency package with name '{dep.Name}' and version range '{dep.VersionRange}' could not be found in repository '{repository.Name}'", currentResult?.exception ?? new ItemNotFoundException()), + "DependencyPackageNotFound", + ErrorCategory.ObjectNotFound, + this)); + } + else { - TryAddToPackagesFound(currentPkg); + depPkg = currentResult.returnedObject; - yield return currentPkg; + TryAddToKnownLatestPkgVersion(depPkg); + + string pkgVersion = FormatPkgVersionString(depPkg); + string key = $"{depPkg.Name}{pkgVersion}"; + if (!depPkgsFound.ContainsKey(key)) + { + // Add pkg to collection of packages found then find dependencies + // depPkgsFound creates a new instance of depPkgsFound each time FindDependencyPackages() is called. + // This will eventually return the PSResourceInfo object to the main cmdlet class. + depPkgsFound.TryAdd(key, depPkg); + FindDependencyPackagesHelper(currentServer, currentResponseUtil, depPkg, repository); + } } } - + + return depPkg; } - #endregion + #endregion } } diff --git a/src/code/GetHelper.cs b/src/code/GetHelper.cs index 1fa98887e..649fa7f66 100644 --- a/src/code/GetHelper.cs +++ b/src/code/GetHelper.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using Microsoft.PowerShell.PSResourceGet.UtilClasses; @@ -95,10 +95,39 @@ public IEnumerable GetPackagesFromPath( bool selectPrereleaseOnly) { _cmdletPassedIn.WriteDebug("In GetHelper::GetPackagesFromPath()"); + if (name.Length < 1 ) + { + _cmdletPassedIn.WriteDebug("In GetHelper::GetPackagesFromPath() :: length of names less than 1"); + } + if (pathsToSearch.Count < 1 ) + { + _cmdletPassedIn.WriteDebug("In GetHelper::GetPackagesFromPath() :: length of pathsToSearch less than 1"); + } + List pkgPathsByName = FilterPkgPathsByName(name, pathsToSearch); + _cmdletPassedIn.WriteDebug("In GetHelper::GetPackagesFromPath():: entering filterpkgpathsbyversion"); + if (versionRange == null ) + { + _cmdletPassedIn.WriteDebug("In GetHelper::GetPackagesFromPath() :: versionRange is null"); + } + if (pkgPathsByName.Count < 1 ) + { + _cmdletPassedIn.WriteDebug("In GetHelper::GetPackagesFromPath() :: length of pkgPathsByName less than 1"); + } + + if (_scriptDictionary == null) + { + _cmdletPassedIn.WriteDebug("In GetHelper::GetPackagesFromPath() :: _scriptDictionary is null"); + } + foreach (string pkgPath in FilterPkgPathsByVersion(versionRange, pkgPathsByName, selectPrereleaseOnly)) { + if (pkgPath == null) + { + _cmdletPassedIn.WriteDebug("In GetHelper::GetPackagesFromPath() :: pkgPath is null"); + } + PSResourceInfo pkg = OutputPackageObject(pkgPath, _scriptDictionary); if (pkg != null) { diff --git a/src/code/InstallHelper.cs b/src/code/InstallHelper.cs index 0616cf040..242af2cc4 100644 --- a/src/code/InstallHelper.cs +++ b/src/code/InstallHelper.cs @@ -2,19 +2,24 @@ // Licensed under the MIT License. using Microsoft.PowerShell.PSResourceGet.UtilClasses; +using NuGet.Protocol.Core.Types; using NuGet.Versioning; using System; using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Management.Automation; +using System.Management.Automation.Language; using System.Net; using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System.Threading; +using System.Threading.Tasks; namespace Microsoft.PowerShell.PSResourceGet.Cmdlets { @@ -53,6 +58,7 @@ internal class InstallHelper private string _tmpPath; private NetworkCredential _networkCredential; private HashSet _packagesOnMachine; + private FindHelper _findHelper; #endregion @@ -64,6 +70,8 @@ public InstallHelper(PSCmdlet cmdletPassedIn, NetworkCredential networkCredentia _cancellationToken = source.Token; _cmdletPassedIn = cmdletPassedIn; _networkCredential = networkCredential; + + _findHelper = new FindHelper(_cancellationToken, _cmdletPassedIn, _networkCredential); } /// @@ -165,6 +173,7 @@ public IEnumerable BeginInstallPackages( skipDependencyCheck: skipDependencyCheck, scope: scope ?? ScopeType.CurrentUser); + _cmdletPassedIn.WriteVerbose("Returning from BeginInstallpackages"); return installedPkgs; } @@ -333,11 +342,13 @@ private List ProcessRepositories( repositoryNamesToSearch.Add(repoName); + _cmdletPassedIn.WriteVerbose("InstallHelper line 345"); List installedPkgs = InstallPackages(_pkgNamesToInstall.ToArray(), currentRepository, currentServer, currentResponseUtil, scope, skipDependencyCheck, findHelper); foreach (PSResourceInfo pkg in installedPkgs) { _pkgNamesToInstall.RemoveAll(x => x.Equals(pkg.Name, StringComparison.InvariantCultureIgnoreCase)); } + _cmdletPassedIn.WriteVerbose("InstallHelper line 351"); allPkgsInstalled.AddRange(installedPkgs); } @@ -351,6 +362,7 @@ private List ProcessRepositories( ErrorCategory.InvalidData, _cmdletPassedIn)); } + _cmdletPassedIn.WriteVerbose("InstallHelper line 365"); return allPkgsInstalled; } @@ -426,7 +438,7 @@ private void MoveFilesIntoInstallPath( // Delete the directory path before replacing it with the new module. // If deletion fails (usually due to binary file in use), then attempt restore so that the currently // installed module is not corrupted. - _cmdletPassedIn.WriteVerbose($"Attempting to delete with restore on failure. '{finalModuleVersionDir}'"); + //_cmdletPassedIn.WriteVerbose($"Attempting to delete with restore on failure. '{finalModuleVersionDir}'"); Utils.DeleteDirectoryWithRestore(finalModuleVersionDir); } @@ -508,6 +520,7 @@ private List InstallPackages( FindHelper findHelper) { _cmdletPassedIn.WriteDebug("In InstallHelper::InstallPackages()"); + List pkgsSuccessfullyInstalled = new(); // Install parent package to the temp directory, @@ -524,7 +537,7 @@ private List InstallPackages( // and value as a Hashtable of specific package info: // packageName, { version = "", isScript = "", isModule = "", pkg = "", etc. } // Install parent package to the temp directory. - Hashtable packagesHash = BeginPackageInstall( + ConcurrentDictionary packagesHash = BeginPackageInstall( searchVersionType: _versionType, specificVersion: _nugetVersion, versionRange: _versionRange, @@ -533,10 +546,12 @@ private List InstallPackages( currentServer: currentServer, currentResponseUtil: currentResponseUtil, tempInstallPath: tempInstallPath, - packagesHash: new Hashtable(StringComparer.InvariantCultureIgnoreCase), + skipDependencyCheck: skipDependencyCheck, + packagesHash: new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase), + warning: out string warning, errRecord: out ErrorRecord errRecord); - // At this point parent package is installed to temp path. + // At this point all packages are installed to temp path. if (errRecord != null) { if (errRecord.FullyQualifiedErrorId.Equals("PackageNotFound")) @@ -550,6 +565,10 @@ private List InstallPackages( continue; } + if (warning != null) + { + _cmdletPassedIn.WriteWarning(warning); + } if (packagesHash.Count == 0) { @@ -559,70 +578,6 @@ private List InstallPackages( Hashtable parentPkgInfo = packagesHash[parentPackage] as Hashtable; PSResourceInfo parentPkgObj = parentPkgInfo["psResourceInfoPkg"] as PSResourceInfo; - if (!skipDependencyCheck) - { - // Get the dependencies from the installed package. - if (parentPkgObj.Dependencies.Length > 0) - { - bool depFindFailed = false; - foreach (PSResourceInfo depPkg in findHelper.FindDependencyPackages(currentServer, currentResponseUtil, parentPkgObj, repository)) - { - if (depPkg == null) - { - depFindFailed = true; - continue; - } - - if (String.Equals(depPkg.Name, parentPkgObj.Name, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - NuGetVersion depVersion = null; - if (depPkg.AdditionalMetadata.ContainsKey("NormalizedVersion")) - { - if (!NuGetVersion.TryParse(depPkg.AdditionalMetadata["NormalizedVersion"] as string, out depVersion)) - { - NuGetVersion.TryParse(depPkg.Version.ToString(), out depVersion); - } - } - - string depPkgNameVersion = $"{depPkg.Name}{depPkg.Version.ToString()}"; - if (_packagesOnMachine.Contains(depPkgNameVersion) && !depPkg.IsPrerelease) - { - // if a dependency package is already installed, do not install it again. - // to determine if the package version is already installed, _packagesOnMachine is used but it only contains name, version info, not version with prerelease info - // if the dependency package is found to be prerelease, it is safer to install it (and worse case it reinstalls) - _cmdletPassedIn.WriteVerbose($"Dependency '{depPkg.Name}' with version '{depPkg.Version}' is already installed."); - continue; - } - - packagesHash = BeginPackageInstall( - searchVersionType: VersionType.SpecificVersion, - specificVersion: depVersion, - versionRange: null, - pkgNameToInstall: depPkg.Name, - repository: repository, - currentServer: currentServer, - currentResponseUtil: currentResponseUtil, - tempInstallPath: tempInstallPath, - packagesHash: packagesHash, - errRecord: out ErrorRecord installPkgErrRecord); - - if (installPkgErrRecord != null) - { - _cmdletPassedIn.WriteError(installPkgErrRecord); - continue; - } - } - - if (depFindFailed) - { - continue; - } - } - } - // If -WhatIf is passed in, early out. if (_cmdletPassedIn.MyInvocation.BoundParameters.ContainsKey("WhatIf") && (SwitchParameter)_cmdletPassedIn.MyInvocation.BoundParameters["WhatIf"] == true) { @@ -673,7 +628,7 @@ private List InstallPackages( /// /// Installs a single package to the temporary path. /// - private Hashtable BeginPackageInstall( + private ConcurrentDictionary BeginPackageInstall( VersionType searchVersionType, NuGetVersion specificVersion, VersionRange versionRange, @@ -682,13 +637,17 @@ private Hashtable BeginPackageInstall( ServerApiCall currentServer, ResponseUtil currentResponseUtil, string tempInstallPath, - Hashtable packagesHash, + bool skipDependencyCheck, + ConcurrentDictionary packagesHash, + out string warning, out ErrorRecord errRecord) { _cmdletPassedIn.WriteDebug("In InstallHelper::InstallPackage()"); FindResults responses = null; + warning = null; errRecord = null; + // Find the parent package that needs to be installed switch (searchVersionType) { case VersionType.VersionRange: @@ -717,6 +676,9 @@ private Hashtable BeginPackageInstall( default: // VersionType.NoVersion responses = currentServer.FindName(pkgNameToInstall, _prerelease, ResourceType.None, out ErrorRecord findNameErrRecord); + + _cmdletPassedIn.WriteDebug($"returned back to BeginPackageInstall()"); + if (findNameErrRecord != null) { errRecord = findNameErrRecord; @@ -726,9 +688,21 @@ private Hashtable BeginPackageInstall( break; } + _cmdletPassedIn.WriteDebug($"BeginPackageInstall() - 679"); + // Convert parent package to PSResourceInfo PSResourceInfo pkgToInstall = null; foreach (PSResourceResult currentResult in currentResponseUtil.ConvertToPSResourceResult(responses)) { + if (currentResult == null) + { + _cmdletPassedIn.WriteDebug($"BeginPackageInstall() - currentResult is null"); + continue; + } + else + { + _cmdletPassedIn.WriteDebug($"BeginPackageInstall() - currentResult is not null"); + } + if (currentResult.exception != null && !currentResult.exception.Message.Equals(string.Empty)) { errRecord = new ErrorRecord( @@ -763,6 +737,8 @@ private Hashtable BeginPackageInstall( } } + _cmdletPassedIn.WriteDebug($"BeginPackageInstall() - after for loop 729"); + if (pkgToInstall == null) { return packagesHash; @@ -780,6 +756,7 @@ private Hashtable BeginPackageInstall( pkgVersion += $"-{pkgToInstall.Prerelease}"; } } + _cmdletPassedIn.WriteDebug($"BeginPackageInstall() - line 748"); // For most repositories/providers the server will use the normalized version, which pkgVersion originally reflects // However, for container registries the version must exactly match what was in the artifact manifest and then reflected in PSResourceInfo.Version.ToString() @@ -788,10 +765,14 @@ private Hashtable BeginPackageInstall( pkgVersion = String.IsNullOrEmpty(pkgToInstall.Prerelease) ? pkgToInstall.Version.ToString() : $"{pkgToInstall.Version.ToString()}-{pkgToInstall.Prerelease}"; } + _cmdletPassedIn.WriteDebug($"BeginPackageInstall() - line 757"); + // Check to see if the pkg is already installed (ie the pkg is installed and the version satisfies the version range provided via param) + // TODO: can use cache for this if (!_reinstall) { string currPkgNameVersion = $"{pkgToInstall.Name}{pkgToInstall.Version}"; + // Use HashSet lookup instead of Contains for O(1) performance if (_packagesOnMachine.Contains(currPkgNameVersion)) { _cmdletPassedIn.WriteWarning($"Resource '{pkgToInstall.Name}' with version '{pkgVersion}' is already installed. If you would like to reinstall, please run the cmdlet again with the -Reinstall parameter"); @@ -809,62 +790,240 @@ private Hashtable BeginPackageInstall( } - Hashtable updatedPackagesHash = packagesHash; + ConcurrentDictionary updatedPackagesHash = packagesHash; - // -WhatIf processing. - if (_savePkg && !_cmdletPassedIn.ShouldProcess($"Package to save: '{pkgToInstall.Name}', version: '{pkgVersion}'")) + _cmdletPassedIn.WriteDebug($"BeginPackageInstall() - line 784"); + + if (!_savePkg) + { + _cmdletPassedIn.WriteDebug($"BeginPackageInstall() - line 788"); + + } + else + { + _cmdletPassedIn.WriteDebug($"BeginPackageInstall() - line 793"); + } + + if (pkgToInstall == null) + { + _cmdletPassedIn.WriteDebug($"BeginPackageInstall() - packageToInstall is null 798"); + } + else + { + _cmdletPassedIn.WriteDebug($"BeginPackageInstall() - packageToInstall is not null 802"); + } + + + if (string.IsNullOrEmpty(pkgToInstall.Name)) + { + _cmdletPassedIn.WriteDebug($"BeginPackageInstall() -pkgtoinstall name is null or empty 808"); + } + else { - if (!updatedPackagesHash.ContainsKey(pkgToInstall.Name)) + _cmdletPassedIn.WriteDebug($"BeginPackageInstall() - pkgtoinstall is available 812 "); + } + + + if (string.IsNullOrEmpty(pkgVersion)) + { + _cmdletPassedIn.WriteDebug($"BeginPackageInstall() -pkgVersion name is null or empty 818"); + } + else + { + _cmdletPassedIn.WriteDebug($"BeginPackageInstall() - pkgVersion is available 822 "); + } + + // -WhatIf processing. + // if (_savePkg && !_cmdletPassedIn.ShouldProcess($"Package to save: '{pkgToInstall.Name}', version: '{pkgVersion}'")) + // { + + + // if (!updatedPackagesHash.ContainsKey(pkgToInstall.Name)) + // { + + // _cmdletPassedIn.WriteDebug($"BeginPackageInstall() - line 784"); + // updatedPackagesHash.TryAdd(pkgToInstall.Name, new Hashtable(StringComparer.InvariantCultureIgnoreCase) + // { + // { "isModule", "" }, + // { "isScript", "" }, + // { "psResourceInfoPkg", pkgToInstall }, + // { "tempDirNameVersionPath", tempInstallPath }, + // { "pkgVersion", "" }, + // { "scriptPath", "" }, + // { "installPath", "" } + // }); + // } + // } + // else if (!_cmdletPassedIn.ShouldProcess($"Package to install: '{pkgToInstall.Name}', version: '{pkgVersion}'")) + // { + + + + // if (!updatedPackagesHash.ContainsKey(pkgToInstall.Name)) + // { + // updatedPackagesHash.TryAdd(pkgToInstall.Name, new Hashtable(StringComparer.InvariantCultureIgnoreCase) + // { + // { "isModule", "" }, + // { "isScript", "" }, + // { "psResourceInfoPkg", pkgToInstall }, + // { "tempDirNameVersionPath", tempInstallPath }, + // { "pkgVersion", "" }, + // { "scriptPath", "" }, + // { "installPath", "" } + // }); + // } + // } + //else + //{ + _cmdletPassedIn.WriteDebug($"BeginPackageInstall() - line 821"); + // Concurrent updates + // Find all dependencies + if (!skipDependencyCheck) { - updatedPackagesHash.Add(pkgToInstall.Name, new Hashtable(StringComparer.InvariantCultureIgnoreCase) + _cmdletPassedIn.WriteDebug($"BeginPackageInstall() - line 826"); + // concurrency updates + List parentAndDeps = _findHelper.FindDependencyPackages(currentServer, currentResponseUtil, pkgToInstall, repository).ToList(); + // List returned only includes dependencies, so we'll add the parent pkg to this list to pass on to installation method + parentAndDeps.Add(pkgToInstall); + + _cmdletPassedIn.WriteDebug("In InstallHelper::InstallPackage(), found all dependencies"); + + return InstallParentAndDependencyPackages(parentAndDeps, currentServer, tempInstallPath, packagesHash, updatedPackagesHash, pkgToInstall); + } + else { + // If we don't install dependencies, we're only installing the parent pkg so we can short circut and simply install the parent pkg. + // TODO: check this version and prerelease combo + Stream responseStream = currentServer.InstallPackage(pkgToInstall.Name, pkgToInstall.Version.ToString(), true, out ErrorRecord installNameErrRecord); + + _cmdletPassedIn.WriteDebug("In BeginInstallPackage 898"); + + if (installNameErrRecord != null) { - { "isModule", "" }, - { "isScript", "" }, - { "psResourceInfoPkg", pkgToInstall }, - { "tempDirNameVersionPath", tempInstallPath }, - { "pkgVersion", "" }, - { "scriptPath", "" }, - { "installPath", "" } - }); + _cmdletPassedIn.WriteDebug("In BeginInstallPackage 902"); + errRecord = installNameErrRecord; + return packagesHash; + } + _cmdletPassedIn.WriteDebug("In BeginInstallPackage 905"); + bool installedToTempPathSuccessfully = _asNupkg ? TrySaveNupkgToTempPath(responseStream, tempInstallPath, pkgToInstall.Name, pkgToInstall.Version.ToString(), pkgToInstall, packagesHash, out updatedPackagesHash, out errRecord) : + TryInstallToTempPath(responseStream, tempInstallPath, pkgToInstall.Name, pkgToInstall.Version.ToString(), pkgToInstall, packagesHash, out updatedPackagesHash, out warning, out errRecord); + if (!installedToTempPathSuccessfully) + { + return packagesHash; + } } - } - else if (!_cmdletPassedIn.ShouldProcess($"Package to install: '{pkgToInstall.Name}', version: '{pkgVersion}'")) + //} + + return updatedPackagesHash; + } + + private ConcurrentDictionary InstallParentAndDependencyPackages(List parentAndDeps, ServerApiCall currentServer, string tempInstallPath, ConcurrentDictionary packagesHash, ConcurrentDictionary updatedPackagesHash, PSResourceInfo pkgToInstall) + { + string warning = string.Empty; + ConcurrentBag errors = new ConcurrentBag(); + + // TODO: figure out a good threshold and parallel count + int processorCount = Environment.ProcessorCount; + _cmdletPassedIn.WriteDebug($"parentAndDeps.Count is {parentAndDeps.Count}, processor count is: {processorCount}"); + if (parentAndDeps.Count > processorCount) { - if (!updatedPackagesHash.ContainsKey(pkgToInstall.Name)) + _cmdletPassedIn.WriteDebug($"parentAndDeps.Count is greater than processor count"); + // Set the maximum degree of parallelism to 32? (Invoke-Command has default of 32, that's where we got this number from) + // If installing more than 3 packages, do so concurrently + // If the number of dependencies is very small (e.g., ≤ CPU cores), parallelism may add overhead instead of improving speed. + int maxDegreeOfParallelism = processorCount * 4; + Parallel.ForEach(parentAndDeps, new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism }, depPkg => { - updatedPackagesHash.Add(pkgToInstall.Name, new Hashtable(StringComparer.InvariantCultureIgnoreCase) + var depPkgName = depPkg.Name; + var depPkgVersion = depPkg.Version.ToString(); + + Stream responseStream = currentServer.InstallPackage(depPkgName, depPkgVersion, true, out ErrorRecord installNameErrRecord); + + // if (currentServer.Repository.ApiVersion == PSRepositoryInfo.APIVersion.V2) + // { + // // See if the network call we're making is already cached, if not, call FindNameAsync() and cache results + // string key = $"{dep.Name}|{dep.VersionRange.MaxVersion.ToString()}|{_type}"; + // response = _cachedNetworkCalls.GetOrAdd(key, _ => currentServer.FindVersionAsync(dep.Name, dep.VersionRange.MaxVersion.ToString(), _type)); + + // responses = response.GetAwaiter().GetResult(); + // } + // else + // { + // responses = currentServer.FindVersion(dep.Name, dep.VersionRange.MaxVersion.ToString(), _type, out errRecord); + // } + + + + //_cmdletPassedIn.WriteDebug("In BeginInstallPackage 938"); + + if (installNameErrRecord != null) { - { "isModule", "" }, - { "isScript", "" }, - { "psResourceInfoPkg", pkgToInstall }, - { "tempDirNameVersionPath", tempInstallPath }, - { "pkgVersion", "" }, - { "scriptPath", "" }, - { "installPath", "" } - }); + // _cmdletPassedIn.WriteDebug("In BeginInstallPackage 942"); + //errors.Add(installNameErrRecord); + } + // _cmdletPassedIn.WriteDebug("In BeginInstallPackage 945"); + ErrorRecord tempSaveErrRecord = null, tempInstallErrRecord = null; + bool installedToTempPathSuccessfully = _asNupkg ? TrySaveNupkgToTempPath(responseStream, tempInstallPath, depPkgName, depPkgVersion, depPkg, packagesHash, out updatedPackagesHash, out tempSaveErrRecord) : + TryInstallToTempPath(responseStream, tempInstallPath, depPkgName, depPkgVersion, depPkg, packagesHash, out updatedPackagesHash, out warning, out tempInstallErrRecord); + + if (!installedToTempPathSuccessfully) + { + errors.Add(tempSaveErrRecord ?? tempInstallErrRecord); + } + }); + + if (errors.Count > 0) + { + // Write out all errors collected from Parallel.ForEach + foreach (var err in errors) + { + _cmdletPassedIn.WriteError(err); + } + + return packagesHash; } + if (!string.IsNullOrEmpty(warning)) + { + _cmdletPassedIn.WriteWarning(warning); + } + + return updatedPackagesHash; } else { - // Download the package. - string pkgName = pkgToInstall.Name; - Stream responseStream = currentServer.InstallPackage(pkgName, pkgVersion, _prerelease, out ErrorRecord installNameErrRecord); - if (installNameErrRecord != null) + // Install the good old fashioned way + foreach (var pkgToBeInstalled in parentAndDeps) { - errRecord = installNameErrRecord; - return packagesHash; - } + var pkgToInstallName = pkgToBeInstalled.Name; + var pkgToInstallVersion = Utils.GetNormalizedVersionString(pkgToBeInstalled.Version.ToString(), pkgToBeInstalled.Prerelease); + _cmdletPassedIn.WriteDebug("In BeginInstallPackage - not going through concurrency"); + Stream responseStream = currentServer.InstallPackage(pkgToInstallName, pkgToInstallVersion, true, out ErrorRecord installNameErrRecord); + _cmdletPassedIn.WriteDebug("In BeginInstallPackage 981"); - bool installedToTempPathSuccessfully = _asNupkg ? TrySaveNupkgToTempPath(responseStream, tempInstallPath, pkgName, pkgVersion, pkgToInstall, packagesHash, out updatedPackagesHash, out errRecord) : - TryInstallToTempPath(responseStream, tempInstallPath, pkgName, pkgVersion, pkgToInstall, packagesHash, out updatedPackagesHash, out errRecord); + if (installNameErrRecord != null) + { + _cmdletPassedIn.WriteDebug("In BeginInstallPackage 985"); + //_cmdletPassedIn.WriteError(installNameErrRecord); // todo revert this + return packagesHash; + } + _cmdletPassedIn.WriteDebug("In BeginInstallPackage 988"); - if (!installedToTempPathSuccessfully) - { - return packagesHash; + ErrorRecord tempSaveErrRecord = null, tempInstallErrRecord = null; + bool installedToTempPathSuccessfully = _asNupkg ? TrySaveNupkgToTempPath(responseStream, tempInstallPath, pkgToInstallName, pkgToInstallVersion, pkgToBeInstalled, packagesHash, out updatedPackagesHash, out tempSaveErrRecord) : + TryInstallToTempPath(responseStream, tempInstallPath, pkgToInstallName, pkgToInstallVersion, pkgToBeInstalled, packagesHash, out updatedPackagesHash, out warning, out tempInstallErrRecord); + + if (!installedToTempPathSuccessfully) + { + _cmdletPassedIn.WriteError(tempSaveErrRecord ?? tempInstallErrRecord); + return packagesHash; + } + if (!string.IsNullOrEmpty(warning)) + { + _cmdletPassedIn.WriteWarning(warning); + } } - } - return updatedPackagesHash; + return updatedPackagesHash; + } } /// @@ -926,17 +1085,22 @@ private bool TryInstallToTempPath( string pkgName, string normalizedPkgVersion, PSResourceInfo pkgToInstall, - Hashtable packagesHash, - out Hashtable updatedPackagesHash, + ConcurrentDictionary packagesHash, + out ConcurrentDictionary updatedPackagesHash, + out string warning, out ErrorRecord error) { - _cmdletPassedIn.WriteDebug("In InstallHelper::TryInstallToTempPath()"); + warning = null; error = null; updatedPackagesHash = packagesHash; try { var pathToFile = Path.Combine(tempInstallPath, $"{pkgName}.{normalizedPkgVersion}.zip"); using var fs = File.Create(pathToFile); + // if (responseStream == null) + // { + // throw new InvalidOperationException("responseStream is null"); + // } responseStream.Seek(0, System.IO.SeekOrigin.Begin); responseStream.CopyTo(fs); fs.Close(); @@ -969,6 +1133,7 @@ private bool TryInstallToTempPath( pkgName, tempDirNameVersion, _cmdletPassedIn, + out warning, out error)) { return false; @@ -987,8 +1152,8 @@ private bool TryInstallToTempPath( ErrorCategory.ReadError, _cmdletPassedIn); - return false; - } + return false; + } if (!Utils.TryReadManifestFile( manifestFilePath: moduleManifest, @@ -1001,7 +1166,7 @@ private bool TryInstallToTempPath( ErrorCategory.ReadError, _cmdletPassedIn); - return false; + return false; } // Accept License verification @@ -1029,17 +1194,7 @@ private bool TryInstallToTempPath( out ErrorRecord[] parseScriptFileErrors, out string[] _)) { - foreach (ErrorRecord parseError in parseScriptFileErrors) - { - _cmdletPassedIn.WriteError(parseError); - } - - error = new ErrorRecord( - new InvalidOperationException($"PSScriptFile could not be parsed"), - "PSScriptParseError", - ErrorCategory.ReadError, - _cmdletPassedIn); - + error = parseScriptFileErrors.FirstOrDefault(); return false; } } @@ -1048,8 +1203,8 @@ private bool TryInstallToTempPath( // This package is not a PowerShell package (eg a resource from the NuGet Gallery). installPath = _pathsToInstallPkg.Find(path => path.EndsWith("Modules", StringComparison.InvariantCultureIgnoreCase)); - _cmdletPassedIn.WriteVerbose($"This resource is not a PowerShell package and will be installed to the modules path: {installPath}."); - isModule = true; + //_cmdletPassedIn.WriteVerbose($"This resource is not a PowerShell package and will be installed to the modules path: {installPath}."); + isModule = true; } installPath = _savePkg ? _pathsToInstallPkg.First() : installPath; @@ -1067,7 +1222,7 @@ private bool TryInstallToTempPath( if (!updatedPackagesHash.ContainsKey(pkgName)) { // Add pkg info to hashtable. - updatedPackagesHash.Add(pkgName, new Hashtable(StringComparer.InvariantCultureIgnoreCase) + updatedPackagesHash.TryAdd(pkgName, new Hashtable(StringComparer.InvariantCultureIgnoreCase) { { "isModule", isModule }, { "isScript", isScript }, @@ -1085,8 +1240,8 @@ private bool TryInstallToTempPath( { error = new ErrorRecord( new PSInvalidOperationException( - message: $"Unable to successfully install package '{pkgName}': '{e.Message}' to temporary installation path.", - innerException: e), + message: $"Unable to successfully install package '{pkgName}': '{e.Message}' to temporary installation path.", + innerException: e), "InstallPackageFailed", ErrorCategory.InvalidOperation, _cmdletPassedIn); @@ -1104,11 +1259,11 @@ private bool TrySaveNupkgToTempPath( string pkgName, string normalizedPkgVersion, PSResourceInfo pkgToInstall, - Hashtable packagesHash, - out Hashtable updatedPackagesHash, + ConcurrentDictionary packagesHash, + out ConcurrentDictionary updatedPackagesHash, out ErrorRecord error) { - _cmdletPassedIn.WriteDebug("In InstallHelper::TrySaveNupkgToTempPath()"); + // _cmdletPassedIn.WriteDebug("In InstallHelper::TrySaveNupkgToTempPath()"); error = null; updatedPackagesHash = packagesHash; @@ -1132,7 +1287,7 @@ private bool TrySaveNupkgToTempPath( if (!updatedPackagesHash.ContainsKey(pkgName)) { // Add pkg info to hashtable. - updatedPackagesHash.Add(pkgName, new Hashtable(StringComparer.InvariantCultureIgnoreCase) + updatedPackagesHash.TryAdd(pkgName, new Hashtable(StringComparer.InvariantCultureIgnoreCase) { { "isModule", "" }, { "isScript", "" }, @@ -1228,7 +1383,7 @@ private bool TryExtractToDirectory(string zipPath, string extractPath, out Error /// /// Moves package files/directories from the temp install path into the final install path location. /// - private bool TryMoveInstallContent(string tempInstallPath, ScopeType scope, Hashtable packagesHash) + private bool TryMoveInstallContent(string tempInstallPath, ScopeType scope, ConcurrentDictionary packagesHash) { _cmdletPassedIn.WriteDebug("In InstallHelper::TryMoveInstallContent()"); foreach (string pkgName in packagesHash.Keys) @@ -1287,7 +1442,7 @@ private bool TryMoveInstallContent(string tempInstallPath, ScopeType scope, Hash "InstallPackageFailed", ErrorCategory.InvalidOperation, _cmdletPassedIn)); - + return false; } } @@ -1300,7 +1455,6 @@ private bool TryMoveInstallContent(string tempInstallPath, ScopeType scope, Hash /// private bool CallAcceptLicense(PSResourceInfo p, string moduleManifest, string tempInstallPath, string newVersion, out ErrorRecord error) { - _cmdletPassedIn.WriteDebug("In InstallHelper::CallAcceptLicense()"); error = null; var requireLicenseAcceptance = false; @@ -1404,7 +1558,7 @@ private bool CallAcceptLicense(PSResourceInfo p, string moduleManifest, string t /// private bool DetectClobber(string pkgName, Hashtable parsedMetadataHashtable, out ErrorRecord error) { - _cmdletPassedIn.WriteDebug("In InstallHelper::DetectClobber()"); + //_cmdletPassedIn.WriteDebug("In InstallHelper::DetectClobber()"); error = null; bool foundClobber = false; @@ -1468,7 +1622,6 @@ private bool DetectClobber(string pkgName, Hashtable parsedMetadataHashtable, ou /// private bool CreateMetadataXMLFile(string dirNameVersion, string installPath, PSResourceInfo pkg, bool isModule, out ErrorRecord error) { - _cmdletPassedIn.WriteDebug("In InstallHelper::CreateMetadataXMLFile()"); error = null; bool success = true; // Script will have a metadata file similar to: "TestScript_InstalledScriptInfo.xml" @@ -1498,7 +1651,7 @@ private bool CreateMetadataXMLFile(string dirNameVersion, string installPath, PS /// private void DeleteExtraneousFiles(string packageName, string dirNameVersion) { - _cmdletPassedIn.WriteDebug("In InstallHelper::DeleteExtraneousFiles()"); + // _cmdletPassedIn.WriteDebug("In InstallHelper::DeleteExtraneousFiles()"); // Deleting .nupkg SHA file, .nuspec, and .nupkg after unpacking the module // since we download as .zip for HTTP calls, we shouldn't have .nupkg* files // var nupkgSHAToDelete = Path.Combine(dirNameVersion, pkgIdString + ".nupkg.sha512"); @@ -1511,22 +1664,18 @@ private void DeleteExtraneousFiles(string packageName, string dirNameVersion) if (File.Exists(nuspecToDelete)) { - _cmdletPassedIn.WriteDebug($"Deleting '{nuspecToDelete}'"); File.Delete(nuspecToDelete); } if (File.Exists(contentTypesToDelete)) { - _cmdletPassedIn.WriteDebug($"Deleting '{contentTypesToDelete}'"); File.Delete(contentTypesToDelete); } if (Directory.Exists(relsDirToDelete)) { - _cmdletPassedIn.WriteDebug($"Deleting '{relsDirToDelete}'"); Utils.DeleteDirectory(relsDirToDelete); } if (Directory.Exists(packageDirToDelete)) { - _cmdletPassedIn.WriteDebug($"Deleting '{packageDirToDelete}'"); Utils.DeleteDirectory(packageDirToDelete); } } diff --git a/src/code/LocalServerApiCalls.cs b/src/code/LocalServerApiCalls.cs index cc43c340d..f32f161ab 100644 --- a/src/code/LocalServerApiCalls.cs +++ b/src/code/LocalServerApiCalls.cs @@ -13,6 +13,7 @@ using System.Net; using System.Management.Automation; using System.Runtime.ExceptionServices; +using System.Threading.Tasks; namespace Microsoft.PowerShell.PSResourceGet.Cmdlets { @@ -39,6 +40,15 @@ public LocalServerAPICalls (PSRepositoryInfo repository, PSCmdlet cmdletPassedIn #region Overridden Methods + public override Task FindVersionAsync(string packageName, string version, ResourceType type) + { + throw new NotImplementedException(); + } + + public override Task FindVersionGlobbingAsync(string packageName, VersionRange versionRange, bool includePrerelease, ResourceType type, bool getOnlyLatest) + { + throw new NotImplementedException(); + } /// /// Find method which allows for searching for all packages from a repository and returns latest version for each. /// Examples: Search -Repository PSGallery @@ -47,7 +57,7 @@ public LocalServerAPICalls (PSRepositoryInfo repository, PSCmdlet cmdletPassedIn /// public override FindResults FindAll(bool includePrerelease, ResourceType type, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In LocalServerApiCalls::FindAll()"); + //_cmdletPassedIn.WriteDebug("In LocalServerApiCalls::FindAll()"); return FindTagsHelper(tags: Utils.EmptyStrArray, includePrerelease, out errRecord); } @@ -59,7 +69,7 @@ public override FindResults FindAll(bool includePrerelease, ResourceType type, o /// public override FindResults FindTags(string[] tags, bool includePrerelease, ResourceType _type, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In LocalServerApiCalls::FindTags()"); + //_cmdletPassedIn.WriteDebug("In LocalServerApiCalls::FindTags()"); FindResults tagFindResults = FindTagsHelper(tags, includePrerelease, out errRecord); if (tagFindResults.IsFindResultsEmpty()) { @@ -78,7 +88,7 @@ public override FindResults FindTags(string[] tags, bool includePrerelease, Reso /// public override FindResults FindCommandOrDscResource(string[] tags, bool includePrerelease, bool isSearchingForCommands, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In LocalServerApiCalls::FindCommandOrDscResource()"); + //_cmdletPassedIn.WriteDebug("In LocalServerApiCalls::FindCommandOrDscResource()"); string[] cmdsOrDSCs = GetCmdsOrDSCTags(tags: tags, isSearchingForCommands: isSearchingForCommands); FindResults cmdOrDSCFindResults = FindTagsHelper(cmdsOrDSCs, includePrerelease, out errRecord); if (cmdOrDSCFindResults.IsFindResultsEmpty()) @@ -105,10 +115,15 @@ public override FindResults FindCommandOrDscResource(string[] tags, bool include /// public override FindResults FindName(string packageName, bool includePrerelease, ResourceType type, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In LocalServerApiCalls::FindName()"); + ////_cmdletPassedIn.WriteDebug("In LocalServerApiCalls::FindName()"); return FindNameHelper(packageName, Utils.EmptyStrArray, includePrerelease, type, out errRecord); } + public override Task FindNameAsync(string packageName, bool includePrerelease, ResourceType type) + { + throw new NotImplementedException(); + } + /// /// Find method which allows for searching for single name and tag and returns latest version. /// Name: no wildcard support @@ -117,7 +132,7 @@ public override FindResults FindName(string packageName, bool includePrerelease, /// public override FindResults FindNameWithTag(string packageName, string[] tags, bool includePrerelease, ResourceType type, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In LocalServerApiCalls::FindNameWithTag()"); + ////_cmdletPassedIn.WriteDebug("In LocalServerApiCalls::FindNameWithTag()"); return FindNameHelper(packageName, tags, includePrerelease, type, out errRecord); } @@ -131,7 +146,7 @@ public override FindResults FindNameWithTag(string packageName, string[] tags, b /// public override FindResults FindNameGlobbing(string packageName, bool includePrerelease, ResourceType type, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In LocalServerApiCalls::FindNameGlobbing()"); + //_cmdletPassedIn.WriteDebug("In LocalServerApiCalls::FindNameGlobbing()"); return FindNameGlobbingHelper(packageName, Utils.EmptyStrArray, includePrerelease, type, out errRecord); } @@ -143,7 +158,7 @@ public override FindResults FindNameGlobbing(string packageName, bool includePre /// public override FindResults FindNameGlobbingWithTag(string packageName, string[] tags, bool includePrerelease, ResourceType type, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In LocalServerApiCalls::FindNameGlobbingWithTag()"); + //_cmdletPassedIn.WriteDebug("In LocalServerApiCalls::FindNameGlobbingWithTag()"); return FindNameGlobbingHelper(packageName, tags, includePrerelease, type, out errRecord); } @@ -158,7 +173,7 @@ public override FindResults FindNameGlobbingWithTag(string packageName, string[] /// public override FindResults FindVersionGlobbing(string packageName, VersionRange versionRange, bool includePrerelease, ResourceType type, bool getOnlyLatest, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In LocalServerApiCalls::FindVersionGlobbing()"); + ////_cmdletPassedIn.WriteDebug("In LocalServerApiCalls::FindVersionGlobbing()"); FindResults findResponse = new FindResults(); errRecord = null; string actualPkgName = packageName; @@ -198,7 +213,7 @@ public override FindResults FindVersionGlobbing(string packageName, VersionRange /// public override FindResults FindVersion(string packageName, string version, ResourceType type, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In LocalServerApiCalls::FindVersion()"); + //_cmdletPassedIn.WriteDebug("In LocalServerApiCalls::FindVersion()"); return FindVersionHelper(packageName, version, Utils.EmptyStrArray, type, out errRecord); } @@ -210,7 +225,7 @@ public override FindResults FindVersion(string packageName, string version, Reso /// public override FindResults FindVersionWithTag(string packageName, string version, string[] tags, ResourceType type, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In LocalServerApiCalls::FindVersionWithTag()"); + //_cmdletPassedIn.WriteDebug("In LocalServerApiCalls::FindVersionWithTag()"); return FindVersionHelper(packageName, version, tags, type, out errRecord); } @@ -251,7 +266,7 @@ public override Stream InstallPackage(string packageName, string packageVersion, /// private FindResults FindNameHelper(string packageName, string[] tags, bool includePrerelease, ResourceType type, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In LocalServerApiCalls::FindNameHelper()"); + //_cmdletPassedIn.WriteDebug("In LocalServerApiCalls::FindNameHelper()"); FindResults findResponse = new FindResults(); errRecord = null; @@ -261,7 +276,7 @@ private FindResults FindNameHelper(string packageName, string[] tags, bool inclu // this regex pattern matches packageName followed by a version (4 digit or 3 with prerelease word) string regexPattern = $"{packageName}" + @"(\.\d+){1,3}(?:[a-zA-Z0-9-.]+|.\d)?\.nupkg"; - _cmdletPassedIn.WriteDebug($"package file name pattern to be searched for is: {regexPattern}"); + //_cmdletPassedIn.WriteDebug($"package file name pattern to be searched for is: {regexPattern}"); foreach (string path in Directory.GetFiles(Repository.Uri.LocalPath)) { @@ -273,7 +288,7 @@ private FindResults FindNameHelper(string packageName, string[] tags, bool inclu } NuGetVersion nugetVersion = GetInfoFromFileName(packageFullName: packageFullName, packageName: packageName, actualName: out actualPkgName, out errRecord); - _cmdletPassedIn.WriteDebug($"Version parsed as '{nugetVersion}'"); + //_cmdletPassedIn.WriteDebug($"Version parsed as '{nugetVersion}'"); if (errRecord != null) { @@ -328,7 +343,7 @@ private FindResults FindNameHelper(string packageName, string[] tags, bool inclu /// private FindResults FindNameGlobbingHelper(string packageName, string[] tags, bool includePrerelease, ResourceType type, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In LocalServerApiCalls::FindNameGlobbingHelper()"); + //_cmdletPassedIn.WriteDebug("In LocalServerApiCalls::FindNameGlobbingHelper()"); FindResults findResponse = new FindResults(); List pkgsFound = new List(); errRecord = null; @@ -340,7 +355,7 @@ private FindResults FindNameGlobbingHelper(string packageName, string[] tags, bo { Hashtable pkgInfo = pkgVersionsFound[pkgFound] as Hashtable; string pkgPath = pkgInfo["path"] as string; - _cmdletPassedIn.WriteDebug($"Package '{pkgFound}' found from path '{pkgPath}'"); + //_cmdletPassedIn.WriteDebug($"Package '{pkgFound}' found from path '{pkgPath}'"); Hashtable pkgMetadata = GetMetadataFromNupkg(packageName: pkgFound, packagePath: pkgPath, requiredTags: tags, errRecord: out errRecord); if (errRecord != null || pkgMetadata.Count == 0) @@ -361,7 +376,7 @@ private FindResults FindNameGlobbingHelper(string packageName, string[] tags, bo /// private FindResults FindVersionHelper(string packageName, string version, string[] tags, ResourceType type, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In LocalServerApiCalls::FindVersionHelper()"); + //_cmdletPassedIn.WriteDebug("In LocalServerApiCalls::FindVersionHelper()"); FindResults findResponse = new FindResults(); errRecord = null; @@ -378,7 +393,7 @@ private FindResults FindVersionHelper(string packageName, string version, string // this regex pattern matches packageName followed by the requested version string regexPattern = $"{packageName}.{requiredVersion.ToNormalizedString()}" + @".nupkg"; - _cmdletPassedIn.WriteDebug($"pattern is: {regexPattern}"); + //_cmdletPassedIn.WriteDebug($"pattern is: {regexPattern}"); string pkgPath = String.Empty; string actualPkgName = String.Empty; @@ -392,7 +407,7 @@ private FindResults FindVersionHelper(string packageName, string version, string } NuGetVersion nugetVersion = GetInfoFromFileName(packageFullName: packageFullName, packageName: packageName, actualName: out actualPkgName, out errRecord); - _cmdletPassedIn.WriteDebug($"Version parsed as '{nugetVersion}'"); + //_cmdletPassedIn.WriteDebug($"Version parsed as '{nugetVersion}'"); if (errRecord != null) { @@ -401,7 +416,7 @@ private FindResults FindVersionHelper(string packageName, string version, string if (nugetVersion == requiredVersion) { - _cmdletPassedIn.WriteDebug("Found matching version"); + //_cmdletPassedIn.WriteDebug("Found matching version"); string pkgFullName = $"{actualPkgName}.{nugetVersion.ToString()}.nupkg"; pkgPath = path; break; @@ -445,7 +460,7 @@ private FindResults FindVersionHelper(string packageName, string version, string /// private FindResults FindTagsHelper(string[] tags, bool includePrerelease, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In LocalServerApiCalls::FindTagsHelper()"); + //_cmdletPassedIn.WriteDebug("In LocalServerApiCalls::FindTagsHelper()"); FindResults findResponse = new FindResults(); List pkgsFound = new List(); errRecord = null; @@ -458,7 +473,7 @@ private FindResults FindTagsHelper(string[] tags, bool includePrerelease, out Er Hashtable pkgInfo = pkgVersionsFound[pkgFound] as Hashtable; NuGetVersion pkgVersion = pkgInfo["version"] as NuGetVersion; string pkgPath = pkgInfo["path"] as string; - _cmdletPassedIn.WriteDebug($"Found package '{pkgFound}' from path '{pkgPath}'"); + //_cmdletPassedIn.WriteDebug($"Found package '{pkgFound}' from path '{pkgPath}'"); Hashtable pkgMetadata = GetMetadataFromNupkg(packageName: pkgFound, packagePath: pkgPath, requiredTags: tags, errRecord: out errRecord); if (errRecord != null) @@ -489,7 +504,7 @@ private FindResults FindTagsHelper(string[] tags, bool includePrerelease, out Er /// private Stream InstallName(string packageName, bool includePrerelease, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In LocalServerApiCalls::InstallName()"); + //_cmdletPassedIn.WriteDebug("In LocalServerApiCalls::InstallName()"); FileStream fs = null; errRecord = null; WildcardPattern pkgNamePattern = new WildcardPattern($"{packageName}.*", WildcardOptions.IgnoreCase); @@ -502,12 +517,12 @@ private Stream InstallName(string packageName, bool includePrerelease, out Error if (!String.IsNullOrEmpty(packageFullName) && pkgNamePattern.IsMatch(packageFullName)) { - _cmdletPassedIn.WriteDebug($"'{packageName}' found in '{path}'"); + //_cmdletPassedIn.WriteDebug($"'{packageName}' found in '{path}'"); string[] packageWithoutName = packageFullName.ToLower().Split(new string[] { $"{packageName.ToLower()}." }, StringSplitOptions.RemoveEmptyEntries); string packageVersionAndExtension = packageWithoutName[0]; int extensionDot = packageVersionAndExtension.LastIndexOf('.'); string version = packageVersionAndExtension.Substring(0, extensionDot); - _cmdletPassedIn.WriteDebug($"Parsing version '{version}' of package '{packageName}'"); + //_cmdletPassedIn.WriteDebug($"Parsing version '{version}' of package '{packageName}'"); NuGetVersion.TryParse(version, out NuGetVersion nugetVersion); if ((!nugetVersion.IsPrerelease || includePrerelease) && (nugetVersion > latestVersion)) @@ -531,7 +546,7 @@ private Stream InstallName(string packageName, bool includePrerelease, out Error try { - _cmdletPassedIn.WriteDebug($"Reading file '{latestVersionPath}'"); + //_cmdletPassedIn.WriteDebug($"Reading file '{latestVersionPath}'"); fs = new FileStream(latestVersionPath, FileMode.Open, FileAccess.Read); if (fs == null) { @@ -564,14 +579,14 @@ private Stream InstallName(string packageName, bool includePrerelease, out Error /// private Stream InstallVersion(string packageName, string version, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In LocalServerApiCalls::InstallVersion()"); + //_cmdletPassedIn.WriteDebug("In LocalServerApiCalls::InstallVersion()"); errRecord = null; FileStream fs = null; // if 4 digits and last is 0, create 3 digit equiv string // 4 digit version (where last is 0) is always passed in. NuGetVersion.TryParse(version, out NuGetVersion pkgVersion); - _cmdletPassedIn.WriteDebug($"Version parsed as '{pkgVersion}'"); + //_cmdletPassedIn.WriteDebug($"Version parsed as '{pkgVersion}'"); if (pkgVersion.Revision == 0) { @@ -587,7 +602,7 @@ private Stream InstallVersion(string packageName, string version, out ErrorRecor if (!String.IsNullOrEmpty(packageFullName) && pkgNamePattern.IsMatch(packageFullName)) { - _cmdletPassedIn.WriteDebug($"Found match with '{path}'"); + //_cmdletPassedIn.WriteDebug($"Found match with '{path}'"); pkgVersionPath = path; } } @@ -605,7 +620,7 @@ private Stream InstallVersion(string packageName, string version, out ErrorRecor try { - _cmdletPassedIn.WriteDebug($"Reading file '{pkgVersionPath}'"); + //_cmdletPassedIn.WriteDebug($"Reading file '{pkgVersionPath}'"); fs = new FileStream(pkgVersionPath, FileMode.Open, FileAccess.Read); if (fs == null) @@ -635,7 +650,7 @@ private Stream InstallVersion(string packageName, string version, out ErrorRecor /// private Hashtable GetMetadataFromNupkg(string packageName, string packagePath, string[] requiredTags, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In LocalServerApiCalls::GetMetadataFromNupkg()"); + //_cmdletPassedIn.WriteDebug("In LocalServerApiCalls::GetMetadataFromNupkg()"); Hashtable pkgMetadata = new Hashtable(StringComparer.OrdinalIgnoreCase); errRecord = null; @@ -657,7 +672,7 @@ private Hashtable GetMetadataFromNupkg(string packageName, string packagePath, s File.Move(destNupkgPath, zipFilePath); // extract from .zip - _cmdletPassedIn.WriteDebug($"Extracting '{zipFilePath}' to '{tempDiscoveryPath}'"); + //_cmdletPassedIn.WriteDebug($"Extracting '{zipFilePath}' to '{tempDiscoveryPath}'"); System.IO.Compression.ZipFile.ExtractToDirectory(zipFilePath, tempDiscoveryPath); string psd1FilePath = String.Empty; @@ -669,7 +684,7 @@ private Hashtable GetMetadataFromNupkg(string packageName, string packagePath, s if (File.Exists(psd1FilePath)) { - _cmdletPassedIn.WriteDebug($"Attempting to read module manifest file '{psd1FilePath}'"); + //_cmdletPassedIn.WriteDebug($"Attempting to read module manifest file '{psd1FilePath}'"); if (!Utils.TryReadManifestFile(psd1FilePath, out pkgMetadata, out Exception readManifestError)) { errRecord = new ErrorRecord( @@ -695,7 +710,7 @@ private Hashtable GetMetadataFromNupkg(string packageName, string packagePath, s } else if (File.Exists(ps1FilePath)) { - _cmdletPassedIn.WriteDebug($"Attempting to read script file '{ps1FilePath}'"); + //_cmdletPassedIn.WriteDebug($"Attempting to read script file '{ps1FilePath}'"); if (!PSScriptFileInfo.TryTestPSScriptFileInfo(ps1FilePath, out PSScriptFileInfo parsedScript, out ErrorRecord[] errors, out string[] verboseMsgs)) { errRecord = new ErrorRecord( @@ -715,7 +730,7 @@ private Hashtable GetMetadataFromNupkg(string packageName, string packagePath, s } else if (File.Exists(nuspecFilePath)) { - _cmdletPassedIn.WriteDebug($"Attempting to read nuspec file '{nuspecFilePath}'"); + //_cmdletPassedIn.WriteDebug($"Attempting to read nuspec file '{nuspecFilePath}'"); pkgMetadata = GetHashtableForNuspec(nuspecFilePath, out errRecord); if (errRecord != null) { @@ -771,7 +786,7 @@ private Hashtable GetMetadataFromNupkg(string packageName, string packagePath, s /// private Hashtable GetMatchingFilesGivenSpecificName(string packageName, bool includePrerelease, VersionRange versionRange, out string actualName, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In LocalServerApiCalls::GetMatchingFilesGivenSpecificName()"); + //_cmdletPassedIn.WriteDebug("In LocalServerApiCalls::GetMatchingFilesGivenSpecificName()"); actualName = packageName; // used for FindVersionGlobbing where we know exact non-wildcard name of the package WildcardPattern pkgNamePattern = new WildcardPattern($"{packageName}.*", WildcardOptions.IgnoreCase); @@ -785,7 +800,7 @@ private Hashtable GetMatchingFilesGivenSpecificName(string packageName, bool inc if (!String.IsNullOrEmpty(packageFullName) && pkgNamePattern.IsMatch(packageFullName)) { NuGetVersion nugetVersion = GetInfoFromFileName(packageFullName: packageFullName, packageName: packageName, out actualName, errRecord: out errRecord); - _cmdletPassedIn.WriteDebug($"Found package '{packageName}' from path '{path}'"); + //_cmdletPassedIn.WriteDebug($"Found package '{packageName}' from path '{path}'"); if (errRecord != null) { continue; @@ -811,7 +826,7 @@ private Hashtable GetMatchingFilesGivenSpecificName(string packageName, bool inc /// private Hashtable GetMatchingFilesGivenNamePattern(string packageNameWithWildcard, bool includePrerelease) { - _cmdletPassedIn.WriteDebug("In LocalServerApiCalls::GetMatchingFilesGivenNamePattern()"); + //_cmdletPassedIn.WriteDebug("In LocalServerApiCalls::GetMatchingFilesGivenNamePattern()"); bool isNameFilteringRequired = !String.IsNullOrEmpty(packageNameWithWildcard); // wildcard name possibilities: power*, *get, power*get @@ -850,7 +865,7 @@ private Hashtable GetMatchingFilesGivenNamePattern(string packageNameWithWildcar } string version = packageFullName.Substring(group.Index + 1, packageFullName.LastIndexOf('.') - group.Index - 1); - _cmdletPassedIn.WriteDebug($"Found package '{pkgFoundName}', version '{version}', from path '{path}'"); + //_cmdletPassedIn.WriteDebug($"Found package '{pkgFoundName}', version '{version}', from path '{path}'"); if (!NuGetVersion.TryParse(version, out NuGetVersion nugetVersion)) { @@ -888,7 +903,7 @@ private Hashtable GetMatchingFilesGivenNamePattern(string packageNameWithWildcar /// private NuGetVersion GetInfoFromFileName(string packageFullName, string packageName, out string actualName, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In LocalServerApiCalls::GetInfoFromFileName()"); + //_cmdletPassedIn.WriteDebug("In LocalServerApiCalls::GetInfoFromFileName()"); // packageFullName will look like package.1.0.0.nupkg errRecord = null; diff --git a/src/code/NuGetServerAPICalls.cs b/src/code/NuGetServerAPICalls.cs index 1497c83da..81d9ca8f6 100644 --- a/src/code/NuGetServerAPICalls.cs +++ b/src/code/NuGetServerAPICalls.cs @@ -48,6 +48,15 @@ public NuGetServerAPICalls (PSRepositoryInfo repository, PSCmdlet cmdletPassedIn #region Overridden Methods + public override Task FindVersionAsync(string packageName, string version, ResourceType type) + { + throw new NotImplementedException(); + } + + public override Task FindVersionGlobbingAsync(string packageName, VersionRange versionRange, bool includePrerelease, ResourceType type, bool getOnlyLatest) + { + throw new NotImplementedException(); + } /// /// Find method which allows for searching for all packages from a repository and returns latest version for each. /// Examples: Search -Repository MyNuGetServer @@ -183,6 +192,11 @@ public override FindResults FindName(string packageName, bool includePrerelease, return new FindResults(stringResponse: new string[]{ response }, hashtableResponse: emptyHashResponses, responseType: FindResponseType); } + public override Task FindNameAsync(string packageName, bool includePrerelease, ResourceType type) + { + throw new NotImplementedException(); + } + /// /// Find method which allows for searching for single name and tag and returns latest version. /// Name: no wildcard support diff --git a/src/code/ServerApiCall.cs b/src/code/ServerApiCall.cs index 4580e362e..7096162d7 100644 --- a/src/code/ServerApiCall.cs +++ b/src/code/ServerApiCall.cs @@ -10,6 +10,7 @@ using System.Text; using System.Runtime.ExceptionServices; using System.Management.Automation; +using System.Threading.Tasks; namespace Microsoft.PowerShell.PSResourceGet.Cmdlets { @@ -81,6 +82,13 @@ public ServerApiCall(PSRepositoryInfo repository, NetworkCredential networkCrede /// public abstract FindResults FindName(string packageName, bool includePrerelease, ResourceType type, out ErrorRecord errRecord); + /// + /// Find method which allows for async searching for package by single name and returns latest version. + /// Name: no wildcard support + /// Examples: Search "PowerShellGet" + /// + public abstract Task FindNameAsync(string packageName, bool includePrerelease, ResourceType type); + /// /// Find method which allows for searching for package by single name and tag and returns latest version. /// Name: no wildcard support @@ -95,6 +103,15 @@ public ServerApiCall(PSRepositoryInfo repository, NetworkCredential networkCrede /// public abstract FindResults FindNameGlobbing(string packageName, bool includePrerelease, ResourceType type, out ErrorRecord errRecord); + /// + /// Find method which allows for async searching for single name with version range. + /// Name: no wildcard support + /// Version: supports wildcards + /// Examples: Search "PowerShellGet" "[3.0.0.0, 5.0.0.0]" + /// Search "PowerShellGet" "3.*" + /// + public abstract Task FindVersionGlobbingAsync(string packageName, VersionRange versionRange, bool includePrerelease, ResourceType type, bool getOnlyLatest); + /// /// Find method which allows for searching for single name with wildcards and tag and returns latest version. /// Name: supports wildcards @@ -118,6 +135,14 @@ public ServerApiCall(PSRepositoryInfo repository, NetworkCredential networkCrede /// public abstract FindResults FindVersion(string packageName, string version, ResourceType type, out ErrorRecord errRecord); + /// + /// Find method which allows for async searching for single name with specific version. + /// Name: no wildcard support + /// Version: no wildcard support + /// Examples: Search "PowerShellGet" "2.2.5" + /// + public abstract Task FindVersionAsync(string packageName, string version, ResourceType type); + /// /// Find method which allows for searching for single name with specific version. /// Name: no wildcard support diff --git a/src/code/Utils.cs b/src/code/Utils.cs index 26d3ab25e..8db3e7c77 100644 --- a/src/code/Utils.cs +++ b/src/code/Utils.cs @@ -1333,7 +1333,6 @@ public static bool TryReadRequiredResourceFile( out resourceInfo, out error); } - private static bool TryReadPSDataFile( string filePath, string[] allowedVariables, @@ -1342,37 +1341,97 @@ private static bool TryReadPSDataFile( out Hashtable dataFileInfo, out Exception error) { + dataFileInfo = null; + error = null; try { if (filePath is null) { throw new PSArgumentNullException(nameof(filePath)); } - string contents = System.IO.File.ReadAllText(filePath); - var scriptBlock = System.Management.Automation.ScriptBlock.Create(contents); - - // Ensure that the content script block is safe to convert into a PSDataFile Hashtable. - // This will throw for unsafe content. - scriptBlock.CheckRestrictedLanguage( - allowedCommands: allowedCommands, - allowedVariables: allowedVariables, - allowEnvironmentVariables: allowEnvironmentVariables); - - // Convert contents into PSDataFile Hashtable by executing content as script. - object result = scriptBlock.InvokeReturnAsIs(); - if (result is PSObject psObject) + + // Parallel.ForEach calls into this method. + // Each thread needs its own runspace created to provide a separate environment for operations to run independently. + Runspace runspace = RunspaceFactory.CreateRunspace(); + runspace.Open(); + runspace.SessionStateProxy.LanguageMode = PSLanguageMode.ConstrainedLanguage; + + // Save and set the default runspace for the current thread to prevent + // stale DefaultRunspace from a prior operation on this reused thread-pool thread. + Runspace previousDefaultRunspace = Runspace.DefaultRunspace; + try { - result = psObject.BaseObject; - } + Runspace.DefaultRunspace = runspace; - dataFileInfo = (Hashtable)result; - error = null; - return true; + using (System.Management.Automation.PowerShell pwsh = System.Management.Automation.PowerShell.Create()) + { + pwsh.Runspace = runspace; + + var cmd = new Command( + command: contents, + isScript: true, + useLocalScope: true); + cmd.MergeMyResults( + myResult: PipelineResultTypes.Error | PipelineResultTypes.Warning | PipelineResultTypes.Verbose | PipelineResultTypes.Debug | PipelineResultTypes.Information, + toResult: PipelineResultTypes.Output); + pwsh.Commands.AddCommand(cmd); + + try + { + // Invoke the pipeline and retrieve the results + var results = pwsh.Invoke(); + + if (results[0] is PSObject pwshObj) + { + switch (pwshObj.BaseObject) + { + case ErrorRecord err: + //_cmdletPassedIn.WriteError(error); + break; + + case WarningRecord warning: + //cmdlet.WriteWarning(warning.Message); + break; + + case VerboseRecord verbose: + //cmdlet.WriteVerbose(verbose.Message); + break; + + case DebugRecord debug: + //cmdlet.WriteDebug(debug.Message); + break; + + case InformationRecord info: + //cmdlet.WriteInformation(info); + break; + + case Hashtable result: + dataFileInfo = result; + return true; + } + } + } + catch (Exception ex) + { + error = ex; + } + } + // Return false to indicate "we couldn't parse a valid Hashtable from this .psd1 file." + // The only success path is the 'case Hashtable result' branch above which does return true. + return false; + } + finally + { + // Always restore the previous default runspace and close/dispose + // the per-thread runspace, even on success (return true) or exception paths. + Runspace.DefaultRunspace = previousDefaultRunspace; + runspace.Close(); + runspace.Dispose(); + } } catch (Exception ex) { - dataFileInfo = null; error = ex; return false; } @@ -2184,14 +2243,16 @@ internal static bool CheckAuthenticodeSignature( string pkgName, string tempDirNameVersion, PSCmdlet cmdletPassedIn, + out string warning, out ErrorRecord errorRecord) { + warning = string.Empty; errorRecord = null; // Because authenticode and catalog verifications are only applicable on Windows, we allow all packages by default to be installed on unix systems. if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - cmdletPassedIn.WriteWarning("Authenticode check cannot be performed on Linux or MacOS."); + warning = "Authenticode check cannot be performed on Linux or MacOS."; return true; } @@ -2200,16 +2261,16 @@ internal static bool CheckAuthenticodeSignature( try { string[] listOfExtensions = { "*.ps1", "*.psd1", "*.psm1", "*.mof", "*.cat", "*.ps1xml" }; - authenticodeSignatures = cmdletPassedIn.InvokeCommand.InvokeScript( - script: @"param ( - [string] $tempDirNameVersion, - [string[]] $listOfExtensions - ) - Get-ChildItem $tempDirNameVersion -Recurse -Include $listOfExtensions | Get-AuthenticodeSignature -ErrorAction SilentlyContinue", - useNewScope: true, - writeToPipeline: System.Management.Automation.Runspaces.PipelineResultTypes.None, - input: null, - args: new object[] { tempDirNameVersion, listOfExtensions }); + using (var pwsh = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace)) + { + authenticodeSignatures = pwsh.AddCommand("Get-ChildItem") + .AddParameter("Path", tempDirNameVersion) + .AddParameter("Recurse") + .AddParameter("Include", listOfExtensions) + .AddCommand("Get-AuthenticodeSignature") + .AddParameter("ErrorAction", "SilentlyContinue") + .Invoke(); + } } catch (Exception e) { diff --git a/src/code/V2ServerAPICalls.cs b/src/code/V2ServerAPICalls.cs index ec3551f79..2ad252595 100644 --- a/src/code/V2ServerAPICalls.cs +++ b/src/code/V2ServerAPICalls.cs @@ -397,6 +397,83 @@ public override FindResults FindName(string packageName, bool includePrerelease, return new FindResults(stringResponse: new string[]{ response }, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); } + /// + /// Find method which allows for searching for single name and returns latest version. + /// Name: no wildcard support + /// Examples: Search "PowerShellGet" + /// API call: + /// - No prerelease: http://www.powershellgallery.com/api/v2/FindPackagesById()?id='PowerShellGet' + /// - Include prerelease: http://www.powershellgallery.com/api/v2/FindPackagesById()?id='PowerShellGet' + /// Implementation Note: Need to filter further for latest version (prerelease or non-prerelease depending on user preference) + /// + public override async Task FindNameAsync(string packageName, bool includePrerelease, ResourceType type) + { + // TODO: pass all debug output into a list that can be written to console later. + //_cmdletPassedIn.WriteDebug("In V2ServerAPICalls::FindNameAsync()"); + // Make sure to include quotations around the package name + + // This should return the latest stable version or the latest prerelease version (respectively) + // https://www.powershellgallery.com/api/v2/FindPackagesById()?id='PowerShellGet'&$filter=IsLatestVersion and substringof('PSModule', Tags) eq true + // We need to explicitly add 'Id eq ' whenever $filter is used, otherwise arbitrary results are returned. + + var queryBuilder = new NuGetV2QueryBuilder(new Dictionary{ + { "$inlinecount", "allpages" }, + { "id", $"'{packageName}'" }, + }); + var filterBuilder = queryBuilder.FilterBuilder; + + // If it's a JFrog repository do not include the Id filter portion since JFrog uses 'Title' instead of 'Id', + // however filtering on 'and Title eq '' returns "Response status code does not indicate success: 500". + if (!_isJFrogRepo) { + filterBuilder.AddCriterion($"Id eq '{packageName}'"); + } + + filterBuilder.AddCriterion(includePrerelease ? "IsAbsoluteLatestVersion eq true" : "IsLatestVersion eq true"); + if (type != ResourceType.None) { + filterBuilder.AddCriterion(GetTypeFilterForRequest(type)); + } + + var requestUrlV2 = $"{Repository.Uri}/FindPackagesById()?{queryBuilder.BuildQueryString()}"; + string response; + try + { + response = await HttpRequestCallAsync(requestUrlV2); + } + catch (Exception e) + { + // usually this is for errors in calling the V2 server, but for ADO V2 this error will include package not found errors which we want to deliver in a standard message + if (_isADORepo && e is ResourceNotFoundException) + { + throw new ResourceNotFoundException($"Package with name '{packageName}' could not be found in repository '{Repository.Name}'. For ADO feed, if the package is in an upstream feed make sure you are authenticated to the upstream feed.", e); + } + else if (e is UnauthorizedException) + { + throw new UnauthorizedException($"Package with name '{packageName}' could not be retrieved from repository '{Repository.Name}'.", e); + } + else if (e is HttpRequestException) + { + throw new HttpRequestException($"Package with name '{packageName}' could not be retrieved from repository '{Repository.Name}'.", e); + } + else + { + throw new Exception($"Package with name '{packageName}' could not be retrieved from repository '{Repository.Name}'.", e); + } + } + + int count = GetCountFromResponse(response, out ErrorRecord errRecord); + if (errRecord != null) + { + throw new Exception($"Error retrieving count from response for package '{packageName}' from repository '{Repository.Name}'.", errRecord.Exception); + } + + if (count == 0) + { + throw new ResourceNotFoundException($"Package with name '{packageName}' could not be found in repository '{Repository.Name}'."); + } + + return new FindResults(stringResponse: new string[]{ response }, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); + } + /// /// Find method which allows for searching for single name and tag and returns latest version. /// Name: no wildcard support @@ -706,6 +783,72 @@ public override FindResults FindVersion(string packageName, string version, Reso return new FindResults(stringResponse: new string[] { response }, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); } + + public override async Task FindVersionAsync(string packageName, string version, ResourceType type) + { + //_cmdletPassedIn.WriteDebug("In V2ServerAPICalls::FindVersionAsync()"); + // https://www.powershellgallery.com/api/v2/FindPackagesById()?id='blah'&includePrerelease=false&$filter= NormalizedVersion eq '1.1.0' and substringof('PSModule', Tags) eq true + // Quotations around package name and version do not matter, same metadata gets returned. + // We need to explicitly add 'Id eq ' whenever $filter is used, otherwise arbitrary results are returned. + + var queryBuilder = new NuGetV2QueryBuilder(new Dictionary{ + { "$inlinecount", "allpages" }, + { "id", $"'{packageName}'" }, + }); + var filterBuilder = queryBuilder.FilterBuilder; + + // If it's a JFrog repository do not include the Id filter portion since JFrog uses 'Title' instead of 'Id', + // however filtering on 'and Title eq '' returns "Response status code does not indicate success: 500". + if (!_isJFrogRepo) { + filterBuilder.AddCriterion($"Id eq '{packageName}'"); + } + + filterBuilder.AddCriterion($"NormalizedVersion eq '{version}'"); + if (type != ResourceType.None) { + filterBuilder.AddCriterion(GetTypeFilterForRequest(type)); + } + + var requestUrlV2 = $"{Repository.Uri}/FindPackagesById()?{queryBuilder.BuildQueryString()}"; + string response; + try + { + response = await HttpRequestCallAsync(requestUrlV2); + } + catch (Exception e) + { + // usually this is for errors in calling the V2 server, but for ADO V2 this error will include package not found errors which we want to deliver with a standard message + if (_isADORepo && e is ResourceNotFoundException) + { + throw new ResourceNotFoundException($"Package with name '{packageName}' and version '{version}' could not be found in repository '{Repository.Name}'. For ADO feed, if the package is in an upstream feed make sure you are authenticated to the upstream feed.", e); + } + else if (e is UnauthorizedException) + { + throw new UnauthorizedException($"Package with name '{packageName}' could not be retrieved from repository '{Repository.Name}'.", e); + } + else if (e is HttpRequestException) + { + throw new HttpRequestException($"Package with name '{packageName}' could not be retrieved from repository '{Repository.Name}'.", e); + } + else + { + throw new Exception($"Package with name '{packageName}' could not be retrieved from repository '{Repository.Name}'.", e); + } + } + + int count = GetCountFromResponse(response, out ErrorRecord errRecord); + if (errRecord != null) + { + throw new Exception($"Error retrieving count from response for package '{packageName}' from repository '{Repository.Name}'.", errRecord.Exception); + } + + if (count == 0) + { + throw new ResourceNotFoundException($"Package with name '{packageName}', version '{version}' could not be found in repository '{Repository.Name}'."); + } + + return new FindResults(stringResponse: new string[] { response }, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); + } + /// /// Find method which allows for searching for single name with specific version and tag. /// Name: no wildcard support @@ -779,6 +922,7 @@ public override FindResults FindVersionWithTag(string packageName, string versio /// public override Stream InstallPackage(string packageName, string packageVersion, bool includePrerelease, out ErrorRecord errRecord) { + errRecord = null; Stream results = new MemoryStream(); if (string.IsNullOrEmpty(packageVersion)) { @@ -791,7 +935,7 @@ public override Stream InstallPackage(string packageName, string packageVersion, return results; } - results = InstallVersion(packageName, packageVersion, out errRecord); + results = InstallVersionAsync(packageName, packageVersion).GetAwaiter().GetResult(); return results; } @@ -852,18 +996,91 @@ private string HttpRequestCall(string requestUrlV2, out ErrorRecord errRecord) return response; } + /// + /// Helper method that makes the HTTP request for the V2 server protocol url passed in for find APIs. + /// + private async Task HttpRequestCallAsync(string requestUrlV2) + { + // TODO: Async methods cannot have out ref, so currently handling errorRecords as thrown exceptions. + string response = string.Empty; + + try + { + HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrlV2); + + response = await SendV2RequestAsync(request, _sessionClient); + } + catch (ResourceNotFoundException) + { + throw new ResourceNotFoundException($"Failure when attempting to make request for '${requestUrlV2}'"); + } + catch (UnauthorizedException) + { + throw new UnauthorizedException($"Failure when attempting to make request for '${requestUrlV2}'"); + } + catch (HttpRequestException) + { + throw new HttpRequestException($"Failure when attempting to make request for '${requestUrlV2}'"); + } + catch (Exception) + { + throw new Exception($"Failure when attempting to make request for '${requestUrlV2}'"); + } + + if (string.IsNullOrEmpty(response)) + { + throw new ArgumentException($"Response returned is null or empty. Request made: '${requestUrlV2}'"); + } + + return response; + } + /// /// Helper method that makes the HTTP request for the V2 server protocol url passed in for install APIs. /// + private async Task HttpRequestCallForContentAsync(string requestUrlV2) + { + // TODO: Async methods cannot have out ref, so need to handle errorRecords a different way. + // _cmdletPassedIn.WriteDebug("In V2ServerAPICalls::HttpRequestCallForContentAsync()"); + HttpContent content = null; + + try + { + //_cmdletPassedIn.WriteDebug($"Request url is '{requestUrlV2}'"); + HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrlV2); + + content = await SendV2RequestForContentAsync(request, _sessionClient); + } + catch (HttpRequestException) + { + throw new HttpRequestException($"Failure when attempting to make request for content with '${requestUrlV2}'"); + } + catch (ArgumentNullException) + { + throw new ArgumentNullException($"Failure when attempting to make request for content with '${requestUrlV2}'"); + } + catch (InvalidOperationException) + { + throw new InvalidOperationException($"Failure when attempting to make request for content with '${requestUrlV2}'"); + } + + if (content == null || string.IsNullOrEmpty(content.ToString())) + { + throw new ArgumentException($"Response returned is null or empty. Request for content made: '${requestUrlV2}'"); + } + + return content; + } + private HttpContent HttpRequestCallForContent(string requestUrlV2, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In V2ServerAPICalls::HttpRequestCallForContent()"); + // _cmdletPassedIn.WriteDebug("In V2ServerAPICalls::HttpRequestCallForContent()"); errRecord = null; HttpContent content = null; try { - _cmdletPassedIn.WriteDebug($"Request url is '{requestUrlV2}'"); + //_cmdletPassedIn.WriteDebug($"Request url is '{requestUrlV2}'"); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrlV2); content = SendV2RequestForContentAsync(request, _sessionClient).GetAwaiter().GetResult(); @@ -895,7 +1112,7 @@ private HttpContent HttpRequestCallForContent(string requestUrlV2, out ErrorReco if (content == null || string.IsNullOrEmpty(content.ToString())) { - _cmdletPassedIn.WriteDebug("Response is empty"); + //_cmdletPassedIn.WriteDebug("Response is empty"); } return content; @@ -1288,7 +1505,7 @@ private string FindNameGlobbingWithTag(string packageName, string[] tags, Resour /// private string FindVersionGlobbing(string packageName, VersionRange versionRange, bool includePrerelease, ResourceType type, int skip, bool getOnlyLatest, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In V2ServerAPICalls::FindVersionGlobbing()"); + //_cmdletPassedIn.WriteDebug("In V2ServerAPICalls::FindVersionGlobbing()"); //https://www.powershellgallery.com/api/v2//FindPackagesById()?id='blah'&includePrerelease=false&$filter= NormalizedVersion gt '1.0.0' and NormalizedVersion lt '2.2.5' and substringof('PSModule', Tags) eq true //https://www.powershellgallery.com/api/v2//FindPackagesById()?id='PowerShellGet'&includePrerelease=false&$filter= NormalizedVersion gt '1.1.1' and NormalizedVersion lt '2.2.5' // NormalizedVersion doesn't include trailing zeroes @@ -1384,6 +1601,153 @@ private string FindVersionGlobbing(string packageName, VersionRange versionRange return HttpRequestCall(requestUrlV2, out errRecord); } + + public override async Task FindVersionGlobbingAsync(string packageName, VersionRange versionRange, bool includePrerelease, ResourceType type, bool getOnlyLatest) + { + //_cmdletPassedIn.WriteDebug("In V2ServerAPICalls::FindVersionGlobbing()"); + List responses = new List(); + int skip = 0; + + var initialResponse = await FindVersionGlobbingAsync(packageName, versionRange, includePrerelease, type, skip, getOnlyLatest); + + int initialCount = GetCountFromResponse(initialResponse, out ErrorRecord errRecord); + if (errRecord != null) + { + return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); + } + + if (initialCount == 0) + { + return new FindResults(stringResponse: responses.ToArray(), hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); + } + + responses.Add(initialResponse); + + if (!getOnlyLatest) + { + int count = (int)Math.Ceiling((double)(initialCount / 100)); + + while (count > 0) + { + //_cmdletPassedIn.WriteDebug($"Count is '{count}'"); + // skip 100 + skip += 100; + var tmpResponse = FindVersionGlobbing(packageName, versionRange, includePrerelease, type, skip, getOnlyLatest, out errRecord); + if (errRecord != null) + { + return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); + } + responses.Add(tmpResponse); + count--; + } + } + + return new FindResults(stringResponse: responses.ToArray(), hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); + } + + + /// + /// Helper method for string[] FindVersionGlobbing() + /// + private async Task FindVersionGlobbingAsync(string packageName, VersionRange versionRange, bool includePrerelease, ResourceType type, int skip, bool getOnlyLatest) + { + //_cmdletPassedIn.WriteDebug("In V2ServerAPICalls::FindVersionGlobbing()"); + //https://www.powershellgallery.com/api/v2//FindPackagesById()?id='blah'&includePrerelease=false&$filter= NormalizedVersion gt '1.0.0' and NormalizedVersion lt '2.2.5' and substringof('PSModule', Tags) eq true + //https://www.powershellgallery.com/api/v2//FindPackagesById()?id='PowerShellGet'&includePrerelease=false&$filter= NormalizedVersion gt '1.1.1' and NormalizedVersion lt '2.2.5' + // NormalizedVersion doesn't include trailing zeroes + // Notes: this could allow us to take a version range (i.e (2.0.0, 3.0.0.0]) and deconstruct it and add options to the Filter for Version to describe that range + // will need to filter additionally, if IncludePrerelease=false, by default we get stable + prerelease both back + // Current bug: Find PSGet -Version "2.0.*" -> https://www.powershellgallery.com/api/v2//FindPackagesById()?id='PowerShellGet'&includePrerelease=false&$filter= Version gt '2.0.*' and Version lt '2.1' + // Make sure to include quotations around the package name + + //and IsPrerelease eq false + // ex: + // (2.0.0, 3.0.0) + // $filter= NVersion gt '2.0.0' and NVersion lt '3.0.0' + + // [2.0.0, 3.0.0] + // $filter= NVersion ge '2.0.0' and NVersion le '3.0.0' + + // [2.0.0, 3.0.0) + // $filter= NVersion ge '2.0.0' and NVersion lt '3.0.0' + + // (2.0.0, 3.0.0] + // $filter= NVersion gt '2.0.0' and NVersion le '3.0.0' + + // [, 2.0.0] + // $filter= NVersion le '2.0.0' + + string format = "NormalizedVersion {0} {1}"; + string minPart = String.Empty; + string maxPart = String.Empty; + + var queryBuilder = new NuGetV2QueryBuilder(new Dictionary { + {"$inlinecount", "allpages"}, + {"$skip", skip.ToString()}, + {"$orderby", "NormalizedVersion desc"}, + {"id", $"'{packageName}'"} + }); + + var filterBuilder = queryBuilder.FilterBuilder; + + if (versionRange.MinVersion != null) + { + string operation = versionRange.IsMinInclusive ? "ge" : "gt"; + minPart = String.Format(format, operation, $"'{versionRange.MinVersion.ToNormalizedString()}'"); + } + + if (versionRange.MaxVersion != null) + { + string operation = versionRange.IsMaxInclusive ? "le" : "lt"; + // Adding '9' as a digit to the end of the patch portion of the version + // because we want to retrieve all the prerelease versions for the upper end of the range + // and PSGallery views prerelease as higher than its stable. + // eg 3.0.0-prerelease > 3.0.0 + // If looking for versions within '[1.9.9,1.9.9]' including prerelease values, this will change it to search for '[1.9.9,1.9.99]' + // and find any pkg versions that are 1.9.9-prerelease. + string maxString = includePrerelease ? $"{versionRange.MaxVersion.Major}.{versionRange.MaxVersion.Minor}.{versionRange.MaxVersion.Patch.ToString() + "9"}" : + $"{versionRange.MaxVersion.ToNormalizedString()}"; + 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; + if (!String.IsNullOrEmpty(minPart)) + { + filterBuilder.AddCriterion(minPart); + } + if (!String.IsNullOrEmpty(maxPart)) + { + filterBuilder.AddCriterion(maxPart); + } + if (!includePrerelease) { + filterBuilder.AddCriterion("IsPrerelease eq false"); + } + + // We need to explicitly add 'Id eq ' whenever $filter is used, otherwise arbitrary results are returned. + + // If it's a JFrog repository do not include the Id filter portion since JFrog uses 'Title' instead of 'Id', + // however filtering on 'and Title eq '' returns "Response status code does not indicate success: 500". + if (!_isJFrogRepo) { + filterBuilder.AddCriterion($"Id eq '{packageName}'"); + } + + if (type == ResourceType.Script) { + filterBuilder.AddCriterion($"substringof('PS{type.ToString()}', Tags) eq true"); + } + + + var requestUrlV2 = $"{Repository.Uri}/FindPackagesById()?{queryBuilder.BuildQueryString()}"; + + return await HttpRequestCallAsync(requestUrlV2); + } + + /// /// Installs package with specific name and version. /// Name: no wildcard support. @@ -1394,7 +1758,7 @@ private string FindVersionGlobbing(string packageName, VersionRange versionRange /// private Stream InstallVersion(string packageName, string version, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In V2ServerAPICalls::InstallVersion()"); + //_cmdletPassedIn.WriteDebug("In V2ServerAPICalls::InstallVersion()"); string requestUrlV2; if (_isADORepo) @@ -1433,6 +1797,38 @@ private Stream InstallVersion(string packageName, string version, out ErrorRecor return response.ReadAsStreamAsync().Result; } + private async Task InstallVersionAsync(string packageName, string version) + { + //_cmdletPassedIn.WriteDebug("In V2ServerAPICalls::InstallVersionAsync()"); + string requestUrlV2; + + if (_isADORepo) + { + // eg: https://pkgs.dev.azure.com///_packaging//nuget/v2?id=test_module&version=5.0.0 + requestUrlV2 = $"{Repository.Uri}?id={packageName.ToLower()}&version={version}"; + } + else if (_isJFrogRepo) + { + // eg: https://.jfrog.io/artifactory/api/nuget//Download/test_module/5.0.0 + requestUrlV2 = $"{Repository.Uri}/Download/{packageName}/{version}"; + } + else + { + requestUrlV2 = $"{Repository.Uri}/package/{packageName}/{version}"; + } + + var response = await HttpRequestCallForContentAsync(requestUrlV2); + + if (response is null) + { + throw new Exception($"No content was returned by repository '{Repository.Name}'"); + } + + var retResponse = await response.ReadAsStreamAsync(); + + return retResponse; + } + private string GetTypeFilterForRequest(ResourceType type) { string typeFilterPart = string.Empty; if (type == ResourceType.Script) @@ -1495,7 +1891,11 @@ public int GetCountFromResponse(string httpResponse, out ErrorRecord errRecord) } else { - _cmdletPassedIn.WriteDebug($"Property 'count' and 'd:Id' could not be found in response. This may indicate that the package could not be found"); + errRecord = new ErrorRecord( + new PSArgumentException("Property 'count' and 'd:Id' could not be found in response. This may indicate that the package could not be found"), + "GetCountFromResponseFailure", + ErrorCategory.InvalidData, + this); } } } diff --git a/src/code/V3ServerAPICalls.cs b/src/code/V3ServerAPICalls.cs index 903b1da55..549bcdb7a 100644 --- a/src/code/V3ServerAPICalls.cs +++ b/src/code/V3ServerAPICalls.cs @@ -88,6 +88,16 @@ public V3ServerAPICalls(PSRepositoryInfo repository, PSCmdlet cmdletPassedIn, Ne #region Overridden Methods + public override Task FindVersionAsync(string packageName, string version, ResourceType type) + { + throw new NotImplementedException("FindVersionAsync is not implemented for V3ServerAPICalls."); + } + + public override Task FindVersionGlobbingAsync(string packageName, VersionRange versionRange, bool includePrerelease, ResourceType type, bool getOnlyLatest) + { + throw new NotImplementedException("FindVersionAsync is not implemented for V3ServerAPICalls."); + } + /// /// Find method which allows for searching for all packages from a repository and returns latest version for each. /// Not supported for V3 repository. @@ -152,7 +162,16 @@ public override FindResults FindCommandOrDscResource(string[] tags, bool include public override FindResults FindName(string packageName, bool includePrerelease, ResourceType type, out ErrorRecord errRecord) { _cmdletPassedIn.WriteDebug("In V3ServerAPICalls::FindName()"); - return FindNameHelper(packageName, tags: Utils.EmptyStrArray, includePrerelease, type, out errRecord); + var res = FindNameHelper(packageName, tags: Utils.EmptyStrArray, includePrerelease, type, out errRecord); + _cmdletPassedIn.WriteDebug($"returned back to FindName()"); + + return res; + + } + + public override Task FindNameAsync(string packageName, bool includePrerelease, ResourceType type) + { + throw new NotImplementedException("FindVersionAsync is not implemented for V3ServerAPICalls."); } /// @@ -164,7 +183,11 @@ public override FindResults FindName(string packageName, bool includePrerelease, public override FindResults FindNameWithTag(string packageName, string[] tags, bool includePrerelease, ResourceType type, out ErrorRecord errRecord) { _cmdletPassedIn.WriteDebug("In V3ServerAPICalls::FindNameWithTag()"); - return FindNameHelper(packageName, tags, includePrerelease, type, out errRecord); + var res = FindNameHelper(packageName, tags, includePrerelease, type, out errRecord); + + _cmdletPassedIn.WriteDebug($"returned back to FIndNamewithtag"); + + return res; } /// @@ -312,7 +335,8 @@ public override FindResults FindVersionWithTag(string packageName, string versio /// public override Stream InstallPackage(string packageName, string packageVersion, bool includePrerelease, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In V3ServerAPICalls::InstallPackage()"); + // this can be called from inside concurrency + //_cmdletPassedIn.WriteDebug("In V3ServerAPICalls::InstallPackage()"); Stream results = new MemoryStream(); if (string.IsNullOrEmpty(packageVersion)) { @@ -326,6 +350,9 @@ public override Stream InstallPackage(string packageName, string packageVersion, } results = InstallVersion(packageName, packageVersion, out errRecord); + + //_cmdletPassedIn.WriteDebug("Returning from V3ServerAPICalls::InstallPackage()"); + return results; } @@ -496,10 +523,21 @@ private FindResults FindNameHelper(string packageName, string[] tags, bool inclu bool isTagMatch = true; foreach (string response in versionedResponses) { + _cmdletPassedIn.WriteDebug("In V3ServerAPICalls::FindNameHelper():: response"); + if (response == null) + { + _cmdletPassedIn.WriteDebug("In V3ServerAPICalls::FindNameHelper():: response is NULL"); + } + else + { + _cmdletPassedIn.WriteDebug("In V3ServerAPICalls::FindNameHelper():: response is NOT null"); + } + try { using (JsonDocument pkgVersionEntry = JsonDocument.Parse(response)) { + _cmdletPassedIn.WriteDebug("In V3ServerAPICalls::FindNameHelper():: using 1"); JsonElement rootDom = pkgVersionEntry.RootElement; if (!rootDom.TryGetProperty(versionName, out JsonElement pkgVersionElement)) { @@ -522,14 +560,18 @@ private FindResults FindNameHelper(string packageName, string[] tags, bool inclu return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v3FindResponseType); } + _cmdletPassedIn.WriteDebug("In V3ServerAPICalls::FindNameHelper():: nuget parse version"); + if (NuGetVersion.TryParse(pkgVersionElement.ToString(), out NuGetVersion pkgVersion)) { _cmdletPassedIn.WriteDebug($"'{packageName}' version parsed as '{pkgVersion}'"); if (!pkgVersion.IsPrerelease || includePrerelease) { + _cmdletPassedIn.WriteDebug($"entered if statement"); // Versions are always in descending order i.e 5.0.0, 3.0.0, 1.0.0 so grabbing the first match suffices latestVersionResponse = response; isTagMatch = IsRequiredTagSatisfied(tagsItem, tags, out errRecord); + _cmdletPassedIn.WriteDebug($"right before break"); break; } @@ -548,6 +590,8 @@ private FindResults FindNameHelper(string packageName, string[] tags, bool inclu } } + _cmdletPassedIn.WriteDebug($"FindNameHelper line 581"); + if (String.IsNullOrEmpty(latestVersionResponse)) { errRecord = new ErrorRecord( @@ -559,6 +603,8 @@ private FindResults FindNameHelper(string packageName, string[] tags, bool inclu return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v3FindResponseType); } + _cmdletPassedIn.WriteDebug($"Right before tag match"); + // Check and write error for tags matching requirement. If no tags were required the isTagMatch variable will be true. if (!isTagMatch) { @@ -573,6 +619,8 @@ private FindResults FindNameHelper(string packageName, string[] tags, bool inclu return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v3FindResponseType); } + + _cmdletPassedIn.WriteDebug($"return results"); return new FindResults(stringResponse: new string[] { latestVersionResponse }, hashtableResponse: emptyHashResponses, responseType: v3FindResponseType); } @@ -691,7 +739,11 @@ private FindResults FindVersionHelper(string packageName, string version, string private Stream InstallName(string packageName, out ErrorRecord errRecord) { _cmdletPassedIn.WriteDebug("In V3ServerAPICalls::InstallName()"); - return InstallHelper(packageName, version: null, out errRecord); + var inst = InstallHelper(packageName, version: null, out errRecord); + + _cmdletPassedIn.WriteDebug("Returning from V3ServerAPICalls::InstallName()"); + + return inst; } /// @@ -703,9 +755,15 @@ private Stream InstallName(string packageName, out ErrorRecord errRecord) /// private Stream InstallVersion(string packageName, string version, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In V3ServerAPICalls::InstallVersion()"); + // gets called from inside concurrency + //_cmdletPassedIn.WriteDebug("In V3ServerAPICalls::InstallVersion()"); if (!NuGetVersion.TryParse(version, out NuGetVersion requiredVersion)) { + //_cmdletPassedIn.WriteDebug("InstallVersion - could not parse version"); + if (string.IsNullOrEmpty(version)) + { + //_cmdletPassedIn.WriteDebug("version is null or empty"); + } errRecord = new ErrorRecord( new ArgumentException($"Version {version} to be installed is not a valid NuGet version."), "InstallVersionFailure", @@ -714,8 +772,12 @@ private Stream InstallVersion(string packageName, string version, out ErrorRecor return null; } + //_cmdletPassedIn.WriteDebug("InstallVersion 765"); + + var inst = InstallHelper(packageName, requiredVersion, out errRecord); + //_cmdletPassedIn.WriteDebug("Returning from V3ServerAPICalls::InstallVersion()"); - return InstallHelper(packageName, requiredVersion, out errRecord); + return inst; } /// @@ -724,7 +786,8 @@ private Stream InstallVersion(string packageName, string version, out ErrorRecor /// private Stream InstallHelper(string packageName, NuGetVersion version, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In V3ServerAPICalls::InstallHelper()"); + // gets called from concurrency + //_cmdletPassedIn.WriteDebug("In V3ServerAPICalls::InstallHelper()"); Stream pkgStream = null; bool getLatestVersion = true; if (version != null) @@ -786,6 +849,8 @@ private Stream InstallHelper(string packageName, NuGetVersion version, out Error return null; } + //_cmdletPassedIn.WriteDebug("In V3ServerAPICalls::InstallHelper() before checking if content is null"); + if (content is null) { errRecord = new ErrorRecord( @@ -796,8 +861,9 @@ private Stream InstallHelper(string packageName, NuGetVersion version, out Error return null; } + //_cmdletPassedIn.WriteDebug("Exiting method V3ServerAPICalls::InstallHelper()"); - return content.ReadAsStreamAsync().Result; + return content.ReadAsStreamAsync().GetAwaiter().GetResult(); } /// @@ -807,7 +873,8 @@ private Stream InstallHelper(string packageName, NuGetVersion version, out Error /// private string[] GetVersionedPackageEntriesFromRegistrationsResource(string packageName, string propertyName, bool isSearch, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In V3ServerAPICalls::GetVersionedPackageEntriesFromRegistrationsResource()"); + // gets called from concurrency + //_cmdletPassedIn.WriteDebug("In V3ServerAPICalls::GetVersionedPackageEntriesFromRegistrationsResource()"); string[] responses = Utils.EmptyStrArray; Dictionary resources = GetResourcesFromServiceIndex(out errRecord); if (errRecord != null) @@ -827,6 +894,15 @@ private string[] GetVersionedPackageEntriesFromRegistrationsResource(string pack return Utils.EmptyStrArray; } + // if (responses == null) + // { + // _cmdletPassedIn.WriteDebug("In V3ServerAPICalls::GetVersionedPackageEntriesFromRegistrationsResource() - responses null"); + // } + // else + // { + // _cmdletPassedIn.WriteDebug("In V3ServerAPICalls::GetVersionedPackageEntriesFromRegistrationsResource() - responses NOT null"); + // } + return responses; } @@ -878,7 +954,8 @@ private List GetVersionedPackageEntriesFromSearchQueryResource(stri /// private Dictionary GetResourcesFromServiceIndex(out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In V3ServerAPICalls::GetResourcesFromServiceIndex()"); + // gets called from concurrency + //_cmdletPassedIn.WriteDebug("In V3ServerAPICalls::GetResourcesFromServiceIndex()"); Dictionary resources = new Dictionary(); JsonElement[] resourcesArray = GetJsonElementArr($"{Repository.Uri}", resourcesName, out int totalHits, out errRecord); if (errRecord != null) @@ -938,7 +1015,7 @@ private Dictionary GetResourcesFromServiceIndex(out ErrorRecord /// private string FindRegistrationsBaseUrl(Dictionary resources, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In V3ServerAPICalls::FindRegistrationsBaseUrl()"); + //_cmdletPassedIn.WriteDebug("In V3ServerAPICalls::FindRegistrationsBaseUrl()"); errRecord = null; string registrationsBaseUrl = String.Empty; @@ -1096,7 +1173,7 @@ private JsonElement[] GetMetadataElementFromIdLinkElement(JsonElement idLinkElem /// private JsonElement[] GetMetadataElementFromItemsElement(JsonElement itemsElement, string packageName, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In V3ServerAPICalls::GetMetadataElementFromItemsElement()"); + //_cmdletPassedIn.WriteDebug("In V3ServerAPICalls::GetMetadataElementFromItemsElement()"); errRecord = null; List innerItemsList = new List(); @@ -1128,7 +1205,7 @@ private JsonElement[] GetMetadataElementFromItemsElement(JsonElement itemsElemen /// private string[] GetMetadataElementsFromResponse(string response, string property, string packageName, out string upperVersion, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In V3ServerAPICalls::GetMetadataElementsFromResponse()"); + //_cmdletPassedIn.WriteDebug("In V3ServerAPICalls::GetMetadataElementsFromResponse()"); errRecord = null; upperVersion = String.Empty; List versionedPkgResponses = new List(); @@ -1174,7 +1251,7 @@ private string[] GetMetadataElementsFromResponse(string response, string propert } else { - _cmdletPassedIn.WriteDebug($"Package with name '{packageName}' did not have 'upper' property so package versions may not be in descending order."); + // _cmdletPassedIn.WriteDebug($"Package with name '{packageName}' did not have 'upper' property so package versions may not be in descending order."); } innerItemsElements.AddRange(innerItemsFromItemsElement); @@ -1260,7 +1337,7 @@ private string[] GetMetadataElementsFromResponse(string response, string propert /// private string[] GetVersionedResponsesFromRegistrationsResource(string registrationsBaseUrl, string packageName, string property, bool isSearch, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In V3ServerAPICalls::GetVersionedResponsesFromRegistrationsResource()"); + //_cmdletPassedIn.WriteDebug("In V3ServerAPICalls::GetVersionedResponsesFromRegistrationsResource()"); List versionedResponses = new List(); var requestPkgMapping = registrationsBaseUrl.EndsWith("/") ? $"{registrationsBaseUrl}{packageName.ToLower()}/index.json" : $"{registrationsBaseUrl}/{packageName.ToLower()}/index.json"; @@ -1308,6 +1385,8 @@ private string[] GetVersionedResponsesFromRegistrationsResource(string registrat } } + //_cmdletPassedIn.WriteDebug("In V3ServerAPICalls::GetVersionedResponse() - Exiting"); + return versionedResponseArr; } @@ -1328,11 +1407,15 @@ private bool IsLatestVersionFirstForSearch(string[] versionedResponses, out Erro return latestVersionFirst; } + _cmdletPassedIn.WriteDebug("In V3ServerAPICalls::IsLatestVersionFirstForSearch() - 2"); + string firstResponse = versionedResponses[0]; string lastResponse = versionedResponses[versionResponsesCount - 1]; NuGetVersion firstPkgVersion; NuGetVersion lastPkgVersion; + _cmdletPassedIn.WriteDebug("In V3ServerAPICalls::IsLatestVersionFirstForSearch() - 3"); + try { using (JsonDocument firstResponseJson = JsonDocument.Parse(firstResponse)) @@ -1362,6 +1445,8 @@ private bool IsLatestVersionFirstForSearch(string[] versionedResponses, out Erro return latestVersionFirst; } } + + _cmdletPassedIn.WriteDebug("In V3ServerAPICalls::IsLatestVersionFirstForSearch() - 4"); using (JsonDocument lastResponseJson = JsonDocument.Parse(lastResponse)) { @@ -1391,6 +1476,8 @@ private bool IsLatestVersionFirstForSearch(string[] versionedResponses, out Erro } } + _cmdletPassedIn.WriteDebug("In V3ServerAPICalls::IsLatestVersionFirstForSearch() - 5"); + if (firstPkgVersion < lastPkgVersion) { latestVersionFirst = false; @@ -1417,7 +1504,7 @@ private bool IsLatestVersionFirstForSearch(string[] versionedResponses, out Erro /// private bool IsLatestVersionFirstForInstall(string[] versionedResponses, string upperVersion, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In V3ServerAPICalls::IsLatestVersionFirstForInstall()"); + //_cmdletPassedIn.WriteDebug("In V3ServerAPICalls::IsLatestVersionFirstForInstall()"); errRecord = null; bool latestVersionFirst = true; @@ -1553,13 +1640,13 @@ private JsonElement[] GetJsonElementArr(string request, string propertyName, out /// private string HttpRequestCall(string requestUrlV3, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In V3ServerAPICalls::HttpRequestCall()"); + //_cmdletPassedIn.WriteDebug("In V3ServerAPICalls::HttpRequestCall()"); errRecord = null; string response = string.Empty; try { - _cmdletPassedIn.WriteDebug($"Request url is '{requestUrlV3}'"); + //_cmdletPassedIn.WriteDebug($"Request url is '{requestUrlV3}'"); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrlV3); response = SendV3RequestAsync(request, _sessionClient).GetAwaiter().GetResult(); @@ -1605,12 +1692,12 @@ private string HttpRequestCall(string requestUrlV3, out ErrorRecord errRecord) /// private HttpContent HttpRequestCallForContent(string requestUrlV3, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In V3ServerAPICalls::HttpRequestCallForContent()"); + //_cmdletPassedIn.WriteDebug("In V3ServerAPICalls::HttpRequestCallForContent()"); errRecord = null; HttpContent content = null; try { - _cmdletPassedIn.WriteDebug($"Request url is '{requestUrlV3}'"); + // _cmdletPassedIn.WriteDebug($"Request url is '{requestUrlV3}'"); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrlV3); content = SendV3RequestForContentAsync(request, _sessionClient).GetAwaiter().GetResult(); @@ -1628,8 +1715,10 @@ private HttpContent HttpRequestCallForContent(string requestUrlV3, out ErrorReco if (string.IsNullOrEmpty(content?.ToString())) { - _cmdletPassedIn.WriteDebug("Response is empty"); + //_cmdletPassedIn.WriteDebug("Response is empty"); } + + //_cmdletPassedIn.WriteDebug($"Returning from HttpRequestCallForContent"); return content; } diff --git a/test/FindPSResourceTests/FindPSResourceADOV2Server.Tests.ps1 b/test/FindPSResourceTests/FindPSResourceADOV2Server.Tests.ps1 index c45e6d05a..f419bfa7a 100644 --- a/test/FindPSResourceTests/FindPSResourceADOV2Server.Tests.ps1 +++ b/test/FindPSResourceTests/FindPSResourceADOV2Server.Tests.ps1 @@ -190,7 +190,7 @@ Describe 'Test HTTP Find-PSResource for ADO V2 Server Protocol' -tags 'CI' { $res = Find-PSResource -Name $testModuleName -Tag $requiredTag -Repository $ADOV2RepoName -ErrorVariable err -ErrorAction SilentlyContinue $res | Should -BeNullOrEmpty $err.Count | Should -BeGreaterThan 0 - $err[0].FullyQualifiedErrorId | Should -BeExactly "PackageNotFound,Microsoft.PowerShell.PSResourceGet.Cmdlets.FindPSResource" + $err[0].FullyQualifiedErrorId | Should -BeExactly "GetCountFromResponseFailure,Microsoft.PowerShell.PSResourceGet.Cmdlets.FindPSResource" } It "Find resource that satisfies given Name and Tag property (multiple tags)" { @@ -208,7 +208,7 @@ Describe 'Test HTTP Find-PSResource for ADO V2 Server Protocol' -tags 'CI' { $res = Find-PSResource -Name $testModuleName -Tag $requiredTags -Repository $ADOV2RepoName -ErrorVariable err -ErrorAction SilentlyContinue $res | Should -BeNullOrEmpty $err.Count | Should -BeGreaterThan 0 - $err[0].FullyQualifiedErrorId | Should -BeExactly "PackageNotFound,Microsoft.PowerShell.PSResourceGet.Cmdlets.FindPSResource" + $err[0].FullyQualifiedErrorId | Should -BeExactly "GetCountFromResponseFailure,Microsoft.PowerShell.PSResourceGet.Cmdlets.FindPSResource" } It "Find all resources that satisfy Name pattern and have specified Tag (single tag)" { @@ -244,7 +244,7 @@ Describe 'Test HTTP Find-PSResource for ADO V2 Server Protocol' -tags 'CI' { $res = Find-PSResource -Name $testModuleName -Version "5.0.0" -Tag $requiredTag -Repository $ADOV2RepoName -ErrorVariable err -ErrorAction SilentlyContinue $res | Should -BeNullOrEmpty $err.Count | Should -BeGreaterThan 0 - $err[0].FullyQualifiedErrorId | Should -BeExactly "PackageNotFound,Microsoft.PowerShell.PSResourceGet.Cmdlets.FindPSResource" + $err[0].FullyQualifiedErrorId | Should -BeExactly "GetCountFromResponseFailure,Microsoft.PowerShell.PSResourceGet.Cmdlets.FindPSResource" } It "Find resource that satisfies given Name, Version and Tag property (multiple tags)" { @@ -264,6 +264,6 @@ Describe 'Test HTTP Find-PSResource for ADO V2 Server Protocol' -tags 'CI' { $res = Find-PSResource -Name $testModuleName -Version "5.0.0" -Tag $requiredTags -Repository $ADOV2RepoName -ErrorVariable err -ErrorAction SilentlyContinue $res | Should -BeNullOrEmpty $err.Count | Should -BeGreaterThan 0 - $err[0].FullyQualifiedErrorId | Should -BeExactly "PackageNotFound,Microsoft.PowerShell.PSResourceGet.Cmdlets.FindPSResource" + $err[0].FullyQualifiedErrorId | Should -BeExactly "GetCountFromResponseFailure,Microsoft.PowerShell.PSResourceGet.Cmdlets.FindPSResource" } } diff --git a/test/FindPSResourceTests/FindPSResourceContainerRegistryServer.Tests.ps1 b/test/FindPSResourceTests/FindPSResourceContainerRegistryServer.Tests.ps1 index e372720b0..cf2a9c83b 100644 --- a/test/FindPSResourceTests/FindPSResourceContainerRegistryServer.Tests.ps1 +++ b/test/FindPSResourceTests/FindPSResourceContainerRegistryServer.Tests.ps1 @@ -298,18 +298,6 @@ Describe 'Test Find-PSResource for MAR Repository' -tags 'CI' { $res.Dependencies.Length | Should -Not -Be 0 } - It "Should find resource with wildcard in Name" { - $res = Find-PSResource -Name "Az.App*" -Repository "MAR" - $res | Should -Not -BeNullOrEmpty - $res.Count | Should -BeGreaterThan 1 - } - - It "Should find all resource with wildcard in Name" { - $res = Find-PSResource -Name "*" -Repository "MAR" - $res | Should -Not -BeNullOrEmpty - $res.Count | Should -BeGreaterThan 1 - } - It "Should find version range for Az dependencies" { # Target known version to know the output from the API won't change $res = Find-PSResource -Repository 'MAR' -Name 'Az' -Version '14.4.0' diff --git a/test/FindPSResourceTests/FindPSResourceLocal.Tests.ps1 b/test/FindPSResourceTests/FindPSResourceLocal.Tests.ps1 index a3cbe2335..bdaed980b 100644 --- a/test/FindPSResourceTests/FindPSResourceLocal.Tests.ps1 +++ b/test/FindPSResourceTests/FindPSResourceLocal.Tests.ps1 @@ -1,350 +1,350 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -$modPath = "$psscriptroot/../PSGetTestUtils.psm1" -Import-Module $modPath -Force -Verbose - -$psmodulePaths = $env:PSModulePath -split ';' -Write-Verbose -Verbose "Current module search paths: $psmodulePaths" - -Describe 'Test Find-PSResource for local repositories' -tags 'CI' { - - BeforeAll{ - $localRepo = "psgettestlocal" - $localUNCRepo = 'psgettestlocal3' - $testModuleName = "test_local_mod" - $testModuleName2 = "test_local_mod2" - $testModuleName3 = "Test_Local_Mod3" - $similarTestModuleName = "test_local_mod.similar" - $commandName = "cmd1" - $dscResourceName = "dsc1" - $prereleaseLabel = "" - $localNupkgRepo = "localNupkgRepo" - Get-NewPSResourceRepositoryFile - Register-LocalRepos - Register-LocalTestNupkgsRepo - - $localRepoUriAddress = Join-Path -Path $TestDrive -ChildPath "testdir" - $tagsEscaped = @("'Test'", "'Tag2'", "'PSCommand_$cmdName'", "'PSDscResource_$dscName'") - $prereleaseLabel = "alpha001" - - New-TestModule -moduleName $testModuleName -repoName $localRepo -packageVersion "1.0.0" -prereleaseLabel "" -tags @() - New-TestModule -moduleName $testModuleName -repoName $localRepo -packageVersion "3.0.0" -prereleaseLabel "" -tags @() -dscResourceToExport $dscResourceName -commandToExport $commandName - New-TestModule -moduleName $testModuleName -repoName $localRepo -packageVersion "5.0.0" -prereleaseLabel "" -tags $tagsEscaped - New-TestModule -moduleName $testModuleName -repoName $localRepo -packageVersion "5.2.5" -prereleaseLabel $prereleaseLabel -tags $tagsEscaped - - New-TestModule -moduleName $testModuleName2 -repoName $localRepo -packageVersion "5.0.0" -prereleaseLabel "" -tags $tagsEscaped - New-TestModule -moduleName $testModuleName2 -repoName $localRepo -packageVersion "5.2.5" -prereleaseLabel $prereleaseLabel -tags $tagsEscaped - - New-TestModule -moduleName $testModuleName3 -repoName $localRepo -packageVersion "1.0.0" -prereleaseLabel "" -tags @() - - New-TestModule -moduleName $similarTestModuleName -repoName $localRepo -packageVersion "4.0.0" -prereleaseLabel "" -tags $tagsEscaped - New-TestModule -moduleName $similarTestModuleName -repoName $localRepo -packageVersion "5.0.0" -prereleaseLabel "" -tags $tagsEscaped - } - - AfterAll { - Get-RevertPSResourceRepositoryFile - } - - It "find resource given specific Name, Version null (module)" { - # FindName() - $res = Find-PSResource -Name $testModuleName -Repository $localRepo - $res.Name | Should -Be $testModuleName - $res.Version | Should -Be "5.0.0" - } - - It "find resource given specific Name with incorrect casing (should return correct casing)" { - # FindName() - $res = Find-PSResource -Name "test_local_mod3" -Repository $localRepo - $res.Name | Should -Be $testModuleName3 - $res.Version | Should -Be "1.0.0" - } - - It "find resource given specific Name with incorrect casing and Version (should return correct casing)" { - # FindVersion() - $res = Find-PSResource -Name "test_local_mod3" -Version "1.0.0" -Repository $localRepo - $res.Name | Should -Be $testModuleName3 - $res.Version | Should -Be "1.0.0" - } - - It "find resource given specific Name, Version null (module) from a UNC-based local repository" { - # FindName() - $res = Find-PSResource -Name $testModuleName -Repository $localUNCRepo - $res.Name | Should -Be $testModuleName - $res.Version | Should -Be "5.0.0" - } - - It "find resource given Name, Version null (package containing nuspec only)" { - # FindName() - $pkgName = "PowerShell" - Save-PSResource -Name $pkgName -Repository "NuGetGallery" -Path $localRepoUriAddress -AsNupkg -TrustRepository - $res = Find-PSResource -Name $pkgName -Repository $localRepo - $res.Name | Should -Be $pkgName - $res.Repository | Should -Be $localRepo - } - - It "find script without RequiredModules" { - # FindName() - $pkgName = "Required-Script1" - $requiredTag = "Tag1" - Save-PSResource -Name $pkgName -Repository "PSGallery" -Path $localRepoUriAddress -AsNupkg -TrustRepository - # $res = Find-PSResource -Name $pkgName -Repository $localRepo - # $res.Name | Should -Be $pkgName - # $res.Repository | Should -Be $localRepo - # $res.Tags | Should -Contain $requiredTag - } - - It "should not find resource given nonexistent Name" { - # FindName() - $res = Find-PSResource -Name NonExistentModule -Repository $localRepo -ErrorVariable err -ErrorAction SilentlyContinue - $res | Should -BeNullOrEmpty - $err.Count | Should -Not -Be 0 - $err[0].FullyQualifiedErrorId | Should -BeExactly "PackageNotFound,Microsoft.PowerShell.PSResourceGet.Cmdlets.FindPSResource" - $res | Should -BeNullOrEmpty - } - - It "find resource given specific Name when another package with similar name (with period) exists" { - # FindName() - $res = Find-PSResource -Name $testModuleName -Repository $localRepo - $res.Name | Should -Be $testModuleName - $res.Version | Should -Be "5.0.0" - - $res = Find-PSResource -Name $similarTestModuleName -Repository $localRepo - $res.Name | Should -Be $similarTestModuleName - $res.Version | Should -Be "5.0.0" - } - - It "find resource(s) given wildcard Name" { - # FindNameGlobbing - $res = Find-PSResource -Name "test_local_*" -Repository $localRepo - $res.Count | Should -BeGreaterThan 1 - } - - $testCases2 = @{Version="[5.0.0.0]"; ExpectedVersions=@("5.0.0"); Reason="validate version, exact match"}, - @{Version="5.0.0.0"; ExpectedVersions=@("5.0.0"); Reason="validate version, exact match without bracket syntax"}, - @{Version="[1.0.0.0, 5.0.0.0]"; ExpectedVersions=@("1.0.0", "3.0.0", "5.0.0"); Reason="validate version, exact range inclusive"}, - @{Version="(1.0.0.0, 5.0.0.0)"; ExpectedVersions=@("3.0.0"); Reason="validate version, exact range exclusive"}, - @{Version="(1.0.0.0,)"; ExpectedVersions=@("3.0.0", "5.0.0"); Reason="validate version, minimum version exclusive"}, - @{Version="[1.0.0.0,)"; ExpectedVersions=@("1.0.0", "3.0.0", "5.0.0"); Reason="validate version, minimum version inclusive"}, - @{Version="(,3.0.0.0)"; ExpectedVersions=@("1.0.0"); Reason="validate version, maximum version exclusive"}, - @{Version="(,3.0.0.0]"; ExpectedVersions=@("1.0.0", "3.0.0"); Reason="validate version, maximum version inclusive"}, - @{Version="[1.0.0.0, 5.0.0.0)"; ExpectedVersions=@("1.0.0", "3.0.0"); Reason="validate version, mixed inclusive minimum and exclusive maximum version"} - @{Version="(1.0.0.0, 5.0.0.0]"; ExpectedVersions=@("3.0.0", "5.0.0"); Reason="validate version, mixed exclusive minimum and inclusive maximum version"} - - It "find resource when given Name to " -TestCases $testCases2{ - # FindVersionGlobbing() - param($Version, $ExpectedVersions) - $res = Find-PSResource -Name $testModuleName -Version $Version -Repository $localRepo - foreach ($item in $res) { - $item.Name | Should -Be $testModuleName - $ExpectedVersions | Should -Contain $item.Version - } - } - - It "find all versions of resource when given specific Name, Version not null --> '*'" { - # FindVersionGlobbing() - $res = Find-PSResource -Name $testModuleName -Version "*" -Repository $localRepo - $res | ForEach-Object { - $_.Name | Should -Be $testModuleName - } - - $res.Count | Should -BeGreaterOrEqual 1 - } - - It "find resource with latest (including prerelease) version given Prerelease parameter" { - # FindName() - # test_module resource's latest version is a prerelease version, before that it has a non-prerelease version - $res = Find-PSResource -Name $testModuleName -Repository $localRepo - $res.Version | Should -Be "5.0.0" - - $resPrerelease = Find-PSResource -Name $testModuleName -Prerelease -Repository $localRepo - $resPrerelease.Version | Should -Be "5.2.5" - $resPrerelease.Prerelease | Should -Be "alpha001" - } - - It "find resource given specific Name when another package with similar name (with period) exists" { - # FindVersion() - # Package $testModuleName version 4.0.0 does not exist - # previously if Find-PSResource -Version against local repo did not find that package's version it kept looking at - # similar named packages and would fault. This test is to ensure only the specified package and its version is checked - $res = Find-PSResource -Name $testModuleName -Version "4.0.0" -Repository $localRepo -ErrorVariable err -ErrorAction SilentlyContinue - $res | Should -BeNullOrEmpty - $err.Count | Should -Not -Be 0 - $err[0].FullyQualifiedErrorId | Should -BeExactly "PackageNotFound,Microsoft.PowerShell.PSResourceGet.Cmdlets.FindPSResource" - $res | Should -BeNullOrEmpty - - $res = Find-PSResource -Name $similarTestModuleName -Version "4.0.0" -Repository $localRepo - $res.Name | Should -Be $similarTestModuleName - $res.Version | Should -Be "4.0.0" - } - - It "find resources, including Prerelease version resources, when given Prerelease parameter" { - # FindVersionGlobbing() - $resWithoutPrerelease = Find-PSResource -Name $testModuleName -Version "*" -Repository $localRepo - $resWithPrerelease = Find-PSResource -Name $testModuleName -Version "*" -Repository $localRepo - $resWithPrerelease.Count | Should -BeGreaterOrEqual $resWithoutPrerelease.Count - } - - It "find resource that satisfies given Name and Tag property (single tag)" { - # FindNameWithTag() - $requiredTag = "test" - $res = Find-PSResource -Name $testModuleName -Tag $requiredTag -Repository $localRepo - $res.Name | Should -Be $testModuleName - $res.Tags | Should -Contain $requiredTag - } - - It "should not find resource if Name and Tag are not both satisfied (single tag)" { - # FindNameWithTag - $requiredTag = "Windows" # tag "windows" is not present for test_module package - $res = Find-PSResource -Name $testModuleName -Tag $requiredTag -Repository $localRepo -ErrorVariable err -ErrorAction SilentlyContinue - $res | Should -BeNullOrEmpty - $err.Count | Should -Not -Be 0 - $err[0].FullyQualifiedErrorId | Should -BeExactly "PackageNotFound,Microsoft.PowerShell.PSResourceGet.Cmdlets.FindPSResource" - $res | Should -BeNullOrEmpty - } - - It "find resource that satisfies given Name and Tag property (multiple tags)" { - # FindNameWithTag() - $requiredTags = @("test", "Tag2") - $res = Find-PSResource -Name $testModuleName -Tag $requiredTags -Repository $localRepo - $res.Name | Should -Be $testModuleName - $res.Tags | Should -Contain $requiredTags[0] - $res.Tags | Should -Contain $requiredTags[1] - } - - It "find all resources that satisfy Name pattern and have specified Tag (single tag)" { - # FindNameGlobbingWithTag() - $requiredTag = "test" - $nameWithWildcard = "test_local_mod*" - - $res = Find-PSResource -Name $nameWithWildcard -Tag $requiredTag -Repository $localRepo - $res.Count | Should -BeGreaterThan 1 - foreach ($pkg in $res) - { - $pkg.Name | Should -BeLike $nameWithWildcard - $pkg.Tags | Should -Contain "$requiredTag" - } - } - - It "should not find resources if both Name pattern and Tags are not satisfied (single tag)" { - # FindNameGlobbingWithTag() - $requiredTag = "windows" # tag "windows" is not present for test_module package - $res = Find-PSResource -Name "test_module*" -Tag $requiredTag -Repository $localRepo - $res | Should -BeNullOrEmpty - } - - It "find all resources that satisfy Name pattern and have specified Tag (multiple tags)" { - # FindNameGlobbingWithTag() - $requiredTags = @("test", "Tag2") - $nameWithWildcard = "test_local_mod*" - - $res = Find-PSResource -Name $nameWithWildcard -Tag $requiredTags -Repository $localRepo - $res.Count | Should -BeGreaterThan 1 - foreach ($pkg in $res) - { - $pkg.Name | Should -BeLike $nameWithWildcard - $pkg.Tags | Should -Contain $requiredTags[0] - $pkg.Tags | Should -Contain $requiredTags[1] - } - } - - It "find resource that satisfies given Name, Version and Tag property (single tag)" { - # FindVersionWithTag() - $requiredTag = "test" - $res = Find-PSResource -Name $testModuleName -Version "5.0.0.0" -Tag $requiredTag -Repository $localRepo - $res.Name | Should -Be $testModuleName - $res.Version | Should -Be "5.0.0" - $res.Tags | Should -Contain $requiredTag - } - - It "should not find resource if Name, Version and Tag property are not all satisfied (single tag)" { - # FindVersionWithTag() - $requiredTag = "windows" # tag "windows" is not present for test_module package - $res = Find-PSResource -Name $testModuleName -Version "5.0.0.0" -Tag $requiredTag -Repository $localRepo -ErrorVariable err -ErrorAction SilentlyContinue - $res | Should -BeNullOrEmpty - $err.Count | Should -Not -Be 0 - $err[0].FullyQualifiedErrorId | Should -BeExactly "PackageNotFound,Microsoft.PowerShell.PSResourceGet.Cmdlets.FindPSResource" - $res | Should -BeNullOrEmpty - } - - It "find resource that satisfies given Name, Version and Tag property (multiple tags)" { - # FindVersionWithTag() - $requiredTags = @("test", "Tag2") - $res = Find-PSResource -Name $testModuleName -Version "5.0.0.0" -Tag $requiredTags -Repository $localRepo - $res.Name | Should -Be $testModuleName - $res.Version | Should -Be "5.0.0" - $res.Tags | Should -Contain $requiredTags[0] - $res.Tags | Should -Contain $requiredTags[1] - } - - It "find scripts given -Type parameter" { - Get-ScriptResourcePublishedToLocalRepoTestDrive "testScriptName" $localRepo "1.0.0" - - $res = Find-PSResource -Type Script -Repository $localRepo - $res | Should -Not -BeNullOrEmpty - $res.Count | Should -Be 2 - $res.Type | Should -Be @("Script", "Script") - } - - It "find modules given -Type parameter" { - Get-ScriptResourcePublishedToLocalRepoTestDrive "testScriptName" $localRepo "1.0.0" - - $res = Find-PSResource -Type Module -Repository $localRepo - $res | Should -Not -BeNullOrEmpty - $res.Count | Should -BeGreaterOrEqual 1 - foreach ($module in $res) { - $module.Type | Should -Be "Module" - } - } - - It "find resource given CommandName" -Pending { - $res = Find-PSResource -CommandName $commandName -Repository $localRepo - $res | Should -Not -BeNullOrEmpty - foreach ($item in $res) { - $item.Names | Should -Be $commandName - $item.ParentResource.Includes.Command | Should -Contain $commandName - } - } - - It "find resource given DscResourceName" -Pending { - $res = Find-PSResource -DscResourceName $dscResourceName -Repository $localRepo - $res | Should -Not -BeNullOrEmpty - foreach ($item in $res) { - $item.Names | Should -Be $dscResourceName - $item.ParentResource.Includes.DscResource | Should -Contain $dscResourceName - } - } - - It "Get definition for alias 'fdres'" { - (Get-Alias fdres).Definition | Should -BeExactly 'Find-PSResource' - } - - It "not find resource with tag value that is non-existent for the packages" { - # Since this is pattern matching based search no error should be written out. - $res = Find-PSResource -Tag "nonexistenttag" -Repository $localRepo -ErrorVariable err -ErrorAction SilentlyContinue - $res | Should -BeNullOrEmpty - $err.Count | Should -Not -Be 0 - $err[0].FullyQualifiedErrorId | Should -BeExactly "FindTagsPackageNotFound,Microsoft.PowerShell.PSResourceGet.Cmdlets.FindPSResource" - } - - It "find package where prerelease label includes digits and period (i.e prerelease label is not just words)" { - $nupkgName = "WebView2.Avalonia" - $nupkgVersion = "1.0.1518.46" - $prereleaseLabel = "preview.230207.17" - $res = Find-PSResource -Name $nupkgName -Prerelease -Repository $localNupkgRepo - $res.Name | Should -Be $nupkgName - $res.Version | Should -Be $nupkgVersion - $res.Prerelease | Should -Be $prereleaseLabel - } - - It "find module that has multiple manifests and use exact name match one" { - # Az.KeyVault has 2 manifest files - Az.KeyVault.psd1 and Az.KeyVault.Extension.psd1 - # this test was added because PSResourceGet would previously pick the .psd1 file by pattern matching the module name, not exact matching it - # this meant Az.KeyVault.Extension.psd1 and its metadata was being returned. - # The package is present on PSGallery but issue reproduces when looking at the package's file paths in local repo - $PSGalleryName = Get-PSGalleryName - Save-PSResource -Name 'Az.KeyVault' -Version '6.3.1' -Repository $PSGalleryName -AsNupkg -Path $localRepoUriAddress -TrustRepository - $res = Find-PSResource -Name 'Az.KeyVault' -Repository $localRepo - $res.Version | Should -Be "6.3.1" - } -} +# # Copyright (c) Microsoft Corporation. +# # Licensed under the MIT License. + +# $modPath = "$psscriptroot/../PSGetTestUtils.psm1" +# Import-Module $modPath -Force -Verbose + +# $psmodulePaths = $env:PSModulePath -split ';' +# Write-Verbose -Verbose "Current module search paths: $psmodulePaths" + +# Describe 'Test Find-PSResource for local repositories' -tags 'CI' { + +# BeforeAll{ +# $localRepo = "psgettestlocal" +# $localUNCRepo = 'psgettestlocal3' +# $testModuleName = "test_local_mod" +# $testModuleName2 = "test_local_mod2" +# $testModuleName3 = "Test_Local_Mod3" +# $similarTestModuleName = "test_local_mod.similar" +# $commandName = "cmd1" +# $dscResourceName = "dsc1" +# $prereleaseLabel = "" +# $localNupkgRepo = "localNupkgRepo" +# Get-NewPSResourceRepositoryFile +# Register-LocalRepos +# Register-LocalTestNupkgsRepo + +# $localRepoUriAddress = Join-Path -Path $TestDrive -ChildPath "testdir" +# $tagsEscaped = @("'Test'", "'Tag2'", "'PSCommand_$cmdName'", "'PSDscResource_$dscName'") +# $prereleaseLabel = "alpha001" + +# New-TestModule -moduleName $testModuleName -repoName $localRepo -packageVersion "1.0.0" -prereleaseLabel "" -tags @() +# New-TestModule -moduleName $testModuleName -repoName $localRepo -packageVersion "3.0.0" -prereleaseLabel "" -tags @() -dscResourceToExport $dscResourceName -commandToExport $commandName +# New-TestModule -moduleName $testModuleName -repoName $localRepo -packageVersion "5.0.0" -prereleaseLabel "" -tags $tagsEscaped +# New-TestModule -moduleName $testModuleName -repoName $localRepo -packageVersion "5.2.5" -prereleaseLabel $prereleaseLabel -tags $tagsEscaped + +# New-TestModule -moduleName $testModuleName2 -repoName $localRepo -packageVersion "5.0.0" -prereleaseLabel "" -tags $tagsEscaped +# New-TestModule -moduleName $testModuleName2 -repoName $localRepo -packageVersion "5.2.5" -prereleaseLabel $prereleaseLabel -tags $tagsEscaped + +# New-TestModule -moduleName $testModuleName3 -repoName $localRepo -packageVersion "1.0.0" -prereleaseLabel "" -tags @() + +# New-TestModule -moduleName $similarTestModuleName -repoName $localRepo -packageVersion "4.0.0" -prereleaseLabel "" -tags $tagsEscaped +# New-TestModule -moduleName $similarTestModuleName -repoName $localRepo -packageVersion "5.0.0" -prereleaseLabel "" -tags $tagsEscaped +# } + +# AfterAll { +# Get-RevertPSResourceRepositoryFile +# } + +# It "find resource given specific Name, Version null (module)" { +# # FindName() +# $res = Find-PSResource -Name $testModuleName -Repository $localRepo +# $res.Name | Should -Be $testModuleName +# $res.Version | Should -Be "5.0.0" +# } + +# It "find resource given specific Name with incorrect casing (should return correct casing)" { +# # FindName() +# $res = Find-PSResource -Name "test_local_mod3" -Repository $localRepo +# $res.Name | Should -Be $testModuleName3 +# $res.Version | Should -Be "1.0.0" +# } + +# It "find resource given specific Name with incorrect casing and Version (should return correct casing)" { +# # FindVersion() +# $res = Find-PSResource -Name "test_local_mod3" -Version "1.0.0" -Repository $localRepo +# $res.Name | Should -Be $testModuleName3 +# $res.Version | Should -Be "1.0.0" +# } + +# It "find resource given specific Name, Version null (module) from a UNC-based local repository" { +# # FindName() +# $res = Find-PSResource -Name $testModuleName -Repository $localUNCRepo +# $res.Name | Should -Be $testModuleName +# $res.Version | Should -Be "5.0.0" +# } + +# It "find resource given Name, Version null (package containing nuspec only)" { +# # FindName() +# $pkgName = "PowerShell" +# Save-PSResource -Name $pkgName -Repository "NuGetGallery" -Path $localRepoUriAddress -AsNupkg -TrustRepository +# $res = Find-PSResource -Name $pkgName -Repository $localRepo +# $res.Name | Should -Be $pkgName +# $res.Repository | Should -Be $localRepo +# } + +# It "find script without RequiredModules" { +# # FindName() +# $pkgName = "Required-Script1" +# $requiredTag = "Tag1" +# Save-PSResource -Name $pkgName -Repository "PSGallery" -Path $localRepoUriAddress -AsNupkg -TrustRepository +# # $res = Find-PSResource -Name $pkgName -Repository $localRepo +# # $res.Name | Should -Be $pkgName +# # $res.Repository | Should -Be $localRepo +# # $res.Tags | Should -Contain $requiredTag +# } + +# It "should not find resource given nonexistent Name" { +# # FindName() +# $res = Find-PSResource -Name NonExistentModule -Repository $localRepo -ErrorVariable err -ErrorAction SilentlyContinue +# $res | Should -BeNullOrEmpty +# $err.Count | Should -Not -Be 0 +# $err[0].FullyQualifiedErrorId | Should -BeExactly "PackageNotFound,Microsoft.PowerShell.PSResourceGet.Cmdlets.FindPSResource" +# $res | Should -BeNullOrEmpty +# } + +# It "find resource given specific Name when another package with similar name (with period) exists" { +# # FindName() +# $res = Find-PSResource -Name $testModuleName -Repository $localRepo +# $res.Name | Should -Be $testModuleName +# $res.Version | Should -Be "5.0.0" + +# $res = Find-PSResource -Name $similarTestModuleName -Repository $localRepo +# $res.Name | Should -Be $similarTestModuleName +# $res.Version | Should -Be "5.0.0" +# } + +# It "find resource(s) given wildcard Name" { +# # FindNameGlobbing +# $res = Find-PSResource -Name "test_local_*" -Repository $localRepo +# $res.Count | Should -BeGreaterThan 1 +# } + +# $testCases2 = @{Version="[5.0.0.0]"; ExpectedVersions=@("5.0.0"); Reason="validate version, exact match"}, +# @{Version="5.0.0.0"; ExpectedVersions=@("5.0.0"); Reason="validate version, exact match without bracket syntax"}, +# @{Version="[1.0.0.0, 5.0.0.0]"; ExpectedVersions=@("1.0.0", "3.0.0", "5.0.0"); Reason="validate version, exact range inclusive"}, +# @{Version="(1.0.0.0, 5.0.0.0)"; ExpectedVersions=@("3.0.0"); Reason="validate version, exact range exclusive"}, +# @{Version="(1.0.0.0,)"; ExpectedVersions=@("3.0.0", "5.0.0"); Reason="validate version, minimum version exclusive"}, +# @{Version="[1.0.0.0,)"; ExpectedVersions=@("1.0.0", "3.0.0", "5.0.0"); Reason="validate version, minimum version inclusive"}, +# @{Version="(,3.0.0.0)"; ExpectedVersions=@("1.0.0"); Reason="validate version, maximum version exclusive"}, +# @{Version="(,3.0.0.0]"; ExpectedVersions=@("1.0.0", "3.0.0"); Reason="validate version, maximum version inclusive"}, +# @{Version="[1.0.0.0, 5.0.0.0)"; ExpectedVersions=@("1.0.0", "3.0.0"); Reason="validate version, mixed inclusive minimum and exclusive maximum version"} +# @{Version="(1.0.0.0, 5.0.0.0]"; ExpectedVersions=@("3.0.0", "5.0.0"); Reason="validate version, mixed exclusive minimum and inclusive maximum version"} + +# It "find resource when given Name to " -TestCases $testCases2{ +# # FindVersionGlobbing() +# param($Version, $ExpectedVersions) +# $res = Find-PSResource -Name $testModuleName -Version $Version -Repository $localRepo +# foreach ($item in $res) { +# $item.Name | Should -Be $testModuleName +# $ExpectedVersions | Should -Contain $item.Version +# } +# } + +# It "find all versions of resource when given specific Name, Version not null --> '*'" { +# # FindVersionGlobbing() +# $res = Find-PSResource -Name $testModuleName -Version "*" -Repository $localRepo +# $res | ForEach-Object { +# $_.Name | Should -Be $testModuleName +# } + +# $res.Count | Should -BeGreaterOrEqual 1 +# } + +# It "find resource with latest (including prerelease) version given Prerelease parameter" { +# # FindName() +# # test_module resource's latest version is a prerelease version, before that it has a non-prerelease version +# $res = Find-PSResource -Name $testModuleName -Repository $localRepo +# $res.Version | Should -Be "5.0.0" + +# $resPrerelease = Find-PSResource -Name $testModuleName -Prerelease -Repository $localRepo +# $resPrerelease.Version | Should -Be "5.2.5" +# $resPrerelease.Prerelease | Should -Be "alpha001" +# } + +# It "find resource given specific Name when another package with similar name (with period) exists" { +# # FindVersion() +# # Package $testModuleName version 4.0.0 does not exist +# # previously if Find-PSResource -Version against local repo did not find that package's version it kept looking at +# # similar named packages and would fault. This test is to ensure only the specified package and its version is checked +# $res = Find-PSResource -Name $testModuleName -Version "4.0.0" -Repository $localRepo -ErrorVariable err -ErrorAction SilentlyContinue +# $res | Should -BeNullOrEmpty +# $err.Count | Should -Not -Be 0 +# $err[0].FullyQualifiedErrorId | Should -BeExactly "PackageNotFound,Microsoft.PowerShell.PSResourceGet.Cmdlets.FindPSResource" +# $res | Should -BeNullOrEmpty + +# $res = Find-PSResource -Name $similarTestModuleName -Version "4.0.0" -Repository $localRepo +# $res.Name | Should -Be $similarTestModuleName +# $res.Version | Should -Be "4.0.0" +# } + +# It "find resources, including Prerelease version resources, when given Prerelease parameter" { +# # FindVersionGlobbing() +# $resWithoutPrerelease = Find-PSResource -Name $testModuleName -Version "*" -Repository $localRepo +# $resWithPrerelease = Find-PSResource -Name $testModuleName -Version "*" -Repository $localRepo +# $resWithPrerelease.Count | Should -BeGreaterOrEqual $resWithoutPrerelease.Count +# } + +# It "find resource that satisfies given Name and Tag property (single tag)" { +# # FindNameWithTag() +# $requiredTag = "test" +# $res = Find-PSResource -Name $testModuleName -Tag $requiredTag -Repository $localRepo +# $res.Name | Should -Be $testModuleName +# $res.Tags | Should -Contain $requiredTag +# } + +# It "should not find resource if Name and Tag are not both satisfied (single tag)" { +# # FindNameWithTag +# $requiredTag = "Windows" # tag "windows" is not present for test_module package +# $res = Find-PSResource -Name $testModuleName -Tag $requiredTag -Repository $localRepo -ErrorVariable err -ErrorAction SilentlyContinue +# $res | Should -BeNullOrEmpty +# $err.Count | Should -Not -Be 0 +# $err[0].FullyQualifiedErrorId | Should -BeExactly "PackageNotFound,Microsoft.PowerShell.PSResourceGet.Cmdlets.FindPSResource" +# $res | Should -BeNullOrEmpty +# } + +# It "find resource that satisfies given Name and Tag property (multiple tags)" { +# # FindNameWithTag() +# $requiredTags = @("test", "Tag2") +# $res = Find-PSResource -Name $testModuleName -Tag $requiredTags -Repository $localRepo +# $res.Name | Should -Be $testModuleName +# $res.Tags | Should -Contain $requiredTags[0] +# $res.Tags | Should -Contain $requiredTags[1] +# } + +# It "find all resources that satisfy Name pattern and have specified Tag (single tag)" { +# # FindNameGlobbingWithTag() +# $requiredTag = "test" +# $nameWithWildcard = "test_local_mod*" + +# $res = Find-PSResource -Name $nameWithWildcard -Tag $requiredTag -Repository $localRepo +# $res.Count | Should -BeGreaterThan 1 +# foreach ($pkg in $res) +# { +# $pkg.Name | Should -BeLike $nameWithWildcard +# $pkg.Tags | Should -Contain "$requiredTag" +# } +# } + +# It "should not find resources if both Name pattern and Tags are not satisfied (single tag)" { +# # FindNameGlobbingWithTag() +# $requiredTag = "windows" # tag "windows" is not present for test_module package +# $res = Find-PSResource -Name "test_module*" -Tag $requiredTag -Repository $localRepo +# $res | Should -BeNullOrEmpty +# } + +# It "find all resources that satisfy Name pattern and have specified Tag (multiple tags)" { +# # FindNameGlobbingWithTag() +# $requiredTags = @("test", "Tag2") +# $nameWithWildcard = "test_local_mod*" + +# $res = Find-PSResource -Name $nameWithWildcard -Tag $requiredTags -Repository $localRepo +# $res.Count | Should -BeGreaterThan 1 +# foreach ($pkg in $res) +# { +# $pkg.Name | Should -BeLike $nameWithWildcard +# $pkg.Tags | Should -Contain $requiredTags[0] +# $pkg.Tags | Should -Contain $requiredTags[1] +# } +# } + +# It "find resource that satisfies given Name, Version and Tag property (single tag)" { +# # FindVersionWithTag() +# $requiredTag = "test" +# $res = Find-PSResource -Name $testModuleName -Version "5.0.0.0" -Tag $requiredTag -Repository $localRepo +# $res.Name | Should -Be $testModuleName +# $res.Version | Should -Be "5.0.0" +# $res.Tags | Should -Contain $requiredTag +# } + +# It "should not find resource if Name, Version and Tag property are not all satisfied (single tag)" { +# # FindVersionWithTag() +# $requiredTag = "windows" # tag "windows" is not present for test_module package +# $res = Find-PSResource -Name $testModuleName -Version "5.0.0.0" -Tag $requiredTag -Repository $localRepo -ErrorVariable err -ErrorAction SilentlyContinue +# $res | Should -BeNullOrEmpty +# $err.Count | Should -Not -Be 0 +# $err[0].FullyQualifiedErrorId | Should -BeExactly "PackageNotFound,Microsoft.PowerShell.PSResourceGet.Cmdlets.FindPSResource" +# $res | Should -BeNullOrEmpty +# } + +# It "find resource that satisfies given Name, Version and Tag property (multiple tags)" { +# # FindVersionWithTag() +# $requiredTags = @("test", "Tag2") +# $res = Find-PSResource -Name $testModuleName -Version "5.0.0.0" -Tag $requiredTags -Repository $localRepo +# $res.Name | Should -Be $testModuleName +# $res.Version | Should -Be "5.0.0" +# $res.Tags | Should -Contain $requiredTags[0] +# $res.Tags | Should -Contain $requiredTags[1] +# } + +# It "find scripts given -Type parameter" { +# Get-ScriptResourcePublishedToLocalRepoTestDrive "testScriptName" $localRepo "1.0.0" + +# $res = Find-PSResource -Type Script -Repository $localRepo +# $res | Should -Not -BeNullOrEmpty +# $res.Count | Should -Be 2 +# $res.Type | Should -Be @("Script", "Script") +# } + +# It "find modules given -Type parameter" { +# Get-ScriptResourcePublishedToLocalRepoTestDrive "testScriptName" $localRepo "1.0.0" + +# $res = Find-PSResource -Type Module -Repository $localRepo +# $res | Should -Not -BeNullOrEmpty +# $res.Count | Should -BeGreaterOrEqual 1 +# foreach ($module in $res) { +# $module.Type | Should -Be "Module" +# } +# } + +# It "find resource given CommandName" -Pending { +# $res = Find-PSResource -CommandName $commandName -Repository $localRepo +# $res | Should -Not -BeNullOrEmpty +# foreach ($item in $res) { +# $item.Names | Should -Be $commandName +# $item.ParentResource.Includes.Command | Should -Contain $commandName +# } +# } + +# It "find resource given DscResourceName" -Pending { +# $res = Find-PSResource -DscResourceName $dscResourceName -Repository $localRepo +# $res | Should -Not -BeNullOrEmpty +# foreach ($item in $res) { +# $item.Names | Should -Be $dscResourceName +# $item.ParentResource.Includes.DscResource | Should -Contain $dscResourceName +# } +# } + +# It "Get definition for alias 'fdres'" { +# (Get-Alias fdres).Definition | Should -BeExactly 'Find-PSResource' +# } + +# It "not find resource with tag value that is non-existent for the packages" { +# # Since this is pattern matching based search no error should be written out. +# $res = Find-PSResource -Tag "nonexistenttag" -Repository $localRepo -ErrorVariable err -ErrorAction SilentlyContinue +# $res | Should -BeNullOrEmpty +# $err.Count | Should -Not -Be 0 +# $err[0].FullyQualifiedErrorId | Should -BeExactly "FindTagsPackageNotFound,Microsoft.PowerShell.PSResourceGet.Cmdlets.FindPSResource" +# } + +# It "find package where prerelease label includes digits and period (i.e prerelease label is not just words)" { +# $nupkgName = "WebView2.Avalonia" +# $nupkgVersion = "1.0.1518.46" +# $prereleaseLabel = "preview.230207.17" +# $res = Find-PSResource -Name $nupkgName -Prerelease -Repository $localNupkgRepo +# $res.Name | Should -Be $nupkgName +# $res.Version | Should -Be $nupkgVersion +# $res.Prerelease | Should -Be $prereleaseLabel +# } + +# It "find module that has multiple manifests and use exact name match one" { +# # Az.KeyVault has 2 manifest files - Az.KeyVault.psd1 and Az.KeyVault.Extension.psd1 +# # this test was added because PSResourceGet would previously pick the .psd1 file by pattern matching the module name, not exact matching it +# # this meant Az.KeyVault.Extension.psd1 and its metadata was being returned. +# # The package is present on PSGallery but issue reproduces when looking at the package's file paths in local repo +# $PSGalleryName = Get-PSGalleryName +# Save-PSResource -Name 'Az.KeyVault' -Version '6.3.1' -Repository $PSGalleryName -AsNupkg -Path $localRepoUriAddress -TrustRepository +# $res = Find-PSResource -Name 'Az.KeyVault' -Repository $localRepo +# $res.Version | Should -Be "6.3.1" +# } +# } diff --git a/test/FindPSResourceTests/FindPSResourceRepositorySearching.Tests.ps1 b/test/FindPSResourceTests/FindPSResourceRepositorySearching.Tests.ps1 index 4f010de2f..4d8b0f202 100644 --- a/test/FindPSResourceTests/FindPSResourceRepositorySearching.Tests.ps1 +++ b/test/FindPSResourceTests/FindPSResourceRepositorySearching.Tests.ps1 @@ -531,33 +531,6 @@ Describe 'Test Find-PSResource for searching and looping through repositories' - $pkgFoundFromPSGallery | Should -BeTrue } - It "find resources from all pattern matching repositories where it exists (-Repository with wildcard)" { - # Package with CommandName "Get-TargetResource" exists in the following repositories: PSGallery, localRepo - $res = Find-PSResource -CommandName $cmdName -Repository "*Gallery" -ErrorVariable err -ErrorAction SilentlyContinue - $err | Should -HaveCount 0 - $res.Count | Should -BeGreaterOrEqual 9 - - $pkgFoundFromLocalRepo = $false - $pkgFoundFromPSGallery = $false - - foreach ($pkg in $res) - { - if ($pkg.ParentResource.Repository -eq $localRepoName) - { - $pkgFoundFromLocalRepo = $true - } - elseif ($pkg.ParentResource.Repository -eq $PSGalleryName) - { - $pkgFoundFromPSGallery = $true - } - } - - $pkg.Names | Should -Be $cmdName - $pkg.ParentResource.Includes.Command | Should -Contain $cmdName - $pkgFoundFromLocalRepo | Should -BeFalse - $pkgFoundFromPSGallery | Should -BeTrue - } - It "should not allow for repository name with wildcard and non-wildcard command name specified in same command run" { {Find-PSResource -CommandName $cmdName -Repository "*Gallery",$localRepoName} | Should -Throw -ErrorId "RepositoryNamesWithWildcardsAndNonWildcardUnsupported,Microsoft.PowerShell.PSResourceGet.Cmdlets.FindPSResource" } diff --git a/test/FindPSResourceTests/FindPSResourceV2Server.Tests.ps1 b/test/FindPSResourceTests/FindPSResourceV2Server.Tests.ps1 index 911471f3b..3386be9df 100644 --- a/test/FindPSResourceTests/FindPSResourceV2Server.Tests.ps1 +++ b/test/FindPSResourceTests/FindPSResourceV2Server.Tests.ps1 @@ -353,24 +353,6 @@ Describe 'Test HTTP Find-PSResource for V2 Server Protocol' -tags 'CI' { $res | Should -HaveCount 2 } - It "find resource given CommandName" { - $res = Find-PSResource -CommandName $commandName -Repository $PSGalleryName - $res | Should -Not -BeNullOrEmpty - foreach ($item in $res) { - $item.Names | Should -Be $commandName - $item.ParentResource.Includes.Command | Should -Contain $commandName - } - } - - It "find resource given DscResourceName" { - $res = Find-PSResource -DscResourceName $dscResourceName -Repository $PSGalleryName - $res | Should -Not -BeNullOrEmpty - foreach ($item in $res) { - $item.Names | Should -Be $dscResourceName - $item.ParentResource.Includes.DscResource | Should -Contain $dscResourceName - } - } - It "find resource, but only show listed versions" { # testmodule99 version 1.0.0-beta1 is unlisted $res = Find-PSResource -Name "testmodule99" -Repository $PSGalleryName diff --git a/test/GetInstalledPSResource/GetInstalledPSResource.Tests.ps1 b/test/GetInstalledPSResource/GetInstalledPSResource.Tests.ps1 index 21887bea3..21dcb40c3 100644 --- a/test/GetInstalledPSResource/GetInstalledPSResource.Tests.ps1 +++ b/test/GetInstalledPSResource/GetInstalledPSResource.Tests.ps1 @@ -25,8 +25,8 @@ Describe 'Test Get-InstalledPSResource for Module' -tags 'CI' { } AfterAll { - Uninstall-PSResource -Name $testModuleName -Version "*" -ErrorAction SilentlyContinue - Uninstall-PSResource -Name $testScriptName -Version "*" -ErrorAction SilentlyContinue + Uninstall-PSResource -Name $testModuleName -Version "*" -ErrorAction SilentlyContinue -SkipDependencyCheck + Uninstall-PSResource -Name $testScriptName -Version "*" -ErrorAction SilentlyContinue -SkipDependencyCheck Get-RevertPSResourceRepositoryFile if (Test-Path -Path $TestEmptyDirectoryPath -PathType 'Container') { @@ -184,7 +184,7 @@ Describe 'Test Get-InstalledPSResource for Module' -tags 'CI' { # Windows only It "Get resource under CurrentUser scope when module is installed under AllUsers - Windows only" -Skip:(!((Get-IsWindows) -and (Test-IsAdmin))) { - Uninstall-PSResource -Name $testModuleName -Version "*" + Uninstall-PSResource -Name $testModuleName -Version "*" -SkipDependencyCheck Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -Scope AllUsers $pkg = Get-InstalledPSResource -Name $testModuleName -Scope CurrentUser $pkg | Should -BeNullOrEmpty diff --git a/test/InstallPSResourceTests/InstallAz.Tests.ps1 b/test/InstallPSResourceTests/InstallAz.Tests.ps1 index db91d10a9..25ea90df5 100644 --- a/test/InstallPSResourceTests/InstallAz.Tests.ps1 +++ b/test/InstallPSResourceTests/InstallAz.Tests.ps1 @@ -19,7 +19,7 @@ Describe 'Test Install-PSResource for the Az module' -tags 'CI' { } AfterEach { - Uninstall-PSResource $azName, $azDepWildCard -ErrorAction SilentlyContinue + Uninstall-PSResource $azName, $azDepWildCard -ErrorAction SilentlyContinue -SkipDependencyCheck } AfterAll { diff --git a/test/InstallPSResourceTests/InstallPSResourceContainerRegistryServer.Tests.ps1 b/test/InstallPSResourceTests/InstallPSResourceContainerRegistryServer.Tests.ps1 index afe81d2c3..e29fe6aa2 100644 --- a/test/InstallPSResourceTests/InstallPSResourceContainerRegistryServer.Tests.ps1 +++ b/test/InstallPSResourceTests/InstallPSResourceContainerRegistryServer.Tests.ps1 @@ -166,7 +166,7 @@ Describe 'Test Install-PSResource for ACR scenarios' -tags 'CI' { $Version = "(1.0.0.0)" { Install-PSResource -Name $testModuleName -Version $Version -Repository $ACRRepoName -TrustRepository -ErrorAction SilentlyContinue } | Should -Throw -ErrorId "IncorrectVersionFormat,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource" - $res = Get-InstalledPSResource $testModuleName + $res = Get-InstalledPSResource $testModuleName -ErrorAction Ignore $res | Should -BeNullOrEmpty } @@ -369,7 +369,7 @@ Describe 'Test Install-PSResource for MAR Repository' -tags 'CI' { } finally { if ($pkg) { - Uninstall-PSResource -Name "Az.Accounts" -Version "3.0.4" + Uninstall-PSResource -Name "Az.Accounts" -Version "3.0.4" -SkipDependencyCheck } } } diff --git a/test/InstallPSResourceTests/InstallPSResourceGithubPackages.Tests.ps1 b/test/InstallPSResourceTests/InstallPSResourceGithubPackages.Tests.ps1 index 7f38f05b3..7d23feafa 100644 --- a/test/InstallPSResourceTests/InstallPSResourceGithubPackages.Tests.ps1 +++ b/test/InstallPSResourceTests/InstallPSResourceGithubPackages.Tests.ps1 @@ -1,270 +1,276 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -$ProgressPreference = "SilentlyContinue" -$modPath = "$psscriptroot/../PSGetTestUtils.psm1" -Import-Module $modPath -Force -Verbose - -Describe 'Test Install-PSResource for GitHub packages' -tags 'CI' { - - BeforeAll { - $testModuleName = "test_module" - $testModuleName2 = "test_module2" - $testScriptName = "test_script" - $GithubPackagesRepoName = "GithubPackagesRepo" - $GithubPackagesRepoUri = "https://nuget.pkg.github.com/PowerShell/index.json" - Get-NewPSResourceRepositoryFile - Register-PSResourceRepository -Name $GithubPackagesRepoName -Uri $GithubPackagesRepoUri - - $secureString = ConvertTo-SecureString $env:MAPPED_GITHUB_PAT -AsPlainText -Force - $credential = New-Object pscredential ($env:GITHUB_USERNAME, $secureString) - - Uninstall-PSResource $testModuleName, $testScriptName -Version "*" -SkipDependencyCheck -ErrorAction SilentlyContinue - } - - AfterEach { - Uninstall-PSResource $testModuleName, $testScriptName -Version "*" -SkipDependencyCheck -ErrorAction SilentlyContinue - } - - AfterAll { - Get-RevertPSResourceRepositoryFile - } - - $testCases = @{Name="*"; ErrorId="NameContainsWildcard"}, - @{Name="Test_m*"; ErrorId="NameContainsWildcard"}, - @{Name="Test?module","Test[module"; ErrorId="ErrorFilteringNamesForUnsupportedWildcards"} - - It "Should not install resource with wildcard in name" -TestCases $testCases { - param($Name, $ErrorId) - Install-PSResource -Name $Name -Repository $GithubPackagesRepoName -Credential $credential -ErrorVariable err -ErrorAction SilentlyContinue - $err.Count | Should -BeGreaterThan 0 - $err[0].FullyQualifiedErrorId | Should -BeExactly "$ErrorId,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource" - $res = Get-InstalledPSResource $testModuleName -ErrorAction SilentlyContinue - $res | Should -BeNullOrEmpty - } - - It "Install specific module resource by name" { - Install-PSResource -Name $testModuleName -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - $pkg.Version | Should -Be "5.0.0" - } - - It "Install specific script resource by name" { - Install-PSResource -Name $testScriptName -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository - $pkg = Get-InstalledPSResource $testScriptName - $pkg.Name | Should -Be $testScriptName - $pkg.Version | Should -Be "3.5.0" - } - - It "Install multiple resources by name" { - $pkgNames = @($testModuleName, $testModuleName2) - Install-PSResource -Name $pkgNames -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository - $pkg = Get-InstalledPSResource $pkgNames - $pkg.Name | Should -Be $pkgNames - } - - It "Should not install resource given nonexistent name" { - Install-PSResource -Name "NonExistentModule" -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository -ErrorVariable err -ErrorAction SilentlyContinue - $pkg = Get-InstalledPSResource "NonExistentModule" -ErrorAction SilentlyContinue - $pkg | Should -BeNullOrEmpty - $err.Count | Should -BeGreaterThan 0 - $err[0].FullyQualifiedErrorId | Should -BeExactly "InstallPackageFailure,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource" - } - - # Do some version testing, but Find-PSResource should be doing thorough testing - It "Should install resource given name and exact version" { - Install-PSResource -Name $testModuleName -Version "1.0.0" -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - $pkg.Version | Should -Be "1.0.0" - } - - It "Should install resource given name and exact version with bracket syntax" { - Install-PSResource -Name $testModuleName -Version "[1.0.0]" -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - $pkg.Version | Should -Be "1.0.0" - } - - It "Should install resource given name and exact range inclusive [1.0.0, 5.0.0]" { - Install-PSResource -Name $testModuleName -Version "[1.0.0, 5.0.0]" -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - $pkg.Version | Should -Be "5.0.0" - } - - It "Should install resource given name and exact range exclusive (1.0.0, 5.0.0)" { - Install-PSResource -Name $testModuleName -Version "(1.0.0, 5.0.0)" -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - $pkg.Version | Should -Be "3.0.0" - } - - # TODO: Update this test and others like it that use try/catch blocks instead of Should -Throw - It "Should not install resource with incorrectly formatted version such as exclusive version (1.0.0.0)" { - $Version = "(1.0.0.0)" - try { - Install-PSResource -Name $testModuleName -Version $Version -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository -ErrorAction SilentlyContinue - } - catch - {} - $Error[0].FullyQualifiedErrorId | Should -be "IncorrectVersionFormat,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource" - - $res = Get-InstalledPSResource $testModuleName -ErrorAction SilentlyContinue - $res | Should -BeNullOrEmpty - } - - It "Install resource when given Name, Version '*', should install the latest version" { - Install-PSResource -Name $testModuleName -Version "*" -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - $pkg.Version | Should -Be "5.0.0" - } - - It "Install resource with latest (including prerelease) version given Prerelease parameter" { - Install-PSResource -Name $testModuleName -Prerelease -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - $pkg.Version | Should -Be "5.2.5" - $pkg.Prerelease | Should -Be "alpha001" - } - - It "Install resource via InputObject by piping from Find-PSresource" { - Find-PSResource -Name $testModuleName -Repository $GithubPackagesRepoName -Credential $credential | Install-PSResource -Credential $credential -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - $pkg.Version | Should -Be "5.0.0" - } - - It "Install resource with companyname and repository source location and validate properties" { - Install-PSResource -Name $testModuleName -Version "5.2.5-alpha001" -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Version | Should -Be "5.2.5" - $pkg.Prerelease | Should -Be "alpha001" - - $pkg.CompanyName | Should -Be "Anam Navied" - $pkg.RepositorySourceLocation | Should -Be $GithubPackagesRepoUri - } - - # Windows only - It "Install resource under CurrentUser scope - Windows only" -Skip:(!(Get-IsWindows)) { - Install-PSResource -Name $testModuleName -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository -Scope CurrentUser - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - $pkg.InstalledLocation.ToString().Contains("Documents") | Should -Be $true - } - - # Windows only - It "Install resource under AllUsers scope - Windows only" -Skip:(!((Get-IsWindows) -and (Test-IsAdmin))) { - Uninstall-PSResource -Name $testModuleName -SkipDependencyCheck -Scope AllUsers - Install-PSResource -Name $testModuleName -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository -Scope AllUsers -Verbose - $pkg = Get-InstalledPSResource $testModuleName -Scope AllUsers - $pkg.Name | Should -Be $testModuleName - $pkg.InstalledLocation.ToString().Contains("Program Files") | Should -Be $true - } - - # Windows only - It "Install resource under no specified scope - Windows only" -Skip:(!(Get-IsWindows)) { - Install-PSResource -Name $testModuleName -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - $pkg.InstalledLocation.ToString().Contains("Documents") | Should -Be $true - } - - # Unix only - # Expected path should be similar to: '/home/janelane/.local/share/powershell/Modules' - It "Install resource under CurrentUser scope - Unix only" -Skip:(Get-IsWindows) { - Install-PSResource -Name $testModuleName -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository -Scope CurrentUser - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - $pkg.InstalledLocation.ToString().Contains("$env:HOME/.local") | Should -Be $true - } - - # Unix only - # Expected path should be similar to: '/home/janelane/.local/share/powershell/Modules' - It "Install resource under no specified scope - Unix only" -Skip:(Get-IsWindows) { - Install-PSResource -Name $testModuleName -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - $pkg.InstalledLocation.ToString().Contains("$env:HOME/.local") | Should -Be $true - } - - It "Should not install resource that is already installed" { - Install-PSResource -Name $testModuleName -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - Install-PSResource -Name $testModuleName -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository -WarningVariable WarningVar -warningaction SilentlyContinue - $WarningVar | Should -Not -BeNullOrEmpty - } - - It "Reinstall resource that is already installed with -Reinstall parameter" { - Install-PSResource -Name $testModuleName2 -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName2 - $pkg.Name | Should -Be $testModuleName2 - $pkg.Version | Should -Be "5.0.0" - Install-PSResource -Name $testModuleName2 -Repository $GithubPackagesRepoName -Credential $credential -Reinstall -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName2 - $pkg.Name | Should -Be $testModuleName2 - $pkg.Version | Should -Be "5.0.0" - } - - It "Install PSResourceInfo object piped in" { - Find-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $GithubPackagesRepoName -Credential $credential | Install-PSResource -Credential $credential -TrustRepository - $res = Get-InstalledPSResource -Name $testModuleName - $res.Name | Should -Be $testModuleName - $res.Version | Should -Be "1.0.0" - } - - It "Install module using -PassThru" { - $res = Install-PSResource -Name $testModuleName -Repository $GithubPackagesRepoName -Credential $credential -PassThru -TrustRepository - $res.Name | Should -Contain $testModuleName - } -} - -Describe 'Test Install-PSResource for GitHub Packages scenarios - Manual Validation' -tags 'ManualValidationOnly' { - - BeforeAll { - $testModuleName = "TestModule" - $testModuleName2 = "testModuleWithlicense" - Get-NewPSResourceRepositoryFile - Register-LocalRepos - } - - AfterEach { - Uninstall-PSResource $testModuleName, $testModuleName2 -SkipDependencyCheck -ErrorAction SilentlyContinue - } - - AfterAll { - Get-RevertPSResourceRepositoryFile - } - - # Unix only manual test - # Expected path should be similar to: '/usr/local/share/powershell/Modules' - It "Install resource under AllUsers scope - Unix only" -Skip:(Get-IsWindows) { - Install-PSResource -Name $testModuleName -Repository $TestGalleryName -Scope AllUsers - $pkg = Get-Module $testModuleName -ListAvailable - $pkg.Name | Should -Be $testModuleName - $pkg.Path.Contains("/usr/") | Should -Be $true - } - - # This needs to be manually tested due to prompt - It "Install resource that requires accept license without -AcceptLicense flag" { - Install-PSResource -Name $testModuleName2 -Repository $TestGalleryName - $pkg = Get-InstalledPSResource $testModuleName2 - $pkg.Name | Should -Be $testModuleName2 - $pkg.Version | Should -Be "0.0.1.0" - } - - # This needs to be manually tested due to prompt - It "Install resource should prompt 'trust repository' if repository is not trusted" { - Set-PSResourceRepository PoshTestGallery -Trusted:$false - - Install-PSResource -Name $testModuleName -Repository $TestGalleryName -confirm:$false - - $pkg = Get-Module $testModuleName -ListAvailable - $pkg.Name | Should -Be $testModuleName - - Set-PSResourceRepository PoshTestGallery -Trusted - } -} +# # Copyright (c) Microsoft Corporation. +# # Licensed under the MIT License. + +# $ProgressPreference = "SilentlyContinue" +# $modPath = "$psscriptroot/../PSGetTestUtils.psm1" +# Import-Module $modPath -Force -Verbose + +# Describe 'Test Install-PSResource for GitHub packages' -tags 'CI' { + +# BeforeAll { +# $testModuleName = "test_module" +# $testModuleName2 = "test_module2" +# $testScriptName = "test_script" +# $GithubPackagesRepoName = "GithubPackagesRepo" +# $GithubPackagesRepoUri = "https://nuget.pkg.github.com/PowerShell/index.json" +# # Get-NewPSResourceRepositoryFile +# # Register-PSResourceRepository -Name $GithubPackagesRepoName -Uri $GithubPackagesRepoUri + +# $secureString = ConvertTo-SecureString $env:MAPPED_GITHUB_PAT -AsPlainText -Force +# $credential = New-Object pscredential ($env:GITHUB_USERNAME, $secureString) + +# Uninstall-PSResource $testModuleName, $testScriptName -Version "*" -SkipDependencyCheck -ErrorAction SilentlyContinue +# } + +# AfterEach { +# Uninstall-PSResource $testModuleName, $testScriptName -Version "*" -SkipDependencyCheck -ErrorAction SilentlyContinue +# } + +# AfterAll { +# Get-RevertPSResourceRepositoryFile +# } + +# $testCases = @{Name="*"; ErrorId="NameContainsWildcard"}, +# @{Name="Test_m*"; ErrorId="NameContainsWildcard"}, +# @{Name="Test?module","Test[module"; ErrorId="ErrorFilteringNamesForUnsupportedWildcards"} + +# It "Should not install resource with wildcard in name" -TestCases $testCases { +# param($Name, $ErrorId) +# Install-PSResource -Name $Name -Repository $GithubPackagesRepoName -Credential $credential -ErrorVariable err -ErrorAction SilentlyContinue +# $err.Count | Should -BeGreaterThan 0 +# $err[0].FullyQualifiedErrorId | Should -BeExactly "$ErrorId,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource" +# $res = Get-InstalledPSResource $testModuleName -ErrorAction SilentlyContinue +# $res | Should -BeNullOrEmpty +# } + +# It "Install specific module resource by name" { +# $DebugPreference = 'Continue' +# Install-PSResource -Name $testModuleName -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository -Verbose -Debug +# $pkg = Get-InstalledPSResource $testModuleName +# $pkg.Name | Should -Be $testModuleName +# $pkg.Version | Should -Be "5.0.0" +# } + +# It "Install specific script resource by name" { +# #$DebugPreference = 'Continue' +# Install-PSResource -Name $testScriptName -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository #-Verbose -Debug +# $pkg = Get-InstalledPSResource $testScriptName +# $pkg.Name | Should -Be $testScriptName +# $pkg.Version | Should -Be "3.5.0" +# } + +# It "Install multiple resources by name" { +# #$DebugPreference = 'Continue' +# $pkgNames = @($testModuleName, $testModuleName2) +# Install-PSResource -Name $pkgNames -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository #-debug -verbose +# $pkg = Get-InstalledPSResource $pkgNames +# $pkg.Name | Should -Be $pkgNames +# } + +# It "Should not install resource given nonexistent name" { +# Install-PSResource -Name "NonExistentModule" -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository -ErrorVariable err -ErrorAction SilentlyContinue +# $pkg = Get-InstalledPSResource "NonExistentModule" -ErrorAction SilentlyContinue +# $pkg | Should -BeNullOrEmpty +# $err.Count | Should -BeGreaterThan 0 +# $err[0].FullyQualifiedErrorId | Should -BeExactly "InstallPackageFailure,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource" +# } + +# # Do some version testing, but Find-PSResource should be doing thorough testing +# It "Should install resource given name and exact version" { +# Install-PSResource -Name $testModuleName -Version "1.0.0" -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository +# $pkg = Get-InstalledPSResource $testModuleName +# $pkg.Name | Should -Be $testModuleName +# $pkg.Version | Should -Be "1.0.0" +# } + +# It "Should install resource given name and exact version with bracket syntax" { +# Install-PSResource -Name $testModuleName -Version "[1.0.0]" -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository +# $pkg = Get-InstalledPSResource $testModuleName +# $pkg.Name | Should -Be $testModuleName +# $pkg.Version | Should -Be "1.0.0" +# } + +# It "Should install resource given name and exact range inclusive [1.0.0, 5.0.0]" { +# #$DebugPreference = 'Continue' +# Install-PSResource -Name $testModuleName -Version "[1.0.0, 5.0.0]" -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository #-Debug -Verbose +# $pkg = Get-InstalledPSResource $testModuleName +# $pkg.Name | Should -Be $testModuleName +# $pkg.Version | Should -Be "5.0.0" +# } + +# It "Should install resource given name and exact range exclusive (1.0.0, 5.0.0)" { +# #$DebugPreference = 'Continue' +# Install-PSResource -Name $testModuleName -Version "(1.0.0, 5.0.0)" -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository #-Verbose -Debug +# $pkg = Get-InstalledPSResource $testModuleName +# $pkg.Name | Should -Be $testModuleName +# $pkg.Version | Should -Be "3.0.0" +# } + +# # TODO: Update this test and others like it that use try/catch blocks instead of Should -Throw +# It "Should not install resource with incorrectly formatted version such as exclusive version (1.0.0.0)" { +# $Version = "(1.0.0.0)" +# try { +# Install-PSResource -Name $testModuleName -Version $Version -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository -ErrorAction SilentlyContinue +# } +# catch +# {} +# $Error[0].FullyQualifiedErrorId | Should -be "IncorrectVersionFormat,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource" + +# $res = Get-InstalledPSResource $testModuleName -ErrorAction SilentlyContinue +# $res | Should -BeNullOrEmpty +# } + +# It "Install resource when given Name, Version '*', should install the latest version" { +# $DebugPreference = 'Continue' +# Install-PSResource -Name $testModuleName -Version "*" -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository -Verbose -Debug +# $pkg = Get-InstalledPSResource $testModuleName +# $pkg.Name | Should -Be $testModuleName +# $pkg.Version | Should -Be "5.0.0" +# } + +# It "Install resource with latest (including prerelease) version given Prerelease parameter" { +# Install-PSResource -Name $testModuleName -Prerelease -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository +# $pkg = Get-InstalledPSResource $testModuleName +# $pkg.Name | Should -Be $testModuleName +# $pkg.Version | Should -Be "5.2.5" +# $pkg.Prerelease | Should -Be "alpha001" +# } + +# It "Install resource via InputObject by piping from Find-PSresource" { +# Find-PSResource -Name $testModuleName -Repository $GithubPackagesRepoName -Credential $credential | Install-PSResource -Credential $credential -TrustRepository +# $pkg = Get-InstalledPSResource $testModuleName +# $pkg.Name | Should -Be $testModuleName +# $pkg.Version | Should -Be "5.0.0" +# } + +# It "Install resource with companyname and repository source location and validate properties" { +# Install-PSResource -Name $testModuleName -Version "5.2.5-alpha001" -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository +# $pkg = Get-InstalledPSResource $testModuleName +# $pkg.Version | Should -Be "5.2.5" +# $pkg.Prerelease | Should -Be "alpha001" + +# $pkg.CompanyName | Should -Be "Anam Navied" +# $pkg.RepositorySourceLocation | Should -Be $GithubPackagesRepoUri +# } + +# # Windows only +# It "Install resource under CurrentUser scope - Windows only" -Skip:(!(Get-IsWindows)) { +# Install-PSResource -Name $testModuleName -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository -Scope CurrentUser +# $pkg = Get-InstalledPSResource $testModuleName +# $pkg.Name | Should -Be $testModuleName +# $pkg.InstalledLocation.ToString().Contains("Documents") | Should -Be $true +# } + +# # Windows only +# It "Install resource under AllUsers scope - Windows only" -Skip:(!((Get-IsWindows) -and (Test-IsAdmin))) { +# Uninstall-PSResource -Name $testModuleName -SkipDependencyCheck -Scope AllUsers +# Install-PSResource -Name $testModuleName -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository -Scope AllUsers -Verbose +# $pkg = Get-InstalledPSResource $testModuleName -Scope AllUsers +# $pkg.Name | Should -Be $testModuleName +# $pkg.InstalledLocation.ToString().Contains("Program Files") | Should -Be $true +# } + +# # Windows only +# It "Install resource under no specified scope - Windows only" -Skip:(!(Get-IsWindows)) { +# Install-PSResource -Name $testModuleName -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository +# $pkg = Get-InstalledPSResource $testModuleName +# $pkg.Name | Should -Be $testModuleName +# $pkg.InstalledLocation.ToString().Contains("Documents") | Should -Be $true +# } + +# # Unix only +# # Expected path should be similar to: '/home/janelane/.local/share/powershell/Modules' +# It "Install resource under CurrentUser scope - Unix only" -Skip:(Get-IsWindows) { +# Install-PSResource -Name $testModuleName -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository -Scope CurrentUser +# $pkg = Get-InstalledPSResource $testModuleName +# $pkg.Name | Should -Be $testModuleName +# $pkg.InstalledLocation.ToString().Contains("$env:HOME/.local") | Should -Be $true +# } + +# # Unix only +# # Expected path should be similar to: '/home/janelane/.local/share/powershell/Modules' +# It "Install resource under no specified scope - Unix only" -Skip:(Get-IsWindows) { +# Install-PSResource -Name $testModuleName -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository +# $pkg = Get-InstalledPSResource $testModuleName +# $pkg.Name | Should -Be $testModuleName +# $pkg.InstalledLocation.ToString().Contains("$env:HOME/.local") | Should -Be $true +# } + +# It "Should not install resource that is already installed" { +# Install-PSResource -Name $testModuleName -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository +# $pkg = Get-InstalledPSResource $testModuleName +# $pkg.Name | Should -Be $testModuleName +# Install-PSResource -Name $testModuleName -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository -WarningVariable WarningVar -warningaction SilentlyContinue +# $WarningVar | Should -Not -BeNullOrEmpty +# } + +# It "Reinstall resource that is already installed with -Reinstall parameter" { +# Install-PSResource -Name $testModuleName2 -Repository $GithubPackagesRepoName -Credential $credential -TrustRepository +# $pkg = Get-InstalledPSResource $testModuleName2 +# $pkg.Name | Should -Be $testModuleName2 +# $pkg.Version | Should -Be "5.0.0" +# Install-PSResource -Name $testModuleName2 -Repository $GithubPackagesRepoName -Credential $credential -Reinstall -TrustRepository +# $pkg = Get-InstalledPSResource $testModuleName2 +# $pkg.Name | Should -Be $testModuleName2 +# $pkg.Version | Should -Be "5.0.0" +# } + +# It "Install PSResourceInfo object piped in" { +# Find-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $GithubPackagesRepoName -Credential $credential | Install-PSResource -Credential $credential -TrustRepository +# $res = Get-InstalledPSResource -Name $testModuleName +# $res.Name | Should -Be $testModuleName +# $res.Version | Should -Be "1.0.0" +# } + +# It "Install module using -PassThru" { +# $res = Install-PSResource -Name $testModuleName -Repository $GithubPackagesRepoName -Credential $credential -PassThru -TrustRepository +# $res.Name | Should -Contain $testModuleName +# } +# } + +# Describe 'Test Install-PSResource for GitHub Packages scenarios - Manual Validation' -tags 'ManualValidationOnly' { + +# BeforeAll { +# $testModuleName = "TestModule" +# $testModuleName2 = "testModuleWithlicense" +# Get-NewPSResourceRepositoryFile +# Register-LocalRepos +# } + +# AfterEach { +# Uninstall-PSResource $testModuleName, $testModuleName2 -SkipDependencyCheck -ErrorAction SilentlyContinue +# } + +# AfterAll { +# Get-RevertPSResourceRepositoryFile +# } + +# # Unix only manual test +# # Expected path should be similar to: '/usr/local/share/powershell/Modules' +# It "Install resource under AllUsers scope - Unix only" -Skip:(Get-IsWindows) { +# Install-PSResource -Name $testModuleName -Repository $TestGalleryName -Scope AllUsers +# $pkg = Get-Module $testModuleName -ListAvailable +# $pkg.Name | Should -Be $testModuleName +# $pkg.Path.Contains("/usr/") | Should -Be $true +# } + +# # This needs to be manually tested due to prompt +# It "Install resource that requires accept license without -AcceptLicense flag" { +# Install-PSResource -Name $testModuleName2 -Repository $TestGalleryName +# $pkg = Get-InstalledPSResource $testModuleName2 +# $pkg.Name | Should -Be $testModuleName2 +# $pkg.Version | Should -Be "0.0.1.0" +# } + +# # This needs to be manually tested due to prompt +# It "Install resource should prompt 'trust repository' if repository is not trusted" { +# Set-PSResourceRepository PoshTestGallery -Trusted:$false + +# Install-PSResource -Name $testModuleName -Repository $TestGalleryName -confirm:$false + +# $pkg = Get-Module $testModuleName -ListAvailable +# $pkg.Name | Should -Be $testModuleName + +# Set-PSResourceRepository PoshTestGallery -Trusted +# } +# } diff --git a/test/InstallPSResourceTests/InstallPSResourceV2Server.Tests.ps1 b/test/InstallPSResourceTests/InstallPSResourceV2Server.Tests.ps1 index ee35c3396..78e3530af 100644 --- a/test/InstallPSResourceTests/InstallPSResourceV2Server.Tests.ps1 +++ b/test/InstallPSResourceTests/InstallPSResourceV2Server.Tests.ps1 @@ -39,662 +39,664 @@ Describe 'Test Install-PSResource for V2 Server scenarios' -tags 'CI' { Get-RevertPSResourceRepositoryFile } - $testCases = [array]( - @{Name = "*"; ErrorId = "NameContainsWildcard" }, - @{Name = "Test_Module*"; ErrorId = "NameContainsWildcard" }, - @{Name = "Test?Module", "Test[Module"; ErrorId = "ErrorFilteringNamesForUnsupportedWildcards" } - ) - - It "Should not install resource with wildcard in name" -TestCases $testCases { - param($Name, $ErrorId) - Install-PSResource -Name $Name -Repository $PSGalleryName -ErrorVariable err -ErrorAction SilentlyContinue - $err.Count | Should -BeGreaterThan 0 - $err[0].FullyQualifiedErrorId | Should -BeExactly "$ErrorId,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource" - } - - It "Install specific module resource by name" { - Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - $pkg.Version | Should -Be "5.0.0.0" - } + # $testCases = [array]( + # @{Name = "*"; ErrorId = "NameContainsWildcard" }, + # @{Name = "Test_Module*"; ErrorId = "NameContainsWildcard" }, + # @{Name = "Test?Module", "Test[Module"; ErrorId = "ErrorFilteringNamesForUnsupportedWildcards" } + # ) + + # It "Should not install resource with wildcard in name" -TestCases $testCases { + # param($Name, $ErrorId) + # Install-PSResource -Name $Name -Repository $PSGalleryName -ErrorVariable err -ErrorAction SilentlyContinue + # $err.Count | Should -BeGreaterThan 0 + # $err[0].FullyQualifiedErrorId | Should -BeExactly "$ErrorId,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource" + # } - It "Install specific script resource by name" { - Install-PSResource -Name $testScriptName -Repository $PSGalleryName -TrustRepository - $pkg = Get-InstalledPSResource $testScriptName - $pkg.Name | Should -Be $testScriptName - $pkg.Version | Should -Be "3.5.0.0" - } + # It "Install specific module resource by name" { + # Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository + # $pkg = Get-InstalledPSResource $testModuleName + # $pkg.Name | Should -Be $testModuleName + # $pkg.Version | Should -Be "5.0.0.0" + # } - It "Install multiple resources by name" { - $pkgNames = @($testModuleName, $testModuleName2) - Install-PSResource -Name $pkgNames -Repository $PSGalleryName -TrustRepository - $pkg = Get-InstalledPSResource $pkgNames - $pkg.Name | Should -Be $pkgNames - } + # It "Install specific script resource by name" { + # Install-PSResource -Name $testScriptName -Repository $PSGalleryName -TrustRepository + # $pkg = Get-InstalledPSResource $testScriptName + # $pkg.Name | Should -Be $testScriptName + # $pkg.Version | Should -Be "3.5.0.0" + # } - It "Should not install resource given nonexistent name" { - Install-PSResource -Name "NonExistentModule" -Repository $PSGalleryName -TrustRepository -ErrorVariable err -ErrorAction SilentlyContinue - $pkg = Get-InstalledPSResource "NonExistentModule" - $pkg.Name | Should -BeNullOrEmpty - $err.Count | Should -BeGreaterThan 0 - $err[0].FullyQualifiedErrorId | Should -BeExactly "InstallPackageFailure,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource" - } + # It "Install multiple resources by name" { + # $pkgNames = @($testModuleName, $testModuleName2) + # Install-PSResource -Name $pkgNames -Repository $PSGalleryName -TrustRepository + # $pkg = Get-InstalledPSResource $pkgNames + # $pkg.Name | Should -Be $pkgNames + # } - # Do some version testing, but Find-PSResource should be doing thorough testing - It "Should install resource given name and exact version" { - Install-PSResource -Name $testModuleName -Version "1.0.0" -Repository $PSGalleryName -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - $pkg.Version | Should -Be "1.0.0.0" - } + # It "Should not install resource given nonexistent name" { + # Install-PSResource -Name "NonExistentModule" -Repository $PSGalleryName -TrustRepository -ErrorVariable err -ErrorAction SilentlyContinue + # $pkg = Get-InstalledPSResource "NonExistentModule" + # $pkg.Name | Should -BeNullOrEmpty + # $err.Count | Should -BeGreaterThan 0 + # $err[0].FullyQualifiedErrorId | Should -BeExactly "InstallPackageFailure,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource" + # } - It "Should install resource given name and exact version with bracket syntax" { - Install-PSResource -Name $testModuleName -Version "3.*" -Repository $PSGalleryName -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - $pkg.Version | Should -Be "3.0.0.0" - } + # # Do some version testing, but Find-PSResource should be doing thorough testing + # It "Should install resource given name and exact version" { + # Install-PSResource -Name $testModuleName -Version "1.0.0" -Repository $PSGalleryName -TrustRepository + # $pkg = Get-InstalledPSResource $testModuleName + # $pkg.Name | Should -Be $testModuleName + # $pkg.Version | Should -Be "1.0.0.0" + # } - It "Should install resource given name and exact version with bracket syntax" { - Install-PSResource -Name $testModuleName -Version "[1.0.0]" -Repository $PSGalleryName -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - $pkg.Version | Should -Be "1.0.0.0" - } + # It "Should install resource given name and exact version with bracket syntax" { + # Install-PSResource -Name $testModuleName -Version "3.*" -Repository $PSGalleryName -TrustRepository + # $pkg = Get-InstalledPSResource $testModuleName + # $pkg.Name | Should -Be $testModuleName + # $pkg.Version | Should -Be "3.0.0.0" + # } - It "Should install resource given name and exact range inclusive [1.0.0, 5.0.0]" { - Install-PSResource -Name $testModuleName -Version "[1.0.0, 5.0.0]" -Repository $PSGalleryName -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - $pkg.Version | Should -Be "5.0.0.0" - } + # It "Should install resource given name and exact version with bracket syntax" { + # Install-PSResource -Name $testModuleName -Version "[1.0.0]" -Repository $PSGalleryName -TrustRepository + # $pkg = Get-InstalledPSResource $testModuleName + # $pkg.Name | Should -Be $testModuleName + # $pkg.Version | Should -Be "1.0.0.0" + # } - It "Should install resource given name and exact range exclusive (1.0.0, 5.0.0)" { - Install-PSResource -Name $testModuleName -Version "(1.0.0, 5.0.0)" -Repository $PSGalleryName -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - $pkg.Version | Should -Be "3.0.0.0" - } + # It "Should install resource given name and exact range inclusive [1.0.0, 5.0.0]" { + # Install-PSResource -Name $testModuleName -Version "[1.0.0, 5.0.0]" -Repository $PSGalleryName -TrustRepository + # $pkg = Get-InstalledPSResource $testModuleName + # $pkg.Name | Should -Be $testModuleName + # $pkg.Version | Should -Be "5.0.0.0" + # } - # TODO: Update this test and others like it that use try/catch blocks instead of Should -Throw - It "Should not install resource with incorrectly formatted version such as exclusive version (1.0.0.0)" { - $Version = "(1.0.0.0)" - try { - Install-PSResource -Name $testModuleName -Version $Version -Repository $PSGalleryName -TrustRepository -ErrorAction SilentlyContinue - } - catch { - } - $Error[0].FullyQualifiedErrorId | Should -Be "IncorrectVersionFormat,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource" + # It "Should install resource given name and exact range exclusive (1.0.0, 5.0.0)" { + # Install-PSResource -Name $testModuleName -Version "(1.0.0, 5.0.0)" -Repository $PSGalleryName -TrustRepository + # $pkg = Get-InstalledPSResource $testModuleName + # $pkg.Name | Should -Be $testModuleName + # $pkg.Version | Should -Be "3.0.0.0" + # } - $res = Get-InstalledPSResource $testModuleName - $res | Should -BeNullOrEmpty - } + # # TODO: Update this test and others like it that use try/catch blocks instead of Should -Throw + # It "Should not install resource with incorrectly formatted version such as exclusive version (1.0.0.0)" { + # $Version = "(1.0.0.0)" + # try { + # Install-PSResource -Name $testModuleName -Version $Version -Repository $PSGalleryName -TrustRepository -ErrorAction SilentlyContinue + # } + # catch { + # } + # $Error[0].FullyQualifiedErrorId | Should -Be "IncorrectVersionFormat,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource" - It "Install resource when given Name, Version '*', should install the latest version" { - Install-PSResource -Name $testModuleName -Version "*" -Repository $PSGalleryName -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - $pkg.Version | Should -Be "5.0.0.0" - } + # $res = Get-InstalledPSResource $testModuleName + # $res | Should -BeNullOrEmpty + # } - It "Install resource with latest (including prerelease) version given Prerelease parameter" { - Install-PSResource -Name $testModuleName -Prerelease -Repository $PSGalleryName -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - $pkg.Version | Should -Be "5.2.5" - $pkg.Prerelease | Should -Be "alpha001" - } + # It "Install resource when given Name, Version '*', should install the latest version" { + # Install-PSResource -Name $testModuleName -Version "*" -Repository $PSGalleryName -TrustRepository + # $pkg = Get-InstalledPSResource $testModuleName + # $pkg.Name | Should -Be $testModuleName + # $pkg.Version | Should -Be "5.0.0.0" + # } - It "Install a module with a dependency" { - Uninstall-PSResource -Name "TestModuleWithDependency*" -Version "*" -SkipDependencyCheck -ErrorAction SilentlyContinue - Install-PSResource -Name "TestModuleWithDependencyC" -Version "5.0.0.0" -Repository $PSGalleryName -TrustRepository + # It "Install resource with latest (including prerelease) version given Prerelease parameter" { + # Install-PSResource -Name $testModuleName -Prerelease -Repository $PSGalleryName -TrustRepository + # $pkg = Get-InstalledPSResource $testModuleName + # $pkg.Name | Should -Be $testModuleName + # $pkg.Version | Should -Be "5.2.5" + # $pkg.Prerelease | Should -Be "alpha001" + # } - $pkg = Get-InstalledPSResource "TestModuleWithDependencyC" - $pkg.Name | Should -Be "TestModuleWithDependencyC" - $pkg.Version | Should -Be "5.0" + # It "Install a module with a dependency" { + # Uninstall-PSResource -Name "TestModuleWithDependency*" -Version "*" -SkipDependencyCheck -ErrorAction SilentlyContinue + # Install-PSResource -Name "TestModuleWithDependencyC" -Version "5.0.0.0" -Repository $PSGalleryName -TrustRepository - $pkg = Get-InstalledPSResource "TestModuleWithDependencyB" - $pkg.Name | Should -Be "TestModuleWithDependencyB" - $pkg.Version | Should -Be "3.0" + # $pkg = Get-InstalledPSResource "TestModuleWithDependencyC" + # $pkg.Name | Should -Be "TestModuleWithDependencyC" + # $pkg.Version | Should -Be "5.0" - $pkg = Get-InstalledPSResource "TestModuleWithDependencyD" - $pkg.Name | Should -Be "TestModuleWithDependencyD" - $pkg.Version | Should -Be "2.0" - } + # $pkg = Get-InstalledPSResource "TestModuleWithDependencyB" + # $pkg.Name | Should -Be "TestModuleWithDependencyB" + # $pkg.Version | Should -Be "3.0" - It "Install a module with a prerelease dependency" { - Install-PSResource -Name "TestModuleWithPrereleaseDep" -Repository $PSGalleryName -TrustRepository - - $pkg = Get-InstalledPSResource "TestModuleWithPrereleaseDep" - $pkg.Name | Should -Be "TestModuleWithPrereleaseDep" - $pkg.Version | Should -Be "0.0.1" + # $pkg = Get-InstalledPSResource "TestModuleWithDependencyD" + # $pkg.Name | Should -Be "TestModuleWithDependencyD" + # $pkg.Version | Should -Be "2.0" + # } - $pkg = Get-InstalledPSResource "PrereleaseModule" - $pkg.Name | Should -Be "PrereleaseModule" - $pkg.Version | Should -Be "0.0.1" - $pkg.Prerelease | Should -Be "Prerelease" - } + # It "Install a module with a prerelease dependency" { + # Install-PSResource -Name "TestModuleWithPrereleaseDep" -Repository $PSGalleryName -TrustRepository - It "Install a module with a dependency and skip installing the dependency" { - Uninstall-PSResource -Name "TestModuleWithDependency*" -Version "*" -SkipDependencyCheck - Install-PSResource -Name "TestModuleWithDependencyC" -Version "1.0.0.0" -SkipDependencyCheck -Repository $PSGalleryName -TrustRepository - $pkg = Get-InstalledPSResource "TestModuleWithDependencyC" - $pkg.Name | Should -Be "TestModuleWithDependencyC" - $pkg.Version | Should -Be "1.0" + # $pkg = Get-InstalledPSResource "TestModuleWithPrereleaseDep" + # $pkg.Name | Should -Be "TestModuleWithPrereleaseDep" + # $pkg.Version | Should -Be "0.0.1" - $pkg = Get-InstalledPSResource "TestModuleWithDependencyB", "TestModuleWithDependencyD" - $pkg | Should -BeNullOrEmpty - } + # $pkg = Get-InstalledPSResource "PrereleaseModule" + # $pkg.Name | Should -Be "PrereleaseModule" + # $pkg.Version | Should -Be "0.0.1" + # $pkg.Prerelease | Should -Be "Prerelease" + # } - It "Install resource via InputObject by piping from Find-PSresource" { - Find-PSResource -Name $testModuleName -Repository $PSGalleryName | Install-PSResource -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - $pkg.Version | Should -Be "5.0.0.0" - } + # It "Install a module with a dependency and skip installing the dependency" { + # Uninstall-PSResource -Name "TestModuleWithDependency*" -Version "*" -SkipDependencyCheck + # Install-PSResource -Name "TestModuleWithDependencyC" -Version "1.0.0.0" -SkipDependencyCheck -Repository $PSGalleryName -TrustRepository + # $pkg = Get-InstalledPSResource "TestModuleWithDependencyC" + # $pkg.Name | Should -Be "TestModuleWithDependencyC" + # $pkg.Version | Should -Be "1.0" - It "Install resource under specified in PSModulePath" { - Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - ($env:PSModulePath).Contains($pkg.InstalledLocation) - } + # $pkg = Get-InstalledPSResource "TestModuleWithDependencyB", "TestModuleWithDependencyD" + # $pkg | Should -BeNullOrEmpty + # } - It "Install resource with companyname, copyright and repository source location and validate" { - Install-PSResource -Name $testModuleName -Version "5.2.5-alpha001" -Repository PSGallery -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Version | Should -Be "5.2.5" - $pkg.Prerelease | Should -Be "alpha001" + # It "Install resource via InputObject by piping from Find-PSresource" { + # Find-PSResource -Name $testModuleName -Repository $PSGalleryName | Install-PSResource -TrustRepository + # $pkg = Get-InstalledPSResource $testModuleName + # $pkg.Name | Should -Be $testModuleName + # $pkg.Version | Should -Be "5.0.0.0" + # } - $pkg.CompanyName | Should -Be "Anam" - $pkg.Copyright | Should -Be "(c) Anam Navied. All rights reserved." - $pkg.RepositorySourceLocation | Should -Be $PSGalleryUri - } + # It "Install resource under specified in PSModulePath" { + # Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository + # $pkg = Get-InstalledPSResource $testModuleName + # $pkg.Name | Should -Be $testModuleName + # ($env:PSModulePath).Contains($pkg.InstalledLocation) + # } + # It "Install resource with companyname, copyright and repository source location and validate" { + # Install-PSResource -Name $testModuleName -Version "5.2.5-alpha001" -Repository PSGallery -TrustRepository + # $pkg = Get-InstalledPSResource $testModuleName + # $pkg.Version | Should -Be "5.2.5" + # $pkg.Prerelease | Should -Be "alpha001" - It "Install script with companyname, copyright, and repository source location and validate" { - Install-PSResource -Name "Install-VSCode" -Version "1.4.2" -Repository $PSGalleryName -TrustRepository + # $pkg.CompanyName | Should -Be "Anam" + # $pkg.Copyright | Should -Be "(c) Anam Navied. All rights reserved." + # $pkg.RepositorySourceLocation | Should -Be $PSGalleryUri + # } - $res = Get-InstalledPSResource "Install-VSCode" -Version "1.4.2" - $res.Name | Should -Be "Install-VSCode" - $res.Version | Should -Be "1.4.2" - $res.CompanyName | Should -Be "Microsoft Corporation" - $res.Copyright | Should -Be "(c) Microsoft Corporation" - $res.RepositorySourceLocation | Should -Be $PSGalleryUri - } - # Windows only - It "Install resource under CurrentUser scope - Windows only" -Skip:(!(Get-IsWindows)) { - Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -Scope CurrentUser - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - $pkg.InstalledLocation.ToString().Contains("Documents") | Should -Be $true - } + # It "Install script with companyname, copyright, and repository source location and validate" { + # Install-PSResource -Name "Install-VSCode" -Version "1.4.2" -Repository $PSGalleryName -TrustRepository - # Windows only - It "Install resource under AllUsers scope - Windows only" -Skip:(!((Get-IsWindows) -and (Test-IsAdmin))) { - Install-PSResource -Name "testmodule99" -Repository $PSGalleryName -TrustRepository -Scope AllUsers -Verbose - $pkg = Get-Module "testmodule99" -ListAvailable - $pkg.Name | Should -Be "testmodule99" - $pkg.Path.ToString().Contains("Program Files") - } + # $res = Get-InstalledPSResource "Install-VSCode" -Version "1.4.2" + # $res.Name | Should -Be "Install-VSCode" + # $res.Version | Should -Be "1.4.2" + # $res.CompanyName | Should -Be "Microsoft Corporation" + # $res.Copyright | Should -Be "(c) Microsoft Corporation" + # $res.RepositorySourceLocation | Should -Be $PSGalleryUri + # } - # Windows only - It "Install resource under no specified scope - Windows only" -Skip:(!(Get-IsWindows)) { - Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - $pkg.InstalledLocation.ToString().Contains("Documents") | Should -Be $true - } + # # Windows only + # It "Install resource under CurrentUser scope - Windows only" -Skip:(!(Get-IsWindows)) { + # Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -Scope CurrentUser + # $pkg = Get-InstalledPSResource $testModuleName + # $pkg.Name | Should -Be $testModuleName + # $pkg.InstalledLocation.ToString().Contains("Documents") | Should -Be $true + # } - # Unix only - # Expected path should be similar to: '/home/janelane/.local/share/powershell/Modules' - It "Install resource under CurrentUser scope - Unix only" -Skip:(Get-IsWindows) { - Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -Scope CurrentUser - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - $pkg.InstalledLocation.ToString().Contains("$env:HOME/.local") | Should -Be $true - } + # # Windows only + # It "Install resource under AllUsers scope - Windows only" -Skip:(!((Get-IsWindows) -and (Test-IsAdmin))) { + # Install-PSResource -Name "testmodule99" -Repository $PSGalleryName -TrustRepository -Scope AllUsers -Verbose + # $pkg = Get-Module "testmodule99" -ListAvailable + # $pkg.Name | Should -Be "testmodule99" + # $pkg.Path.ToString().Contains("Program Files") + # } - # Unix only - # Expected path should be similar to: '/home/janelane/.local/share/powershell/Modules' - It "Install resource under no specified scope - Unix only" -Skip:(Get-IsWindows) { - Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - $pkg.InstalledLocation.ToString().Contains("$env:HOME/.local") | Should -Be $true - } + # # Windows only + # It "Install resource under no specified scope - Windows only" -Skip:(!(Get-IsWindows)) { + # Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository + # $pkg = Get-InstalledPSResource $testModuleName + # $pkg.Name | Should -Be $testModuleName + # $pkg.InstalledLocation.ToString().Contains("Documents") | Should -Be $true + # } - It "Should not install resource that is already installed" { - Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -WarningVariable WarningVar -WarningAction SilentlyContinue - $WarningVar | Should -Not -BeNullOrEmpty - } + # # Unix only + # # Expected path should be similar to: '/home/janelane/.local/share/powershell/Modules' + # It "Install resource under CurrentUser scope - Unix only" -Skip:(Get-IsWindows) { + # Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -Scope CurrentUser + # $pkg = Get-InstalledPSResource $testModuleName + # $pkg.Name | Should -Be $testModuleName + # $pkg.InstalledLocation.ToString().Contains("$env:HOME/.local") | Should -Be $true + # } - It "Reinstall resource that is already installed with -Reinstall parameter" { - Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - $pkg.Version | Should -Be "5.0.0.0" - Install-PSResource -Name $testModuleName -Repository $PSGalleryName -Reinstall -TrustRepository - $pkg = Get-InstalledPSResource $testModuleName - $pkg.Name | Should -Be $testModuleName - $pkg.Version | Should -Be "5.0.0.0" - } + # # Unix only + # # Expected path should be similar to: '/home/janelane/.local/share/powershell/Modules' + # It "Install resource under no specified scope - Unix only" -Skip:(Get-IsWindows) { + # Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository + # $pkg = Get-InstalledPSResource $testModuleName + # $pkg.Name | Should -Be $testModuleName + # $pkg.InstalledLocation.ToString().Contains("$env:HOME/.local") | Should -Be $true + # } - # It "Restore resource after reinstall fails" { + # It "Should not install resource that is already installed" { # Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository # $pkg = Get-InstalledPSResource $testModuleName - # $pkg.Name | Should -Contain $testModuleName - # $pkg.Version | Should -Contain "5.0.0.0" - - # $resourcePath = Split-Path -Path $pkg.InstalledLocation -Parent - # $resourceFiles = Get-ChildItem -Path $resourcePath -Recurse - - # # Lock resource file to prevent reinstall from succeeding. - # $fs = [System.IO.File]::Open($resourceFiles[0].FullName, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read) - # try - # { - # # Reinstall of resource should fail with one of its files locked. - # Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -Reinstall -ErrorVariable ev -ErrorAction Silent - # $ev.FullyQualifiedErrorId | Should -BeExactly 'InstallPackageFailed,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource' - # } - # finally - # { - # $fs.Close() - # } + # $pkg.Name | Should -Be $testModuleName + # Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -WarningVariable WarningVar -WarningAction SilentlyContinue + # $WarningVar | Should -Not -BeNullOrEmpty + # } - # # Verify that resource module has been restored. - # (Get-ChildItem -Path $resourcePath -Recurse).Count | Should -BeExactly $resourceFiles.Count + # It "Reinstall resource that is already installed with -Reinstall parameter" { + # Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository + # $pkg = Get-InstalledPSResource $testModuleName + # $pkg.Name | Should -Be $testModuleName + # $pkg.Version | Should -Be "5.0.0.0" + # Install-PSResource -Name $testModuleName -Repository $PSGalleryName -Reinstall -TrustRepository + # $pkg = Get-InstalledPSResource $testModuleName + # $pkg.Name | Should -Be $testModuleName + # $pkg.Version | Should -Be "5.0.0.0" # } - # It "Install resource that requires accept license with -AcceptLicense flag" { - # Install-PSResource -Name "testModuleWithlicense" -Repository $TestGalleryName -AcceptLicense - # $pkg = Get-InstalledPSResource "testModuleWithlicense" - # $pkg.Name | Should -Be "testModuleWithlicense" - # $pkg.Version | Should -Be "0.0.3.0" + # # It "Restore resource after reinstall fails" { + # # Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository + # # $pkg = Get-InstalledPSResource $testModuleName + # # $pkg.Name | Should -Contain $testModuleName + # # $pkg.Version | Should -Contain "5.0.0.0" + + # # $resourcePath = Split-Path -Path $pkg.InstalledLocation -Parent + # # $resourceFiles = Get-ChildItem -Path $resourcePath -Recurse + + # # # Lock resource file to prevent reinstall from succeeding. + # # $fs = [System.IO.File]::Open($resourceFiles[0].FullName, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read) + # # try + # # { + # # # Reinstall of resource should fail with one of its files locked. + # # Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -Reinstall -ErrorVariable ev -ErrorAction Silent + # # $ev.FullyQualifiedErrorId | Should -BeExactly 'InstallPackageFailed,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource' + # # } + # # finally + # # { + # # $fs.Close() + # # } + + # # # Verify that resource module has been restored. + # # (Get-ChildItem -Path $resourcePath -Recurse).Count | Should -BeExactly $resourceFiles.Count + # # } + + # # It "Install resource that requires accept license with -AcceptLicense flag" { + # # Install-PSResource -Name "testModuleWithlicense" -Repository $TestGalleryName -AcceptLicense + # # $pkg = Get-InstalledPSResource "testModuleWithlicense" + # # $pkg.Name | Should -Be "testModuleWithlicense" + # # $pkg.Version | Should -Be "0.0.3.0" + # # } + + # It "Install resource with cmdlet names from a module already installed with -NoClobber (should not clobber)" { + # Install-PSResource -Name $clobberTestModule -Repository $PSGalleryName -TrustRepository + # $pkg = Get-InstalledPSResource $clobberTestModule + # $pkg.Name | Should -Be $clobberTestModule + # $pkg.Version | Should -Be "0.0.1" + + # Install-PSResource -Name $clobberTestModule2 -Repository $PSGalleryName -TrustRepository -NoClobber -ErrorVariable ev -ErrorAction SilentlyContinue + # $pkg = Get-InstalledPSResource $clobberTestModule2 + # $pkg | Should -BeNullOrEmpty + # $ev.Count | Should -Be 1 + # $ev[0] | Should -Be "'ClobberTestModule2' package could not be installed with error: The following commands are already available on this system: 'Test-Command2, Test-Command2'. This module 'ClobberTestModule2' may override the existing commands. If you still want to install this module 'ClobberTestModule2', remove the -NoClobber parameter." # } - It "Install resource with cmdlet names from a module already installed with -NoClobber (should not clobber)" { - Install-PSResource -Name $clobberTestModule -Repository $PSGalleryName -TrustRepository - $pkg = Get-InstalledPSResource $clobberTestModule - $pkg.Name | Should -Be $clobberTestModule - $pkg.Version | Should -Be "0.0.1" + # It "Install resource with cmdlet names from a module already installed (should clobber)" { + # Install-PSResource -Name $clobberTestModule -Repository $PSGalleryName -TrustRepository + # $pkg = Get-InstalledPSResource $clobberTestModule + # $pkg.Name | Should -Be $clobberTestModule + # $pkg.Version | Should -Be "0.0.1" - Install-PSResource -Name $clobberTestModule2 -Repository $PSGalleryName -TrustRepository -NoClobber -ErrorVariable ev -ErrorAction SilentlyContinue - $pkg = Get-InstalledPSResource $clobberTestModule2 - $pkg | Should -BeNullOrEmpty - $ev.Count | Should -Be 1 - $ev[0] | Should -Be "'ClobberTestModule2' package could not be installed with error: The following commands are already available on this system: 'Test-Command2, Test-Command2'. This module 'ClobberTestModule2' may override the existing commands. If you still want to install this module 'ClobberTestModule2', remove the -NoClobber parameter." - } + # Install-PSResource -Name $clobberTestModule2 -Repository $PSGalleryName -TrustRepository + # $pkg = Get-InstalledPSResource $clobberTestModule2 + # $pkg.Name | Should -Be $clobberTestModule2 + # $pkg.Version | Should -Be "0.0.1" + # } - It "Install resource with cmdlet names from a module already installed (should clobber)" { - Install-PSResource -Name $clobberTestModule -Repository $PSGalleryName -TrustRepository - $pkg = Get-InstalledPSResource $clobberTestModule - $pkg.Name | Should -Be $clobberTestModule - $pkg.Version | Should -Be "0.0.1" + # It "Install resource with -NoClobber (should install)" { + # Install-PSResource -Name $clobberTestModule -Repository $PSGalleryName -TrustRepository -NoClobber + # $pkg = Get-InstalledPSResource $clobberTestModule + # $pkg.Name | Should -Be $clobberTestModule + # $pkg.Version | Should -Be "0.0.1" + # } - Install-PSResource -Name $clobberTestModule2 -Repository $PSGalleryName -TrustRepository - $pkg = Get-InstalledPSResource $clobberTestModule2 - $pkg.Name | Should -Be $clobberTestModule2 - $pkg.Version | Should -Be "0.0.1" - } + # It "Install module using -WhatIf, should not install the module" { + # Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -WhatIf + # $? | Should -BeTrue - It "Install resource with -NoClobber (should install)" { - Install-PSResource -Name $clobberTestModule -Repository $PSGalleryName -TrustRepository -NoClobber - $pkg = Get-InstalledPSResource $clobberTestModule - $pkg.Name | Should -Be $clobberTestModule - $pkg.Version | Should -Be "0.0.1" - } + # $res = Get-InstalledPSResource $testModuleName + # $res | Should -BeNullOrEmpty + # } - It "Install module using -WhatIf, should not install the module" { - Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -WhatIf - $? | Should -BeTrue + # It "Validates that a module with module-name script files (like Pester) installs under Modules path" { - $res = Get-InstalledPSResource $testModuleName - $res | Should -BeNullOrEmpty - } + # Install-PSResource -Name "testModuleWithScript" -Repository $PSGalleryName -TrustRepository - It "Validates that a module with module-name script files (like Pester) installs under Modules path" { + # $res = Get-InstalledPSResource "testModuleWithScript" + # $res.InstalledLocation.ToString().Contains("Modules") | Should -Be $true + # } - Install-PSResource -Name "testModuleWithScript" -Repository $PSGalleryName -TrustRepository + # # It "Install module using -NoClobber, should throw clobber error and not install the module" { + # # Install-PSResource -Name "ClobberTestModule1" -Repository $PSGalleryName -TrustRepository - $res = Get-InstalledPSResource "testModuleWithScript" - $res.InstalledLocation.ToString().Contains("Modules") | Should -Be $true - } + # # $res = Get-InstalledPSResource "ClobberTestModule1" + # # $res.Name | Should -Be "ClobberTestModule1" - # It "Install module using -NoClobber, should throw clobber error and not install the module" { - # Install-PSResource -Name "ClobberTestModule1" -Repository $PSGalleryName -TrustRepository + # # Install-PSResource -Name "ClobberTestModule2" -Repository $PSGalleryName -TrustRepository -NoClobber -ErrorAction SilentlyContinue + # # $Error[0].FullyQualifiedErrorId | Should -be "CommandAlreadyExists,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource" + # # } - # $res = Get-InstalledPSResource "ClobberTestModule1" - # $res.Name | Should -Be "ClobberTestModule1" + # It "Install PSResourceInfo object piped in" { + # Find-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName | Install-PSResource -TrustRepository + # $res = Get-InstalledPSResource -Name $testModuleName + # $res.Name | Should -Be $testModuleName + # $res.Version | Should -Be "1.0.0.0" + # } - # Install-PSResource -Name "ClobberTestModule2" -Repository $PSGalleryName -TrustRepository -NoClobber -ErrorAction SilentlyContinue - # $Error[0].FullyQualifiedErrorId | Should -be "CommandAlreadyExists,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource" + # It "Install module using -PassThru" { + # $res = Install-PSResource -Name $testModuleName -Repository $PSGalleryName -PassThru -TrustRepository + # $res.Name | Should -Contain $testModuleName # } - It "Install PSResourceInfo object piped in" { - Find-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName | Install-PSResource -TrustRepository - $res = Get-InstalledPSResource -Name $testModuleName - $res.Name | Should -Be $testModuleName - $res.Version | Should -Be "1.0.0.0" - } + # # -RequiredResource + # It "Install modules using -RequiredResource with hashtable" { + # $rrHash = @{ + # test_module = @{ + # version = "[1.0.0,5.0.0)" + # repository = $PSGalleryName + # } + # test_module2 = @{ + # version = "[1.0.0,3.0.0)" + # repository = $PSGalleryName + # prerelease = "true" + # } + # TestModule99 = @{} + # } - It "Install module using -PassThru" { - $res = Install-PSResource -Name $testModuleName -Repository $PSGalleryName -PassThru -TrustRepository - $res.Name | Should -Contain $testModuleName - } + # Install-PSResource -RequiredResource $rrHash -TrustRepository - # -RequiredResource - It "Install modules using -RequiredResource with hashtable" { - $rrHash = @{ - test_module = @{ - version = "[1.0.0,5.0.0)" - repository = $PSGalleryName - } - test_module2 = @{ - version = "[1.0.0,3.0.0)" - repository = $PSGalleryName - prerelease = "true" - } - TestModule99 = @{} - } - - Install-PSResource -RequiredResource $rrHash -TrustRepository - - $res1 = Get-InstalledPSResource $testModuleName - $res1.Name | Should -Be $testModuleName - $res1.Version | Should -Be "3.0.0.0" - - $res2 = Get-InstalledPSResource "test_module2" -Version "2.5.0-beta" - $res2.Name | Should -Be "test_module2" - $res2.Version | Should -Be "2.5.0" - $res2.Prerelease | Should -Be "beta" - - $res3 = Get-InstalledPSResource $testModuleName2 - $res3.Name | Should -Be $testModuleName2 - $res3.Version | Should -Be "0.0.93" - } + # $res1 = Get-InstalledPSResource $testModuleName + # $res1.Name | Should -Be $testModuleName + # $res1.Version | Should -Be "3.0.0.0" - It "Install module using -RequiredResource with hashtable, and prerelease is boolean true" { - Install-PSResource -TrustRepository -RequiredResource @{ - 'TestModule99' = @{ - 'repository' = 'PSGallery' - 'prerelease' = $true - } - } - (Get-InstalledPSResource -Name 'TestModule99').'Prerelease' | Should -Be 'beta2' - } + # $res2 = Get-InstalledPSResource "test_module2" -Version "2.5.0-beta" + # $res2.Name | Should -Be "test_module2" + # $res2.Version | Should -Be "2.5.0" + # $res2.Prerelease | Should -Be "beta" - It "Install module using -RequiredResource with TrustRepository in hashtable" { - # This test verifies that TrustRepository specified in -RequiredResource hashtable is respected - Install-PSResource -RequiredResource @{ - 'TestModule99' = @{ - 'repository' = 'PSGallery' - 'trustrepository' = 'true' - } - } - $res = Get-InstalledPSResource -Name 'TestModule99' - $res.Name | Should -Be 'TestModule99' - $res.Version | Should -Be '0.0.93' - } + # $res3 = Get-InstalledPSResource $testModuleName2 + # $res3.Name | Should -Be $testModuleName2 + # $res3.Version | Should -Be "0.0.93" + # } - It "Install modules using -RequiredResource with JSON string" { - $rrJSON = "{ - 'test_module': { - 'version': '[1.0.0,5.0.0)', - 'repository': 'PSGallery' - }, - 'test_module2': { - 'version': '[1.0.0,3.0.0)', - 'repository': 'PSGallery', - 'prerelease': 'true' - }, - 'TestModule99': { - 'repository': 'PSGallery' - } - }" - - Install-PSResource -RequiredResource $rrJSON -TrustRepository - - $res1 = Get-InstalledPSResource $testModuleName - $res1.Name | Should -Be $testModuleName - $res1.Version | Should -Be "3.0.0.0" - - $res2 = Get-InstalledPSResource "test_module2" -Version "2.5.0-beta" - $res2.Name | Should -Be "test_module2" - $res2.Version | Should -Be "2.5.0" - $res2.Prerelease | Should -Be "beta" - - $res3 = Get-InstalledPSResource $testModuleName2 - $res3.Name | Should -Be $testModuleName2 - $res3.Version | Should -Be "0.0.93" - } + # It "Install module using -RequiredResource with hashtable, and prerelease is boolean true" { + # Install-PSResource -TrustRepository -RequiredResource @{ + # 'TestModule99' = @{ + # 'repository' = 'PSGallery' + # 'prerelease' = $true + # } + # } + # (Get-InstalledPSResource -Name 'TestModule99').'Prerelease' | Should -Be 'beta2' + # } - It "Install modules using -RequiredResourceFile with PSD1 file" { - $rrFilePSD1 = "$psscriptroot/../$RequiredResourcePSD1FileName" + # It "Install module using -RequiredResource with TrustRepository in hashtable" { + # # This test verifies that TrustRepository specified in -RequiredResource hashtable is respected + # Install-PSResource -RequiredResource @{ + # 'TestModule99' = @{ + # 'repository' = 'PSGallery' + # 'trustrepository' = 'true' + # } + # } + # $res = Get-InstalledPSResource -Name 'TestModule99' + # $res.Name | Should -Be 'TestModule99' + # $res.Version | Should -Be '0.0.93' + # } - Install-PSResource -RequiredResourceFile $rrFilePSD1 -TrustRepository + # It "Install modules using -RequiredResource with JSON string" { + # $rrJSON = "{ + # 'test_module': { + # 'version': '[1.0.0,5.0.0)', + # 'repository': 'PSGallery' + # }, + # 'test_module2': { + # 'version': '[1.0.0,3.0.0)', + # 'repository': 'PSGallery', + # 'prerelease': 'true' + # }, + # 'TestModule99': { + # 'repository': 'PSGallery' + # } + # }" + + # Install-PSResource -RequiredResource $rrJSON -TrustRepository + + # $res1 = Get-InstalledPSResource $testModuleName + # $res1.Name | Should -Be $testModuleName + # $res1.Version | Should -Be "3.0.0.0" + + # $res2 = Get-InstalledPSResource "test_module2" -Version "2.5.0-beta" + # $res2.Name | Should -Be "test_module2" + # $res2.Version | Should -Be "2.5.0" + # $res2.Prerelease | Should -Be "beta" + + # $res3 = Get-InstalledPSResource $testModuleName2 + # $res3.Name | Should -Be $testModuleName2 + # $res3.Version | Should -Be "0.0.93" + # } - $res1 = Get-InstalledPSResource $testModuleName - $res1.Name | Should -Be $testModuleName - $res1.Version | Should -Be "3.0.0.0" + # It "Install modules using -RequiredResourceFile with PSD1 file" { + # $rrFilePSD1 = "$psscriptroot/../$RequiredResourcePSD1FileName" - $res2 = Get-InstalledPSResource "test_module2" -Version "2.5.0-beta" - $res2.Name | Should -Be "test_module2" - $res2.Version | Should -Be "2.5.0" - $res2.Prerelease | Should -Be "beta" + # Install-PSResource -RequiredResourceFile $rrFilePSD1 -TrustRepository - $res3 = Get-InstalledPSResource $testModuleName2 - $res3.Name | Should -Be $testModuleName2 - $res3.Version | Should -Be "0.0.93" - } + # $res1 = Get-InstalledPSResource $testModuleName + # $res1.Name | Should -Be $testModuleName + # $res1.Version | Should -Be "3.0.0.0" - It "Install modules using -RequiredResourceFile with JSON file" { - $rrFileJSON = "$psscriptroot/../$RequiredResourceJSONFileName" + # $res2 = Get-InstalledPSResource "test_module2" -Version "2.5.0-beta" + # $res2.Name | Should -Be "test_module2" + # $res2.Version | Should -Be "2.5.0" + # $res2.Prerelease | Should -Be "beta" - Install-PSResource -RequiredResourceFile $rrFileJSON -TrustRepository + # $res3 = Get-InstalledPSResource $testModuleName2 + # $res3.Name | Should -Be $testModuleName2 + # $res3.Version | Should -Be "0.0.93" + # } - $res1 = Get-InstalledPSResource $testModuleName - $res1.Name | Should -Be $testModuleName - $res1.Version | Should -Be "3.0.0.0" + # It "Install modules using -RequiredResourceFile with JSON file" { + # $rrFileJSON = "$psscriptroot/../$RequiredResourceJSONFileName" - $res2 = Get-InstalledPSResource "test_module2" -Version "2.5.0-beta" - $res2.Name | Should -Be "test_module2" - $res2.Version | Should -Be "2.5.0" - $res2.Prerelease | Should -Be "beta" + # Install-PSResource -RequiredResourceFile $rrFileJSON -TrustRepository - $res3 = Get-InstalledPSResource $testModuleName2 - $res3.Name | Should -Be $testModuleName2 - $res3.Version | Should -Be "0.0.93" - } + # $res1 = Get-InstalledPSResource $testModuleName + # $res1.Name | Should -Be $testModuleName + # $res1.Version | Should -Be "3.0.0.0" - # Install module 1.4.3 (is authenticode signed and has catalog file) - # Should install successfully - It "Install modules with catalog file using publisher validation" -Skip:(!(Get-IsWindows)) { - Install-PSResource -Name $PackageManagement -Version "1.4.3" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository + # $res2 = Get-InstalledPSResource "test_module2" -Version "2.5.0-beta" + # $res2.Name | Should -Be "test_module2" + # $res2.Version | Should -Be "2.5.0" + # $res2.Prerelease | Should -Be "beta" - $res1 = Get-InstalledPSResource $PackageManagement -Version "1.4.3" - $res1.Name | Should -Be $PackageManagement - $res1.Version | Should -Be "1.4.3" - } + # $res3 = Get-InstalledPSResource $testModuleName2 + # $res3.Name | Should -Be $testModuleName2 + # $res3.Version | Should -Be "0.0.93" + # } - #TODO: update this test to use something other than PackageManagement. - # Install module 1.4.7 (is authenticode signed and has no catalog file) - # Should not install successfully - It "Install module with no catalog file" -Skip:(!(Get-IsWindows)) { - Install-PSResource -Name $PackageManagement -Version "1.4.7" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository - $res1 = Get-InstalledPSResource $PackageManagement -Version "1.4.7" - $res1.Name | Should -Be $PackageManagement - $res1.Version | Should -Be "1.4.7" - } + # # Install module 1.4.3 (is authenticode signed and has catalog file) + # # Should install successfully + # It "Install modules with catalog file using publisher validation" -Skip:(!(Get-IsWindows)) { + # Install-PSResource -Name $PackageManagement -Version "1.4.3" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository - # Install module that is not authenticode signed - # Should FAIL to install the module - It "Install module that is not authenticode signed" -Skip:(!(Get-IsWindows)) { - Install-PSResource -Name $testModuleName -Version "5.0.0" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository -ErrorVariable err -ErrorAction SilentlyContinue - $err.Count | Should -BeGreaterThan 0 - $err[0].FullyQualifiedErrorId | Should -BeExactly "GetAuthenticodeSignatureError,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource" - } + # $res1 = Get-InstalledPSResource $PackageManagement -Version "1.4.3" + # $res1.Name | Should -Be $PackageManagement + # $res1.Version | Should -Be "1.4.3" + # } + + # #TODO: update this test to use something other than PackageManagement. + # # Install module 1.4.7 (is authenticode signed and has no catalog file) + # # Should not install successfully + # It "Install module with no catalog file" -Skip:(!(Get-IsWindows)) { + # Install-PSResource -Name $PackageManagement -Version "1.4.7" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository + # $res1 = Get-InstalledPSResource $PackageManagement -Version "1.4.7" + # $res1.Name | Should -Be $PackageManagement + # $res1.Version | Should -Be "1.4.7" + # } - # # Install 1.4.4.1 (with incorrect catalog file) + # # Install module that is not authenticode signed # # Should FAIL to install the module - # It "Install module with incorrect catalog file" -Skip:(!(Get-IsWindows)) { - # { Install-PSResource -Name $PackageManagement -Version "1.4.4.1" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository } | Should -Throw -ErrorId "TestFileCatalogError,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource" + # It "Install module that is not authenticode signed" -Skip:(!(Get-IsWindows)) { + # Install-PSResource -Name $testModuleName -Version "5.0.0" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository -ErrorVariable err -ErrorAction SilentlyContinue + # $err.Count | Should -BeGreaterThan 0 + # $err[0].FullyQualifiedErrorId | Should -BeExactly "GetAuthenticodeSignatureError,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource" # } - # Install script that is signed - # Should install successfully - It "Install script that is authenticode signed" -Skip:(!(Get-IsWindows)) { - Install-PSResource -Name "Install-VSCode" -Version "1.4.2" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository + # # # Install 1.4.4.1 (with incorrect catalog file) + # # # Should FAIL to install the module + # # It "Install module with incorrect catalog file" -Skip:(!(Get-IsWindows)) { + # # { Install-PSResource -Name $PackageManagement -Version "1.4.4.1" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository } | Should -Throw -ErrorId "TestFileCatalogError,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource" + # # } - $res1 = Get-InstalledPSResource "Install-VSCode" -Version "1.4.2" - $res1.Name | Should -Be "Install-VSCode" - $res1.Version | Should -Be "1.4.2" - } + # # Install script that is signed + # # Should install successfully + # It "Install script that is authenticode signed" -Skip:(!(Get-IsWindows)) { + # Install-PSResource -Name "Install-VSCode" -Version "1.4.2" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository - # Install script that is not signed - # Should throw and fail to install - It "Install script that is not signed" -Skip:(!(Get-IsWindows)) { - Install-PSResource -Name "TestTestScript" -Version "1.3.1.1" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository -ErrorVariable err -ErrorAction SilentlyContinue - Write-Information -InformationAction Continue -MessageData $err.Count - $err.Count | Should -HaveCount 1 - Write-Information -InformationAction Continue -MessageData $err - Write-Information -InformationAction Continue -MessageData $err[0] - $err[0].FullyQualifiedErrorId | Should -BeExactly "GetAuthenticodeSignatureError,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource" - } + # $res1 = Get-InstalledPSResource "Install-VSCode" -Version "1.4.2" + # $res1.Name | Should -Be "Install-VSCode" + # $res1.Version | Should -Be "1.4.2" + # } + + # # Install script that is not signed + # # Should throw and fail to install + # It "Install script that is not signed" -Skip:(!(Get-IsWindows)) { + # Install-PSResource -Name "TestTestScript" -Version "1.3.1.1" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository -ErrorVariable err -ErrorAction SilentlyContinue + # Write-Information -InformationAction Continue -MessageData $err.Count + # $err.Count | Should -HaveCount 1 + # Write-Information -InformationAction Continue -MessageData $err + # Write-Information -InformationAction Continue -MessageData $err[0] + # $err[0].FullyQualifiedErrorId | Should -BeExactly "GetAuthenticodeSignatureError,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource" + # } # Test that AuthenticodeCheck parameter displays warning on non-Windows It "Install with AuthenticodeCheck on non-Windows should display warning" -Skip:(Get-IsWindows) { - Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -AuthenticodeCheck -WarningVariable warn -WarningAction SilentlyContinue + Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -AuthenticodeCheck -WarningVariable warn -WarningAction SilentlyContinue -Verbose -Debug + Write-Verbose "warning:::: $($warn[0])" $warn[0] | Should -Match "Authenticode check cannot be performed on Linux or MacOS" + $res = Get-InstalledPSResource $testModuleName $res.Name | Should -Be $testModuleName } - # Unix test for installing scripts - It "Install script resource - Unix only" -Skip:(Get-IsWindows) { - # previously installing pester on Unix was throwing an error due to how the environment PATH variable was being gotten. - Install-PSResource -Name "Pester" -Repository $PSGalleryName -Reinstall -TrustRepository - $res = Get-InstalledPSResource "Pester" - $res.Name | Should -Be "Pester" - } + # # Unix test for installing scripts + # It "Install script resource - Unix only" -Skip:(Get-IsWindows) { + # # previously installing pester on Unix was throwing an error due to how the environment PATH variable was being gotten. + # Install-PSResource -Name "Pester" -Repository $PSGalleryName -Reinstall -TrustRepository + # $res = Get-InstalledPSResource "Pester" + # $res.Name | Should -Be "Pester" + # } - It "not install resource and error handle when repository's ApiVersion is ApiVersion.unknown" { - Register-PSResourceRepository -Name "UnknownTypeRepo" -Uri "https://org.MyCompany.com/repository/shared-feed/" -Trusted - $repo = Get-PSResourceRepository -Name "UnknownTypeRepo" - $repo.ApiVersion | Should -Be "unknown" + # It "not install resource and error handle when repository's ApiVersion is ApiVersion.unknown" { + # Register-PSResourceRepository -Name "UnknownTypeRepo" -Uri "https://org.MyCompany.com/repository/shared-feed/" -Trusted + # $repo = Get-PSResourceRepository -Name "UnknownTypeRepo" + # $repo.ApiVersion | Should -Be "unknown" - $res = Install-PSResource -Name "MyPackage" -Repository "UnknownTypeRepo" -ErrorAction SilentlyContinue -ErrorVariable err -PassThru - $err[0].FullyQualifiedErrorId | Should -BeExactly "RepositoryApiVersionUnknown,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource" - $res | Should -BeNullOrEmpty - } + # $res = Install-PSResource -Name "MyPackage" -Repository "UnknownTypeRepo" -ErrorAction SilentlyContinue -ErrorVariable err -PassThru + # $err[0].FullyQualifiedErrorId | Should -BeExactly "RepositoryApiVersionUnknown,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource" + # $res | Should -BeNullOrEmpty + # } - # This is testing FindVersionGlobbing when it creates the 'maxString' variable, - # specifically when the prerelease versions need to be accounted for since that's when PSResourceGet - # modifies the version range to also account for prerelease versions. - # - It "install resource when version has a nine in the digit and -Prerelease parameter is passed in" { - $moduleName = "TestModuleVersionWithNine" - $version = "1.9.9" - Install-PSResource -Name $moduleName -Repository $PSGalleryName -TrustRepository -Version "[$version, $version]" -Prerelease - $res = Get-InstalledPSResource $moduleName - $res | Should -Not -BeNullOrEmpty - $res.Version | Should -Be $version - } + # # This is testing FindVersionGlobbing when it creates the 'maxString' variable, + # # specifically when the prerelease versions need to be accounted for since that's when PSResourceGet + # # modifies the version range to also account for prerelease versions. + # # + # It "install resource when version has a nine in the digit and -Prerelease parameter is passed in" { + # $moduleName = "TestModuleVersionWithNine" + # $version = "1.9.9" + # Install-PSResource -Name $moduleName -Repository $PSGalleryName -TrustRepository -Version "[$version, $version]" -Prerelease + # $res = Get-InstalledPSResource $moduleName + # $res | Should -Not -BeNullOrEmpty + # $res.Version | Should -Be $version + # } - It "Install resource that is unlisted" { - # InstallVersion scenario - # 'test_unlisted' version 0.0.3 is unlisted - $moduleName = 'test_unlisted' - $version = '0.0.3' - Install-PSResource -Name $moduleName -Version $version -Repository $PSGalleryName -TrustRepository - $res = Get-InstalledPSResource $moduleName - $res | Should -Not -BeNullOrEmpty - $res.Version | Should -Be $version - } + # It "Install resource that is unlisted" { + # # InstallVersion scenario + # # 'test_unlisted' version 0.0.3 is unlisted + # $moduleName = 'test_unlisted' + # $version = '0.0.3' + # Install-PSResource -Name $moduleName -Version $version -Repository $PSGalleryName -TrustRepository + # $res = Get-InstalledPSResource $moduleName + # $res | Should -Not -BeNullOrEmpty + # $res.Version | Should -Be $version + # } - It "Install resource that takes a dependency on package with specific version" { - $moduleName = 'test-nugetversion-parent' - $version = '4.0.0' - $depPkgName = 'test-nugetversion' - $depPkgVer = '5.0.1' - - Install-PSResource -Name $moduleName -Version $version -Repository $PSGalleryName -TrustRepository - $res = Get-InstalledPSResource $moduleName - $res.Name | Should -Be $moduleName - $res.Version | Should -Be $version - $depRes = Get-InstalledPSResource $depPkgName - $depRes.Name | Should -Be $depPkgName - $depRes.Version | Should -Be $depPkgVer - } + # It "Install resource that takes a dependency on package with specific version" { + # $moduleName = 'test-nugetversion-parent' + # $version = '4.0.0' + # $depPkgName = 'test-nugetversion' + # $depPkgVer = '5.0.1' + + # Install-PSResource -Name $moduleName -Version $version -Repository $PSGalleryName -TrustRepository + # $res = Get-InstalledPSResource $moduleName + # $res.Name | Should -Be $moduleName + # $res.Version | Should -Be $version + # $depRes = Get-InstalledPSResource $depPkgName + # $depRes.Name | Should -Be $depPkgName + # $depRes.Version | Should -Be $depPkgVer + # } - It "Install resource that takes a dependency on package with specific version with differing normalized and semver versions" { - $moduleName = 'test-pkg-normalized-dependency' - $version = '3.9.2' - $depPkgName1 = "PowerShellGet" - $depPkgName2 = "PackageManagement" - - Install-PSResource -Name $moduleName -Prerelease -Repository $PSGalleryName -TrustRepository - $res = Get-InstalledPSResource $moduleName - $res.Name | Should -Be $moduleName - $res.Version | Should -Be $version - - $depRes = Get-InstalledPSResource $depPkgName1, $depPkgName2 - $depRes.Name | Should -Contain $depPkgName1 - $depRes.Name | Should -Contain $depPkgName2 - } + # It "Install resource that takes a dependency on package with specific version with differing normalized and semver versions" { + # $moduleName = 'test-pkg-normalized-dependency' + # $version = '3.9.2' + # $depPkgName1 = "PowerShellGet" + # $depPkgName2 = "PackageManagement" + + # Install-PSResource -Name $moduleName -Prerelease -Repository $PSGalleryName -TrustRepository + # $res = Get-InstalledPSResource $moduleName + # $res.Name | Should -Be $moduleName + # $res.Version | Should -Be $version + + # $depRes = Get-InstalledPSResource $depPkgName1, $depPkgName2 + # $depRes.Name | Should -Contain $depPkgName1 + # $depRes.Name | Should -Contain $depPkgName2 + # } } -Describe 'Test Install-PSResource for V2 Server scenarios' -tags 'ManualValidationOnly' { - - BeforeAll { - $PSGalleryName = Get-PSGalleryName - $PSGalleryUri = Get-PSGalleryLocation - $NuGetGalleryName = Get-NuGetGalleryName - $testModuleName = "TestModule" - $testModuleName2 = "testModuleWithlicense" - Get-NewPSResourceRepositoryFile - Register-LocalRepos - } - - AfterEach { - Uninstall-PSResource $testModuleName, $testModuleName2 -ErrorAction SilentlyContinue - } - - AfterAll { - Get-RevertPSResourceRepositoryFile - } - - # Unix only manual test - # Expected path should be similar to: '/usr/local/share/powershell/Modules' - It "Install resource under AllUsers scope - Unix only" -Skip:(Get-IsWindows) { - Install-PSResource -Name $testModuleName -Repository $TestGalleryName -Scope AllUsers - $pkg = Get-Module $testModuleName -ListAvailable - $pkg.Name | Should -Be $testModuleName - $pkg.Path.Contains("/usr/") | Should -Be $true - } - - # This needs to be manually tested due to prompt - It "Install resource that requires accept license without -AcceptLicense flag" { - Install-PSResource -Name $testModuleName2 -Repository $TestGalleryName - $pkg = Get-InstalledPSResource $testModuleName2 - $pkg.Name | Should -Be $testModuleName2 - $pkg.Version | Should -Be "0.0.1.0" - } - - # This needs to be manually tested due to prompt - It "Install resource should prompt 'trust repository' if repository is not trusted" { - Set-PSResourceRepository PoshTestGallery -Trusted:$false - - Install-PSResource -Name $testModuleName -Repository $TestGalleryName -Confirm:$false - - $pkg = Get-Module $testModuleName -ListAvailable - $pkg.Name | Should -Be $testModuleName - - Set-PSResourceRepository PoshTestGallery -Trusted - } -} +# Describe 'Test Install-PSResource for V2 Server scenarios' -tags 'ManualValidationOnly' { + +# BeforeAll { +# $PSGalleryName = Get-PSGalleryName +# $PSGalleryUri = Get-PSGalleryLocation +# $NuGetGalleryName = Get-NuGetGalleryName +# $testModuleName = "TestModule" +# $testModuleName2 = "testModuleWithlicense" +# Get-NewPSResourceRepositoryFile +# Register-LocalRepos +# } + +# AfterEach { +# Uninstall-PSResource $testModuleName, $testModuleName2 -ErrorAction SilentlyContinue -SkipDependencyCheck +# } + +# AfterAll { +# Get-RevertPSResourceRepositoryFile +# } + +# # Unix only manual test +# # Expected path should be similar to: '/usr/local/share/powershell/Modules' +# It "Install resource under AllUsers scope - Unix only" -Skip:(Get-IsWindows) { +# Install-PSResource -Name $testModuleName -Repository $TestGalleryName -Scope AllUsers +# $pkg = Get-Module $testModuleName -ListAvailable +# $pkg.Name | Should -Be $testModuleName +# $pkg.Path.Contains("/usr/") | Should -Be $true +# } + +# # This needs to be manually tested due to prompt +# It "Install resource that requires accept license without -AcceptLicense flag" { +# Install-PSResource -Name $testModuleName2 -Repository $TestGalleryName +# $pkg = Get-InstalledPSResource $testModuleName2 +# $pkg.Name | Should -Be $testModuleName2 +# $pkg.Version | Should -Be "0.0.1.0" +# } + +# # This needs to be manually tested due to prompt +# It "Install resource should prompt 'trust repository' if repository is not trusted" { +# Set-PSResourceRepository PoshTestGallery -Trusted:$false + +# Install-PSResource -Name $testModuleName -Repository $TestGalleryName -Confirm:$false + +# $pkg = Get-Module $testModuleName -ListAvailable +# $pkg.Name | Should -Be $testModuleName + +# Set-PSResourceRepository PoshTestGallery -Trusted +# } +# } diff --git a/test/InstallPSResourceTests/InstallPSResourceV3Server.Tests.ps1 b/test/InstallPSResourceTests/InstallPSResourceV3Server.Tests.ps1 index 835e0c0c6..8552bc841 100644 --- a/test/InstallPSResourceTests/InstallPSResourceV3Server.Tests.ps1 +++ b/test/InstallPSResourceTests/InstallPSResourceV3Server.Tests.ps1 @@ -393,7 +393,7 @@ Describe 'Test Install-PSResource for V3Server scenarios' -tags 'CI' { } It "Install module and its dependencies" { - $res = Install-PSResource 'TestModuleWithDependencyE' -Repository $NuGetGalleryName -TrustRepository -PassThru + $res = Install-PSResource 'TestModuleWithDependencyE' -Repository $NuGetGalleryName -TrustRepository -PassThru -Verbose -Debug $res.Length | Should -Be 4 } } diff --git a/test/InstallPSResourceTests/ReleaseTests.ps1 b/test/InstallPSResourceTests/ReleaseTests.ps1 new file mode 100644 index 000000000..11c6bbfff --- /dev/null +++ b/test/InstallPSResourceTests/ReleaseTests.ps1 @@ -0,0 +1,136 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +$modPath = "$psscriptroot/../PSGetTestUtils.psm1" +Import-Module $modPath -Force -Verbose + +$psmodulePaths = $env:PSModulePath -split ';' +Write-Verbose -Verbose "Current module search paths: $psmodulePaths" + + +Describe 'Test Find-PSResource for MAR Repository' -tags 'Release' { + BeforeAll { + Register-PSResourceRepository -Name "MAR" -Uri "https://mcr.microsoft.com" -ApiVersion "ContainerRegistry" + } + + AfterAll { + Unregister-PSResourceRepository -Name "MAR" + } + + It "Should find resource with wildcard in Name" { + $res = Find-PSResource -Name "Az.App*" -Repository "MAR" + $res | Should -Not -BeNullOrEmpty + $res.Count | Should -BeGreaterThan 1 + } + + It "Should find all resource with wildcard in Name" { + $res = Find-PSResource -Name "*" -Repository "MAR" + $res | Should -Not -BeNullOrEmpty + $res.Count | Should -BeGreaterThan 1 + } + +} + + +Describe 'Test Find-PSResource for searching and looping through repositories' -tags 'Release' { + + BeforeAll{ + $testModuleName = "test_module" + $testModuleName2 = "test_module2" + $testCmdDSCParentPkg = "myCmdDSCModule" + $testScriptName = "test_script" + + $tag1 = "CommandsAndResource" + $tag2 = "Tag-Required-Script1-2.5" + + $cmdName = "Get-TargetResource" + $dscName = "SystemLocale" + $tagsEscaped = @("'$tag1'", "'PSCommand_$cmdName'", "'PSDscResource_$dscName'") + + $cmdName2 = "Get-MyCommand" + $dscName2 = "MyDSCResource" + $tagsEscaped2 = @("'PSCommand_$cmdName2'", "'PSDscResource_$dscName2'") + + $PSGalleryName = "PSGallery" + $NuGetGalleryName = "NuGetGallery" + $localRepoName = "localRepo" + + Get-NewPSResourceRepositoryFile + + $localRepoUriAddress = Join-Path -Path $TestDrive -ChildPath "testdir" + $null = New-Item $localRepoUriAddress -ItemType Directory -Force + Register-PSResourceRepository -Name $localRepoName -Uri $localRepoUriAddress + + New-TestModule -moduleName $testModuleName -repoName localRepo -packageVersion "1.0.0" -prereleaseLabel "" -tags $tagsEscaped + New-TestModule -moduleName $testCmdDSCParentPkg -repoName localRepo -packageVersion "1.0.0" -prereleaseLabel "" -tags $tagsEscaped2 + } + + AfterAll { + Get-RevertPSResourceRepositoryFile + } + + It "find resources from all pattern matching repositories where it exists (-Repository with wildcard)" { + # Package with CommandName "Get-TargetResource" exists in the following repositories: PSGallery, localRepo + $res = Find-PSResource -CommandName $cmdName -Repository "*Gallery" -ErrorVariable err -ErrorAction SilentlyContinue + $err | Should -HaveCount 0 + $res.Count | Should -BeGreaterOrEqual 9 + + $pkgFoundFromLocalRepo = $false + $pkgFoundFromPSGallery = $false + + foreach ($pkg in $res) + { + if ($pkg.ParentResource.Repository -eq $localRepoName) + { + $pkgFoundFromLocalRepo = $true + } + elseif ($pkg.ParentResource.Repository -eq $PSGalleryName) + { + $pkgFoundFromPSGallery = $true + } + } + + $pkg.Names | Should -Be $cmdName + $pkg.ParentResource.Includes.Command | Should -Contain $cmdName + $pkgFoundFromLocalRepo | Should -BeFalse + $pkgFoundFromPSGallery | Should -BeTrue + } + +} + + + +Describe 'Test HTTP Find-PSResource for V2 Server Protocol' -tags 'Release' { + + BeforeAll{ + $PSGalleryName = Get-PSGalleryName + $testModuleName = "test_module" + $testScriptName = "test_script" + $commandName = "Get-TargetResource" + $dscResourceName = "SystemLocale" + $parentModuleName = "SystemLocaleDsc" + Get-NewPSResourceRepositoryFile + } + + AfterAll { + Get-RevertPSResourceRepositoryFile + } + + It "find resource given CommandName" { + $res = Find-PSResource -CommandName $commandName -Repository $PSGalleryName + $res | Should -Not -BeNullOrEmpty + foreach ($item in $res) { + $item.Names | Should -Be $commandName + $item.ParentResource.Includes.Command | Should -Contain $commandName + } + } + + It "find resource given DscResourceName" { + $res = Find-PSResource -DscResourceName $dscResourceName -Repository $PSGalleryName + $res | Should -Not -BeNullOrEmpty + foreach ($item in $res) { + $item.Names | Should -Be $dscResourceName + $item.ParentResource.Includes.DscResource | Should -Contain $dscResourceName + } + } +} diff --git a/test/SavePSResourceTests/SavePSResourceV2.Tests.ps1 b/test/SavePSResourceTests/SavePSResourceV2.Tests.ps1 index 4b0269d82..d8e536ffa 100644 --- a/test/SavePSResourceTests/SavePSResourceV2.Tests.ps1 +++ b/test/SavePSResourceTests/SavePSResourceV2.Tests.ps1 @@ -154,7 +154,7 @@ Describe 'Test HTTP Save-PSResource for V2 Server Protocol' -tags 'CI' { It "Save module as a nupkg" { Save-PSResource -Name $testModuleName -Version "1.0.0" -Repository $PSGalleryName -Path $SaveDir -AsNupkg -TrustRepository - $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -EQ "test_module.1.0.0.nupkg" + $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -EQ "test_module.1.0.0.0.nupkg" $pkgDir | Should -Not -BeNullOrEmpty } @@ -199,7 +199,6 @@ Describe 'Test HTTP Save-PSResource for V2 Server Protocol' -tags 'CI' { Save-PSResource -Name $testModuleName -Version "5.0.0" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository -Path $SaveDir -ErrorVariable err -ErrorAction SilentlyContinue $err.Count | Should -BeGreaterThan 0 $err[0].FullyQualifiedErrorId | Should -Contain "GetAuthenticodeSignatureError,Microsoft.PowerShell.PSResourceGet.Cmdlets.SavePSResource" - $err[1].FullyQualifiedErrorId | Should -Contain "InstallPackageFailure,Microsoft.PowerShell.PSResourceGet.Cmdlets.SavePSResource" } # Save resource that requires license diff --git a/test/SavePSResourceTests/SavePSResourceV3.Tests.ps1 b/test/SavePSResourceTests/SavePSResourceV3.Tests.ps1 index f4118d265..73d65ee00 100644 --- a/test/SavePSResourceTests/SavePSResourceV3.Tests.ps1 +++ b/test/SavePSResourceTests/SavePSResourceV3.Tests.ps1 @@ -156,7 +156,8 @@ Describe 'Test HTTP Save-PSResource for V3 Server Protocol' -tags 'CI' { } It "Save module and its dependencies" { - $res = Save-PSResource 'TestModuleWithDependencyE' -Repository $NuGetGalleryName -TrustRepository -PassThru + $DebugPreference = 'Continue' + $res = Save-PSResource 'TestModuleWithDependencyE' -Repository $NuGetGalleryName -TrustRepository -PassThru -Confirm:$false -Debug -Verbose $res.Length | Should -Be 4 } diff --git a/test/UpdatePSResourceTests/UpdatePSResourceLocal.Tests.ps1 b/test/UpdatePSResourceTests/UpdatePSResourceLocal.Tests.ps1 index f5334be83..d947cf3ef 100644 --- a/test/UpdatePSResourceTests/UpdatePSResourceLocal.Tests.ps1 +++ b/test/UpdatePSResourceTests/UpdatePSResourceLocal.Tests.ps1 @@ -27,7 +27,7 @@ Describe 'Test Update-PSResource for local repositories' -tags 'CI' { } AfterEach { - Uninstall-PSResource $moduleName, $moduleName2 -Version "*" + Uninstall-PSResource $moduleName, $moduleName2 -Version "*" -SkipDependencyCheck } AfterAll { diff --git a/test/UpdatePSResourceTests/UpdatePSResourceV2.Tests.ps1 b/test/UpdatePSResourceTests/UpdatePSResourceV2.Tests.ps1 index 07db15035..cd50abcf1 100644 --- a/test/UpdatePSResourceTests/UpdatePSResourceV2.Tests.ps1 +++ b/test/UpdatePSResourceTests/UpdatePSResourceV2.Tests.ps1 @@ -19,7 +19,7 @@ Describe 'Test HTTP Update-PSResource for V2 Server Protocol' -tags 'CI' { } AfterEach { - Uninstall-PSResource "test_module", "TestModule99", "TestModuleWithLicense", "test_module2", "test_script" -Version "*" -ErrorAction SilentlyContinue + Uninstall-PSResource "test_module", "TestModule99", "TestModuleWithLicense", "test_module2", "test_script" -Version "*" -ErrorAction SilentlyContinue -SkipDependencyCheck } AfterAll { diff --git a/test/UpdatePSResourceTests/UpdatePSResourceV3.Tests.ps1 b/test/UpdatePSResourceTests/UpdatePSResourceV3.Tests.ps1 index 88ed957dc..348851728 100644 --- a/test/UpdatePSResourceTests/UpdatePSResourceV3.Tests.ps1 +++ b/test/UpdatePSResourceTests/UpdatePSResourceV3.Tests.ps1 @@ -13,7 +13,7 @@ Describe 'Test HTTP Update-PSResource for V3 Server Protocol' -tags 'CI' { } AfterEach { - Uninstall-PSResource $testModuleName -Version "*" + Uninstall-PSResource $testModuleName -Version "*" -SkipDependencyCheck } AfterAll { Get-RevertPSResourceRepositoryFile