Skip to content

Commit ab834e1

Browse files
authored
Add -NoClobber functionality to Install-PSResource (#509)
1 parent 7e4c59c commit ab834e1

File tree

5 files changed

+100
-18
lines changed

5 files changed

+100
-18
lines changed

src/code/InstallHelper.cs

Lines changed: 79 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ internal class InstallHelper : PSCmdlet
4949
private string _specifiedPath;
5050
private bool _asNupkg;
5151
private bool _includeXML;
52+
private bool _noClobber;
53+
List<string> _pathsToSearch;
5254

5355
#endregion
5456

@@ -72,25 +74,24 @@ public void InstallPackages(
7274
bool reinstall,
7375
bool force,
7476
bool trustRepository,
77+
bool noClobber,
7578
PSCredential credential,
76-
string requiredResourceFile,
77-
string requiredResourceJson,
78-
Hashtable requiredResourceHash,
7979
string specifiedPath,
8080
bool asNupkg,
8181
bool includeXML,
8282
List<string> pathsToInstallPkg)
8383
{
8484
_cmdletPassedIn.WriteVerbose(string.Format("Parameters passed in >>> Name: '{0}'; Version: '{1}'; Prerelease: '{2}'; Repository: '{3}'; " +
85-
"AcceptLicense: '{4}'; Quiet: '{5}'; Reinstall: '{6}'; TrustRepository: '{7}';",
85+
"AcceptLicense: '{4}'; Quiet: '{5}'; Reinstall: '{6}'; TrustRepository: '{7}'; NoClobber: '{8}';",
8686
string.Join(",", names),
8787
(versionRange != null ? versionRange.OriginalString : string.Empty),
8888
prerelease.ToString(),
8989
repository != null ? string.Join(",", repository) : string.Empty,
9090
acceptLicense.ToString(),
9191
quiet.ToString(),
9292
reinstall.ToString(),
93-
trustRepository.ToString()));
93+
trustRepository.ToString(),
94+
noClobber.ToString()));
9495

9596
_versionRange = versionRange;
9697
_prerelease = prerelease;
@@ -99,12 +100,28 @@ public void InstallPackages(
99100
_reinstall = reinstall;
100101
_force = force;
101102
_trustRepository = trustRepository;
103+
_noClobber = noClobber;
102104
_credential = credential;
103105
_specifiedPath = specifiedPath;
104106
_asNupkg = asNupkg;
105107
_includeXML = includeXML;
106108
_pathsToInstallPkg = pathsToInstallPkg;
107109

110+
// Create list of installation paths to search.
111+
_pathsToSearch = new List<string>();
112+
113+
// _pathsToInstallPkg will only contain the paths specified within the -Scope param (if applicable)
114+
// _pathsToSearch will contain all resource package subdirectories within _pathsToInstallPkg path locations
115+
// e.g.:
116+
// ./InstallPackagePath1/PackageA
117+
// ./InstallPackagePath1/PackageB
118+
// ./InstallPackagePath2/PackageC
119+
// ./InstallPackagePath3/PackageD
120+
foreach (var path in _pathsToInstallPkg)
121+
{
122+
_pathsToSearch.AddRange(Utils.GetSubDirectories(path));
123+
}
124+
108125
// Go through the repositories and see which is the first repository to have the pkg version available
109126
ProcessRepositories(names, repository, _trustRepository, _credential);
110127
}
@@ -209,7 +226,6 @@ private IEnumerable<PSResourceInfo> FilterByInstalledPkgs(IEnumerable<PSResource
209226
{
210227
// Create list of installation paths to search.
211228
List<string> _pathsToSearch = new List<string>();
212-
GetHelper getHelper = new GetHelper(_cmdletPassedIn);
213229
// _pathsToInstallPkg will only contain the paths specified within the -Scope param (if applicable)
214230
// _pathsToSearch will contain all resource package subdirectories within _pathsToInstallPkg path locations
215231
// e.g.:
@@ -228,6 +244,7 @@ private IEnumerable<PSResourceInfo> FilterByInstalledPkgs(IEnumerable<PSResource
228244
filteredPackages.Add(pkg.Name, pkg);
229245
}
230246

247+
GetHelper getHelper = new GetHelper(_cmdletPassedIn);
231248
// Get currently installed packages.
232249
IEnumerable<PSResourceInfo> pkgsAlreadyInstalled = getHelper.GetPackagesFromPath(
233250
name: filteredPackages.Keys.ToArray(),
@@ -410,6 +427,12 @@ private List<string> InstallPackage(IEnumerable<PSResourceInfo> pkgsToInstall, s
410427
{
411428
continue;
412429
}
430+
431+
// If NoClobber is specified, ensure command clobbering does not happen
432+
if (_noClobber && !DetectClobber(p.Name, tempDirNameVersion, parsedMetadataHashtable))
433+
{
434+
continue;
435+
}
413436
}
414437

415438
// Delete the extra nupkg related files that are not needed and not part of the module/script
@@ -548,6 +571,56 @@ private bool CallAcceptLicense(PSResourceInfo p, string moduleManifest, string t
548571
return success;
549572
}
550573

574+
private bool DetectClobber(string pkgName, string tempDirNameVersion, Hashtable parsedMetadataHashtable)
575+
{
576+
// Get installed modules, then get all possible paths
577+
bool foundClobber = false;
578+
GetHelper getHelper = new GetHelper(_cmdletPassedIn);
579+
IEnumerable<PSResourceInfo> pkgsAlreadyInstalled = getHelper.GetPackagesFromPath(new string[] { "*" }, VersionRange.All, _pathsToSearch);
580+
// user parsed metadata hash
581+
List<string> listOfCmdlets = new List<string>();
582+
foreach (var cmdletName in parsedMetadataHashtable["CmdletsToExport"] as object[])
583+
{
584+
listOfCmdlets.Add(cmdletName as string);
585+
586+
}
587+
588+
foreach (var pkg in pkgsAlreadyInstalled)
589+
{
590+
List<string> duplicateCmdlets = new List<string>();
591+
List<string> duplicateCmds = new List<string>();
592+
// See if any of the cmdlets or commands in the pkg we're trying to install exist within a package that's already installed
593+
if (pkg.Includes.Cmdlet != null && pkg.Includes.Cmdlet.Any())
594+
{
595+
duplicateCmdlets = listOfCmdlets.Where(cmdlet => pkg.Includes.Cmdlet.Contains(cmdlet)).ToList();
596+
597+
}
598+
if (pkg.Includes.Command != null && pkg.Includes.Command.Any())
599+
{
600+
duplicateCmds = listOfCmdlets.Where(commands => pkg.Includes.Command.Contains(commands, StringComparer.InvariantCultureIgnoreCase)).ToList();
601+
}
602+
if (duplicateCmdlets.Any() || duplicateCmds.Any())
603+
{
604+
605+
duplicateCmdlets.AddRange(duplicateCmds);
606+
607+
var errMessage = string.Format(
608+
"The following commands are already available on this system: '{0}'. This module '{1}' may override the existing commands. If you still want to install this module '{1}', remove the -NoClobber parameter.",
609+
String.Join(", ", duplicateCmdlets), pkgName);
610+
611+
var ex = new ArgumentException(errMessage);
612+
var noClobberError = new ErrorRecord(ex, "CommandAlreadyExists", ErrorCategory.ResourceExists, null);
613+
614+
_cmdletPassedIn.WriteError(noClobberError);
615+
foundClobber = true;
616+
617+
return foundClobber;
618+
}
619+
}
620+
621+
return foundClobber;
622+
}
623+
551624
private void CreateMetadataXMLFile(string dirNameVersion, string installPath, string repoName, PSResourceInfo pkg, bool isModule)
552625
{
553626
// Script will have a metadata file similar to: "TestScript_InstalledScriptInfo.xml"

src/code/InstallPSResource.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ class InstallPSResource : PSCmdlet
7070
[Parameter(ParameterSetName = NameParameterSet)]
7171
[Parameter(ParameterSetName = InputObjectParameterSet)]
7272
public SwitchParameter TrustRepository { get; set; }
73-
73+
7474
/// <summary>
7575
/// Overwrites a previously installed resource with the same name and version.
7676
/// </summary>
@@ -92,6 +92,12 @@ class InstallPSResource : PSCmdlet
9292
[Parameter(ParameterSetName = InputObjectParameterSet)]
9393
public SwitchParameter AcceptLicense { get; set; }
9494

95+
/// <summary>
96+
/// Prevents installing a package that contains cmdlets that already exist on the machine.
97+
/// </summary>
98+
[Parameter(ParameterSetName = NameParameterSet)]
99+
public SwitchParameter NoClobber { get; set; }
100+
95101
/// <summary>
96102
/// Used for pipeline input.
97103
/// </summary>
@@ -235,11 +241,9 @@ private void ProcessInstallHelper(InstallHelper installHelper, string[] pkgNames
235241
reinstall: Reinstall,
236242
force: false,
237243
trustRepository: TrustRepository,
244+
noClobber: NoClobber,
238245
credential: Credential,
239-
requiredResourceFile: null,
240-
requiredResourceJson: null,
241-
requiredResourceHash: null,
242-
specifiedPath: null,
246+
specifiedPath: null,
243247
asNupkg: false,
244248
includeXML: true,
245249
pathsToInstallPkg: _pathsToInstallPkg);

src/code/SavePSResource.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,9 +236,7 @@ private void ProcessSaveHelper(InstallHelper installHelper, string[] pkgNames, b
236236
force: false,
237237
trustRepository: TrustRepository,
238238
credential: Credential,
239-
requiredResourceFile: null,
240-
requiredResourceJson: null,
241-
requiredResourceHash: null,
239+
noClobber: false,
242240
specifiedPath: _path,
243241
asNupkg: false,
244242
includeXML: false,

src/code/UpdatePSResource.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,7 @@ protected override void ProcessRecord()
154154
force: Force,
155155
trustRepository: TrustRepository,
156156
credential: Credential,
157-
requiredResourceFile: null,
158-
requiredResourceJson: null,
159-
requiredResourceHash: null,
157+
noClobber: false,
160158
specifiedPath: null,
161159
asNupkg: false,
162160
includeXML: true,

test/InstallPSResource.Tests.ps1

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,16 @@ Describe 'Test Install-PSResource for Module' {
279279
$res.Path.Contains("Modules") | Should -Be $true
280280
}
281281

282-
It "Install PSResourceInfo object piped in" {
282+
It "Install module using -NoClobber, should throw clobber error and not install the module" {
283+
Install-PSResource -Name "ClobberTestModule1" -Repository $TestGalleryName
284+
285+
$res = Get-Module "ClobberTestModule1" -ListAvailable
286+
$res.Name | Should -Be "ClobberTestModule1"
287+
288+
Install-PSResource -Name "ClobberTestModule2" -Repository $TestGalleryName -NoClobber -ErrorAction SilentlyContinue
289+
$Error[0].FullyQualifiedErrorId | Should -be "CommandAlreadyExists,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource"
290+
}
291+
It "Install PSResourceInfo object piped in" {
283292
Find-PSResource -Name $testModuleName -Version "1.1.0.0" -Repository $TestGalleryName | Install-PSResource
284293
$res = Get-InstalledPSResource -Name $testModuleName
285294
$res.Name | Should -Be $testModuleName

0 commit comments

Comments
 (0)