Skip to content
104 changes: 96 additions & 8 deletions src/code/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
using Azure.Identity;
using System.Text.RegularExpressions;
using System.Threading;
using System.Text.Json;
using System.Threading.Tasks;
using System.Xml;

Expand Down Expand Up @@ -1049,14 +1050,14 @@ public static List<string> GetPathsFromEnvVarAndScope(
{
GetStandardPlatformPaths(
psCmdlet,
out string myDocumentsPath,
out string psUserContentPath,
out string programFilesPath);

List<string> resourcePaths = new List<string>();
if (scope is null || scope.Value is ScopeType.CurrentUser)
{
resourcePaths.Add(Path.Combine(myDocumentsPath, "Modules"));
resourcePaths.Add(Path.Combine(myDocumentsPath, "Scripts"));
resourcePaths.Add(Path.Combine(psUserContentPath, "Modules"));
resourcePaths.Add(Path.Combine(psUserContentPath, "Scripts"));
}

if (scope.Value is ScopeType.AllUsers)
Expand Down Expand Up @@ -1156,28 +1157,115 @@ private static string GetHomeOrCreateTempHome()
}

private readonly static Version PSVersion6 = new Version(6, 0);
private readonly static Version PSVersion7_7 = new Version(7, 7, 0);

/// <summary>
/// Gets the user content directory path using PowerShell's Get-PSContentPath cmdlet.
/// Falls back to legacy path if the cmdlet is not available or PowerShell version is below 7.7.0.
/// </summary>
private static string GetUserContentPath(PSCmdlet psCmdlet, string legacyPath)
{
// Get PowerShell engine version from $PSVersionTable.PSVersion
Version psVersion = psCmdlet.SessionState.PSVariable.GetValue("PSVersionTable") is Hashtable versionTable
&& versionTable["PSVersion"] is Version version
? version
: new Version(5, 1);

// Only use Get-PSContentPath cmdlet if PowerShell version is 7.7.0 or greater (when PSContentPath feature is available)
if (psVersion >= PSVersion7_7)
{
// Try to use PowerShell's Get-PSContentPath cmdlet
// This cmdlet automatically respects PSContentPath settings
try
{
using (System.Management.Automation.PowerShell pwsh = System.Management.Automation.PowerShell.Create())
{
Comment thread
alerickson marked this conversation as resolved.
pwsh.AddCommand("Get-PSContentPath");
var results = pwsh.Invoke();

if (!pwsh.HadErrors && results != null && results.Count > 0)
{
// Get-PSContentPath returns a PSObject, extract the path string
string userContentPath = results[0]?.ToString();
if (!string.IsNullOrEmpty(userContentPath))
{
psCmdlet.WriteVerbose($"User content path from Get-PSContentPath: {userContentPath}");
return userContentPath;
}
}

if (pwsh.HadErrors)
{
foreach (var error in pwsh.Streams.Error)
{
psCmdlet.WriteVerbose($"Get-PSContentPath error: {error}");
}
}
}
}
catch (Exception ex)
{
psCmdlet.WriteVerbose($"Get-PSContentPath cmdlet not available: {ex.Message}");
}
}
else
{
psCmdlet.WriteVerbose($"PowerShell version {psVersion} is below 7.7.0, using legacy location");
}

// Fallback to legacy location
psCmdlet.WriteVerbose($"Using legacy location: {legacyPath}");
return legacyPath;
}

private static void GetStandardPlatformPaths(
PSCmdlet psCmdlet,
out string localUserDir,
out string allUsersDir)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
string powerShellType = (psCmdlet.Host.Version >= PSVersion6) ? "PowerShell" : "WindowsPowerShell";
localUserDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), powerShellType);
// Get PowerShell engine version from $PSVersionTable.PSVersion
Version psVersion = psCmdlet.SessionState.PSVariable.GetValue("PSVersionTable") is Hashtable versionTable
&& versionTable["PSVersion"] is Version version
? version
: new Version(5, 1); // Default to Windows PowerShell version if unable to determine

string powerShellType = (psVersion >= PSVersion6) ? "PowerShell" : "WindowsPowerShell";

// Windows PowerShell doesn't support experimental features or PSContentPath
if (powerShellType == "WindowsPowerShell")
{
// Use legacy Documents folder for Windows PowerShell
localUserDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), powerShellType);
psCmdlet.WriteVerbose($"Using Windows PowerShell Documents folder: {localUserDir}");
}
else
{
string legacyPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
powerShellType
);

localUserDir = GetUserContentPath(psCmdlet, legacyPath);
}

allUsersDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), powerShellType);
}
else
{
// paths are the same for both Linux and macOS
localUserDir = Path.Combine(GetHomeOrCreateTempHome(), ".local", "share", "powershell");
// Create the default data directory if it doesn't exist.
string legacyPath = Path.Combine(GetHomeOrCreateTempHome(), ".local", "share", "powershell");

localUserDir = GetUserContentPath(psCmdlet, legacyPath);

// Create the default data directory if it doesn't exist
if (!Directory.Exists(localUserDir))
{
Directory.CreateDirectory(localUserDir);
}

allUsersDir = System.IO.Path.Combine("/usr", "local", "share", "powershell");
allUsersDir = Path.Combine("/usr", "local", "share", "powershell");
Comment thread
anamnavi marked this conversation as resolved.
Outdated
Comment thread
jshigetomi marked this conversation as resolved.
Outdated
}
}

Expand Down
Loading