-
Notifications
You must be signed in to change notification settings - Fork 104
Expand file tree
/
Copy pathGetHelper.cs
More file actions
283 lines (246 loc) · 12.3 KB
/
GetHelper.cs
File metadata and controls
283 lines (246 loc) · 12.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
// 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.Generic;
using System.IO;
using System.Management.Automation;
namespace Microsoft.PowerShell.PSResourceGet.Cmdlets
{
/// <summary>
/// Get helper class provides the core functionality for Get-InstalledPSResource.
/// </summary>
internal class GetHelper
{
#region Members
private readonly PSCmdlet _cmdletPassedIn;
private readonly Dictionary<string, PSResourceInfo> _scriptDictionary;
public const string PSScriptFileExt = ".ps1";
#endregion
#region Constructors
public GetHelper(PSCmdlet cmdletPassedIn)
{
_cmdletPassedIn = cmdletPassedIn;
_scriptDictionary = new Dictionary<string, PSResourceInfo>();
}
#endregion
#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)
{
_cmdletPassedIn.WriteDebug("In GetHelper::GetInstalledPackages()");
foreach (var pkg in pkgs)
{
// Parse Normalized version if present, if not use Version property of the package
NuGetVersion nugetVersion = null;
if (!pkg.AdditionalMetadata.TryGetValue("NormalizedVersion", out string normalizedVersion) ||
!NuGetVersion.TryParse(
value: normalizedVersion,
version: out nugetVersion))
{
if (!NuGetVersion.TryParse(
value: pkg.Version.ToString(),
out nugetVersion))
{
_cmdletPassedIn.WriteVerbose($"Normalized package version '{normalizedVersion}' could not be parsed into NuGetVersion.");
yield break;
}
}
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,
List<string> pathsToSearch,
bool selectPrereleaseOnly)
{
_cmdletPassedIn.WriteDebug("In GetHelper::GetPackagesFromPath()");
List<string> pkgPathsByName = FilterPkgPathsByName(name, pathsToSearch);
foreach (string pkgPath in FilterPkgPathsByVersion(versionRange, pkgPathsByName, selectPrereleaseOnly))
{
PSResourceInfo pkg = OutputPackageObject(pkgPath, _scriptDictionary);
if (pkg != null)
{
yield return pkg;
}
}
}
// Filter packages by user provided name
public List<string> FilterPkgPathsByName(string[] names, List<string> dirsToSearch)
{
_cmdletPassedIn.WriteDebug("In GetHelper::FilterPkgPathsByName()");
List<string> wildCardDirsToSearch = new List<string>();
if (names == null)
{
_cmdletPassedIn.WriteDebug("No names were provided");
return wildCardDirsToSearch;
}
foreach (string name in names)
{
WildcardPattern nameWildCardPattern = new WildcardPattern(name, WildcardOptions.IgnoreCase);
wildCardDirsToSearch.AddRange(dirsToSearch.FindAll(
path => nameWildCardPattern.IsMatch(
GetResourceNameFromPath(path))));
}
return wildCardDirsToSearch;
}
// Filter by user provided version
public IEnumerable<String> FilterPkgPathsByVersion(VersionRange versionRange, List<string> dirsToSearch, bool selectPrereleaseOnly)
{
_cmdletPassedIn.WriteDebug("In GetHelper::FilterPkgPathsByVersion()");
// if no version is specified, just get the latest version
foreach (string pkgPath in dirsToSearch)
{
_cmdletPassedIn.WriteDebug($"Searching through package path: '{pkgPath}'");
// if this is a module directory
if (Directory.Exists(pkgPath))
{
// search modules paths
// ./Modules/Test-Module/1.0.0
// ./Modules/Test-Module/2.0.0
_cmdletPassedIn.WriteDebug($"Searching through package path: '{pkgPath}'");
string[] versionsDirs = Utils.GetSubDirectories(pkgPath);
if (versionsDirs.Length == 0)
{
_cmdletPassedIn.WriteDebug($"No version subdirectories found for path: {pkgPath}");
continue;
}
// sort and reverse to get package versions in descending order to maintain consistency with V2
Array.Sort(versionsDirs);
Array.Reverse(versionsDirs);
foreach (string versionPath in versionsDirs)
{
_cmdletPassedIn.WriteDebug($"Searching through package version path: '{versionPath}'");
if(!Utils.GetVersionForInstallPath(installedPkgPath: versionPath,
isModule: true,
cmdletPassedIn: _cmdletPassedIn,
out NuGetVersion pkgNugetVersion))
{
// skip to next iteration of the loop
continue;
}
_cmdletPassedIn.WriteDebug($"Package version parsed as NuGet version: '{pkgNugetVersion}'");
// For Uninstall-PSResource Prerelease parameter equates to selecting prerelease versions only to uninstall.
// For other cmdlets (Find-PSResource, Install-PSResource) Prerelease parameter equates to selecting stable and prerelease versions.
// We will not just select prerelease versions. For Get-InstalledPSResource, there is no Prerelease parameter.
if (versionRange.Satisfies(pkgNugetVersion))
{
if (!selectPrereleaseOnly || pkgNugetVersion.IsPrerelease)
{
yield return versionPath;
}
}
}
}
else if (File.Exists(pkgPath))
{
// if it's a script
if (versionRange == null || versionRange == VersionRange.All)
{
// yield results then continue with this iteration of the loop
yield return pkgPath;
// We are now done with the current iteration of the for loop because
// only one script version can be installed in a particular script path at a time.
// if looking for all versions and one was found, then we have found all possible versions at that ./Scripts path
// and do not need to parse and check for the version number in the metadata file.
}
else
{
// check to make sure it's within the version range.
// script versions will be parsed from the script xml file
PSResourceInfo scriptInfo = OutputPackageObject(pkgPath, _scriptDictionary);
if(!Utils.GetVersionForInstallPath(installedPkgPath: pkgPath,
isModule: false,
cmdletPassedIn: _cmdletPassedIn,
out NuGetVersion pkgNugetVersion))
{
// skip to next iteration of the loop
yield return pkgPath;
}
_cmdletPassedIn.WriteDebug($"Package version parsed as NuGet version: '{pkgNugetVersion}'");
if (versionRange.Satisfies(pkgNugetVersion))
{
_cmdletPassedIn.WriteDebug($"Version range is satisfied by version '{pkgNugetVersion}'");
_scriptDictionary.Add(pkgPath, scriptInfo);
yield return pkgPath;
}
}
}
}
}
// Create package object for each found resource directory
public PSResourceInfo OutputPackageObject(string pkgPath, Dictionary<string,PSResourceInfo> scriptDictionary)
{
// If the package path is in the deserialized script dictionary, just return that
if (scriptDictionary.ContainsKey(pkgPath))
{
return scriptDictionary[pkgPath];
}
// If the pkgName from pkgpath is a script, find the xml file
string pkgName = Utils.GetInstalledPackageName(pkgPath);
string xmlFilePath;
if (File.Exists(pkgPath))
{
// Package path is a script file
xmlFilePath = System.IO.Path.Combine(
(new DirectoryInfo(pkgPath).Parent).FullName,
"InstalledScriptInfos",
$"{pkgName}_InstalledScriptInfo.xml");
}
else
{
// Otherwise assume it's a module, and look for the xml path that way
xmlFilePath = System.IO.Path.Combine(pkgPath, "PSGetModuleInfo.xml");
}
// Read metadata from XML and parse into PSResourceInfo object
_cmdletPassedIn.WriteDebug($"Reading package metadata from: '{xmlFilePath}'");
if (PSResourceInfo.TryRead(xmlFilePath, out PSResourceInfo psGetInfo, out string errorMsg))
{
return psGetInfo;
}
_cmdletPassedIn.WriteVerbose($"Reading metadata for package '{pkgName}' failed with error: '{errorMsg}'");
return null;
}
#endregion
#region Private methods
private static string GetResourceNameFromPath(string path)
{
// Resource paths may end in a directory or script file name.
// Directory name is the same as the resource name.
// Script file name is the resource name without the file extension.
// ./Modules/Microsoft.PowerShell.Test-Module : Microsoft.PowerShell.Test-Module
// ./Scripts/Microsoft.PowerShell.Test-Script.ps1 : Microsoft.PowerShell.Test-Script
var resourceName = Path.GetFileName(path);
return Path.GetExtension(resourceName).Equals(PSScriptFileExt, StringComparison.OrdinalIgnoreCase)
? Path.GetFileNameWithoutExtension(resourceName) : resourceName;
}
#endregion
}
}