Skip to content

Commit

Permalink
[Core] Preparations for .NET 6.0
Browse files Browse the repository at this point in the history
  • Loading branch information
BAndysc committed Nov 10, 2021
1 parent 8b22078 commit 1bfbedc
Show file tree
Hide file tree
Showing 7 changed files with 306 additions and 1 deletion.
74 changes: 74 additions & 0 deletions WoWDatabaseEditor/Services/DotNetUtils/IDotNetService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using WDE.Module.Attributes;
using WoWDatabaseEditorCore.Services.Processes;

namespace WoWDatabaseEditorCore.Services.DotNetUtils
{
[UniqueProvider]
public interface IDotNetService
{
Task<bool> IsDotNet6Installed();
Uri DownloadDotNet6Link { get; }
}

[AutoRegister]
[SingleInstance]
public class DotNetService : IDotNetService
{
private readonly IProcessService processService;

public DotNetService(IProcessService processService)
{
this.processService = processService;
}

public async Task<bool> IsDotNet6Installed()
{
try
{
var dotnetPath = "dotnet";
var versions = await processService.RunAndGetOutput(dotnetPath, "--list-runtimes", null);
if (versions == null)
return true;
var runtimes = versions.Split('\n');

return runtimes.Any(r => r.StartsWith("Microsoft.NETCore.App 6.") ||
r.StartsWith("Microsoft.NETCore.App 7.") ||
r.StartsWith("Microsoft.NETCore.App 8."));
}
catch (Exception e)
{
Console.WriteLine(e);
return true;
}
}

private Uri GetLink(OSPlatform os, Architecture arch)
{
var stringOs = os == OSPlatform.Windows ? "windows" : (os == OSPlatform.OSX ? "macos" : "linux");
var version = "6.0.0";
return new Uri($"https://dotnet.microsoft.com/download/dotnet/thank-you/runtime-{version}-{stringOs}-{arch.ToString().ToLower()}-installer");
}

public Uri DownloadDotNet6Link
{
get
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
return new Uri("https://docs.microsoft.com/dotnet/core/install/linux?WT.mc_id=dotnet-35129-website");

if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
return GetLink(OSPlatform.OSX, RuntimeInformation.OSArchitecture);

if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return GetLink(OSPlatform.Windows, RuntimeInformation.OSArchitecture);

throw new Exception($"Your OS is not supported by .net 6.0");
}
}
}
}
27 changes: 27 additions & 0 deletions WoWDatabaseEditor/Services/Processes/IProcessService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using WDE.Module.Attributes;

namespace WoWDatabaseEditorCore.Services.Processes
{
public interface IProcess
{
bool IsRunning { get; }
void Kill();
}

[UniqueProvider]
public interface IProcessService
{
IProcess RunAndForget(string path, string arguments, string? workingDirectory, bool noWindow,
params (string, string)[] envVars);

Task<int> Run(CancellationToken token, string path, string arguments, string? workingDirectory,
Action<string>? onOutput,
Action<string>? onError,
Action<TextWriter>? onInput = null,
params (string, string)[] envVars);
}
}
113 changes: 113 additions & 0 deletions WoWDatabaseEditor/Services/Processes/ProcessService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using WDE.Module.Attributes;

namespace WoWDatabaseEditorCore.Services.Processes
{
[AutoRegister]
[SingleInstance]
public class ProcessService : IProcessService
{
public class ProcessData : IProcess
{
private readonly Process process;

public ProcessData(Process process)
{
this.process = process;
}

public bool IsRunning => !process.HasExited;

public void Kill()
{
if (IsRunning)
{
process.Kill();
}
}
}

public IProcess RunAndForget(string path, string arguments, string? workingDirectory, bool noWindow,
params (string, string)[] envVars)
{
var startInfo = new ProcessStartInfo(path, arguments);
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = noWindow;

foreach (var envVar in envVars)
startInfo.Environment.Add(envVar.Item1, envVar.Item2);

if (workingDirectory != null)
startInfo.WorkingDirectory = workingDirectory;

Process process = new Process();
process.StartInfo = startInfo;
if (!process.Start())
throw new Exception("Cannot start " + path);
return new ProcessData(process);
}

public async Task<int> Run(CancellationToken token, string path, string arguments, string? workingDirectory, Action<string>? onOutput,
Action<string>? onError, Action<TextWriter>? onInput,
params (string, string)[] envVars)
{
var startInfo = new ProcessStartInfo(path, arguments);
startInfo.UseShellExecute = false;
if (onOutput != null)
startInfo.RedirectStandardOutput = true;
if (onError != null)
startInfo.RedirectStandardError = true;
if (onInput != null)
startInfo.RedirectStandardInput = true;
startInfo.CreateNoWindow = true;

foreach (var envVar in envVars)
startInfo.Environment.Add(envVar.Item1, envVar.Item2);

if (workingDirectory != null)
startInfo.WorkingDirectory = workingDirectory;

Process process = new Process();
process.StartInfo = startInfo;
process.ErrorDataReceived += (sender, data) =>
{
if (data.Data != null)
{
onError?.Invoke(data.Data);
}
};
process.OutputDataReceived += (sender, data) =>
{
if (data.Data != null)
{
onOutput?.Invoke(data.Data);
}
};
if (!process.Start())
throw new Exception("Cannot start " + path);

if (onOutput != null)
process.BeginOutputReadLine();

if (onError != null)
process.BeginErrorReadLine();

//onInput?.Invoke(process.StandardInput);

try
{
await process.WaitForExitAsync(token);
}
catch (TaskCanceledException)
{
process.Kill();
}

return process.HasExited ? process.ExitCode : -1;
}
}
}
52 changes: 52 additions & 0 deletions WoWDatabaseEditor/Services/Processes/ProcessServiceExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace WoWDatabaseEditorCore.Services.Processes
{
public static class ProcessServiceExtensions
{
public static async Task<T?> RunAndGetJson<T>(this IProcessService service, string path, string arguments,
string? workingDirectory)
{
var output = await service.RunAndGetOutput(path, arguments, workingDirectory);
if (output == null)
return default;

T? t = JsonConvert.DeserializeObject<T>(output);
return t;
}

public static async Task<string?> RunAndGetOutput(this IProcessService service, string path, string arguments, string? workingDirectory)
{
StringBuilder sb = new();
bool any = false;
await service.Run(default, path, arguments, workingDirectory, s =>
{
sb.AppendLine(s);
any = true;
}, null);
if (any)
return sb.ToString();
return null;
}

public static async Task<string?> RunAndGetOutputAndError(this IProcessService service, string path, string arguments, string? workingDirectory)
{
StringBuilder sb = new();
bool any = false;
await service.Run(default, path, arguments, workingDirectory, s =>
{
sb.Append(s);
any = true;
}, s =>
{
sb.Append(s);
any = true;
});
if (any)
return sb.ToString();
return null;
}
}
}
23 changes: 23 additions & 0 deletions WoWDatabaseEditor/ViewModels/QuickStartViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using AsyncAwaitBestPractices.MVVM;
using Prism.Commands;
Expand All @@ -21,6 +22,7 @@
using WDE.MVVM.Observable;
using WoWDatabaseEditorCore.CoreVersion;
using WoWDatabaseEditorCore.Extensions;
using WoWDatabaseEditorCore.Services.DotNetUtils;
using WoWDatabaseEditorCore.Services.Http;
using WoWDatabaseEditorCore.Services.NewItemService;
using WoWDatabaseEditorCore.Services.Statistics;
Expand All @@ -32,6 +34,7 @@ public class QuickStartViewModel : ObservableBase, IDocument
private readonly ISolutionItemIconRegistry iconRegistry;
private readonly ISolutionItemNameRegistry nameRegistry;
private readonly IMostRecentlyUsedService mostRecentlyUsedService;
private readonly IDotNetService dotNetService;
private bool showGiveStarBox;
public AboutViewModel AboutViewModel { get; }
public ObservableCollection<NewItemPrototypeInfo> FlatItemPrototypes { get; } = new();
Expand Down Expand Up @@ -62,11 +65,14 @@ public QuickStartViewModel(ISolutionItemProvideService solutionItemProvideServic
IStatisticsService statisticsService,
IApplicationReleaseConfiguration applicationReleaseConfiguration,
IUrlOpenService urlOpenService,
IDotNetService dotNetService,
IWindowManager windowManager,
AboutViewModel aboutViewModel)
{
this.iconRegistry = iconRegistry;
this.nameRegistry = nameRegistry;
this.mostRecentlyUsedService = mostRecentlyUsedService;
this.dotNetService = dotNetService;
Wizards.AddRange(wizards.Where(w => w.IsCompatibleWithCore(currentCoreVersion.Current)));
HasWizards = Wizards.Count > 0;
AboutViewModel = aboutViewModel;
Expand Down Expand Up @@ -134,13 +140,28 @@ public QuickStartViewModel(ISolutionItemProvideService solutionItemProvideServic
mainThread.Dispatch(ReloadMruList);
}, true));

OpenDotNet6Website = new AsyncAutoCommand(async () =>
{
var url = dotNetService.DownloadDotNet6Link;
windowManager.OpenUrl(url.ToString());
});

CheckDotNet().ListenErrors();

ShowGiveStarBox = statisticsService.RunCounter > 20 &&
!applicationReleaseConfiguration.GetBool("SKIP_STAR_BOX").GetValueOrDefault() &&
!userSettings.Get<QuickStartSettings>().DismissedLeaveStarBox;

ReloadMruList();
}

private async Task CheckDotNet()
{
var isInstalled = await dotNetService.IsDotNet6Installed();
IsDotNet6Installed = isInstalled;
RaisePropertyChanged(nameof(IsDotNet6Installed));
}

private void ReloadMruList()
{
MostRecentlyUsedItems.Clear();
Expand All @@ -154,6 +175,8 @@ private void ReloadMruList()
}
}

public bool IsDotNet6Installed { get; private set; } = true;
public AsyncAutoCommand OpenDotNet6Website { get; }
public AsyncAutoCommand<NewItemPrototypeInfo> LoadItemCommand { get; }
public AsyncAutoCommand<IWizardProvider> LoadWizard { get; }
public AsyncAutoCommand<MostRecentlyUsedViewModel> OpenMostRecentlyUsedCommand { get; }
Expand Down
16 changes: 16 additions & 0 deletions WoWDatabaseEditorCore.Avalonia/Views/QuickStartView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,22 @@
</StackPanel>
</Border>

<Border Background="{DynamicResource SlightlyHighlightedBackground}"
CornerRadius="5"
Padding="10"
Margin="0,10"
BorderThickness="1"
IsVisible="{Binding IsDotNet6Installed, Converter={StaticResource InversedBoolConverter}}"
BorderBrush="Gray">
<StackPanel Orientation="Vertical" TextBlock.FontSize="13">
<TextBlock FontWeight="Bold" TextWrapping="WrapWithOverflow">You need to install .NET 6</TextBlock>
<TextBlock TextWrapping="WrapWithOverflow">In the near future the editor will require new .NET 6. You need to install it in order to continue using the editor. Why not do it now so that you are prepared?</TextBlock>
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
<Button Command="{Binding OpenDotNet6Website}" Margin="0,0,5,0">Okay 👍! (this will open the .net website)</Button>
</StackPanel>
</StackPanel>
</Border>

<Border Background="{DynamicResource SlightlyHighlightedBackground}"
CornerRadius="5"
Padding="10"
Expand Down

0 comments on commit 1bfbedc

Please sign in to comment.