Skip to content

Commit d53dd08

Browse files
committed
Use PowerShell GetPSModulePath API instead
1 parent bbb7148 commit d53dd08

1 file changed

Lines changed: 46 additions & 134 deletions

File tree

src/code/Utils.cs

Lines changed: 46 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,145 +1157,62 @@ private static string GetHomeOrCreateTempHome()
11571157
}
11581158

11591159
private readonly static Version PSVersion6 = new Version(6, 0);
1160+
private readonly static Version PSVersion7_7 = new Version(7, 7, 0);
11601161

11611162
/// <summary>
1162-
/// Gets the user content directory path based on PSContentPath experimental feature settings.
1163-
/// Checks if PSContentPath is enabled and returns the appropriate path (custom, default, or legacy).
1163+
/// Gets the user content directory path using PowerShell's GetPSModulePath API.
1164+
/// Falls back to legacy path if the API is not available or PowerShell version is below 7.7.0.
11641165
/// </summary>
1165-
private static string GetUserContentPath(PSCmdlet psCmdlet, string defaultPSContentPath, string legacyPath)
1166+
private static string GetUserContentPath(PSCmdlet psCmdlet, string legacyPath)
11661167
{
1167-
bool usePSContentPath = IsExperimentalFeatureEnabled(psCmdlet, "PSContentPath");
1168-
1169-
if (usePSContentPath)
1170-
{
1171-
psCmdlet.WriteVerbose("PSContentPath experimental feature is enabled");
1172-
1173-
// Check environment variable and config file for custom PSUserContentPath
1174-
string customPSUserContentPath = GetPSUserContentPath(psCmdlet);
1175-
1176-
if (!string.IsNullOrEmpty(customPSUserContentPath) && Directory.Exists(customPSUserContentPath))
1177-
{
1178-
// Use custom configured path
1179-
psCmdlet.WriteVerbose($"Using custom PSUserContentPath: {customPSUserContentPath}");
1180-
return customPSUserContentPath;
1181-
}
1182-
else
1183-
{
1184-
// Use default PSContentPath location when feature is enabled
1185-
psCmdlet.WriteVerbose($"Using default PSContentPath location: {defaultPSContentPath}");
1186-
return defaultPSContentPath;
1187-
}
1188-
}
1189-
else
1190-
{
1191-
// PSContentPath not enabled, use legacy location
1192-
psCmdlet.WriteVerbose($"Using legacy location: {legacyPath}");
1193-
return legacyPath;
1194-
}
1195-
}
1168+
// Get PowerShell engine version from $PSVersionTable.PSVersion
1169+
Version psVersion = psCmdlet.SessionState.PSVariable.GetValue("PSVersionTable") is Hashtable versionTable
1170+
&& versionTable["PSVersion"] is Version version
1171+
? version
1172+
: new Version(5, 1);
11961173

1197-
/// <summary>
1198-
/// Checks if a PowerShell experimental feature is enabled by reading the PowerShell configuration file.
1199-
/// Returns false if the configuration file doesn't exist or if the feature is not enabled.
1200-
/// </summary>
1201-
private static bool IsExperimentalFeatureEnabled(PSCmdlet psCmdlet, string featureName)
1202-
{
1203-
try
1174+
// Only use GetPSModulePath API if PowerShell version is 7.7.0 or greater (when PSContentPath feature is available)
1175+
if (psVersion >= PSVersion7_7)
12041176
{
1205-
// PowerShell configuration file location
1206-
string configPath = Path.Combine(
1207-
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
1208-
"powershell",
1209-
"powershell.config.json"
1210-
);
1211-
1212-
if (!File.Exists(configPath))
1213-
{
1214-
psCmdlet.WriteVerbose("PowerShell configuration file not found, experimental features not enabled");
1215-
return false;
1216-
}
1217-
1218-
string jsonContent = File.ReadAllText(configPath);
1219-
using (var jsonDoc = JsonDocument.Parse(jsonContent))
1177+
// Try to use PowerShell's GetPSModulePath API via reflection
1178+
// This API automatically respects PSContentPath settings
1179+
try
12201180
{
1221-
// Look for "ExperimentalFeatures": ["FeatureName"] in the config
1222-
if (jsonDoc.RootElement.TryGetProperty("ExperimentalFeatures", out var experimentalFeatures) &&
1223-
experimentalFeatures.ValueKind == JsonValueKind.Array)
1181+
var moduleIntrinsicsType = typeof(PSModuleInfo).Assembly.GetType("System.Management.Automation.ModuleIntrinsics");
1182+
var scopeEnumType = typeof(PSModuleInfo).Assembly.GetType("System.Management.Automation.PSModulePathScope");
1183+
1184+
if (moduleIntrinsicsType != null && scopeEnumType != null)
12241185
{
1225-
foreach (var feature in experimentalFeatures.EnumerateArray())
1186+
var getPSModulePathMethod = moduleIntrinsicsType.GetMethod("GetPSModulePath", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
1187+
1188+
if (getPSModulePathMethod != null)
12261189
{
1227-
if (string.Equals(feature.GetString(), featureName, StringComparison.OrdinalIgnoreCase))
1190+
// PSModulePathScope.User = 0
1191+
object userScope = Enum.ToObject(scopeEnumType, 0);
1192+
string userModulePath = (string)getPSModulePathMethod.Invoke(null, new object[] { userScope });
1193+
1194+
if (!string.IsNullOrEmpty(userModulePath))
12281195
{
1229-
psCmdlet.WriteVerbose(string.Format("Experimental feature '{0}' found in configuration file", featureName));
1230-
return true;
1196+
string userContentPath = Path.GetDirectoryName(userModulePath);
1197+
psCmdlet.WriteVerbose($"User content path from GetPSModulePath API: {userContentPath}");
1198+
return userContentPath;
12311199
}
12321200
}
12331201
}
12341202
}
1235-
1236-
psCmdlet.WriteVerbose(string.Format("Experimental feature '{0}' not found in configuration file", featureName));
1237-
return false;
1238-
}
1239-
catch (Exception ex)
1240-
{
1241-
psCmdlet.WriteVerbose(string.Format("Error reading PowerShell configuration file: {0}", ex.Message));
1242-
return false;
1243-
}
1244-
}
1245-
1246-
/// <summary>
1247-
/// Gets the custom PSUserContentPath from environment variable or PowerShell configuration file.
1248-
/// Environment variable takes precedence over the configuration file setting.
1249-
/// Returns null if neither is set or configured.
1250-
/// </summary>
1251-
private static string GetPSUserContentPath(PSCmdlet psCmdlet)
1252-
{
1253-
try
1254-
{
1255-
// First check the environment variable (takes precedence)
1256-
string envPSUserContentPath = Environment.GetEnvironmentVariable("PSUserContentPath");
1257-
if (!string.IsNullOrEmpty(envPSUserContentPath))
1258-
{
1259-
psCmdlet.WriteVerbose(string.Format("Found PSUserContentPath from environment variable: {0}", envPSUserContentPath));
1260-
return envPSUserContentPath;
1261-
}
1262-
1263-
// If environment variable not set, check the configuration file
1264-
string configPath = Path.Combine(
1265-
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
1266-
"powershell",
1267-
"powershell.config.json"
1268-
);
1269-
1270-
if (!File.Exists(configPath))
1271-
{
1272-
psCmdlet.WriteVerbose("PowerShell configuration file not found");
1273-
return null;
1274-
}
1275-
1276-
string jsonContent = File.ReadAllText(configPath);
1277-
using (var jsonDoc = JsonDocument.Parse(jsonContent))
1203+
catch (Exception ex)
12781204
{
1279-
// Look for PSUserContentPath in the config
1280-
if (jsonDoc.RootElement.TryGetProperty("PSUserContentPath", out var pathElement))
1281-
{
1282-
string psUserContentPath = pathElement.GetString();
1283-
if (!string.IsNullOrEmpty(psUserContentPath))
1284-
{
1285-
psCmdlet.WriteVerbose(string.Format("Found PSUserContentPath in config file: {0}", psUserContentPath));
1286-
return psUserContentPath;
1287-
}
1288-
}
1205+
psCmdlet.WriteVerbose($"GetPSModulePath API not available: {ex.Message}");
12891206
}
1290-
1291-
psCmdlet.WriteVerbose("PSUserContentPath not configured in PowerShell configuration file or environment variable");
1292-
return null;
12931207
}
1294-
catch (Exception ex)
1208+
else
12951209
{
1296-
psCmdlet.WriteVerbose(string.Format("Error reading PSUserContentPath: {0}", ex.Message));
1297-
return null;
1210+
psCmdlet.WriteVerbose($"PowerShell version {psVersion} is below 7.7.0, using legacy location");
12981211
}
1212+
1213+
// Fallback to legacy location
1214+
psCmdlet.WriteVerbose($"Using legacy location: {legacyPath}");
1215+
return legacyPath;
12991216
}
13001217

13011218
private static void GetStandardPlatformPaths(
@@ -1305,7 +1222,13 @@ private static void GetStandardPlatformPaths(
13051222
{
13061223
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
13071224
{
1308-
string powerShellType = (psCmdlet.Host.Version >= PSVersion6) ? "PowerShell" : "WindowsPowerShell";
1225+
// Get PowerShell engine version from $PSVersionTable.PSVersion
1226+
Version psVersion = psCmdlet.SessionState.PSVariable.GetValue("PSVersionTable") is Hashtable versionTable
1227+
&& versionTable["PSVersion"] is Version version
1228+
? version
1229+
: new Version(5, 1); // Default to Windows PowerShell version if unable to determine
1230+
1231+
string powerShellType = (psVersion >= PSVersion6) ? "PowerShell" : "WindowsPowerShell";
13091232

13101233
// Windows PowerShell doesn't support experimental features or PSContentPath
13111234
if (powerShellType == "WindowsPowerShell")
@@ -1316,33 +1239,22 @@ private static void GetStandardPlatformPaths(
13161239
}
13171240
else
13181241
{
1319-
string defaultPSContentPath = Path.Combine(
1320-
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
1321-
powerShellType
1322-
);
13231242
string legacyPath = Path.Combine(
13241243
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
13251244
powerShellType
13261245
);
13271246

1328-
localUserDir = GetUserContentPath(psCmdlet, defaultPSContentPath, legacyPath);
1247+
localUserDir = GetUserContentPath(psCmdlet, legacyPath);
13291248
}
13301249

13311250
allUsersDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), powerShellType);
13321251
}
13331252
else
13341253
{
13351254
// paths are the same for both Linux and macOS
1336-
string xdgDataHome = Environment.GetEnvironmentVariable("XDG_DATA_HOME");
1337-
if (string.IsNullOrEmpty(xdgDataHome))
1338-
{
1339-
xdgDataHome = Path.Combine(GetHomeOrCreateTempHome(), ".local", "share");
1340-
}
1341-
1342-
string defaultPSContentPath = Path.Combine(xdgDataHome, "powershell");
13431255
string legacyPath = Path.Combine(GetHomeOrCreateTempHome(), ".local", "share", "powershell");
13441256

1345-
localUserDir = GetUserContentPath(psCmdlet, defaultPSContentPath, legacyPath);
1257+
localUserDir = GetUserContentPath(psCmdlet, legacyPath);
13461258

13471259
// Create the default data directory if it doesn't exist
13481260
if (!Directory.Exists(localUserDir))

0 commit comments

Comments
 (0)