Skip to content

Fixes for use of IEnumerable and dependent module re-install issues #728

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jul 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 98 additions & 86 deletions src/code/FindHelper.cs

Large diffs are not rendered by default.

35 changes: 12 additions & 23 deletions src/code/FindPSResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,24 +217,19 @@ private void ProcessResourceNameParameterSet()
return;
}

List<PSResourceInfo> foundPackages = new List<PSResourceInfo>();

foreach (PSResourceInfo package in _findHelper.FindByResourceName(
Name,
Type,
Version,
Prerelease,
Tag,
Repository,
Credential,
IncludeDependencies))
{
foundPackages.Add(package);
}
List<PSResourceInfo> foundPackages = _findHelper.FindByResourceName(
name: Name,
type: Type,
version: Version,
prerelease: Prerelease,
tag: Tag,
repository: Repository,
credential: Credential,
includeDependencies: IncludeDependencies);

foreach (var uniquePackageVersion in foundPackages.GroupBy(
m => new {m.Name, m.Version, m.Repository}).Select(
group => group.First()).ToList())
group => group.First()))
{
WriteObject(uniquePackageVersion);
}
Expand Down Expand Up @@ -292,21 +287,15 @@ private void ProcessCommandOrDscParameterSet(bool isSearchingForCommands)
moduleNamesToSearch = new string[] {"*"};
}

List<PSResourceInfo> foundPackages = new List<PSResourceInfo>();

foreach (PSResourceInfo package in _findHelper.FindByResourceName(
List<PSResourceInfo> foundPackages = _findHelper.FindByResourceName(
name: moduleNamesToSearch,
// provide type so Scripts endpoint for PSGallery won't be searched
type: isSearchingForCommands? ResourceType.Command : ResourceType.DscResource,
version: Version,
prerelease: Prerelease,
tag: Tag,
repository: Repository,
credential: Credential,
includeDependencies: IncludeDependencies))
{
foundPackages.Add(package);
}
includeDependencies: IncludeDependencies);

// if a single package contains multiple commands we are interested in, return a unique entry for each:
// Command1 , PackageA
Expand Down
38 changes: 38 additions & 0 deletions src/code/GetHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,44 @@ public GetHelper(PSCmdlet cmdletPassedIn)

#region Public methods

/// <summary>
/// Retrieves package paths from provided search paths for installed packages
/// by name *and* version.
/// </summary>
public IEnumerable<PSResourceInfo> GetInstalledPackages(
IEnumerable<PSResourceInfo> pkgs,
List<string> pathsToSearch)
{
foreach (var pkg in pkgs)
{
// Filter on specific version.
var nugetVersion = new NuGetVersion(pkg.Version);
var pkgVersionRange = new VersionRange(
minVersion: nugetVersion,
includeMinVersion: true,
maxVersion: nugetVersion,
includeMaxVersion: true);

// Search by package name.
var foundPkgPaths = FilterPkgPathsByName(
names: new string[] { pkg.Name },
pathsToSearch);

// Filter by package version.
foreach (var pkgPath in FilterPkgPathsByVersion(
versionRange: pkgVersionRange,
dirsToSearch: foundPkgPaths,
selectPrereleaseOnly: false))
{
PSResourceInfo returnPkg = OutputPackageObject(pkgPath, _scriptDictionary);
if (returnPkg != null)
{
yield return returnPkg;
}
}
}
}

public IEnumerable<PSResourceInfo> GetPackagesFromPath(
string[] name,
VersionRange versionRange,
Expand Down
6 changes: 3 additions & 3 deletions src/code/GetPSResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,15 +127,15 @@ protected override void ProcessRecord()
this));
}

// this catches the case where Name wasn't passed in as null or empty,
// but after filtering out unsupported wildcard names in BeginProcessing() there are no elements left in Name
// This catches the case where Name wasn't passed in as null or empty,
// but after filtering out unsupported wildcard names in BeginProcessing() there are no elements left in Name.
if (namesToSearch.Length == 0)
{
return;
}

// SelectPrereleaseOnly is false because we want both stable and prerelease versions all the time..
GetHelper getHelper = new GetHelper(this);
// selectPrereleaseOnly is false because we want both stable and prerelease versions all the time.
foreach (PSResourceInfo pkg in getHelper.GetPackagesFromPath(
name: namesToSearch,
versionRange: _versionRange,
Expand Down
84 changes: 42 additions & 42 deletions src/code/InstallHelper.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Microsoft.PowerShell.Commands;
using Microsoft.PowerShell.PowerShellGet.UtilClasses;
using MoreLinq.Extensions;
using NuGet.Common;
Expand All @@ -15,7 +14,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.IO;
using System.Linq;
Expand Down Expand Up @@ -192,20 +190,19 @@ private List<PSResourceInfo> ProcessRepositories(
var isLocalRepo = repo.Uri.AbsoluteUri.StartsWith(Uri.UriSchemeFile + Uri.SchemeDelimiter, StringComparison.OrdinalIgnoreCase);

// Finds parent packages and dependencies
IEnumerable<PSResourceInfo> pkgsFromRepoToInstall = findHelper.FindByResourceName(
List<PSResourceInfo> pkgsFromRepoToInstall = findHelper.FindByResourceName(
name: _pkgNamesToInstall.ToArray(),
type: ResourceType.None,
version: _versionRange != null ? _versionRange.OriginalString : null,
version: _versionRange?.OriginalString,
prerelease: _prerelease,
tag: null,
repository: new string[] { repoName },
credential: credential,
includeDependencies: !skipDependencyCheck);

if (!pkgsFromRepoToInstall.Any())
if (pkgsFromRepoToInstall.Count == 0)
{
_cmdletPassedIn.WriteVerbose(string.Format("None of the specified resources were found in the '{0}' repository.", repoName));
// Check in the next repository
continue;
}

Expand All @@ -225,7 +222,7 @@ private List<PSResourceInfo> ProcessRepositories(
pkgsFromRepoToInstall = FilterByInstalledPkgs(pkgsFromRepoToInstall);
}

if (!pkgsFromRepoToInstall.Any())
if (pkgsFromRepoToInstall.Count is 0)
{
continue;
}
Expand Down Expand Up @@ -261,72 +258,75 @@ private List<PSResourceInfo> ProcessRepositories(
}

// Check if any of the pkg versions are already installed, if they are we'll remove them from the list of packages to install
private IEnumerable<PSResourceInfo> FilterByInstalledPkgs(IEnumerable<PSResourceInfo> packages)
private List<PSResourceInfo> FilterByInstalledPkgs(List<PSResourceInfo> packages)
{
// Create list of installation paths to search.
List<string> _pathsToSearch = new List<string>();
// _pathsToInstallPkg will only contain the paths specified within the -Scope param (if applicable)
// _pathsToSearch will contain all resource package subdirectories within _pathsToInstallPkg path locations
// Package install paths.
// _pathsToInstallPkg will only contain the paths specified within the -Scope param (if applicable).
// _pathsToSearch will contain all resource package subdirectories within _pathsToInstallPkg path locations.
// e.g.:
// ./InstallPackagePath1/PackageA
// ./InstallPackagePath1/PackageB
// ./InstallPackagePath2/PackageC
// ./InstallPackagePath3/PackageD
foreach (var path in _pathsToInstallPkg)
{
_pathsToSearch.AddRange(Utils.GetSubDirectories(path));
}

var filteredPackages = new Dictionary<string, PSResourceInfo>();
foreach (var pkg in packages)
// Get currently installed packages.
var getHelper = new GetHelper(_cmdletPassedIn);
var installedPackageNames = new HashSet<string>(StringComparer.CurrentCultureIgnoreCase);
foreach (var installedPkg in getHelper.GetInstalledPackages(
pkgs: packages,
pathsToSearch: _pathsToSearch))
{
filteredPackages.Add(pkg.Name, pkg);
installedPackageNames.Add(installedPkg.Name);
}

GetHelper getHelper = new GetHelper(_cmdletPassedIn);
// Get currently installed packages.
// selectPrereleaseOnly is false because even if Prerelease is true we want to include both stable and prerelease, never select prerelease only.
IEnumerable<PSResourceInfo> pkgsAlreadyInstalled = getHelper.GetPackagesFromPath(
name: filteredPackages.Keys.ToArray(),
versionRange: _versionRange,
pathsToSearch: _pathsToSearch,
selectPrereleaseOnly: false);
if (!pkgsAlreadyInstalled.Any())
if (installedPackageNames.Count is 0)
{
return packages;
}

// Remove from list package versions that are already installed.
foreach (PSResourceInfo pkg in pkgsAlreadyInstalled)
// Return only packages that are not already installed.
var filteredPackages = new List<PSResourceInfo>();
foreach (var pkg in packages)
{
_cmdletPassedIn.WriteWarning(
string.Format("Resource '{0}' with version '{1}' is already installed. If you would like to reinstall, please run the cmdlet again with the -Reinstall parameter",
pkg.Name,
pkg.Version));
if (!installedPackageNames.Contains(pkg.Name))
{
// Add packages that still need to be installed.
filteredPackages.Add(pkg);
}
else
{
// Remove from tracking list of packages to install.
_cmdletPassedIn.WriteWarning(
string.Format("Resource '{0}' with version '{1}' is already installed. If you would like to reinstall, please run the cmdlet again with the -Reinstall parameter",
pkg.Name,
pkg.Version));

filteredPackages.Remove(pkg.Name);
_pkgNamesToInstall.RemoveAll(x => x.Equals(pkg.Name, StringComparison.InvariantCultureIgnoreCase));
_pkgNamesToInstall.RemoveAll(x => x.Equals(pkg.Name, StringComparison.InvariantCultureIgnoreCase));
}
}

return filteredPackages.Values.ToArray();
return filteredPackages;
}

/// <summary>
/// Install provided list of packages, which include Dependent packages if requested.
/// </summary>
private List<PSResourceInfo> InstallPackage(
IEnumerable<PSResourceInfo> pkgsToInstall, // those found to be required to be installed (includes Dependency packages as well)
List<PSResourceInfo> pkgsToInstall,
string repoName,
string repoUri,
PSCredentialInfo repoCredentialInfo,
PSCredential credential,
bool isLocalRepo)
{
List<PSResourceInfo> pkgsSuccessfullyInstalled = new List<PSResourceInfo>();
int totalPkgs = pkgsToInstall.Count();
int totalPkgs = pkgsToInstall.Count;

// Counters for tracking current package out of total
int totalInstalledPkgCount = 0;
int currentInstalledPkgCount = 0;
foreach (PSResourceInfo pkg in pkgsToInstall)
{
totalInstalledPkgCount++;
currentInstalledPkgCount++;
var tempInstallPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
try
{
Expand All @@ -344,7 +344,7 @@ private List<PSResourceInfo> InstallPackage(
if (!_quiet)
{
int activityId = 0;
int percentComplete = ((totalInstalledPkgCount * 100) / totalPkgs);
int percentComplete = ((currentInstalledPkgCount * 100) / totalPkgs);
string activity = string.Format("Installing {0}...", pkg.Name);
string statusDescription = string.Format("{0}% Complete", percentComplete);
_cmdletPassedIn.WriteProgress(
Expand Down
16 changes: 12 additions & 4 deletions src/code/PublishPSResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,6 @@ protected override void EndProcessing()
try
{
Utils.ValidateModuleManifest(resourceFilePath, out errorMsgs);

}
finally {
if (errorMsgs.Length > 0)
Expand Down Expand Up @@ -885,9 +884,17 @@ private bool CheckDependenciesExist(Hashtable dependencies, string repositoryNam
FindHelper findHelper = new FindHelper(_cancellationToken, this);
bool depPrerelease = depVersion.Contains("-");

var repository = new[] { repositoryName };
var dependencyFound = findHelper.FindByResourceName(depName, ResourceType.Module, depVersion, depPrerelease, null, repository, Credential, false);
if (dependencyFound == null || !dependencyFound.Any())
var foundDependencies = findHelper.FindByResourceName(
name: depName,
type: ResourceType.Module,
version: depVersion,
prerelease: depPrerelease,
tag: null,
repository: new[] { repositoryName },
credential: Credential,
includeDependencies: false);

if (foundDependencies.Count is 0)
{
var message = String.Format("Dependency '{0}' was not found in repository '{1}'. Make sure the dependency is published to the repository before publishing this module.", dependency, repositoryName);
var ex = new ArgumentException(message);
Expand All @@ -897,6 +904,7 @@ private bool CheckDependenciesExist(Hashtable dependencies, string repositoryNam
return false;
}
}

return true;
}

Expand Down
2 changes: 1 addition & 1 deletion test/FindPSResource.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ Describe 'Test Find-PSResource for Module' {
) {
param($Version, $Description)

$res = Find-PSResource -Name $testModuleName -Version $Version -Repository $PSGalleryName
$res = Find-PSResource -Name $testModuleName -Version $Version -Repository $PSGalleryName 2>$null
$res | Should -BeNullOrEmpty
}

Expand Down
2 changes: 1 addition & 1 deletion test/UpdatePSResource.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ Describe 'Test Update-PSResource' {
param($Version, $Description)

Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository
Update-PSResource -Name $testModuleName -Version $Version -Repository $PSGalleryName -TrustRepository
Update-PSResource -Name $testModuleName -Version $Version -Repository $PSGalleryName -TrustRepository 2>$null

$res = Get-PSResource -Name $testModuleName
$isPkgUpdated = $false
Expand Down