@@ -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