From d292e1656c1fcacf23d80ff693b955c6de4dfd33 Mon Sep 17 00:00:00 2001 From: Dominik Viererbe Date: Thu, 10 Jul 2025 20:45:05 +0300 Subject: [PATCH 1/3] Use a seperate path for all-users profiles. Currently the all-users profiles are stored in the installation directory. Unix-based systems and derivatives mostly follow recommendations of the Filesystem Hierarchy Standard [FHS]. The FHS recommends to place configuration files into the /etc directory. For example, here are the locations of the system wide configuration files of other popular shells: - bash(1): /etc/bash.bashrc (Debian/Ubuntu) or /etc/bashrc (RedHat/Fedora) - zsh(1): /etc/zshrc - fish(1): /etc/fish/config.fish I took inspiration from where NuGet is locating its system-wide configuration, see [NuGet config file locations]: This commit introduces the environment variable `POWERSHELL_COMMON_APPLICATION_DATA`. If this variable is neither null nor empty, then this location is used. This allows distros that do not follow the FHS to customize the location. On Windows PowerShell keeps using the installation directory. On macOS PowerShell uses the `/Library/Application Support/PowerShell` directory. Otherwise PowerShell uses `/etc/opt/powershell`. [FHS]: https://refspecs.linuxfoundation.org/FHS_3.0/fhs/index.html [NuGet config file locations]: https://learn.microsoft.com/en-us/nuget/consume-packages/configuring-nuget-behavior#config-file-locations-and-uses Fixes: #20336, #25007 Signed-off-by: Dominik Viererbe --- .../engine/hostifaces/HostUtilities.cs | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs b/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs index 003625791b1..f49a31b669f 100644 --- a/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs +++ b/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs @@ -233,16 +233,27 @@ internal static string GetFullProfileFileName(string shellId, bool forCurrentUse /// The base path for all users profiles. private static string GetAllUsersFolderPath(string shellId) { - string folderPath = string.Empty; - try + string folderPath = Environment.GetEnvironmentVariable("POWERSHELL_COMMON_APPLICATION_DATA"); + if (!string.IsNullOrEmpty(folderPath)) return folderPath; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - folderPath = Utils.GetApplicationBase(shellId); + try + { + return Utils.GetApplicationBase(shellId); + } + catch (System.Security.SecurityException) + { + return string.Empty; + } } - catch (System.Security.SecurityException) + + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { + return "/Library/Application Support/PowerShell"; } - return folderPath; + return "/etc/opt/powershell"; } #endregion GetProfileCommands From c1bba18d33d89dae7c8a3e830fc1962b888b783c Mon Sep 17 00:00:00 2001 From: Dominik Viererbe Date: Fri, 8 Aug 2025 13:40:29 +0300 Subject: [PATCH 2/3] Fix minor capitalization inconsistency "IO.Path" instead of "io.path" Signed-off-by: Dominik Viererbe --- test/powershell/Host/Base-Directory.Tests.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/powershell/Host/Base-Directory.Tests.ps1 b/test/powershell/Host/Base-Directory.Tests.ps1 index 203d214e937..4e44d8ef929 100644 --- a/test/powershell/Host/Base-Directory.Tests.ps1 +++ b/test/powershell/Host/Base-Directory.Tests.ps1 @@ -19,12 +19,12 @@ Describe "Configuration file locations" -tags "CI","Slow" { } $expectedCache = [IO.Path]::Combine($env:LOCALAPPDATA, "Microsoft", "Windows", "PowerShell", "StartupProfileData-NonInteractive") $expectedModule = [IO.Path]::Combine($env:USERPROFILE, "Documents", $ProductName, "Modules") - $expectedProfile = [io.path]::Combine($env:USERPROFILE, "Documents", $ProductName, $profileName) + $expectedProfile = [IO.Path]::Combine($env:USERPROFILE, "Documents", $ProductName, $profileName) $expectedReadline = [IO.Path]::Combine($env:AppData, "Microsoft", "Windows", "PowerShell", "PSReadline", "ConsoleHost_history.txt") } else { $expectedCache = [IO.Path]::Combine($env:HOME, ".cache", "powershell", "StartupProfileData-NonInteractive") $expectedModule = [IO.Path]::Combine($env:HOME, ".local", "share", "powershell", "Modules") - $expectedProfile = [io.path]::Combine($env:HOME,".config","powershell",$profileName) + $expectedProfile = [IO.Path]::Combine($env:HOME,".config","powershell",$profileName) $expectedReadline = [IO.Path]::Combine($env:HOME, ".local", "share", "powershell", "PSReadLine", "ConsoleHost_history.txt") } From 00393a651f2d60b123262ad0c033422c7713591b Mon Sep 17 00:00:00 2001 From: Dominik Viererbe Date: Fri, 8 Aug 2025 14:54:07 +0300 Subject: [PATCH 3/3] Add testcases for all users profile Signed-off-by: Dominik Viererbe --- test/powershell/Host/Base-Directory.Tests.ps1 | 37 ++++++++++++++----- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/test/powershell/Host/Base-Directory.Tests.ps1 b/test/powershell/Host/Base-Directory.Tests.ps1 index 4e44d8ef929..ed249ceb957 100644 --- a/test/powershell/Host/Base-Directory.Tests.ps1 +++ b/test/powershell/Host/Base-Directory.Tests.ps1 @@ -17,15 +17,23 @@ Describe "Configuration file locations" -tags "CI","Slow" { { $ProductName = "PowerShell" } - $expectedCache = [IO.Path]::Combine($env:LOCALAPPDATA, "Microsoft", "Windows", "PowerShell", "StartupProfileData-NonInteractive") - $expectedModule = [IO.Path]::Combine($env:USERPROFILE, "Documents", $ProductName, "Modules") - $expectedProfile = [IO.Path]::Combine($env:USERPROFILE, "Documents", $ProductName, $profileName) - $expectedReadline = [IO.Path]::Combine($env:AppData, "Microsoft", "Windows", "PowerShell", "PSReadline", "ConsoleHost_history.txt") + $expectedCache = [IO.Path]::Combine($env:LOCALAPPDATA, "Microsoft", "Windows", "PowerShell", "StartupProfileData-NonInteractive") + $expectedModule = [IO.Path]::Combine($env:USERPROFILE, "Documents", $ProductName, "Modules") + $expectedAllUsersProfile = [IO.Path]::Combine($PSHOME, $profileName) + $expectedCurrentUserProfile = [IO.Path]::Combine($env:USERPROFILE, "Documents", $ProductName, $profileName) + $expectedReadline = [IO.Path]::Combine($env:AppData, "Microsoft", "Windows", "PowerShell", "PSReadline", "ConsoleHost_history.txt") } else { - $expectedCache = [IO.Path]::Combine($env:HOME, ".cache", "powershell", "StartupProfileData-NonInteractive") - $expectedModule = [IO.Path]::Combine($env:HOME, ".local", "share", "powershell", "Modules") - $expectedProfile = [IO.Path]::Combine($env:HOME,".config","powershell",$profileName) - $expectedReadline = [IO.Path]::Combine($env:HOME, ".local", "share", "powershell", "PSReadLine", "ConsoleHost_history.txt") + if ($IsMacOS) { + $PowershellConfigRoot = "/Library/Application Support/PowerShell" + } else { + $PowershellConfigRoot = "/etc/opt/powershell" + } + + $expectedCache = [IO.Path]::Combine($env:HOME, ".cache", "powershell", "StartupProfileData-NonInteractive") + $expectedModule = [IO.Path]::Combine($env:HOME, ".local", "share", "powershell", "Modules") + $expectedAllUsersProfile = [IO.Path]::Combine($PowershellConfigRoot, $profileName) + $expectedCurrentUserProfile = [IO.Path]::Combine($env:HOME,".config","powershell", $profileName) + $expectedReadline = [IO.Path]::Combine($env:HOME, ".local", "share", "powershell", "PSReadLine", "ConsoleHost_history.txt") } $ItArgs = @{} @@ -39,8 +47,17 @@ Describe "Configuration file locations" -tags "CI","Slow" { $env:PSModulePath = $original_PSModulePath } - It @ItArgs "Profile location should be correct" { - & $powershell -noprofile -c `$PROFILE | Should -Be $expectedProfile + It @ItArgs "Current User Profile location should be correct" { + & $powershell -noprofile -c `$PROFILE | Should -Be $expectedCurrentUserProfile + } + + It @ItArgs "All Users Profile location should be correct" { + & $powershell -noprofile -c `$PROFILE.AllUsersAllHosts | Should -Be $expectedAllUsersProfile + } + + It @ItArgs "All Users Profile location should be correct" { + $env:POWERSHELL_COMMON_APPLICATION_DATA = Join-Path -Path $PSHOME -ChildPath $profileName + & $powershell -noprofile -c `$PROFILE.AllUsersAllHosts | Should -Be $env:POWERSHELL_COMMON_APPLICATION_DATA } It @ItArgs "PSModulePath should contain the correct path" {