diff --git a/src/code/FindHelper.cs b/src/code/FindHelper.cs index a2a14fa72..36fc0b092 100644 --- a/src/code/FindHelper.cs +++ b/src/code/FindHelper.cs @@ -2,14 +2,16 @@ // Licensed under the MIT License. using Microsoft.PowerShell.PSResourceGet.UtilClasses; +using NuGet.Protocol.Core.Types; using NuGet.Versioning; using System; using System.Collections.Generic; using System.Linq; using System.Management.Automation; using System.Net; -using System.Runtime.ExceptionServices; +using System.Security.Cryptography; using System.Threading; +using System.Threading.Tasks; namespace Microsoft.PowerShell.PSResourceGet.Cmdlets { @@ -34,7 +36,12 @@ internal class FindHelper private bool _includeDependencies = false; private bool _repositoryNameContainsWildcard = true; private NetworkCredential _networkCredential; + + // TODO: Update to be concurrency safe; TryAdd needs to be updates as well + // TODO: look at # of allocations (lists, etc.) private Dictionary> _packagesFound; + private HashSet _knownLatestPkgVersion; + List depPkgsFound; #endregion @@ -48,6 +55,7 @@ public FindHelper(CancellationToken cancellationToken, PSCmdlet cmdletPassedIn, _cmdletPassedIn = cmdletPassedIn; _networkCredential = networkCredential; _packagesFound = new Dictionary>(StringComparer.OrdinalIgnoreCase); + _knownLatestPkgVersion = new HashSet(StringComparer.OrdinalIgnoreCase); } #endregion @@ -240,7 +248,7 @@ public IEnumerable FindByResourceName( this)); } } -} + } public IEnumerable FindByCommandOrDscResource( bool isSearchingForCommands, @@ -1037,66 +1045,96 @@ 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) { - if (currentPkg.Dependencies.Length > 0) + depPkgsFound = new List(); + FindDependencyPackagesHelper(currentServer, currentResponseUtil, currentPkg, repository); + + foreach (var pkg in depPkgsFound) { - foreach (var dep in currentPkg.Dependencies) + if (!_packagesFound.ContainsKey(pkg.Name)) + { + TryAddToPackagesFound(pkg); + yield return pkg; + } + else { - PSResourceInfo depPkg = null; + List pkgVersions = _packagesFound[pkg.Name]; + // _packagesFound has item.name in it, but the version is not the same + if (!pkgVersions.Contains(FormatPkgVersionString(pkg))) + { + TryAddToPackagesFound(pkg); + + yield return pkg; + } + } + } + } - if (dep.VersionRange.Equals(VersionRange.All)) + // Method 2 + internal void FindDependencyPackagesHelper(ServerApiCall currentServer, ResponseUtil currentResponseUtil, PSResourceInfo currentPkg, PSRepositoryInfo repository) + { + List errors = new List(); + if (currentPkg.Dependencies.Length > 0) + { + // If installing more than 5 packages, do so concurrently + if (currentPkg.Dependencies.Length > 5) + { + Parallel.ForEach(currentPkg.Dependencies, new ParallelOptions { MaxDegreeOfParallelism = 32 }, dep => { - 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; - } + // Console.WriteLine($"FindDependencyPackages Processing number: {dep}, Thread ID: {Task.CurrentId}"); + FindDependencyPackageVersion(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; - } + }); - 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; - } + // todo: write any errors here + } + else + { + foreach (var dep in currentPkg.Dependencies) + { + FindDependencyPackageVersion(dep, currentServer, currentResponseUtil, currentPkg, repository, errors); + } - depPkg = currentResult.returnedObject; + // todo write out errors here + } + } + } - if (!_packagesFound.ContainsKey(depPkg.Name)) + // Method 3 + private void FindDependencyPackageVersion(Dependency dep, ServerApiCall currentServer, ResponseUtil currentResponseUtil, PSResourceInfo currentPkg, PSRepositoryInfo repository, List errors) + { + PSResourceInfo depPkg = null; + if (dep.VersionRange.Equals(VersionRange.All) || !dep.VersionRange.HasUpperBound) + { + FindDependencyWithNoUpperBound(dep, currentServer, currentResponseUtil, currentPkg, repository, errors); + } + else if (dep.VersionRange.MinVersion.Equals(dep.VersionRange.MaxVersion)) + { + FindResults responses = currentServer.FindVersion(dep.Name, dep.VersionRange.MaxVersion.ToString(), _type, out ErrorRecord errRecord); + if (errRecord != null) + { + errors = ProcessErrorRecord(errRecord, errors); + } + 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), + "DependencyPackageNotFound", + ErrorCategory.ObjectNotFound, + this)); + } + else + { + depPkg = currentResult.returnedObject; + if (!depPkgsFound.Contains(depPkg)) { - foreach (PSResourceInfo depRes in FindDependencyPackages(currentServer, currentResponseUtil, depPkg, repository)) - { - yield return depRes; - } + // add pkg then find dependencies + depPkgsFound.Add(depPkg); + FindDependencyPackagesHelper(currentServer, currentResponseUtil, depPkg, repository); } else { @@ -1104,58 +1142,53 @@ internal IEnumerable FindDependencyPackages( // _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; - } + // // Console.WriteLine("Before min version FindDependencyPackagesHelper 2"); + + // add pkg then find dependencies + // for now depPkgsFound.Add(depPkg); + // for now depPkgsFound.AddRange(FindDependencyPackagesHelper(currentServer, currentResponseUtil, depPkg, repository)); + // // Console.WriteLine("After min version FindDependencyPackagesHelper 2"); + } } } - else - { - 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; - } + } + } + else + { + FindResults responses = currentServer.FindVersionGlobbing(dep.Name, dep.VersionRange, includePrerelease: true, ResourceType.None, getOnlyLatest: true, out ErrorRecord errRecord); + if (errRecord != null) + { + errors = ProcessErrorRecord(errRecord, errors); + } - if (responses.IsFindResultsEmpty()) + if (responses.IsFindResultsEmpty()) + { + errors.Add(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)); + } + else + { + foreach (PSResourceResult currentResult in currentResponseUtil.ConvertToPSResourceResult(responses)) + { + if (currentResult == null || currentResult.exception != null && !currentResult.exception.Message.Equals(string.Empty)) { - _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; + 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), + "DependencyPackageNotFound", + ErrorCategory.ObjectNotFound, + this)); } - - foreach (PSResourceResult currentResult in currentResponseUtil.ConvertToPSResourceResult(responses)) + else { - 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)); - - yield return null; - continue; - } - // Check to see if version falls within version range PSResourceInfo foundDep = currentResult.returnedObject; string depVersionStr = $"{foundDep.Version}"; - if (foundDep.IsPrerelease) { + if (foundDep.IsPrerelease) + { depVersionStr += $"-{foundDep.Prerelease}"; } @@ -1163,56 +1196,173 @@ internal IEnumerable FindDependencyPackages( && dep.VersionRange.Satisfies(depVersion)) { depPkg = foundDep; + break; } } + } - if (depPkg == null) + if (depPkg == null) + { + // // Console.WriteLine($"depPkg is null and I don't know what this means"); + } + else + { + if (!depPkgsFound.Contains(depPkg)) { - continue; - } + // // Console.WriteLine($"PackagesFound contains {depPkg.Name}"); - if (!_packagesFound.ContainsKey(depPkg.Name)) - { - foreach (PSResourceInfo depRes in FindDependencyPackages(currentServer, currentResponseUtil, depPkg, repository)) - { - yield return depRes; - } + // add pkg then find dependencies + depPkgsFound.Add(depPkg); + FindDependencyPackagesHelper(currentServer, currentResponseUtil, depPkg, repository); } - else { + 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; - } + // // Console.WriteLine($"pkgVersions does not contain {FormatPkgVersionString(depPkg)}"); + // add pkg then find dependencies + // for now depPkgsFound.Add(depPkg); + // for now depPkgsFound.AddRange(FindDependencyPackagesHelper(currentServer, currentResponseUtil, depPkg, repository)); } } } } } + } - if (!_packagesFound.ContainsKey(currentPkg.Name)) + // Method 4 + private PSResourceInfo FindDependencyWithNoUpperBound(Dependency dep, ServerApiCall currentServer, ResponseUtil currentResponseUtil, PSResourceInfo currentPkg, PSRepositoryInfo repository, List errors) + { + PSResourceInfo depPkg = null; + // _knownLatestPkgVersion will tell us if we already have the latest version available for a particular package. + if (!_knownLatestPkgVersion.Contains(dep.Name)) { - TryAddToPackagesFound(currentPkg); + FindResults responses = currentServer.FindName(dep.Name, includePrerelease: true, _type, out ErrorRecord errRecord); + if (errRecord != null) + { + if (errRecord.Exception is ResourceNotFoundException) + { + errors.Add(new ErrorRecord( + new ResourceNotFoundException($"Dependency package with name '{dep.Name}' could not be found in repository '{repository.Name}': {errRecord.Exception.Message}"), + "DependencyPackageNotFound", + ErrorCategory.ObjectNotFound, + this)); + } + else + { + // todo add error here? + } + + return depPkg; + } + + if (responses == null) + { + // todo error + return depPkg; + } - yield return currentPkg; + PSResourceResult currentResult = currentResponseUtil.ConvertToPSResourceResult(responses).FirstOrDefault(); + if (currentResult == null) + { + // This scenario may occur when the package version requested is unlisted. + errors.Add(new ErrorRecord( + new ResourceNotFoundException($"Dependency package with name '{dep.Name}' could not be found in repository '{repository.Name}'"), + "DependencyPackageNotFound", + ErrorCategory.ObjectNotFound, + this)); + + return depPkg; + } + else if (currentResult.exception != null && !currentResult.exception.Message.Equals(string.Empty)) + { + errors.Add(new ErrorRecord( + new ResourceNotFoundException($"Dependency package with name '{dep.Name}' could not be found in repository '{repository.Name}'", currentResult.exception), + "DependencyPackageNotFound", + ErrorCategory.ObjectNotFound, + this)); + + return depPkg; + } + + depPkg = currentResult.returnedObject; + if (!depPkgsFound.Contains(depPkg)) + { + _knownLatestPkgVersion.Add(depPkg.Name); + + // add pkg then find dependencies + depPkgsFound.Add(depPkg); + FindDependencyPackagesHelper(currentServer, currentResponseUtil, depPkg, repository); + } + 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))) + { + // // Console.WriteLine("Before recursive FindDependencyPackagesHelper 2"); + + // add pkg then find dependencies + // for now depPkgsFound.Add(depPkg); + // for now depPkgsFound.AddRange(FindDependencyPackagesHelper(currentServer, currentResponseUtil, depPkg, repository)); + // // Console.WriteLine("After recursive FindDependencyPackagesHelper 2"); + + } + } + } + + return depPkg; + } + + + private List ProcessErrorRecord(ErrorRecord errRecord, List errors) + { + if (errRecord.Exception is ResourceNotFoundException) + { + 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))) - { - TryAddToPackagesFound(currentPkg); + errors.Add(new ErrorRecord( + new ResourceNotFoundException($"Dependency package could not be found: '{errRecord.Exception.Message}'"), + "DependencyPackageNotFound", + ErrorCategory.ObjectNotFound, + this)); + } - yield return currentPkg; - } + return errors; + } + + private List ProcessPSResourceResult(PSResourceResult currentResult, List errors) + { + if (currentResult == null) + { + // This scenario may occur when the package version requested is unlisted. + errors.Add(new ErrorRecord( + new ResourceNotFoundException($"Dependency package with name '{currentResult.returnedObject.Name}' could not be found in repository '{currentResult.returnedObject.Repository}'."), + "DependencyPackageNotFound", + ErrorCategory.ObjectNotFound, + this)); + } + else if (currentResult.exception != null && !currentResult.exception.Message.Equals(string.Empty)) + { + errors.Add(new ErrorRecord( + new ResourceNotFoundException($"Dependency package with name '{currentResult.returnedObject.Name}' could not be found in repository '{currentResult.returnedObject.Repository}'", currentResult.exception), + "DependencyPackageNotFound", + ErrorCategory.ObjectNotFound, + this)); } + return errors; } - #endregion + #endregion } } diff --git a/src/code/InstallHelper.cs b/src/code/InstallHelper.cs index cd86fe180..f8cade695 100644 --- a/src/code/InstallHelper.cs +++ b/src/code/InstallHelper.cs @@ -2,10 +2,12 @@ // 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.Generic; +using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; @@ -15,6 +17,7 @@ using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System.Threading; +using System.Threading.Tasks; namespace Microsoft.PowerShell.PSResourceGet.Cmdlets { @@ -437,7 +440,7 @@ private void MoveFilesIntoInstallPath( string moduleManifestVersion, string scriptPath) { - _cmdletPassedIn.WriteDebug("In InstallHelper::MoveFilesIntoInstallPath()"); + //_cmdletPassedIn.WriteDebug("In InstallHelper::MoveFilesIntoInstallPath()"); // Creating the proper installation path depending on whether pkg is a module or script var newPathParent = isModule ? Path.Combine(installPath, pkgInfo.Name) : installPath; var finalModuleVersionDir = isModule ? Path.Combine(installPath, pkgInfo.Name, moduleManifestVersion) : installPath; @@ -446,32 +449,32 @@ private void MoveFilesIntoInstallPath( var tempModuleVersionDir = (!isModule || isLocalRepo) ? dirNameVersion : Path.Combine(tempInstallPath, pkgInfo.Name.ToLower(), newVersion); - _cmdletPassedIn.WriteVerbose($"Installation source path is: '{tempModuleVersionDir}'"); - _cmdletPassedIn.WriteVerbose($"Installation destination path is: '{finalModuleVersionDir}'"); + // _cmdletPassedIn.WriteVerbose($"Installation source path is: '{tempModuleVersionDir}'"); + // _cmdletPassedIn.WriteVerbose($"Installation destination path is: '{finalModuleVersionDir}'"); if (isModule) { // If new path does not exist if (!Directory.Exists(newPathParent)) { - _cmdletPassedIn.WriteVerbose($"Attempting to move '{tempModuleVersionDir}' to '{finalModuleVersionDir}'"); + // _cmdletPassedIn.WriteVerbose($"Attempting to move '{tempModuleVersionDir}' to '{finalModuleVersionDir}'"); Directory.CreateDirectory(newPathParent); Utils.MoveDirectory(tempModuleVersionDir, finalModuleVersionDir); } else { - _cmdletPassedIn.WriteVerbose($"Temporary module version directory is: '{tempModuleVersionDir}'"); + // _cmdletPassedIn.WriteVerbose($"Temporary module version directory is: '{tempModuleVersionDir}'"); if (Directory.Exists(finalModuleVersionDir)) { // 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); } - _cmdletPassedIn.WriteVerbose($"Attempting to move '{tempModuleVersionDir}' to '{finalModuleVersionDir}'"); + // _cmdletPassedIn.WriteVerbose($"Attempting to move '{tempModuleVersionDir}' to '{finalModuleVersionDir}'"); Utils.MoveDirectory(tempModuleVersionDir, finalModuleVersionDir); } } @@ -497,27 +500,27 @@ private void MoveFilesIntoInstallPath( if (!Directory.Exists(scriptInfoFolderPath)) { - _cmdletPassedIn.WriteVerbose($"Created '{scriptInfoFolderPath}' path for scripts"); + // _cmdletPassedIn.WriteVerbose($"Created '{scriptInfoFolderPath}' path for scripts"); Directory.CreateDirectory(scriptInfoFolderPath); } // Need to delete old xml files because there can only be 1 per script - _cmdletPassedIn.WriteVerbose(string.Format("Checking if path '{0}' exists: '{1}'", scriptXmlFilePath, File.Exists(scriptXmlFilePath))); + // _cmdletPassedIn.WriteVerbose(string.Format("Checking if path '{0}' exists: '{1}'", scriptXmlFilePath, File.Exists(scriptXmlFilePath))); if (File.Exists(scriptXmlFilePath)) { - _cmdletPassedIn.WriteVerbose("Deleting script metadata XML"); + // _cmdletPassedIn.WriteVerbose("Deleting script metadata XML"); File.Delete(Path.Combine(scriptInfoFolderPath, scriptXML)); } - _cmdletPassedIn.WriteVerbose(string.Format("Moving '{0}' to '{1}'", Path.Combine(dirNameVersion, scriptXML), Path.Combine(installPath, "InstalledScriptInfos", scriptXML))); + // _cmdletPassedIn.WriteVerbose(string.Format("Moving '{0}' to '{1}'", Path.Combine(dirNameVersion, scriptXML), Path.Combine(installPath, "InstalledScriptInfos", scriptXML))); Utils.MoveFiles(Path.Combine(dirNameVersion, scriptXML), Path.Combine(installPath, "InstalledScriptInfos", scriptXML)); // Need to delete old script file, if that exists - _cmdletPassedIn.WriteVerbose(string.Format("Checking if path '{0}' exists: ", File.Exists(Path.Combine(finalModuleVersionDir, pkgInfo.Name + PSScriptFileExt)))); + // _cmdletPassedIn.WriteVerbose(string.Format("Checking if path '{0}' exists: ", File.Exists(Path.Combine(finalModuleVersionDir, pkgInfo.Name + PSScriptFileExt)))); if (File.Exists(Path.Combine(finalModuleVersionDir, pkgInfo.Name + PSScriptFileExt))) { - _cmdletPassedIn.WriteVerbose("Deleting script file"); + // _cmdletPassedIn.WriteVerbose("Deleting script file"); File.Delete(Path.Combine(finalModuleVersionDir, pkgInfo.Name + PSScriptFileExt)); } } @@ -528,7 +531,7 @@ private void MoveFilesIntoInstallPath( } } - _cmdletPassedIn.WriteVerbose(string.Format("Moving '{0}' to '{1}'", scriptPath, Path.Combine(finalModuleVersionDir, pkgInfo.Name + PSScriptFileExt))); + // _cmdletPassedIn.WriteVerbose(string.Format("Moving '{0}' to '{1}'", scriptPath, Path.Combine(finalModuleVersionDir, pkgInfo.Name + PSScriptFileExt))); Utils.MoveFiles(scriptPath, Path.Combine(finalModuleVersionDir, pkgInfo.Name + PSScriptFileExt)); } } @@ -572,10 +575,11 @@ private List InstallPackages( currentServer: currentServer, currentResponseUtil: currentResponseUtil, tempInstallPath: tempInstallPath, + skipDependencyCheck: skipDependencyCheck, packagesHash: new Hashtable(StringComparer.InvariantCultureIgnoreCase), 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")) @@ -640,6 +644,7 @@ private List InstallPackages( currentServer: currentServer, currentResponseUtil: currentResponseUtil, tempInstallPath: tempInstallPath, + skipDependencyCheck: skipDependencyCheck, packagesHash: packagesHash, errRecord: out ErrorRecord installPkgErrRecord); @@ -716,6 +721,7 @@ private Hashtable BeginPackageInstall( ServerApiCall currentServer, ResponseUtil currentResponseUtil, string tempInstallPath, + bool skipDependencyCheck, Hashtable packagesHash, out ErrorRecord errRecord) { @@ -723,6 +729,7 @@ private Hashtable BeginPackageInstall( FindResults responses = null; errRecord = null; + // Find the parent package that needs to be installed switch (searchVersionType) { case VersionType.VersionRange: @@ -751,6 +758,7 @@ private Hashtable BeginPackageInstall( default: // VersionType.NoVersion responses = currentServer.FindName(pkgNameToInstall, _prerelease, ResourceType.None, out ErrorRecord findNameErrRecord); + if (findNameErrRecord != null) { errRecord = findNameErrRecord; @@ -760,6 +768,7 @@ private Hashtable BeginPackageInstall( break; } + // Convert parent package to PSResourceInfo PSResourceInfo pkgToInstall = null; foreach (PSResourceResult currentResult in currentResponseUtil.ConvertToPSResourceResult(responses)) { @@ -805,6 +814,7 @@ private Hashtable BeginPackageInstall( if (pkgVersion == null) { pkgVersion = pkgToInstall.Version.ToString(); } + // Check to see if the pkg is already installed (ie the pkg is installed and the version satisfies the version range provided via param) if (!_reinstall) { @@ -863,25 +873,150 @@ private Hashtable BeginPackageInstall( } else { - // Download the package. + // Concurrently Updates + // Find all dependencies string pkgName = pkgToInstall.Name; - Stream responseStream = currentServer.InstallPackage(pkgName, pkgVersion, _prerelease, out ErrorRecord installNameErrRecord); - if (installNameErrRecord != null) + if (!skipDependencyCheck) { - errRecord = installNameErrRecord; + List allDependencies = FindAllDependencies(currentServer, currentResponseUtil, pkgToInstall, repository); + + return InstallParentAndDependencyPackages(pkgToInstall, allDependencies, currentServer, tempInstallPath, packagesHash, updatedPackagesHash, pkgToInstall); + } + else { + + // TODO: check this version and prerelease combo + Stream responseStream = currentServer.InstallPackage(pkgToInstall.Name, pkgToInstall.Version.ToString(), true, out ErrorRecord installNameErrRecord); + + if (installNameErrRecord != null) + { + errRecord = installNameErrRecord; + return packagesHash; + } + + 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 errRecord); + if (!installedToTempPathSuccessfully) + { + return packagesHash; + } + } + } + + return updatedPackagesHash; + } + + // Concurrency Updates + private List FindAllDependencies(ServerApiCall currentServer, ResponseUtil currentResponseUtil, PSResourceInfo pkgToInstall, PSRepositoryInfo repository) + { + if (currentServer.Repository.ApiVersion == PSRepositoryInfo.APIVersion.V3) + { + _cmdletPassedIn.WriteWarning("Installing dependencies is not currently supported for V3 server protocol repositories. The package will be installed without installing dependencies."); + } + + var findHelper = new FindHelper(_cancellationToken, _cmdletPassedIn, _networkCredential); + _cmdletPassedIn.WriteDebug($"Finding dependency packages for '{pkgToInstall.Name}'"); + + // the last package added will be the parent package. + List allDependencies = findHelper.FindDependencyPackages(currentServer, currentResponseUtil, pkgToInstall, repository).ToList(); + + // allDependencies contains parent package as well + foreach (PSResourceInfo pkg in allDependencies) + { + // Console.WriteLine($"{pkg.Name}: {pkg.Version}"); + } + + return allDependencies; + } + + // Concurrently Updates + private Hashtable InstallParentAndDependencyPackages(PSResourceInfo parentPkg, List allDependencies, ServerApiCall currentServer, string tempInstallPath, Hashtable packagesHash, Hashtable updatedPackagesHash, PSResourceInfo pkgToInstall) + { + List errors = new List(); + // If installing more than 5 packages, do so concurrently + if (allDependencies.Count > 5) + { + // Set the maximum degree of parallelism to 32 (Invoke-Command has default of 32, that's where we got this number from) + Parallel.ForEach(allDependencies, new ParallelOptions { MaxDegreeOfParallelism = 32 }, depPkg => + { + var depPkgName = depPkg.Name; + var depPkgVersion = depPkg.Version.ToString(); + // Console.WriteLine($"Processing number: {depPkg}, Thread ID: {Task.CurrentId}"); + + Stream responseStream = currentServer.InstallPackage(depPkgName, depPkgVersion, true, out ErrorRecord installNameErrRecord); + if (installNameErrRecord != null) + { + errors.Add(installNameErrRecord); + } + + 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 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; } - 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); + // Install parent package + Stream responseStream = currentServer.InstallPackage(parentPkg.Name, parentPkg.Version.ToString(), true, out ErrorRecord installNameErrRecord); + if (installNameErrRecord != null) + { + _cmdletPassedIn.WriteError(installNameErrRecord); + return packagesHash; + } + ErrorRecord tempSaveErrRecord = null, tempInstallErrRecord = null; + bool installedToTempPathSuccessfully = _asNupkg ? TrySaveNupkgToTempPath(responseStream, tempInstallPath, parentPkg.Name, parentPkg.Version.ToString(), pkgToInstall, packagesHash, out updatedPackagesHash, out tempSaveErrRecord) : + TryInstallToTempPath(responseStream, tempInstallPath, parentPkg.Name, parentPkg.Version.ToString(), pkgToInstall, packagesHash, out updatedPackagesHash, out tempInstallErrRecord); if (!installedToTempPathSuccessfully) { + _cmdletPassedIn.WriteError(tempSaveErrRecord ?? tempInstallErrRecord); return packagesHash; } + + return updatedPackagesHash; } + else + { + // Install the good old fashioned way + // Make sure to install dependencies first, then install parent pkg + allDependencies.Add(parentPkg); + foreach (var pkgToBeInstalled in allDependencies) + { + var pkgToInstallName = pkgToBeInstalled.Name; + var pkgToInstallVersion = pkgToBeInstalled.Version.ToString(); + Stream responseStream = currentServer.InstallPackage(pkgToInstallName, pkgToInstallVersion, true, out ErrorRecord installNameErrRecord); + if (installNameErrRecord != null) + { + _cmdletPassedIn.WriteError(installNameErrRecord); + return packagesHash; + } - return updatedPackagesHash; + 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 tempInstallErrRecord); + + if (!installedToTempPathSuccessfully) + { + _cmdletPassedIn.WriteError(tempSaveErrRecord ?? tempInstallErrRecord); + return packagesHash; + } + } + + return updatedPackagesHash; + } } /// @@ -947,7 +1082,7 @@ private bool TryInstallToTempPath( out Hashtable updatedPackagesHash, out ErrorRecord error) { - _cmdletPassedIn.WriteDebug("In InstallHelper::TryInstallToTempPath()"); + //_cmdletPassedIn.WriteDebug("In InstallHelper::TryInstallToTempPath()"); error = null; updatedPackagesHash = packagesHash; try @@ -1047,7 +1182,7 @@ private bool TryInstallToTempPath( { foreach (ErrorRecord parseError in parseScriptFileErrors) { - _cmdletPassedIn.WriteError(parseError); + // _cmdletPassedIn.WriteError(parseError); } error = new ErrorRecord( @@ -1064,7 +1199,7 @@ 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}."); + //_cmdletPassedIn.WriteVerbose($"This resource is not a PowerShell package and will be installed to the modules path: {installPath}."); isModule = true; } @@ -1124,7 +1259,7 @@ private bool TrySaveNupkgToTempPath( out Hashtable updatedPackagesHash, out ErrorRecord error) { - _cmdletPassedIn.WriteDebug("In InstallHelper::TrySaveNupkgToTempPath()"); + // _cmdletPassedIn.WriteDebug("In InstallHelper::TrySaveNupkgToTempPath()"); error = null; updatedPackagesHash = packagesHash; @@ -1246,7 +1381,7 @@ private bool TryExtractToDirectory(string zipPath, string extractPath, out Error /// private bool TryMoveInstallContent(string tempInstallPath, ScopeType scope, Hashtable packagesHash) { - _cmdletPassedIn.WriteDebug("In InstallHelper::TryMoveInstallContent()"); + //_cmdletPassedIn.WriteDebug("In InstallHelper::TryMoveInstallContent()"); foreach (string pkgName in packagesHash.Keys) { Hashtable pkgInfo = packagesHash[pkgName] as Hashtable; @@ -1271,7 +1406,7 @@ private bool TryMoveInstallContent(string tempInstallPath, ScopeType scope, Hash moduleManifestVersion: pkgVersion, scriptPath); - _cmdletPassedIn.WriteVerbose($"Successfully installed package '{pkgName}' to location '{installPath}'"); + //_cmdletPassedIn.WriteVerbose($"Successfully installed package '{pkgName}' to location '{installPath}'"); if (!_savePkg && isScript) { @@ -1290,20 +1425,20 @@ private bool TryMoveInstallContent(string tempInstallPath, ScopeType scope, Hash if (!String.IsNullOrEmpty(envPATHVarValue) && !envPATHVarValue.Contains(installPath) && !envPATHVarValue.Contains(installPathwithBackSlash)) { - _cmdletPassedIn.WriteWarning(String.Format(ScriptPATHWarning, scope, installPath)); + //_cmdletPassedIn.WriteWarning(String.Format(ScriptPATHWarning, scope, installPath)); } } } - catch (Exception e) + catch (Exception) { - _cmdletPassedIn.WriteError(new ErrorRecord( + /*_cmdletPassedIn.WriteError(new ErrorRecord( new PSInvalidOperationException( message: $"Unable to successfully install package '{pkgName}': '{e.Message}'", innerException: e), "InstallPackageFailed", ErrorCategory.InvalidOperation, _cmdletPassedIn)); - + */ return false; } } @@ -1316,7 +1451,7 @@ 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()"); + //_cmdletPassedIn.WriteDebug("In InstallHelper::CallAcceptLicense()"); error = null; var requireLicenseAcceptance = false; var success = true; @@ -1398,7 +1533,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; @@ -1462,7 +1597,7 @@ 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()"); + //_cmdletPassedIn.WriteDebug("In InstallHelper::CreateMetadataXMLFile()"); error = null; bool success = true; // Script will have a metadata file similar to: "TestScript_InstalledScriptInfo.xml" @@ -1492,7 +1627,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"); @@ -1505,22 +1640,22 @@ private void DeleteExtraneousFiles(string packageName, string dirNameVersion) if (File.Exists(nuspecToDelete)) { - _cmdletPassedIn.WriteDebug($"Deleting '{nuspecToDelete}'"); + //_cmdletPassedIn.WriteDebug($"Deleting '{nuspecToDelete}'"); File.Delete(nuspecToDelete); } if (File.Exists(contentTypesToDelete)) { - _cmdletPassedIn.WriteDebug($"Deleting '{contentTypesToDelete}'"); + //_cmdletPassedIn.WriteDebug($"Deleting '{contentTypesToDelete}'"); File.Delete(contentTypesToDelete); } if (Directory.Exists(relsDirToDelete)) { - _cmdletPassedIn.WriteDebug($"Deleting '{relsDirToDelete}'"); + //_cmdletPassedIn.WriteDebug($"Deleting '{relsDirToDelete}'"); Utils.DeleteDirectory(relsDirToDelete); } if (Directory.Exists(packageDirToDelete)) { - _cmdletPassedIn.WriteDebug($"Deleting '{packageDirToDelete}'"); + //_cmdletPassedIn.WriteDebug($"Deleting '{packageDirToDelete}'"); Utils.DeleteDirectory(packageDirToDelete); } } diff --git a/src/code/InstallPSResource.cs b/src/code/InstallPSResource.cs index a65797268..f92a56574 100644 --- a/src/code/InstallPSResource.cs +++ b/src/code/InstallPSResource.cs @@ -286,6 +286,7 @@ protected override void ProcessRecord() pkgRepository: Repository, pkgCredential: Credential, reqResourceParams: null); + break; case InputObjectParameterSet: diff --git a/src/code/ServerApiCall.cs b/src/code/ServerApiCall.cs index 9f8bf4fe9..13e274fb1 100644 --- a/src/code/ServerApiCall.cs +++ b/src/code/ServerApiCall.cs @@ -10,7 +10,6 @@ using System.Text; using System.Runtime.ExceptionServices; using System.Management.Automation; -using System; namespace Microsoft.PowerShell.PSResourceGet.Cmdlets { diff --git a/src/code/Utils.cs b/src/code/Utils.cs index e2dc8406c..8d6292257 100644 --- a/src/code/Utils.cs +++ b/src/code/Utils.cs @@ -1223,37 +1223,85 @@ 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; + + // Set the created runspace as the default for the current thread + Runspace.DefaultRunspace = runspace; + + using (System.Management.Automation.PowerShell pwsh = System.Management.Automation.PowerShell.Create()) { - result = psObject.BaseObject; - } + pwsh.Runspace = runspace; - dataFileInfo = (Hashtable) result; - error = null; - return true; + 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; + } + } + runspace.Close(); + + return false; } catch (Exception ex) { - dataFileInfo = null; error = ex; return false; } diff --git a/src/code/V2ServerAPICalls.cs b/src/code/V2ServerAPICalls.cs index e2f7b9440..a2f51a1fb 100644 --- a/src/code/V2ServerAPICalls.cs +++ b/src/code/V2ServerAPICalls.cs @@ -337,7 +337,7 @@ public override FindResults FindCommandOrDscResource(string[] tags, bool include /// public override FindResults FindName(string packageName, bool includePrerelease, ResourceType type, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In V2ServerAPICalls::FindName()"); + //_cmdletPassedIn.WriteDebug("In V2ServerAPICalls::FindName()"); // Make sure to include quotations around the package name // This should return the latest stable version or the latest prerelease version (respectively) @@ -623,7 +623,7 @@ public override FindResults FindVersionGlobbing(string packageName, VersionRange /// public override FindResults FindVersion(string packageName, string version, ResourceType type, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In V2ServerAPICalls::FindVersion()"); + //_cmdletPassedIn.WriteDebug("In V2ServerAPICalls::FindVersion()"); // 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. @@ -653,7 +653,7 @@ public override FindResults FindVersion(string packageName, string version, Reso } int count = GetCountFromResponse(response, out errRecord); - _cmdletPassedIn.WriteDebug($"Count from response is '{count}'"); + //_cmdletPassedIn.WriteDebug($"Count from response is '{count}'"); if (errRecord != null) { @@ -767,13 +767,13 @@ public override Stream InstallPackage(string packageName, string packageVersion, /// private string HttpRequestCall(string requestUrlV2, out ErrorRecord errRecord) { - _cmdletPassedIn.WriteDebug("In V2ServerAPICalls::HttpRequestCall()"); + //_cmdletPassedIn.WriteDebug("In V2ServerAPICalls::HttpRequestCall()"); errRecord = null; string response = string.Empty; try { - _cmdletPassedIn.WriteDebug($"Request url is '{requestUrlV2}'"); + // _cmdletPassedIn.WriteDebug($"Request url is '{requestUrlV2}'"); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrlV2); response = SendV2RequestAsync(request, _sessionClient).GetAwaiter().GetResult(); @@ -813,7 +813,7 @@ private string HttpRequestCall(string requestUrlV2, out ErrorRecord errRecord) if (string.IsNullOrEmpty(response)) { - _cmdletPassedIn.WriteDebug("Response is empty"); + // _cmdletPassedIn.WriteDebug("Response is empty"); } return response; @@ -824,13 +824,13 @@ private string HttpRequestCall(string requestUrlV2, out ErrorRecord errRecord) /// 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(); @@ -862,7 +862,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; @@ -1195,7 +1195,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 @@ -1301,7 +1301,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) @@ -1402,7 +1402,7 @@ 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"); + //_cmdletPassedIn.WriteDebug($"Property 'count' and 'd:Id' could not be found in response. This may indicate that the package could not be found"); } } }