Skip to content

Commit a60ee23

Browse files
authored
Fix profile collection on non-Windows, add PS 7 profiles (#1260)
* Fix platform information collection on *nix * Add profiles for PS 7-preview.1 * Use correct schema version * Address @bergmeister's feedback * Improve dict name
1 parent 45cdba0 commit a60ee23

File tree

7 files changed

+177
-85
lines changed

7 files changed

+177
-85
lines changed

PSCompatibilityCollector/Microsoft.PowerShell.CrossCompatibility/Collection/CompatibilityProfileCollector.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ public CompatibilityProfileCollector Build(SMA.PowerShell pwsh)
8282
}
8383
}
8484

85-
private static readonly Version s_currentProfileSchemaVersion = new Version(1, 1);
85+
// Increment the minor version if non-breaking additions have been made to the API
86+
// Increment the major version if breaking changes have been made to the API
87+
private static readonly Version s_currentProfileSchemaVersion = new Version(1, 2);
8688

8789
private readonly PowerShellDataCollector _pwshDataCollector;
8890

PSCompatibilityCollector/Microsoft.PowerShell.CrossCompatibility/Collection/PlatformInformationCollector.cs

Lines changed: 157 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.Linq;
1010
using System.Runtime.InteropServices;
1111
using System.Text;
12+
using System.Text.RegularExpressions;
1213
using Microsoft.Management.Infrastructure;
1314
using Microsoft.PowerShell.CrossCompatibility.Data;
1415
using Microsoft.PowerShell.CrossCompatibility.Utility;
@@ -29,6 +30,76 @@ public class PlatformInformationCollector : IDisposable
2930
"/etc/os-release",
3031
};
3132

33+
private static readonly IReadOnlyList<string> s_distributionIdKeys = new string[]
34+
{
35+
"ID",
36+
"DISTRIB_ID"
37+
};
38+
39+
private static readonly IReadOnlyList<string> s_distributionVersionKeys = new string[]
40+
{
41+
"VERSION_ID",
42+
"DISTRIB_RELEASE"
43+
};
44+
45+
private static readonly IReadOnlyList<string> s_distributionPrettyNameKeys = new string[]
46+
{
47+
"PRETTY_NAME",
48+
"DISTRIB_DESCRIPTION"
49+
};
50+
51+
private static readonly Regex s_macOSNameRegex = new Regex(
52+
@"System Version: (.*?)(\(|$)",
53+
RegexOptions.Multiline | RegexOptions.Compiled);
54+
55+
56+
/// <summary>
57+
/// Collect all release info files into a lookup table in memory.
58+
/// Overrides pre-existing keys if there are duplicates.
59+
/// </summary>
60+
/// <returns>A dictionary with the keys and values of all the release info files on the machine.</returns>
61+
public static IReadOnlyDictionary<string, string> GetLinuxReleaseInfo()
62+
{
63+
var releaseInfoKeyValueEntries = new Dictionary<string, string>();
64+
65+
foreach (string path in s_releaseInfoPaths)
66+
{
67+
try
68+
{
69+
using (FileStream fileStream = File.OpenRead(path))
70+
using (var reader = new StreamReader(fileStream))
71+
{
72+
while (!reader.EndOfStream)
73+
{
74+
string line = reader.ReadLine();
75+
76+
if (string.IsNullOrWhiteSpace(line) || line.StartsWith("#"))
77+
{
78+
continue;
79+
}
80+
81+
string[] elements = line.Split('=');
82+
releaseInfoKeyValueEntries[elements[0]] = Dequote(elements[1]);
83+
}
84+
}
85+
}
86+
catch (IOException)
87+
{
88+
// Different Linux distributions have different /etc/*-release files.
89+
// It's more efficient (and correct timing-wise) for us to try to read the file and catch the exception
90+
// than to test for its existence and then open it.
91+
//
92+
// See:
93+
// - https://www.freedesktop.org/software/systemd/man/os-release.html
94+
// - https://gist.github.com/natefoo/814c5bf936922dad97ff
95+
96+
// Ignore that the file doesn't exist and just continue to the next one.
97+
}
98+
}
99+
100+
return releaseInfoKeyValueEntries;
101+
}
102+
32103
private readonly Lazy<Hashtable> _lazyPSVersionTable;
33104

34105
private readonly Lazy<PowerShellVersion> _lazyPSVersion;
@@ -129,16 +200,17 @@ public OperatingSystemData GetOperatingSystemData()
129200
{
130201
var osData = new OperatingSystemData()
131202
{
203+
Description = GetOSDescription(),
132204
Architecture = GetOSArchitecture(),
133205
Family = GetOSFamily(),
134-
Name = GetOSName(),
135206
Platform = GetOSPlatform(),
136207
Version = GetOSVersion(),
137208
};
138209

139210
switch (osData.Family)
140211
{
141212
case OSFamily.Windows:
213+
osData.Name = osData.Description;
142214
if (!string.IsNullOrEmpty(Environment.OSVersion.ServicePack))
143215
{
144216
osData.ServicePack = Environment.OSVersion.ServicePack;
@@ -147,53 +219,83 @@ public OperatingSystemData GetOperatingSystemData()
147219
break;
148220

149221
case OSFamily.Linux:
150-
IReadOnlyDictionary<string, string> lsbInfo = GetLinuxReleaseInfo();
151-
osData.DistributionId = lsbInfo["DistributionId"];
152-
osData.DistributionVersion = lsbInfo["DistributionVersion"];
153-
osData.DistributionPrettyName = lsbInfo["DistributionPrettyName"];
222+
IReadOnlyDictionary<string, string> releaseInfo = GetLinuxReleaseInfo();
223+
224+
osData.DistributionId = GetEntryFromReleaseInfo(releaseInfo, s_distributionIdKeys);
225+
osData.DistributionVersion = GetEntryFromReleaseInfo(releaseInfo, s_distributionVersionKeys);
226+
osData.DistributionPrettyName = GetEntryFromReleaseInfo(releaseInfo, s_distributionPrettyNameKeys);
227+
osData.Name = osData.DistributionPrettyName;
228+
break;
229+
230+
case OSFamily.MacOS:
231+
osData.Name = GetMacOSName();
154232
break;
155233
}
156234

157235
return osData;
158236
}
159237

160-
/// <summary>
161-
/// Collect all release info files into a lookup table in memory.
162-
/// Overrides pre-existing keys if there are duplicates.
163-
/// </summary>
164-
/// <returns>A dictionary with the keys and values of all the release info files on the machine.</returns>
165-
public IReadOnlyDictionary<string, string> GetLinuxReleaseInfo()
238+
private string GetMacOSName()
166239
{
167-
var dict = new Dictionary<string, string>();
168-
169-
foreach (string path in s_releaseInfoPaths)
240+
try
170241
{
171-
try
242+
using (var spProcess = new Process())
172243
{
173-
using (FileStream fileStream = File.OpenRead(path))
174-
using (var reader = new StreamReader(fileStream))
175-
{
176-
while (!reader.EndOfStream)
177-
{
178-
string line = reader.ReadLine();
244+
spProcess.StartInfo.UseShellExecute = false;
245+
spProcess.StartInfo.RedirectStandardOutput = true;
246+
spProcess.StartInfo.CreateNoWindow = true;
247+
spProcess.StartInfo.FileName = "/usr/sbin/system_profiler";
248+
spProcess.StartInfo.Arguments = "SPSoftwareDataType";
179249

180-
if (string.IsNullOrWhiteSpace(line) || line.StartsWith("#"))
181-
{
182-
continue;
183-
}
250+
spProcess.Start();
251+
spProcess.WaitForExit();
184252

185-
string[] elements = line.Split('=');
186-
dict[elements[0]] = Dequote(elements[1]);
187-
}
188-
}
189-
}
190-
catch (IOException)
191-
{
192-
// Do nothing - just continue
253+
string output = spProcess.StandardOutput.ReadToEnd();
254+
return s_macOSNameRegex.Match(output).Groups[1].Value;
193255
}
194256
}
257+
catch
258+
{
259+
return null;
260+
}
261+
}
262+
263+
private string GetOSDescription()
264+
{
265+
#if CoreCLR
266+
// This key was introduced in PowerShell 6
267+
return (string)PSVersionTable["OS"];
268+
#else
269+
if (_lazyWin32OperatingSystemInfo.IsValueCreated)
270+
{
271+
return _lazyWin32OperatingSystemInfo.Value.OSName;
272+
}
195273

196-
return dict;
274+
return RegistryCurrentVersionInfo.ProductName;
275+
#endif
276+
}
277+
278+
private string GetOSVersion()
279+
{
280+
#if CoreCLR
281+
// On Linux, we want to record the kernel branch, since this can differentiate Azure
282+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
283+
{
284+
return File.ReadAllText("/proc/sys/kernel/osrelease");
285+
}
286+
#endif
287+
return Environment.OSVersion.Version.ToString();
288+
}
289+
290+
private string GetOSPlatform()
291+
{
292+
#if CoreCLR
293+
if (PSVersion.Major >= 6)
294+
{
295+
return (string)PSVersionTable["Platform"];
296+
}
297+
#endif
298+
return "Win32NT";
197299
}
198300

199301
private OSFamily GetOSFamily()
@@ -310,46 +412,6 @@ private uint GetWinSkuId()
310412
return (uint)WindowsSku.Undefined;
311413
}
312414

313-
private string GetOSName()
314-
{
315-
#if CoreCLR
316-
// This key was introduced in PowerShell 6
317-
if (PSVersion.Major >= 6)
318-
{
319-
return (string)PSVersionTable["OS"];
320-
}
321-
#endif
322-
if (_lazyWin32OperatingSystemInfo.IsValueCreated)
323-
{
324-
return _lazyWin32OperatingSystemInfo.Value.OSName;
325-
}
326-
327-
return RegistryCurrentVersionInfo.ProductName;
328-
}
329-
330-
private string GetOSVersion()
331-
{
332-
#if CoreCLR
333-
// On Linux, we want to record the kernel branch, since this can differentiate Azure
334-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
335-
{
336-
return File.ReadAllText("/proc/sys/kernel/osrelease");
337-
}
338-
#endif
339-
return Environment.OSVersion.Version.ToString();
340-
}
341-
342-
private string GetOSPlatform()
343-
{
344-
#if CoreCLR
345-
if (PSVersion.Major >= 6)
346-
{
347-
return (string)PSVersionTable["Platform"];
348-
}
349-
#endif
350-
return "Win32NT";
351-
}
352-
353415
private static CurrentVersionInfo ReadCurrentVersionFromRegistry()
354416
{
355417
using (RegistryKey currentVersion = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion"))
@@ -360,13 +422,18 @@ private static CurrentVersionInfo ReadCurrentVersionFromRegistry()
360422
}
361423
}
362424

363-
[DllImport("kernel32.dll")]
364-
private static extern bool GetProductInfo(
365-
int dwOSMajorVersion,
366-
int dwOSMinorVersion,
367-
int dwSpMajorVersion,
368-
int dwSpMinorVersion,
369-
out uint pdwReturnedProductType);
425+
private static string GetEntryFromReleaseInfo(IReadOnlyDictionary<string, string> releaseInfo, IEnumerable<string> possibleKeys)
426+
{
427+
foreach (string key in possibleKeys)
428+
{
429+
if (releaseInfo.TryGetValue(key, out string entry))
430+
{
431+
return entry;
432+
}
433+
}
434+
435+
return null;
436+
}
370437

371438
private static Win32OSCimInfo GetWin32OperatingSystemInfo()
372439
{
@@ -443,6 +510,15 @@ private static string Dequote(string s)
443510
return sb.ToString();
444511
}
445512

513+
[DllImport("kernel32.dll")]
514+
private static extern bool GetProductInfo(
515+
int dwOSMajorVersion,
516+
int dwOSMinorVersion,
517+
int dwSpMajorVersion,
518+
int dwSpMinorVersion,
519+
out uint pdwReturnedProductType);
520+
521+
446522
#region IDisposable Support
447523
private bool disposedValue = false; // To detect redundant calls
448524

PSCompatibilityCollector/Microsoft.PowerShell.CrossCompatibility/Data/Platform/OperatingSystemData.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,18 @@ namespace Microsoft.PowerShell.CrossCompatibility.Data
1515
public class OperatingSystemData : ICloneable
1616
{
1717
/// <summary>
18-
/// The name of the operating system as
19-
/// reported by $PSVersionTable.
18+
/// The name of the operating system.
2019
/// </summary>
2120
[DataMember]
2221
public string Name { get; set; }
2322

23+
/// <summary>
24+
/// The description of the operating system as
25+
/// reported by $PSVersionTable.
26+
/// </summary>
27+
[DataMember]
28+
public string Description { get; set; }
29+
2430
/// <summary>
2531
/// The platform as reported by
2632
/// $PSVersionTable.

PSCompatibilityCollector/Microsoft.PowerShell.CrossCompatibility/Query/Platform/OperatingSystemData.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,15 @@ public OperatingSystemData(OperatingSystemDataMut operatingSystemData)
2323
}
2424

2525
/// <summary>
26-
/// The name of the operating system as reported by $PSVersionTable.OS.
26+
/// The name of the operating system.
2727
/// </summary>
2828
public string Name => _operatingSystemData.Name;
2929

30+
/// <summary>
31+
/// The description of the operating system as reported by $PSVersionTable.OS.
32+
/// </summary>
33+
public string Description => _operatingSystemData.Description;
34+
3035
/// <summary>
3136
/// The name of the platform as reported by $PSVersionTable.Platform.
3237
/// </summary>

PSCompatibilityCollector/optional_profiles/ubuntu_x64_18.04_7.0.0-preview.1_x64_3.0.0_core.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

PSCompatibilityCollector/optional_profiles/win-48_x64_10.0.17763.0_7.0.0-preview.1_x64_3.0.0_core.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

PSCompatibilityCollector/optional_profiles/win-8_x64_10.0.17763.0_7.0.0-preview.1_x64_3.0.0_core.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)