Skip to content

Commit

Permalink
Hostess: MainWindowInstallPackagesCommand에서 Steps Player 분리
Browse files Browse the repository at this point in the history
  • Loading branch information
rkttu committed Jan 15, 2024
1 parent dbabc14 commit 0768457
Show file tree
Hide file tree
Showing 6 changed files with 246 additions and 219 deletions.
3 changes: 2 additions & 1 deletion src/Hostess/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ private void ConfigureServices(IServiceCollection services)
.AddSingleton<IResourceResolver, ResourceResolver>()
.AddSingleton<IResourceCacheManager, ResourceCacheManager>()
.AddSingleton<ICommandLineArguments, CommandLineArguments>()
.AddSingleton<IStepsComposer, StepsComposer>();
.AddSingleton<IStepsComposer, StepsComposer>()
.AddSingleton<IStepsPlayer, StepsPlayer>();

// Shared Commands
services
Expand Down
220 changes: 9 additions & 211 deletions src/Hostess/Commands/MainWindow/MainWindowInstallPackagesCommand.cs
Original file line number Diff line number Diff line change
@@ -1,231 +1,29 @@
using Hostess.Components;
using Hostess.ViewModels;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using TableCloth;
using TableCloth.Resources;

namespace Hostess.Commands.MainWindow
{
public sealed class MainWindowInstallPackagesCommand : ViewModelCommandBase<MainWindowViewModel>
{
public MainWindowInstallPackagesCommand(
IResourceCacheManager resourceCacheManager,
IAppMessageBox appMessageBox,
ISharedLocations sharedLocations,
ICommandLineArguments commandLineArguments)
IStepsPlayer stepsPlayer)
{
_resourceCacheManager = resourceCacheManager;
_appMessageBox = appMessageBox;
_sharedLocations = sharedLocations;
_commandLineArguments = commandLineArguments;
_stepsPlayer = stepsPlayer;
}

private readonly IResourceCacheManager _resourceCacheManager;
private readonly IAppMessageBox _appMessageBox;
private readonly ISharedLocations _sharedLocations;
private readonly ICommandLineArguments _commandLineArguments;

private bool _isRunning = false;
private readonly IStepsPlayer _stepsPlayer;

protected override bool EvaluateCanExecute()
=> !_isRunning;
=> !_stepsPlayer.IsRunning;

public override async void Execute(MainWindowViewModel viewModel)
{
try
{
_isRunning = true;
var hasAnyFailure = false;
var catalog = _resourceCacheManager.CatalogDocument;

foreach (InstallItemViewModel eachItem in viewModel.InstallItems)
{
try
{
if (eachItem.InstallItemType == InstallItemType.DownloadAndInstall)
await ProcessDownloadAndInstallAsync(eachItem);
else if (eachItem.InstallItemType == InstallItemType.PowerShellScript)
await ProcessPowerShellScriptAsync(eachItem);
else if (eachItem.InstallItemType == InstallItemType.OpenWebSite)
await OpenAddInWebSiteAsync(eachItem);
else if (eachItem.InstallItemType == InstallItemType.CustomAction)
await eachItem.CustomAction?.Invoke(eachItem);

eachItem.StatusMessage = UIStringResources.Hostess_Install_Succeed;
eachItem.Installed = true;
eachItem.ErrorMessage = null;
}
catch (Exception ex)
{
hasAnyFailure = true;
eachItem.StatusMessage = UIStringResources.Hostess_Install_Failed;
eachItem.Installed = false;
eachItem.ErrorMessage = ex is AggregateException exception ? exception.InnerException.Message : ex.Message;
await Task.Delay(100);
}
}

if (!hasAnyFailure)
{
var parsedArgs = _commandLineArguments.Current;
var targets = parsedArgs.SelectedServices;

foreach (var eachUrl in catalog.Services.Where(x => targets.Contains(x.Id)).Select(x => x.Url))
await OpenRequestedWebSite(eachUrl);

viewModel.RequestClose(this);
return;
}
}
catch (Exception ex)
{
_appMessageBox.DisplayError(ex, true);
}
finally
{
_isRunning = false;
}
}

private async Task ProcessDownloadAndInstallAsync(InstallItemViewModel eachItem)
{
var parsedArgs = _commandLineArguments.Current;

eachItem.Installed = null;
eachItem.StatusMessage = UIStringResources.Hostess_Download_InProgress;

var downloadFolderPath = _sharedLocations.GetDownloadDirectoryPath();
var tempFileName = $"installer_{Guid.NewGuid():n}.exe";
var tempFilePath = System.IO.Path.Combine(downloadFolderPath, tempFileName);

if (File.Exists(tempFilePath))
File.Delete(tempFilePath);

using (var webClient = new WebClient())
{
webClient.Headers.Add("Accept", "text/html,application/xhtml+xml,application/xml");
webClient.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Trident/7.0; rv:11.0) like Gecko");
await webClient.DownloadFileTaskAsync(eachItem.PackageUrl, tempFilePath).ConfigureAwait(false);

eachItem.StatusMessage = UIStringResources.Hostess_Install_InProgress;

if (parsedArgs.DryRun)
{
await Task.Delay(TimeSpan.FromSeconds(1d)).ConfigureAwait(false);
return;
}

var psi = new ProcessStartInfo(tempFilePath, eachItem.Arguments)
{
UseShellExecute = false,
};

var cpSource = new TaskCompletionSource<int>();
using (var process = new Process() { StartInfo = psi, })
{
process.EnableRaisingEvents = true;
process.Exited += (_sender, _e) =>
{
var realSender = _sender as Process;
cpSource.SetResult(realSender.ExitCode);
};

if (!process.Start())
throw new ApplicationException(ErrorStrings.Error_Package_CanNotStart);

await cpSource.Task.ConfigureAwait(false);
}
}
}

private async Task ProcessPowerShellScriptAsync(InstallItemViewModel eachItem)
{
var parsedArgs = _commandLineArguments.Current;

eachItem.Installed = null;
eachItem.StatusMessage = UIStringResources.Hostess_Install_InProgress;

var downloadFolderPath = _sharedLocations.GetDownloadDirectoryPath();
var tempFileName = $"bootstrap_{Guid.NewGuid():n}.ps1";
var tempFilePath = System.IO.Path.Combine(downloadFolderPath, tempFileName);

if (File.Exists(tempFilePath))
File.Delete(tempFilePath);

File.WriteAllText(tempFilePath, eachItem.ScriptContent, Encoding.Unicode);
var powershellPath = _sharedLocations.GetDefaultPowerShellExecutableFilePath();

if (!File.Exists(powershellPath))
throw new Exception(ErrorStrings.Error_No_WindowsPowerShell);

if (parsedArgs.DryRun)
{
await Task.Delay(TimeSpan.FromSeconds(1d)).ConfigureAwait(false);
return;
}

var psi = new ProcessStartInfo(powershellPath, $"Set-ExecutionPolicy Bypass -Scope Process -Force; {tempFilePath}")
{
UseShellExecute = false,
};

var cpSource = new TaskCompletionSource<int>();
using (var process = new Process() { StartInfo = psi, })
{
process.EnableRaisingEvents = true;
process.Exited += (_sender, _e) =>
{
var realSender = _sender as Process;
cpSource.SetResult(realSender.ExitCode);
};

if (!process.Start())
throw new ApplicationException(ErrorStrings.Error_Package_CanNotStart);

await cpSource.Task.ConfigureAwait(false);
}
}

private async Task OpenAddInWebSiteAsync(InstallItemViewModel viewModel)
{
var parsedArgs = _commandLineArguments.Current;

if (parsedArgs.DryRun)
{
await Task.Delay(TimeSpan.FromSeconds(1d)).ConfigureAwait(false);
return;
}

Process.Start(new ProcessStartInfo(viewModel.PackageUrl)
{
UseShellExecute = true,
WindowStyle = ProcessWindowStyle.Maximized,
});
}

private async Task OpenRequestedWebSite(string targetUrl)
{
var parsedArgs = _commandLineArguments.Current;

if (parsedArgs.DryRun)
{
await Task.Delay(TimeSpan.FromSeconds(1d)).ConfigureAwait(false);
return;
}
var hasAnyFailure = await _stepsPlayer.PlayStepsAsync(
viewModel.InstallItems,
viewModel.ShowDryRunNotification);

Process.Start(new ProcessStartInfo(targetUrl)
{
UseShellExecute = true,
WindowStyle = ProcessWindowStyle.Maximized,
});
if (!hasAnyFailure)
viewModel.RequestClose(this);
}
}
}
9 changes: 8 additions & 1 deletion src/Hostess/Components/IStepsPlayer.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
using Hostess.ViewModels;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace Hostess.Components
{
public interface IStepsPlayer
{
bool PlaySteps(IEnumerable<InstallItemViewModel> composedSteps);
bool IsRunning { get; }

Task<bool> PlayStepsAsync(
IEnumerable<InstallItemViewModel> composedSteps,
bool dryRun,
CancellationToken cancellationToken = default);
}
}
12 changes: 6 additions & 6 deletions src/Hostess/Components/Implementations/StepsComposer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public IEnumerable<InstallItemViewModel> ComposeSteps()
InstallItemType = InstallItemType.CustomAction,
TargetSiteName = UIStringResources.Option_Prerequisites,
PackageName = UIStringResources.Install_VerifyEnvironment,
CustomAction = VerifyWindowsContainerEnvironment,
CustomAction = VerifyWindowsContainerEnvironmentAsync,
},
new InstallItemViewModel()
{
Expand All @@ -70,14 +70,14 @@ public IEnumerable<InstallItemViewModel> ComposeSteps()
InstallItemType = InstallItemType.CustomAction,
TargetSiteName = UIStringResources.Option_Prerequisites,
PackageName = UIStringResources.Install_TryProtectCriticalServices,
CustomAction = TryProtectCriticalServices,
CustomAction = TryProtectCriticalServicesAsync,
},
new InstallItemViewModel()
{
InstallItemType = InstallItemType.CustomAction,
TargetSiteName = UIStringResources.Option_Prerequisites,
PackageName = UIStringResources.Install_SetDesktopWallpaper,
CustomAction = SetDesktopWallpaper,
CustomAction = SetDesktopWallpaperAsync,
},
};

Expand Down Expand Up @@ -243,7 +243,7 @@ private async Task EnableIEModeAsync(InstallItemViewModel viewModel)
}
}

private async Task VerifyWindowsContainerEnvironment(InstallItemViewModel viewModel)
private async Task VerifyWindowsContainerEnvironmentAsync(InstallItemViewModel viewModel)
{
var parsedArgs = _commandLineArguments.Current;

Expand All @@ -264,7 +264,7 @@ private async Task VerifyWindowsContainerEnvironment(InstallItemViewModel viewMo
}
}

private async Task TryProtectCriticalServices(InstallItemViewModel viewModel)
private async Task TryProtectCriticalServicesAsync(InstallItemViewModel viewModel)
{
var parsedArgs = _commandLineArguments.Current;

Expand All @@ -283,7 +283,7 @@ private async Task TryProtectCriticalServices(InstallItemViewModel viewModel)
catch (Exception ex) { _appMessageBox.DisplayError(ex, false); }
}

private async Task SetDesktopWallpaper(InstallItemViewModel viewModel)
private async Task SetDesktopWallpaperAsync(InstallItemViewModel viewModel)
{
var parsedArgs = _commandLineArguments.Current;

Expand Down
Loading

0 comments on commit 0768457

Please sign in to comment.