@@ -37,6 +37,7 @@ $SCRIPT:DefaultSource = 'https://pwsh.gallery/index.json'
37
37
38
38
enum InstallScope {
39
39
CurrentUser
40
+ AllUsers
40
41
}
41
42
42
43
@@ -254,54 +255,50 @@ function Install-ModuleFast {
254
255
begin {
255
256
trap {$PSCmdlet.ThrowTerminatingError($PSItem)}
256
257
257
- # Setup the Destination repository
258
- $defaultRepoPath = $(Join-Path ([Environment]::GetFolderPath('LocalApplicationData')) 'powershell/Modules')
259
258
260
- # Get the current PSModulePath
261
- $PSModulePaths = $env:PSModulePath.Split([Path]::PathSeparator, [StringSplitOptions]::RemoveEmptyEntries)
262
259
263
260
#Clear the ModuleFastCache if -Update is specified to ensure fresh lookups of remote module availability
264
261
if ($Update) {
265
262
Clear-ModuleFastCache
266
263
}
267
264
268
- if ($Scope -eq [InstallScope]::CurrentUser) {
269
- $Destination = 'CurrentUser'
265
+ $defaultRepoPath = $(Join-Path ([Environment]::GetFolderPath('LocalApplicationData')) 'powershell/Modules')
266
+ if (-not $Destination) {
267
+ #Special function that will retrieve the default module path for the current user
268
+ $Destination = Get-PSDefaultModulePath -AllUsers:($Scope -eq 'AllUsers')
269
+
270
+ #Special case for Windows to avoid the default installation path because it has issues with OneDrive
271
+ $defaultWindowsModulePath = Join-Path ([Environment]::GetFolderPath('MyDocuments')) 'PowerShell/Modules'
272
+ if ($IsWindows -and $Destination -eq $defaultWindowsModulePath -and $Scope -ne 'CurrentUser') {
273
+ Write-Debug "Windows Documents module folder detected. Changing to $defaultRepoPath"
274
+ $Destination = $defaultRepoPath
275
+ }
270
276
}
277
+
271
278
if (-not $Destination) {
272
- $Destination = $defaultRepoPath
273
- } elseif ($IsWindows -and $Destination -eq 'CurrentUser') {
274
- $windowsDefaultDocumentsPath = Join-Path ([Environment]::GetFolderPath('MyDocuments')) 'PowerShell/Modules'
275
- $Destination = $windowsDefaultDocumentsPath
276
- # if CurrentUser and is on Windows, we do not need to update the PSModulePath or the user profile.
277
- # this allows for a similar experience to Install-Module and Install-PSResource
278
- $NoPSModulePathUpdate = $true
279
- $NoProfileUpdate = $true
279
+ throw 'Failed to determine destination path. This is a bug, please report it, it should always have something by this point.'
280
280
}
281
281
282
- # Autocreate the default as a convenience, otherwise require the path to be present to avoid mistakes
283
- if ($Destination -eq $defaultRepoPath -and -not (Test-Path $Destination)) {
284
- if (Approve-Action 'Create Destination Folder' $Destination) {
282
+ # Require approval to create the destination folder if it is not our default path, otherwise this is automatic
283
+ if (-not (Test-Path $Destination)) {
284
+ if ($configRepoPath -or
285
+ $Destination -eq $defaultRepoPath -or
286
+ (Approve-Action 'Create Destination Folder' $Destination)
287
+ ) {
285
288
New-Item -ItemType Directory -Path $Destination -Force | Out-Null
286
289
}
287
290
}
288
291
289
292
$Destination = Resolve-Path $Destination
290
293
291
294
if (-not $NoPSModulePathUpdate) {
292
- if ($defaultRepoPath -ne $Destination -and $Destination -notin $PSModulePaths) {
293
- Write-Warning 'Parameter -Destination is set to a custom path not in your current PSModulePath. We will add it to your PSModulePath for this session. You can suppress this behavior with the -NoPSModulePathUpdate switch.'
294
- $NoProfileUpdate = $true
295
- }
295
+ # Get the current PSModulePath
296
+ $PSModulePaths = $env:PSModulePath.Split([Path]::PathSeparator, [StringSplitOptions]::RemoveEmptyEntries)
296
297
297
- $addToPathParams = @{
298
- Destination = $Destination
299
- NoProfileUpdate = $NoProfileUpdate
300
- }
301
- if ($PSBoundParameters.ContainsKey('Confirm')) {
302
- $addToPathParams.Confirm = $PSBoundParameters.Confirm
298
+ #Only update if the module path is not already in the PSModulePath
299
+ if ($Destination -notin $PSModulePaths) {
300
+ Add-DestinationToPSModulePath -Destination $Destination -NoProfileUpdate:$NoProfileUpdate -Confirm:$Confirm
303
301
}
304
- Add-DestinationToPSModulePath @addtoPathParams
305
302
}
306
303
307
304
#We want to maintain a single HttpClient for the life of the module. This isn't as big of a deal as it used to be but
@@ -2160,6 +2157,27 @@ function Approve-Action {
2160
2157
return $ThisCmdlet.ShouldProcess($Target, $Action)
2161
2158
}
2162
2159
2160
+ #Fetches the module path for the current user or all users.
2161
+ #HACK: Uses a private API until https://github.com/PowerShell/PowerShell/issues/15552 is resolved
2162
+ function Get-PSDefaultModulePath ([Switch]$AllUsers) {
2163
+ $scopeType = [Management.Automation.Configuration.ConfigScope]
2164
+ $pscType = $scopeType.
2165
+ Assembly.
2166
+ GetType('System.Management.Automation.Configuration.PowerShellConfig')
2167
+
2168
+ $pscInstance = $pscType.
2169
+ GetField('Instance', [Reflection.BindingFlags]'Static,NonPublic').
2170
+ GetValue($null)
2171
+
2172
+ $getModulePathMethod = $pscType.GetMethod('GetModulePath', [Reflection.BindingFlags]'Instance,NonPublic')
2173
+
2174
+ if ($AllUsers) {
2175
+ $getModulePathMethod.Invoke($pscInstance, $scopeType::AllUsers) ?? [Management.Automation.ModuleIntrinsics]::GetPSModulePath('BuiltIn')
2176
+ } else {
2177
+ $getModulePathMethod.Invoke($pscInstance, $scopeType::CurrentUser) ?? [Management.Automation.ModuleIntrinsics]::GetPSModulePath('User')
2178
+ }
2179
+ }
2180
+
2163
2181
#endregion Helpers
2164
2182
2165
2183
### ISSUES
0 commit comments