diff --git a/MigrationTools.lutconfig b/MigrationTools.lutconfig new file mode 100644 index 000000000..596a86030 --- /dev/null +++ b/MigrationTools.lutconfig @@ -0,0 +1,6 @@ + + + true + true + 180000 + \ No newline at end of file diff --git a/src/MigrationTools.ConsoleFull/Properties/launchSettings.json b/src/MigrationTools.ConsoleFull/Properties/launchSettings.json index 94a32d219..39b593d20 100644 --- a/src/MigrationTools.ConsoleFull/Properties/launchSettings.json +++ b/src/MigrationTools.ConsoleFull/Properties/launchSettings.json @@ -10,7 +10,7 @@ }, "init": { "commandName": "Project", - "commandLineArgs": "init --options Full" + "commandLineArgs": "init --options Full skipVersionCheck" }, "execute2": { "commandName": "Project", diff --git a/src/MigrationTools.Host.Tests/Services/DetectVersionService2Tests.cs b/src/MigrationTools.Host.Tests/Services/DetectVersionService2Tests.cs new file mode 100644 index 000000000..5719a0a66 --- /dev/null +++ b/src/MigrationTools.Host.Tests/Services/DetectVersionService2Tests.cs @@ -0,0 +1,42 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MigrationTools.Services; +using Serilog; +using Serilog.Events; + +namespace MigrationTools.Host.Services.Tests +{ + [TestClass] + public class DetectVersionService2Tests + { + + [TestInitialize] + public void Setup() + { + var loggers = new LoggerConfiguration().MinimumLevel.Verbose().Enrich.FromLogContext(); + loggers.WriteTo.Logger(logger => logger + .WriteTo.Debug(restrictedToMinimumLevel: LogEventLevel.Verbose)); + Log.Logger = loggers.CreateLogger(); + Log.Logger.Information("Logger is initialized"); + } + + + [TestMethod, TestCategory("L3")] + public void DetectVersionServiceTest_Initialise() + { + IDetectVersionService2 dos = new DetectVersionService2(new TelemetryLoggerMock()); + Assert.IsNotNull(dos); + } + + [TestMethod, TestCategory("L3")] + public void DetectVersionServiceTest_Update() + { + IDetectVersionService2 dos = new DetectVersionService2(new TelemetryLoggerMock()); + dos.UpdateFromSource(); + + Assert.IsNotNull(dos); + } + + + } +} \ No newline at end of file diff --git a/src/MigrationTools.Host/MigrationToolHost.cs b/src/MigrationTools.Host/MigrationToolHost.cs index b55bcc6ed..b7c7f1e41 100644 --- a/src/MigrationTools.Host/MigrationToolHost.cs +++ b/src/MigrationTools.Host/MigrationToolHost.cs @@ -97,7 +97,8 @@ public static IHostBuilder CreateDefaultBuilder(string[] args) // Services services.AddTransient(); - services.AddTransient(); + //services.AddTransient(); + services.AddTransient(); // Config services.AddSingleton(); diff --git a/src/MigrationTools.Host/Services/DetectVersionService2.cs b/src/MigrationTools.Host/Services/DetectVersionService2.cs new file mode 100644 index 000000000..e29a9cc4d --- /dev/null +++ b/src/MigrationTools.Host/Services/DetectVersionService2.cs @@ -0,0 +1,173 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Threading; +using Microsoft.ApplicationInsights.DataContracts; +using Microsoft.Extensions.Logging; +using MigrationTools.DataContracts.Pipelines; +using MigrationTools.EndpointEnrichers; +using NuGet.Common; +using NuGet.Configuration; +using NuGet.Protocol; +using NuGet.Protocol.Core.Types; +using NuGet.Versioning; +using Serilog; +using WGetNET; +using Version = System.Version; + +namespace MigrationTools.Host.Services +{ + public class DetectVersionService2 : IDetectVersionService2 + { + private readonly ITelemetryLogger _Telemetry; + + public string PackageId { get; set; } + + private WinGetPackageManager packageManager; + private WinGetPackage package = null; + + public Version RunningVersion { get; private set; } + public Version AvailableVersion { get; private set; } + + public Version InstalledVersion { get; private set; } + + public bool IsPackageInstalled { get; private set; } = false; + + public bool IsPackageManagerInstalled { get; private set; } = false; + + public bool IsUpdateAvailable { + get + { + return (InstalledVersion < AvailableVersion); + } + } + + public bool IsRunningInDebug + { + get + { + return RunningVersion == new Version("0.0.0.1"); + } + } + + public bool IsNewLocalVersionAvailable + { + get + { + return (RunningVersion >= InstalledVersion); + } + } + + public DetectVersionService2(ITelemetryLogger telemetry) + { + _Telemetry = telemetry; + PackageId = "nkdAgility.AzureDevOpsMigrationTools"; + + RunningVersion = Assembly.GetEntryAssembly().GetName().Version; + InitialiseService(); + } + + private void InitialiseService() + { + DateTime startTime = DateTime.Now; + using (var bench = new Benchmark("DetectVersionService2::InitialiseService")) + { + ////////////////////////////////// + WinGetInfo wingetInfo = new WinGetInfo(); + IsPackageManagerInstalled = wingetInfo.WinGetInstalled; + if (IsPackageManagerInstalled) + { + Log.Debug("The Windows Package Manager is installed!"); + packageManager = new WinGetPackageManager(); + } + try + { + + if (IsPackageManagerInstalled) + { + Log.Debug("Searching for package!"); + package = packageManager.GetInstalledPackages(PackageId).GroupBy(e => e.Id, (id, g) => g.First()).SingleOrDefault(); + if (package != null) + { + AvailableVersion = new Version(package.AvailableVersion); + InstalledVersion = new Version(package.Version); + Log.Debug("Found package with id {PackageId}", PackageId); + IsPackageInstalled = true; + } + _Telemetry.TrackDependency(new DependencyTelemetry("PackageRepository", "winget", PackageId, AvailableVersion == null ? "nullVersion" : AvailableVersion.ToString(), startTime, bench.Elapsed, "200", IsPackageInstalled)); + } + } + catch (Exception ex) + { + Log.Error(ex, "DetectVersionService"); + IsPackageInstalled = false; + _Telemetry.TrackDependency(new DependencyTelemetry("PackageRepository", "winget", PackageId, AvailableVersion == null ? "nullVersion" : AvailableVersion.ToString(), startTime, bench.Elapsed, "500", IsPackageInstalled)); + } + } + } + + + public void UpdateFromSource() + { + using (var bench = new Benchmark("DetectVersionService2::UpdateFromSource")) + { + if (IsPackageInstalled && IsUpdateAvailable) + { + Log.Information("Running winget update {PackageId} from v{InstalledVersion} to v{AvailableVersion}", PackageId, InstalledVersion, AvailableVersion); + System.Threading.Tasks.Task t = packageManager.UpgradePackageAsync(PackageId); + while (!t.IsCompleted) + { + Log.Information("Update running..."); + System.Threading.Thread.Sleep(3000); + } + Log.Information("Update Complete..."); + InitialiseService(); + } + else if (!IsPackageInstalled) + { + Log.Information("Running winget install {PackageId} from v{InstalledVersion} to v{AvailableVersion}", PackageId, InstalledVersion, AvailableVersion); + + System.Threading.Tasks.Task t = packageManager.InstallPackageAsync(PackageId); + while (!t.IsCompleted) + { + Log.Information("Install running..."); + System.Threading.Thread.Sleep(5000); + } + Log.Information("Install Complete..."); + InitialiseService(); + } + } + + } + } + + public class Benchmark : IDisposable + { + private readonly Stopwatch timer = new Stopwatch(); + private readonly string benchmarkName; + + public TimeSpan Elapsed + { + get + { + return timer.Elapsed; + } + } + + public Benchmark(string benchmarkName) + { + this.benchmarkName = benchmarkName; + timer.Start(); + Log.Debug("{benchmarkName}||START", benchmarkName); + } + + public void Dispose() + { + timer.Stop(); + Log.Debug("{benchmarkName}||STOP Elapsed: {timerElapsed}", benchmarkName, timer.Elapsed); + } + } + +} \ No newline at end of file diff --git a/src/MigrationTools.Host/Services/IDetectVersionService2.cs b/src/MigrationTools.Host/Services/IDetectVersionService2.cs new file mode 100644 index 000000000..f9dca4b29 --- /dev/null +++ b/src/MigrationTools.Host/Services/IDetectVersionService2.cs @@ -0,0 +1,24 @@ +using System; + +namespace MigrationTools.Host.Services +{ + public interface IDetectVersionService2 + { + public Version AvailableVersion { get; } + + public Version InstalledVersion { get; } + +public Version RunningVersion { get; } + + public bool IsPackageManagerInstalled { get; } + + public bool IsPackageInstalled { get; } + string PackageId { get; } + + bool IsUpdateAvailable { get; } + bool IsRunningInDebug { get; } + bool IsNewLocalVersionAvailable { get; } + + void UpdateFromSource(); + } +} \ No newline at end of file diff --git a/src/MigrationTools.Host/StartupService.cs b/src/MigrationTools.Host/StartupService.cs index b43a58fd0..0e36403b9 100644 --- a/src/MigrationTools.Host/StartupService.cs +++ b/src/MigrationTools.Host/StartupService.cs @@ -1,11 +1,15 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.Eventing.Reader; using System.Linq; using System.Reflection; +using System.Threading; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using MigrationTools.Host.Services; +using Serilog; +using static System.Net.Mime.MediaTypeNames; namespace MigrationTools.Host { @@ -20,12 +24,12 @@ internal class StartupService : IStartupService { private readonly IHostApplicationLifetime _LifeTime; private readonly IDetectOnlineService _detectOnlineService; - private readonly IDetectVersionService _detectVersionService; + private readonly IDetectVersionService2 _detectVersionService; private readonly ILogger _logger; private readonly ITelemetryLogger _telemetryLogger; private static Stopwatch _mainTimer = new Stopwatch(); - public StartupService(IHostApplicationLifetime lifeTime, IDetectOnlineService detectOnlineService, IDetectVersionService detectVersionService, ILogger logger, ITelemetryLogger telemetryLogger) + public StartupService(IHostApplicationLifetime lifeTime, IDetectOnlineService detectOnlineService, IDetectVersionService2 detectVersionService, ILogger logger, ITelemetryLogger telemetryLogger) { _LifeTime = lifeTime; _detectOnlineService = detectOnlineService; @@ -38,28 +42,64 @@ public void RunStartupLogic(string[] args) { ApplicationStartup(args); Configure(_LifeTime); - if (_detectOnlineService.IsOnline()) + if (_detectOnlineService.IsOnline() && !args.Contains("skipVersionCheck")) { - Version latestVersion = _detectVersionService.GetLatestVersion(); + Log.Verbose("Package Management Info:"); + Log.Debug(" IsPackageManagerInstalled: {IsPackageManagerInstalled}", _detectVersionService.IsPackageManagerInstalled); + Log.Debug(" IsPackageInstalled: {IsPackageInstalled}", _detectVersionService.IsPackageInstalled); + Log.Debug(" IsUpdateAvailable: {IsUpdateAvailable}", _detectVersionService.IsUpdateAvailable); + Log.Debug(" IsNewLocalVersionAvailable: {IsNewLocalVersionAvailable}", _detectVersionService.IsNewLocalVersionAvailable); + Log.Debug(" IsRunningInDebug: {IsRunningInDebug}", _detectVersionService.IsRunningInDebug); + Log.Verbose("Full version data: ${_detectVersionService}", _detectVersionService); - _logger.LogInformation($"Latest version detected as {{{nameof(latestVersion)}}}", latestVersion); - var version = Assembly.GetEntryAssembly().GetName().Version; - if (latestVersion > version) + Log.Information("Verion Info:"); + Log.Information(" Running: {RunningVersion}", _detectVersionService.RunningVersion); + Log.Information(" Installed: {InstalledVersion}", _detectVersionService.InstalledVersion); + Log.Information(" Available: {AvailableVersion}", _detectVersionService.AvailableVersion); + + + if (!_detectVersionService.IsPackageManagerInstalled) { - _logger.LogWarning("You are currently running version {Version} and a newer version ({LatestVersion}) is available. You should update now using Winget command 'winget nkdAgility.AzureDevOpsMigrationTools' from the Windows Terminal.", version, latestVersion); - if (!args.Contains("skipVersionCheck")) - { -#if !DEBUG - Console.WriteLine("Do you want to continue? (y/n)"); - if (Console.ReadKey().Key != ConsoleKey.Y) + Log.Warning("Windows Client: The Windows Package Manager is not installed, we use it to determine if you have the latest version, and to make sure that this application is up to date. You can download and install it from https://aka.ms/getwinget. After which you can call `winget install {PackageId}` from the Windows Terminal to get a manged version of this program.", _detectVersionService.PackageId); + Log.Warning("Windows Server: If you are running on Windows Server you can use the experimental version of Winget, or you can still use Chocolatey to manage the install. Install chocolatey from https://chocolatey.org/install and then use `choco install vsts-sync-migrator` to install, and `choco upgrade vsts-sync-migrator` to upgrade to newer versions.", _detectVersionService.PackageId); + } else + { + if (!_detectVersionService.IsPackageInstalled) { - _logger.LogWarning("User aborted to update version"); - throw new Exception("User Abort"); - + Log.Information("It looks like this application has been installed from a zip, would you like to use the managed version?"); + Console.WriteLine("Do you want install the managed version? (y/n)"); + if (Console.ReadKey().Key == ConsoleKey.Y) + { + + _detectVersionService.UpdateFromSource(); + } } -#endif + if (_detectVersionService.IsUpdateAvailable && _detectVersionService.IsPackageInstalled) + { + Log.Information("It looks like this application has been installed from a zip, would you like to use the managed version from Winget?"); + Console.WriteLine("Do you want install the managed version? (y/n)"); + if (Console.ReadKey().Key == ConsoleKey.Y) + { + _detectVersionService.UpdateFromSource(); + } + } + if (_detectVersionService.IsNewLocalVersionAvailable && _detectVersionService.IsPackageInstalled) + { + Log.Information("It looks like this package ({PackageId}) has been updated locally to version {InstalledVersion} and you are not running the latest version?", _detectVersionService.PackageId, _detectVersionService.InstalledVersion); + Console.WriteLine("Do you want to quit and restart? (y/n)"); + if (Console.ReadKey().Key == ConsoleKey.Y) + { + Log.Information("Restarting as {CommandLine}", Environment.CommandLine); + Process.Start("devopsmigration", string.Join(" ", Environment.GetCommandLineArgs().Skip(1))); + Thread.Sleep(2000); + Environment.Exit(0); + } } } + } else + { + /// not online or you have specified not to + Log.Warning("You are either not online or have chosen `skipVersionCheck`. We will not check for a newer version of the tools.", _detectVersionService.PackageId); } }