Skip to content

Commit

Permalink
Set session failure with reason when applicable
Browse files Browse the repository at this point in the history
  • Loading branch information
andyleejordan committed Apr 24, 2023
1 parent 4223eec commit 9c0bea2
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -224,10 +224,7 @@ protected override void EndProcessing()
// Create the configuration from parameters
EditorServicesConfig editorServicesConfig = CreateConfigObject();

SessionFileWriter sessionFileWriter = new(_logger, SessionDetailsPath);
_logger.Log(PsesLogLevel.Diagnostic, "Session file writer created");

using EditorServicesLoader psesLoader = EditorServicesLoader.Create(_logger, editorServicesConfig, sessionFileWriter, _loggerUnsubscribers);
using EditorServicesLoader psesLoader = EditorServicesLoader.Create(_logger, editorServicesConfig, SessionDetailsPath, _loggerUnsubscribers);
_logger.Log(PsesLogLevel.Verbose, "Loading EditorServices");
// Synchronously start editor services and wait here until it shuts down.
#pragma warning disable VSTHRD002
Expand Down Expand Up @@ -394,7 +391,7 @@ private string GetProfilePathFromProfileObject(PSObject profileObject, ProfileUs
$"{HostProfileId}_profile.ps1");
}

// We should only use PSReadLine if we specificied that we want a console repl
// We should only use PSReadLine if we specified that we want a console repl
// and we have not explicitly said to use the legacy ReadLine.
// We also want it if we are either:
// * On Windows on any version OR
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,19 @@ public sealed class SessionFileWriter : ISessionFileWriter

private readonly string _sessionFilePath;

private readonly Version _powerShellVersion;

/// <summary>
/// Construct a new session file writer for the given session file path.
/// </summary>
/// <param name="logger">The logger to log actions with.</param>
/// <param name="sessionFilePath">The path to write the session file path to.</param>
public SessionFileWriter(HostLogger logger, string sessionFilePath)
/// <param name="powerShellVersion">The process's PowerShell version object.</param>
public SessionFileWriter(HostLogger logger, string sessionFilePath, Version powerShellVersion)
{
_logger = logger;
_sessionFilePath = sessionFilePath;
_powerShellVersion = powerShellVersion;
}

/// <summary>
Expand Down Expand Up @@ -84,11 +88,11 @@ public void WriteSessionStarted(ITransportConfig languageServiceTransport, ITran
{ "status", "started" },
};

if (languageServiceTransport != null)
if (languageServiceTransport is not null)
{
sessionObject["languageServiceTransport"] = languageServiceTransport.SessionFileTransportName;

if (languageServiceTransport.SessionFileEntries != null)
if (languageServiceTransport.SessionFileEntries is not null)
{
foreach (KeyValuePair<string, object> sessionEntry in languageServiceTransport.SessionFileEntries)
{
Expand All @@ -97,7 +101,7 @@ public void WriteSessionStarted(ITransportConfig languageServiceTransport, ITran
}
}

if (debugAdapterTransport != null)
if (debugAdapterTransport is not null)
{
sessionObject["debugServiceTransport"] = debugAdapterTransport.SessionFileTransportName;

Expand All @@ -119,6 +123,8 @@ public void WriteSessionStarted(ITransportConfig languageServiceTransport, ITran
/// <param name="sessionObject">The dictionary representing the session file.</param>
private void WriteSessionObject(Dictionary<string, object> sessionObject)
{
sessionObject["powerShellVersion"] = _powerShellVersion.ToString();

string psModulePath = Environment.GetEnvironmentVariable("PSModulePath");
string content = null;
using (SMA.PowerShell pwsh = SMA.PowerShell.Create(RunspaceMode.NewRunspace))
Expand Down
99 changes: 49 additions & 50 deletions src/PowerShellEditorServices.Hosting/EditorServicesLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,37 +49,28 @@ public sealed class EditorServicesLoader : IDisposable
/// </summary>
/// <param name="logger">The host logger to use.</param>
/// <param name="hostConfig">The host configuration to start editor services with.</param>
/// <param name="sessionFileWriter">The session file writer to write the session file with.</param>
/// <returns></returns>
public static EditorServicesLoader Create(
HostLogger logger,
EditorServicesConfig hostConfig,
ISessionFileWriter sessionFileWriter) => Create(logger, hostConfig, sessionFileWriter, loggersToUnsubscribe: null);

/// <summary>
/// Create a new Editor Services loader.
/// </summary>
/// <param name="logger">The host logger to use.</param>
/// <param name="hostConfig">The host configuration to start editor services with.</param>
/// <param name="sessionFileWriter">The session file writer to write the session file with.</param>
/// <param name="sessionDetailsPath">Path to the session file to create on startup or startup failure.</param>
/// <param name="loggersToUnsubscribe">The loggers to unsubscribe form writing to the terminal.</param>
/// <returns></returns>
public static EditorServicesLoader Create(
HostLogger logger,
EditorServicesConfig hostConfig,
ISessionFileWriter sessionFileWriter,
string sessionDetailsPath,
IReadOnlyCollection<IDisposable> loggersToUnsubscribe)
{
if (logger == null)
if (logger is null)
{
throw new ArgumentNullException(nameof(logger));
}

if (hostConfig == null)
if (hostConfig is null)
{
throw new ArgumentNullException(nameof(hostConfig));
}

Version powerShellVersion = GetPSVersion();
SessionFileWriter sessionFileWriter = new(logger, sessionDetailsPath, powerShellVersion);
logger.Log(PsesLogLevel.Diagnostic, "Session file writer created");

#if CoreCLR
// In .NET Core, we add an event here to redirect dependency loading to the new AssemblyLoadContext we load PSES' dependencies into
logger.Log(PsesLogLevel.Verbose, "Adding AssemblyResolve event handler for new AssemblyLoadContext dependency loading");
Expand Down Expand Up @@ -167,7 +158,7 @@ public static EditorServicesLoader Create(
};
#endif

return new EditorServicesLoader(logger, hostConfig, sessionFileWriter, loggersToUnsubscribe);
return new EditorServicesLoader(logger, hostConfig, sessionFileWriter, loggersToUnsubscribe, powerShellVersion);
}

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

private readonly IReadOnlyCollection<IDisposable> _loggersToUnsubscribe;

private readonly Version _powerShellVersion;

private EditorServicesRunner _editorServicesRunner;

private EditorServicesLoader(
HostLogger logger,
EditorServicesConfig hostConfig,
ISessionFileWriter sessionFileWriter,
IReadOnlyCollection<IDisposable> loggersToUnsubscribe)
IReadOnlyCollection<IDisposable> loggersToUnsubscribe,
Version powerShellVersion)
{
_logger = logger;
_hostConfig = hostConfig;
_sessionFileWriter = sessionFileWriter;
_loggersToUnsubscribe = loggersToUnsubscribe;
_powerShellVersion = powerShellVersion;
}

/// <summary>
/// Load Editor Services and its dependencies in an isolated way and start it.
/// This method's returned task will end when Editor Services shuts down.
/// </summary>
/// <returns></returns>
public Task LoadAndRunEditorServicesAsync()
{
// Log important host information here
LogHostInformation();

CheckPowerShellVersion();

#if !CoreCLR
// Make sure the .NET Framework version supports .NET Standard 2.0
CheckNetFxVersion();
CheckDotNetVersion();
#endif

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

private void CheckPowerShellVersion()
{
PSLanguageMode languageMode = Runspace.DefaultRunspace.SessionStateProxy.LanguageMode;

_logger.Log(PsesLogLevel.Verbose, $@"
== PowerShell Details ==
- PowerShell version: {_powerShellVersion}
- Language mode: {languageMode}
");

if ((_powerShellVersion < new Version(5, 1))
|| (_powerShellVersion >= new Version(6, 0) && _powerShellVersion < new Version(7, 2)))
{
_logger.Log(PsesLogLevel.Error, $"PowerShell {_powerShellVersion} is not supported, please update!");
_sessionFileWriter.WriteSessionFailure("powerShellVersion");
}

// TODO: Check if language mode still matters for support.
}

#if !CoreCLR
private void CheckNetFxVersion()
private void CheckDotNetVersion()
{
_logger.Log(PsesLogLevel.Diagnostic, "Checking that .NET Framework version is at least 4.8");
_logger.Log(PsesLogLevel.Verbose, "Checking that .NET Framework version is at least 4.8");
using RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Net Framework Setup\NDP\v4\Full");
object netFxValue = key?.GetValue("Release");
if (netFxValue == null || netFxValue is not int netFxVersion)
Expand All @@ -256,7 +272,8 @@ private void CheckNetFxVersion()

if (netFxVersion < Net48Version)
{
_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.");
_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");
_sessionFileWriter.WriteSessionFailure("dotNetVersion");
}
}
#endif
Expand Down Expand Up @@ -322,30 +339,6 @@ private void LogHostInformation()
- PowerShell output encoding: {GetPSOutputEncoding()}
");

LogPowerShellDetails();

LogOperatingSystemDetails();
}

private static string GetPSOutputEncoding()
{
using SMA.PowerShell pwsh = SMA.PowerShell.Create();
return pwsh.AddScript("$OutputEncoding.EncodingName", useLocalScope: true).Invoke<string>()[0];
}

private void LogPowerShellDetails()
{
PSLanguageMode languageMode = Runspace.DefaultRunspace.SessionStateProxy.LanguageMode;

_logger.Log(PsesLogLevel.Verbose, $@"
== PowerShell Details ==
- PowerShell version: {GetPSVersion()}
- Language mode: {languageMode}
");
}

private void LogOperatingSystemDetails()
{
_logger.Log(PsesLogLevel.Verbose, $@"
== Environment Details ==
- OS description: {RuntimeInformation.OSDescription}
Expand All @@ -354,6 +347,12 @@ private void LogOperatingSystemDetails()
");
}

private static string GetPSOutputEncoding()
{
using SMA.PowerShell pwsh = SMA.PowerShell.Create();
return pwsh.AddScript("$OutputEncoding.EncodingName", useLocalScope: true).Invoke<string>()[0];
}

// TODO: Deduplicate this with VersionUtils.
private static string GetOSArchitecture()
{
Expand Down Expand Up @@ -401,7 +400,7 @@ private void ValidateConfiguration()
}
}

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

0 comments on commit 9c0bea2

Please sign in to comment.