forked from PowerShell/PowerShell
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathInstallPackageProvider.cs
More file actions
369 lines (317 loc) · 16.5 KB
/
Copy pathInstallPackageProvider.cs
File metadata and controls
369 lines (317 loc) · 16.5 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
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
//
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
namespace Microsoft.PowerShell.PackageManagement.Cmdlets
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Management.Automation;
using Microsoft.PackageManagement.Implementation;
using Microsoft.PackageManagement.Internal.Api;
using Microsoft.PackageManagement.Internal.Implementation;
using Microsoft.PackageManagement.Internal.Packaging;
using Microsoft.PackageManagement.Internal.Utility.Extensions;
using Microsoft.PackageManagement.Internal.Utility.Platform;
using Microsoft.PackageManagement.Internal.Utility.Plugin;
using Microsoft.PackageManagement.Packaging;
using Utility;
[Cmdlet("Install", Constants.Nouns.PackageProviderNoun, SupportsShouldProcess = true, DefaultParameterSetName = Constants.ParameterSets.PackageBySearchSet, HelpUri = "https://go.microsoft.com/fwlink/?LinkId=626941")]
public sealed class InstallPackageProvider : CmdletWithSearchAndSource {
private const string FilterOnTag = "FilterOnTag";
private string _scope = "AllUsers";
private List<string> _sourcesFromPipeline = null;
public InstallPackageProvider()
: base(new[] {
OptionCategory.Package, OptionCategory.Install
}) {
ShouldSelectAllProviders = true;
}
protected override IEnumerable<string> ParameterSets {
get {
return new[] {Constants.ParameterSets.PackageBySearchSet, Constants.ParameterSets.PackageByInputObjectSet};
}
}
[Parameter(Position = 0, Mandatory = true, ParameterSetName = Constants.ParameterSets.PackageBySearchSet)]
public override string[] Name {get; set;}
[Parameter(ParameterSetName = Constants.ParameterSets.PackageBySearchSet)]
public override string RequiredVersion {get; set;}
[Parameter(ParameterSetName = Constants.ParameterSets.PackageBySearchSet)]
public override string MinimumVersion {get; set;}
[Parameter(ParameterSetName = Constants.ParameterSets.PackageBySearchSet)]
public override string MaximumVersion {get; set;}
[Parameter(ParameterSetName = Constants.ParameterSets.PackageBySearchSet)]
public override PSCredential Credential {get; set;}
[ValidateSetAttribute("CurrentUser", "AllUsers")]
[Parameter(ParameterSetName = Constants.ParameterSets.PackageBySearchSet)]
[Parameter(ParameterSetName = Constants.ParameterSets.PackageByInputObjectSet)]
public string Scope {
get {
return _scope;
}
set {
_scope = value;
}
}
[Parameter(ValueFromPipelineByPropertyName = true, ParameterSetName = Constants.ParameterSets.PackageBySearchSet)]
public override string[] Source
{
get
{
if(InputObject != null) {
if (_sourcesFromPipeline == null) {
_sourcesFromPipeline = new List<string>();
foreach (var inputObj in InputObject) {
_sourcesFromPipeline.Add(inputObj.Source);
}
}
return _sourcesFromPipeline.ToArray();
}
return base.Source;
}
set
{
base.Source = value;
}
}
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
[Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, ParameterSetName = Constants.ParameterSets.PackageByInputObjectSet)]
public SoftwareIdentity[] InputObject {get; set;}
protected override void GenerateCmdletSpecificParameters(Dictionary<string, object> unboundArguments) {
// only generate these parameters if there is an actual call happening. it won't show in get-command -syntax
if (IsInvocation) {
//Use the InternalPackageManagementInstallOnly flag to indicate we are installing a provider right now.
//This will separate a case from auto-bootstrapping.
var packageManagementService = PackageManagementService as PackageManagementService;
if (packageManagementService != null) {
packageManagementService.InternalPackageManagementInstallOnly = true;
}
}
}
protected override DynamicOption[] CachedDynamicOptions {
get {
//suppress the dynamic parameters from parent classes
return new[] {
new DynamicOption()
};
}
}
protected override string BootstrapNuGet
{
get
{
// Generally bootstrapping NuGet is required for the install-packageprovider as PowerShellGet uses it.
// However, when a user specifies Name as NuGet, e.g., 'install-packageprovider -name NuGet', we do not need to perform a hard
// bootstrap on NuGet because this case has been taken care of already. There is no difference from installing other packages.
return InstallingNugetProvider ? "false" : "true";
}
}
private bool InstallingNugetProvider
{
get
{
return (Name != null) && (Name.Length == 1 && Name.ContainsAnyOfIgnoreCase("NuGet"));
}
}
public override bool ProcessRecordAsync() {
ValidateVersion(RequiredVersion);
ValidateVersion(MinimumVersion);
ValidateVersion(MaximumVersion);
if (!AdminPrivilege.IsElevated && Scope.EqualsIgnoreCase("AllUsers"))
{
var pkgMgmtService = PackageManagementService as PackageManagementService;
if (pkgMgmtService != null)
{
Error(Constants.Errors.InstallRequiresCurrentUserScopeParameterForNonAdminUser, pkgMgmtService.SystemAssemblyLocation, pkgMgmtService.UserAssemblyLocation);
}
return false;
}
if (!string.IsNullOrWhiteSpace(RequiredVersion)) {
if ((!string.IsNullOrWhiteSpace(MaximumVersion)) || (!string.IsNullOrWhiteSpace(MinimumVersion))) {
Error(Constants.Errors.VersionRangeAndRequiredVersionCannotBeSpecifiedTogether);
return false;
}
}
//Error out for the case where multiple provider names with any version specified
if (Name.IsNullOrEmpty()) {
if ((!string.IsNullOrWhiteSpace(RequiredVersion)) || (!string.IsNullOrWhiteSpace(MinimumVersion)) || (!string.IsNullOrWhiteSpace(MaximumVersion))) {
Error(Constants.Errors.MultipleNamesWithVersionNotAllowed);
return false;
}
} else {
if (Name.Any(each => each.ContainsWildcards())) {
Error(Constants.Errors.WildCardCharsAreNotSupported, Name.JoinWithComma());
return false;
}
if ((Name.Length > 1) &&
((!string.IsNullOrWhiteSpace(RequiredVersion)) || (!string.IsNullOrWhiteSpace(MinimumVersion)) || (!string.IsNullOrWhiteSpace(MaximumVersion)))) {
Error(Constants.Errors.MultipleNamesWithVersionNotAllowed);
return false;
}
}
if (IsPackageByObject) {
return InstallPackages(InputObject);
}
// otherwise, just do the search right now.
return base.ProcessRecordAsync();
}
/// <summary>
/// Select existing providers on the system that will be used for searching any package providers in the repositories.
/// </summary>
protected override IEnumerable<PackageProvider> SelectedProviders {
get {
var availableProviders = base.SelectedProviders.ToArray();
if (availableProviders.Any(each => RequiredProviders.ContainsAnyOfIgnoreCase(each.ProviderName))) {
//For 'install-packageprovider NuGet', the PowerShellGet provider does not need to be involved.
//It will causes round trip as the PowerShellGet depends on NuGet. Here we choose the Bootstrap provider only.
return (InstallingNugetProvider)? availableProviders.Where(each => Bootstrap.EqualsIgnoreCase(each.ProviderName)):
availableProviders.Where(each => RequiredProviders.ContainsIgnoreCase(each.ProviderName));
}
Error(Constants.Errors.RegisterPackageSourceRequired, Source.JoinWithComma());
return Enumerable.Empty<PackageProvider>();
}
}
protected override IHostApi GetProviderSpecificOption(PackageProvider pv) {
var host = this.ProviderSpecific(pv);
var host1 = host;
//add filterontag for finding providers. Provider keys are: PackageManagement and Providers
host = host.Extend<IRequest>(
new {
GetOptionValues = new Func<string, IEnumerable<string>>(key => {
if (key.EqualsIgnoreCase(FilterOnTag)) {
return ProviderFilters;
}
return host1.GetOptionValues(key);
}),
});
return host;
}
protected override void ProcessPackage(PackageProvider provider, IEnumerable<string> searchKey, SoftwareIdentity package) {
if (WhatIf) {
// grab the dependencies and return them *first*
foreach (var dep in package.Dependencies) {
// note: future work may be needed if the package sources currently selected by the user don't
// contain the dependencies.
var dependencies = PackageManagementService.FindPackageByCanonicalId(dep, this);
foreach (var depPackage in dependencies) {
ProcessPackage(depPackage.Provider, searchKey.Select(each => each + depPackage.Name).ToArray(), depPackage);
}
}
}
base.ProcessPackage(provider, searchKey, package);
}
protected override bool EnsurePackageIsProvider(SoftwareIdentity package) {
//Make sure the package is a provider package
return ValidatePackageProvider(package);
}
public override bool EndProcessingAsync()
{
if (IsPackageByObject) {
// we should have handled these already.
return ImportProvider(InputObject);
}
var unmatched = _resultsPerName.Keys.Where(each => _resultsPerName[each] == null);
if (unmatched.Any()) {
Error(Constants.Errors.NoMatchFoundForProvider, unmatched.FirstOrDefault());
return false;
}
if (!CheckUnmatchedPackages()) {
// there are unmatched packages
// not going to install.
return false;
}
var list = ProcessMatchedDuplicates().ToArray();
// Now install the provider
if(!base.InstallPackages(list)) {
return false;
}
return ImportProvider(list);
}
private bool ImportProvider(SoftwareIdentity[] list)
{
Verbose("Importing the package provider {0}", Name.JoinWithComma());
//after the provider gets installed, we are trying to load it
var providers = list.SelectMany(each => PackageManagementService.ImportPackageProvider(this.SuppressErrorsAndWarnings(IsProcessing), each.Name, each.Version.ToVersion(), null, null, false, true)).ToArray();
if (providers.Any()) {
Verbose(Resources.Messages.ProviderImported, providers.Select(e => e.ProviderPath).JoinWithComma());
return true;
}
else
{
Warning(Resources.Messages.ProviderNameDifferentFromPackageName, Name.JoinWithComma());
}
return false;
}
private List<SoftwareIdentity> ProcessMatchedDuplicates() {
List<SoftwareIdentity> filteredSoftwareIdentity = new List<SoftwareIdentity>();
var packagetable = _resultsPerName.Values;
foreach (var pkgSet in packagetable) {
if (pkgSet.Count == 0) {
continue;
}
if (pkgSet.Count == 1) {
filteredSoftwareIdentity.AddRange(pkgSet);
continue;
}
// the following are the case (pkgSet.Count > 1)
// there are overmatched packages:
// are they found across multiple providers?
// are they found across multiple sources?
// are they all from the same source?
var providers = pkgSet.Select(each => each.ProviderName).Distinct().ToArray();
var sources = pkgSet.Select(each => each.Source).Distinct().ToArray();
//Handling a case where one provider with multiple sources found the same package
if (providers.Length == 1) {
// it's matching this package multiple times in the same provider.
if (sources.Length == 1) {
string searchKey = null;
foreach (var pkg in pkgSet) {
Warning(Constants.Messages.MatchesMultiplePackages, pkg.SearchKey, pkg.ProviderName, pkg.Name, pkg.Version, pkg.Source);
searchKey = pkg.SearchKey;
}
//found the multiple matches in a single repository, we error out for asking exact -Name and -RequiredVersion.
Error(Constants.Errors.DisambiguateForInstall, searchKey, Resources.Messages.DisambiguateForInstall_SpecifyName);
} else {
//found the multiple sources under one provider matches the same package
foreach (var subset in pkgSet) {
if (Source == null) {
//if a user does not provide -Source AND we are having matches from multiple sources, error out and asking for specifying -Source
Error(Constants.Errors.DisambiguateForInstall, subset.SearchKey, Resources.Messages.DisambiguateForInstall_SpecifySource);
break;
}
//if a user provides -Source, we try to find the match with -Source
if (Source.ContainsAnyOfIgnoreCase(subset.Source)) {
filteredSoftwareIdentity.Add(subset);
break;
}
}
}
} else {
//Handling a case where the multiple providers match one package
//In this case, both bootstrap and PowerShellGet found the same package, let's take PowerShellGet as a precedence
foreach (var subset in pkgSet) {
if (subset.ProviderName.EqualsIgnoreCase(PowerShellGet)) {
//find the match
filteredSoftwareIdentity.Add(subset);
break;
}
}
}
}
return filteredSoftwareIdentity;
}
}
}