Skip to content

Commit 9c0bea2

Browse files
committed
Set session failure with reason when applicable
1 parent 4223eec commit 9c0bea2

File tree

3 files changed

+61
-59
lines changed

3 files changed

+61
-59
lines changed

src/PowerShellEditorServices.Hosting/Commands/StartEditorServicesCommand.cs

+2-5
Original file line numberDiff line numberDiff line change
@@ -224,10 +224,7 @@ protected override void EndProcessing()
224224
// Create the configuration from parameters
225225
EditorServicesConfig editorServicesConfig = CreateConfigObject();
226226

227-
SessionFileWriter sessionFileWriter = new(_logger, SessionDetailsPath);
228-
_logger.Log(PsesLogLevel.Diagnostic, "Session file writer created");
229-
230-
using EditorServicesLoader psesLoader = EditorServicesLoader.Create(_logger, editorServicesConfig, sessionFileWriter, _loggerUnsubscribers);
227+
using EditorServicesLoader psesLoader = EditorServicesLoader.Create(_logger, editorServicesConfig, SessionDetailsPath, _loggerUnsubscribers);
231228
_logger.Log(PsesLogLevel.Verbose, "Loading EditorServices");
232229
// Synchronously start editor services and wait here until it shuts down.
233230
#pragma warning disable VSTHRD002
@@ -394,7 +391,7 @@ private string GetProfilePathFromProfileObject(PSObject profileObject, ProfileUs
394391
$"{HostProfileId}_profile.ps1");
395392
}
396393

397-
// We should only use PSReadLine if we specificied that we want a console repl
394+
// We should only use PSReadLine if we specified that we want a console repl
398395
// and we have not explicitly said to use the legacy ReadLine.
399396
// We also want it if we are either:
400397
// * On Windows on any version OR

src/PowerShellEditorServices.Hosting/Configuration/SessionFileWriter.cs

+10-4
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,19 @@ public sealed class SessionFileWriter : ISessionFileWriter
4242

4343
private readonly string _sessionFilePath;
4444

45+
private readonly Version _powerShellVersion;
46+
4547
/// <summary>
4648
/// Construct a new session file writer for the given session file path.
4749
/// </summary>
4850
/// <param name="logger">The logger to log actions with.</param>
4951
/// <param name="sessionFilePath">The path to write the session file path to.</param>
50-
public SessionFileWriter(HostLogger logger, string sessionFilePath)
52+
/// <param name="powerShellVersion">The process's PowerShell version object.</param>
53+
public SessionFileWriter(HostLogger logger, string sessionFilePath, Version powerShellVersion)
5154
{
5255
_logger = logger;
5356
_sessionFilePath = sessionFilePath;
57+
_powerShellVersion = powerShellVersion;
5458
}
5559

5660
/// <summary>
@@ -84,11 +88,11 @@ public void WriteSessionStarted(ITransportConfig languageServiceTransport, ITran
8488
{ "status", "started" },
8589
};
8690

87-
if (languageServiceTransport != null)
91+
if (languageServiceTransport is not null)
8892
{
8993
sessionObject["languageServiceTransport"] = languageServiceTransport.SessionFileTransportName;
9094

91-
if (languageServiceTransport.SessionFileEntries != null)
95+
if (languageServiceTransport.SessionFileEntries is not null)
9296
{
9397
foreach (KeyValuePair<string, object> sessionEntry in languageServiceTransport.SessionFileEntries)
9498
{
@@ -97,7 +101,7 @@ public void WriteSessionStarted(ITransportConfig languageServiceTransport, ITran
97101
}
98102
}
99103

100-
if (debugAdapterTransport != null)
104+
if (debugAdapterTransport is not null)
101105
{
102106
sessionObject["debugServiceTransport"] = debugAdapterTransport.SessionFileTransportName;
103107

@@ -119,6 +123,8 @@ public void WriteSessionStarted(ITransportConfig languageServiceTransport, ITran
119123
/// <param name="sessionObject">The dictionary representing the session file.</param>
120124
private void WriteSessionObject(Dictionary<string, object> sessionObject)
121125
{
126+
sessionObject["powerShellVersion"] = _powerShellVersion.ToString();
127+
122128
string psModulePath = Environment.GetEnvironmentVariable("PSModulePath");
123129
string content = null;
124130
using (SMA.PowerShell pwsh = SMA.PowerShell.Create(RunspaceMode.NewRunspace))

src/PowerShellEditorServices.Hosting/EditorServicesLoader.cs

+49-50
Original file line numberDiff line numberDiff line change
@@ -49,37 +49,28 @@ public sealed class EditorServicesLoader : IDisposable
4949
/// </summary>
5050
/// <param name="logger">The host logger to use.</param>
5151
/// <param name="hostConfig">The host configuration to start editor services with.</param>
52-
/// <param name="sessionFileWriter">The session file writer to write the session file with.</param>
53-
/// <returns></returns>
54-
public static EditorServicesLoader Create(
55-
HostLogger logger,
56-
EditorServicesConfig hostConfig,
57-
ISessionFileWriter sessionFileWriter) => Create(logger, hostConfig, sessionFileWriter, loggersToUnsubscribe: null);
58-
59-
/// <summary>
60-
/// Create a new Editor Services loader.
61-
/// </summary>
62-
/// <param name="logger">The host logger to use.</param>
63-
/// <param name="hostConfig">The host configuration to start editor services with.</param>
64-
/// <param name="sessionFileWriter">The session file writer to write the session file with.</param>
52+
/// <param name="sessionDetailsPath">Path to the session file to create on startup or startup failure.</param>
6553
/// <param name="loggersToUnsubscribe">The loggers to unsubscribe form writing to the terminal.</param>
66-
/// <returns></returns>
6754
public static EditorServicesLoader Create(
6855
HostLogger logger,
6956
EditorServicesConfig hostConfig,
70-
ISessionFileWriter sessionFileWriter,
57+
string sessionDetailsPath,
7158
IReadOnlyCollection<IDisposable> loggersToUnsubscribe)
7259
{
73-
if (logger == null)
60+
if (logger is null)
7461
{
7562
throw new ArgumentNullException(nameof(logger));
7663
}
7764

78-
if (hostConfig == null)
65+
if (hostConfig is null)
7966
{
8067
throw new ArgumentNullException(nameof(hostConfig));
8168
}
8269

70+
Version powerShellVersion = GetPSVersion();
71+
SessionFileWriter sessionFileWriter = new(logger, sessionDetailsPath, powerShellVersion);
72+
logger.Log(PsesLogLevel.Diagnostic, "Session file writer created");
73+
8374
#if CoreCLR
8475
// In .NET Core, we add an event here to redirect dependency loading to the new AssemblyLoadContext we load PSES' dependencies into
8576
logger.Log(PsesLogLevel.Verbose, "Adding AssemblyResolve event handler for new AssemblyLoadContext dependency loading");
@@ -167,7 +158,7 @@ public static EditorServicesLoader Create(
167158
};
168159
#endif
169160

170-
return new EditorServicesLoader(logger, hostConfig, sessionFileWriter, loggersToUnsubscribe);
161+
return new EditorServicesLoader(logger, hostConfig, sessionFileWriter, loggersToUnsubscribe, powerShellVersion);
171162
}
172163

173164
private readonly EditorServicesConfig _hostConfig;
@@ -178,33 +169,38 @@ public static EditorServicesLoader Create(
178169

179170
private readonly IReadOnlyCollection<IDisposable> _loggersToUnsubscribe;
180171

172+
private readonly Version _powerShellVersion;
173+
181174
private EditorServicesRunner _editorServicesRunner;
182175

183176
private EditorServicesLoader(
184177
HostLogger logger,
185178
EditorServicesConfig hostConfig,
186179
ISessionFileWriter sessionFileWriter,
187-
IReadOnlyCollection<IDisposable> loggersToUnsubscribe)
180+
IReadOnlyCollection<IDisposable> loggersToUnsubscribe,
181+
Version powerShellVersion)
188182
{
189183
_logger = logger;
190184
_hostConfig = hostConfig;
191185
_sessionFileWriter = sessionFileWriter;
192186
_loggersToUnsubscribe = loggersToUnsubscribe;
187+
_powerShellVersion = powerShellVersion;
193188
}
194189

195190
/// <summary>
196191
/// Load Editor Services and its dependencies in an isolated way and start it.
197192
/// This method's returned task will end when Editor Services shuts down.
198193
/// </summary>
199-
/// <returns></returns>
200194
public Task LoadAndRunEditorServicesAsync()
201195
{
202196
// Log important host information here
203197
LogHostInformation();
204198

199+
CheckPowerShellVersion();
200+
205201
#if !CoreCLR
206202
// Make sure the .NET Framework version supports .NET Standard 2.0
207-
CheckNetFxVersion();
203+
CheckDotNetVersion();
208204
#endif
209205

210206
// Add the bundled modules to the PSModulePath
@@ -241,10 +237,30 @@ private static void LoadEditorServices() =>
241237
// The call within this method is therefore a total no-op
242238
EditorServicesLoading.LoadEditorServicesForHost();
243239

240+
private void CheckPowerShellVersion()
241+
{
242+
PSLanguageMode languageMode = Runspace.DefaultRunspace.SessionStateProxy.LanguageMode;
243+
244+
_logger.Log(PsesLogLevel.Verbose, $@"
245+
== PowerShell Details ==
246+
- PowerShell version: {_powerShellVersion}
247+
- Language mode: {languageMode}
248+
");
249+
250+
if ((_powerShellVersion < new Version(5, 1))
251+
|| (_powerShellVersion >= new Version(6, 0) && _powerShellVersion < new Version(7, 2)))
252+
{
253+
_logger.Log(PsesLogLevel.Error, $"PowerShell {_powerShellVersion} is not supported, please update!");
254+
_sessionFileWriter.WriteSessionFailure("powerShellVersion");
255+
}
256+
257+
// TODO: Check if language mode still matters for support.
258+
}
259+
244260
#if !CoreCLR
245-
private void CheckNetFxVersion()
261+
private void CheckDotNetVersion()
246262
{
247-
_logger.Log(PsesLogLevel.Diagnostic, "Checking that .NET Framework version is at least 4.8");
263+
_logger.Log(PsesLogLevel.Verbose, "Checking that .NET Framework version is at least 4.8");
248264
using RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Net Framework Setup\NDP\v4\Full");
249265
object netFxValue = key?.GetValue("Release");
250266
if (netFxValue == null || netFxValue is not int netFxVersion)
@@ -256,7 +272,8 @@ private void CheckNetFxVersion()
256272

257273
if (netFxVersion < Net48Version)
258274
{
259-
_logger.Log(PsesLogLevel.Warning, $".NET Framework version {netFxVersion} lower than .NET 4.8. This runtime is not supported and you may experience errors. Please update your .NET runtime version.");
275+
_logger.Log(PsesLogLevel.Error, $".NET Framework {netFxVersion} is out-of-date, please install at least 4.8: https://dotnet.microsoft.com/en-us/download/dotnet-framework");
276+
_sessionFileWriter.WriteSessionFailure("dotNetVersion");
260277
}
261278
}
262279
#endif
@@ -322,30 +339,6 @@ private void LogHostInformation()
322339
- PowerShell output encoding: {GetPSOutputEncoding()}
323340
");
324341

325-
LogPowerShellDetails();
326-
327-
LogOperatingSystemDetails();
328-
}
329-
330-
private static string GetPSOutputEncoding()
331-
{
332-
using SMA.PowerShell pwsh = SMA.PowerShell.Create();
333-
return pwsh.AddScript("$OutputEncoding.EncodingName", useLocalScope: true).Invoke<string>()[0];
334-
}
335-
336-
private void LogPowerShellDetails()
337-
{
338-
PSLanguageMode languageMode = Runspace.DefaultRunspace.SessionStateProxy.LanguageMode;
339-
340-
_logger.Log(PsesLogLevel.Verbose, $@"
341-
== PowerShell Details ==
342-
- PowerShell version: {GetPSVersion()}
343-
- Language mode: {languageMode}
344-
");
345-
}
346-
347-
private void LogOperatingSystemDetails()
348-
{
349342
_logger.Log(PsesLogLevel.Verbose, $@"
350343
== Environment Details ==
351344
- OS description: {RuntimeInformation.OSDescription}
@@ -354,6 +347,12 @@ private void LogOperatingSystemDetails()
354347
");
355348
}
356349

350+
private static string GetPSOutputEncoding()
351+
{
352+
using SMA.PowerShell pwsh = SMA.PowerShell.Create();
353+
return pwsh.AddScript("$OutputEncoding.EncodingName", useLocalScope: true).Invoke<string>()[0];
354+
}
355+
357356
// TODO: Deduplicate this with VersionUtils.
358357
private static string GetOSArchitecture()
359358
{
@@ -401,7 +400,7 @@ private void ValidateConfiguration()
401400
}
402401
}
403402

404-
private static object GetPSVersion()
403+
private static Version GetPSVersion()
405404
{
406405
// In order to read the $PSVersionTable variable,
407406
// we are forced to create a new runspace to avoid concurrency issues,
@@ -412,7 +411,7 @@ private static object GetPSVersion()
412411
return typeof(PSObject).Assembly
413412
.GetType("System.Management.Automation.PSVersionInfo")
414413
.GetMethod("get_PSVersion", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
415-
.Invoke(null, new object[0] /* Cannot use Array.Empty, since it must work in net452 */);
414+
.Invoke(null, new object[0] /* Cannot use Array.Empty, since it must work in net452 */) as Version;
416415
#pragma warning restore CA1825
417416
}
418417
}

0 commit comments

Comments
 (0)