async Task <List <string> > executeInstall(string currentAssemblyDir, IPackage bundledPackageMetadata, IObserver <int> progress = null) { var fxVersion = determineFxVersionFromPackage(bundledPackageMetadata); var eigenUpdater = new UpdateManager(currentAssemblyDir, bundledPackageMetadata.Id, fxVersion, TargetRootDirectory); var eigenCheckProgress = new Subject <int>(); var eigenCopyFileProgress = new Subject <int>(); var eigenApplyProgress = new Subject <int>(); var realCheckProgress = new Subject <int>(); var realCopyFileProgress = new Subject <int>(); var realApplyProgress = new Subject <int>(); // The real update takes longer than the eigenupdate because we're // downloading from the Internet instead of doing everything // locally, so give it more weight Observable.Concat( Observable.Concat(eigenCheckProgress, eigenCopyFileProgress, eigenCopyFileProgress).Select(x => (x / 3.0) * 0.33), Observable.Concat(realCheckProgress, realCopyFileProgress, realApplyProgress).Select(x => (x / 3.0) * 0.67)) .Select(x => (int)x) .Subscribe(progress); List <string> ret = null; var updateInfo = await eigenUpdater.CheckForUpdate(progress : eigenCheckProgress); await eigenUpdater.DownloadReleases(updateInfo.ReleasesToApply, eigenCopyFileProgress); ret = await eigenUpdater.ApplyReleases(updateInfo, eigenApplyProgress); eigenUpdater.Dispose(); var updateUrl = bundledPackageMetadata.ProjectUrl != null?bundledPackageMetadata.ProjectUrl.ToString() : null; updateUrl = null; //XXX REMOVE ME if (updateUrl == null) { realCheckProgress.OnNext(100); realCheckProgress.OnCompleted(); realCopyFileProgress.OnNext(100); realCopyFileProgress.OnCompleted(); realApplyProgress.OnNext(100); realApplyProgress.OnCompleted(); return(ret); } var realUpdater = new UpdateManager(updateUrl, bundledPackageMetadata.Id, fxVersion, TargetRootDirectory); try { updateInfo = await realUpdater.CheckForUpdate(progress : realCheckProgress); await realUpdater.DownloadReleases(updateInfo.ReleasesToApply, realCopyFileProgress); await realUpdater.ApplyReleases(updateInfo, realApplyProgress); } catch (Exception ex) { log.ErrorException("Failed to update to latest remote version", ex); return(new List <string>()); } realUpdater.Dispose(); return(ret); }
public void CorruptRemoteFileShouldThrowOnCheck() { string localPackagesDir = Path.Combine(".", "theApp", "packages"); string localReleasesFile = Path.Combine(localPackagesDir, "RELEASES"); var fileInfo = new Mock<FileInfoBase>(); fileInfo.Setup(x => x.Exists).Returns(false); var dirInfo = new Mock<DirectoryInfoBase>(); dirInfo.Setup(x => x.Exists).Returns(true); var fs = new Mock<IFileSystemFactory>(); fs.Setup(x => x.GetFileInfo(localReleasesFile)).Returns(fileInfo.Object); fs.Setup(x => x.CreateDirectoryRecursive(localPackagesDir)).Verifiable(); fs.Setup(x => x.DeleteDirectoryRecursive(localPackagesDir)).Verifiable(); fs.Setup(x => x.GetDirectoryInfo(localPackagesDir)).Returns(dirInfo.Object); var urlDownloader = new Mock<IUrlDownloader>(); urlDownloader.Setup(x => x.DownloadUrl(It.IsAny<string>(), It.IsAny<IObserver<int>>())) .Returns(Observable.Return("lol this isn't right")); var fixture = new UpdateManager("http://lol", "theApp", FrameworkVersion.Net40, ".", fs.Object, urlDownloader.Object); using (fixture) { Assert.Throws<Exception>(() => fixture.CheckForUpdate().First()); } }
public void CorruptedReleaseFileMeansWeStartFromScratch() { string localPackagesDir = Path.Combine(".", "theApp", "packages"); string localReleasesFile = Path.Combine(localPackagesDir, "RELEASES"); var fileInfo = new Mock<FileInfoBase>(); fileInfo.Setup(x => x.Exists).Returns(true); fileInfo.Setup(x => x.OpenRead()) .Returns(new MemoryStream(Encoding.UTF8.GetBytes("lol this isn't right"))); var dirInfo = new Mock<DirectoryInfoBase>(); dirInfo.Setup(x => x.Exists).Returns(true); var fs = new Mock<IFileSystemFactory>(); fs.Setup(x => x.GetFileInfo(localReleasesFile)).Returns(fileInfo.Object); fs.Setup(x => x.CreateDirectoryRecursive(localPackagesDir)).Verifiable(); fs.Setup(x => x.DeleteDirectoryRecursive(localPackagesDir)).Verifiable(); fs.Setup(x => x.GetDirectoryInfo(localPackagesDir)).Returns(dirInfo.Object); var urlDownloader = new Mock<IUrlDownloader>(); var dlPath = IntegrationTestHelper.GetPath("fixtures", "RELEASES-OnePointOne"); urlDownloader.Setup(x => x.DownloadUrl(It.IsAny<string>(), It.IsAny<IObserver<int>>())) .Returns(Observable.Return(File.ReadAllText(dlPath, Encoding.UTF8))); var fixture = new UpdateManager("http://lol", "theApp", FrameworkVersion.Net40, ".", fs.Object, urlDownloader.Object); using (fixture) { fixture.CheckForUpdate().First(); } fs.Verify(x => x.CreateDirectoryRecursive(localPackagesDir), Times.Once()); fs.Verify(x => x.DeleteDirectoryRecursive(localPackagesDir), Times.Once()); }
private void button1_Click(object sender, EventArgs e) { if (_updateManager != null) _updateManager.Dispose(); _updateManager = new UpdateManager( "https://raw.github.com/belfryimages/shimmer-test-winforms-client/master/Releases/", //@"..\..\..\..\update-path\", "ShimmerTestApplication", FrameworkVersion.Net40); _updateManager.CheckForUpdate().Subscribe(updateInfo => { if (updateInfo == null) { MessageBox.Show("No updates found"); return; } MessageBox.Show(string.Format("Found {0} releases to apply", updateInfo.ReleasesToApply.Count())); foreach (var r in updateInfo.ReleasesToApply) { MessageBox.Show(string.Format( "{0} {1}", r.PackageName, r.Version.ToString())); } }); }
async Task<List<string>> executeInstall(string currentAssemblyDir, IPackage bundledPackageMetadata, IObserver<int> progress = null) { var fxVersion = determineFxVersionFromPackage(bundledPackageMetadata); var eigenUpdater = new UpdateManager(currentAssemblyDir, bundledPackageMetadata.Id, fxVersion, TargetRootDirectory); var eigenCheckProgress = new Subject<int>(); var eigenCopyFileProgress = new Subject<int>(); var eigenApplyProgress = new Subject<int>(); var realCheckProgress = new Subject<int>(); var realCopyFileProgress = new Subject<int>(); var realApplyProgress = new Subject<int>(); // The real update takes longer than the eigenupdate because we're // downloading from the Internet instead of doing everything // locally, so give it more weight Observable.Concat( Observable.Concat(eigenCheckProgress, eigenCopyFileProgress, eigenCopyFileProgress).Select(x => (x / 3.0) * 0.33), Observable.Concat(realCheckProgress, realCopyFileProgress, realApplyProgress).Select(x => (x / 3.0) * 0.67)) .Select(x => (int)x) .Subscribe(progress); List<string> ret = null; var updateInfo = await eigenUpdater.CheckForUpdate(progress: eigenCheckProgress); await eigenUpdater.DownloadReleases(updateInfo.ReleasesToApply, eigenCopyFileProgress); ret = await eigenUpdater.ApplyReleases(updateInfo, eigenApplyProgress); eigenUpdater.Dispose(); var updateUrl = bundledPackageMetadata.ProjectUrl != null ? bundledPackageMetadata.ProjectUrl.ToString() : null; updateUrl = null; //XXX REMOVE ME if (updateUrl == null) { realCheckProgress.OnNext(100); realCheckProgress.OnCompleted(); realCopyFileProgress.OnNext(100); realCopyFileProgress.OnCompleted(); realApplyProgress.OnNext(100); realApplyProgress.OnCompleted(); return ret; } var realUpdater = new UpdateManager(updateUrl, bundledPackageMetadata.Id, fxVersion, TargetRootDirectory); try { updateInfo = await realUpdater.CheckForUpdate(progress: realCheckProgress); await realUpdater.DownloadReleases(updateInfo.ReleasesToApply, realCopyFileProgress); await realUpdater.ApplyReleases(updateInfo, realApplyProgress); } catch (Exception ex) { log.ErrorException("Failed to update to latest remote version", ex); return new List<string>(); } realUpdater.Dispose(); return ret; }
public void WhenBothFilesAreInSyncNoUpdatesAreApplied() { string tempDir; using (Utility.WithTempDirectory(out tempDir)) { var localPackages = Path.Combine(tempDir, "theApp", "packages"); var remotePackages = Path.Combine(tempDir, "releases"); Directory.CreateDirectory(localPackages); Directory.CreateDirectory(remotePackages); new[] { "Shimmer.Core.1.0.0.0-full.nupkg", "Shimmer.Core.1.1.0.0-delta.nupkg", "Shimmer.Core.1.1.0.0-full.nupkg", }.ForEach(x => { var path = IntegrationTestHelper.GetPath("fixtures", x); File.Copy(path, Path.Combine(localPackages, x)); File.Copy(path, Path.Combine(remotePackages, x)); }); var urlDownloader = new Mock<IUrlDownloader>(); var fixture = new UpdateManager(remotePackages, "theApp", FrameworkVersion.Net40, tempDir, null, urlDownloader.Object); UpdateInfo updateInfo; using (fixture) { // sync both release files fixture.UpdateLocalReleasesFile().Last(); ReleaseEntry.BuildReleasesFile(remotePackages); // check for an update updateInfo = fixture.CheckForUpdate().Wait(); } Assert.NotNull(updateInfo); Assert.Empty(updateInfo.ReleasesToApply); } }
public void NoLocalDirectoryMeansWeStartFromScratch() { string localPackagesDir = Path.Combine(".", "theApp", "packages"); string localReleasesFile = Path.Combine(localPackagesDir, "RELEASES"); var fileInfo = new Mock<FileInfoBase>(); fileInfo.Setup(x => x.Exists).Returns(false); var dirInfo = new Mock<DirectoryInfoBase>(); dirInfo.Setup(x => x.Exists).Returns(false); var fs = new Mock<IFileSystemFactory>(); fs.Setup(x => x.GetFileInfo(localReleasesFile)).Returns(fileInfo.Object); fs.Setup(x => x.CreateDirectoryRecursive(It.IsAny<string>())).Verifiable(); fs.Setup(x => x.GetDirectoryInfo(localPackagesDir)).Returns(dirInfo.Object); var urlDownloader = new Mock<IUrlDownloader>(); var dlPath = IntegrationTestHelper.GetPath("fixtures", "RELEASES-OnePointOne"); urlDownloader.Setup(x => x.DownloadUrl(It.IsAny<string>())) .Returns(Observable.Return(File.ReadAllText(dlPath, Encoding.UTF8))); var fixture = new UpdateManager("http://lol", "theApp", ".", fs.Object, urlDownloader.Object); using (fixture.AcquireUpdateLock()) { fixture.CheckForUpdate().First(); } fs.Verify(x => x.CreateDirectoryRecursive(localPackagesDir), Times.Once()); }
public void NewReleasesShouldBeDetected() { string localReleasesFile = Path.Combine(".", "theApp", "packages", "RELEASES"); var fileInfo = new Mock<FileInfoBase>(); fileInfo.Setup(x => x.OpenRead()) .Returns(File.OpenRead(IntegrationTestHelper.GetPath("fixtures", "RELEASES-OnePointOh"))); var fs = new Mock<IFileSystemFactory>(); fs.Setup(x => x.GetFileInfo(localReleasesFile)).Returns(fileInfo.Object); var urlDownloader = new Mock<IUrlDownloader>(); var dlPath = IntegrationTestHelper.GetPath("fixtures", "RELEASES-OnePointOne"); urlDownloader.Setup(x => x.DownloadUrl(It.IsAny<string>())) .Returns(Observable.Return(File.ReadAllText(dlPath, Encoding.UTF8))); var fixture = new UpdateManager("http://lol", "theApp", ".", fs.Object, urlDownloader.Object); var result = default(UpdateInfo); using (fixture.AcquireUpdateLock()) { result = fixture.CheckForUpdate().First(); } Assert.NotNull(result); Assert.Equal(1, result.ReleasesToApply.Single().Version.Major); Assert.Equal(1, result.ReleasesToApply.Single().Version.Minor); }
public IObservable <List <string> > ExecuteInstall(string currentAssemblyDir, IPackage bundledPackageMetadata, IObserver <int> progress = null) { progress = progress ?? new Subject <int>(); // NB: This bit of code is a bit clever. The binaries that WiX // has installed *itself* meets the qualifications for being a // Shimmer update directory (a RELEASES file and the corresponding // NuGet packages). // // So, in order to reuse some code and not write the same things // twice we're going to "Eigenupdate" from our own directory; // UpdateManager will operate in bootstrap mode and create a // local directory for us. // // Then, we create a *new* UpdateManager whose target is the normal // update URL - we can then apply delta updates against the bundled // NuGet package to get up to vCurrent. The reason we go through // this rigamarole is so that developers don't have to rebuild the // installer as often (never, technically). return(Observable.Start(() => { var fxVersion = determineFxVersionFromPackage(bundledPackageMetadata); var eigenUpdater = new UpdateManager(currentAssemblyDir, bundledPackageMetadata.Id, fxVersion, TargetRootDirectory); var eigenCheckProgress = new Subject <int>(); var eigenCopyFileProgress = new Subject <int>(); var eigenApplyProgress = new Subject <int>(); var realCheckProgress = new Subject <int>(); var realCopyFileProgress = new Subject <int>(); var realApplyProgress = new Subject <int>(); // The real update takes longer than the eigenupdate because we're // downloading from the Internet instead of doing everything // locally, so give it more weight Observable.Concat( Observable.Concat(eigenCheckProgress, eigenCopyFileProgress, eigenCopyFileProgress).Select(x => (x / 3.0) * 0.33), Observable.Concat(realCheckProgress, realCopyFileProgress, realApplyProgress).Select(x => (x / 3.0) * 0.67)) .Select(x => (int)x) .Subscribe(progress); List <string> ret = null; ret = eigenUpdater.CheckForUpdate(progress: eigenCheckProgress) .SelectMany(x => eigenUpdater.DownloadReleases(x.ReleasesToApply, eigenCopyFileProgress).Select(_ => x)) .SelectMany(x => eigenUpdater.ApplyReleases(x, eigenApplyProgress)) .First(); eigenUpdater.Dispose(); var updateUrl = bundledPackageMetadata.ProjectUrl != null ? bundledPackageMetadata.ProjectUrl.ToString() : null; updateUrl = null; //XXX REMOVE ME if (updateUrl == null) { realCheckProgress.OnNext(100); realCheckProgress.OnCompleted(); realCopyFileProgress.OnNext(100); realCopyFileProgress.OnCompleted(); realApplyProgress.OnNext(100); realApplyProgress.OnCompleted(); return ret; } var realUpdater = new UpdateManager(updateUrl, bundledPackageMetadata.Id, fxVersion, TargetRootDirectory); realUpdater.CheckForUpdate(progress: realCheckProgress) .SelectMany(x => realUpdater.DownloadReleases(x.ReleasesToApply, realCopyFileProgress).Select(_ => x)) .SelectMany(x => realUpdater.ApplyReleases(x, realApplyProgress)) .LoggedCatch <List <string>, InstallManager, Exception>(this, ex => { // NB: If we don't do this, we won't Collapse the Wave // Function(tm) below on 'progress' and it will never complete realCheckProgress.OnError(ex); return Observable.Return(Enumerable.Empty <string>().ToList()); }, "Failed to update to latest remote version") .First(); realUpdater.Dispose(); return ret; }).ObserveOn(RxApp.DeferredScheduler)); }
public void WhenFolderDoesNotExistThrowHelpfulError() { string tempDir; using (Utility.WithTempDirectory(out tempDir)) { var directory = Path.Combine(tempDir, "missing-folder"); var fixture = new UpdateManager(directory, "MyAppName", FrameworkVersion.Net40); using (fixture) { Assert.Throws<ShimmerConfigurationException>( () => fixture.CheckForUpdate().Wait()); } } }
public void WhenTwoRemoteUpdatesAreAvailableChoosesDeltaVersion() { string tempDir; using (Utility.WithTempDirectory(out tempDir)) { var localPackages = Path.Combine(tempDir, "theApp", "packages"); var remotePackages = Path.Combine(tempDir, "releases"); Directory.CreateDirectory(localPackages); Directory.CreateDirectory(remotePackages); new[] { "Shimmer.Core.1.0.0.0-full.nupkg", }.ForEach(x => { var path = IntegrationTestHelper.GetPath("fixtures", x); File.Copy(path, Path.Combine(localPackages, x)); }); new[] { "Shimmer.Core.1.0.0.0-full.nupkg", "Shimmer.Core.1.1.0.0-delta.nupkg", "Shimmer.Core.1.1.0.0-full.nupkg", }.ForEach(x => { var path = IntegrationTestHelper.GetPath("fixtures", x); File.Copy(path, Path.Combine(remotePackages, x)); }); var urlDownloader = new Mock<IUrlDownloader>(); var fixture = new UpdateManager(remotePackages, "theApp", FrameworkVersion.Net40, tempDir, null, urlDownloader.Object); UpdateInfo updateInfo; using (fixture) { // sync both release files fixture.UpdateLocalReleasesFile().Last(); ReleaseEntry.BuildReleasesFile(remotePackages); updateInfo = fixture.CheckForUpdate().Wait(); Assert.True(updateInfo.ReleasesToApply.First().IsDelta); updateInfo = fixture.CheckForUpdate(ignoreDeltaUpdates:true).Wait(); Assert.False(updateInfo.ReleasesToApply.First().IsDelta); } } }
public void WhenReleasesFileDoesntExistThrowACustomError() { string tempDir; using (Utility.WithTempDirectory(out tempDir)) { var fixture = new UpdateManager(tempDir, "MyAppName", FrameworkVersion.Net40); using (fixture) { Assert.Throws<ShimmerConfigurationException>( () => fixture.CheckForUpdate().Wait()); } } }
public IObservable<List<string>> ExecuteInstall(string currentAssemblyDir, IPackage bundledPackageMetadata, IObserver<int> progress = null) { progress = progress ?? new Subject<int>(); // NB: This bit of code is a bit clever. The binaries that WiX // has installed *itself* meets the qualifications for being a // Shimmer update directory (a RELEASES file and the corresponding // NuGet packages). // // So, in order to reuse some code and not write the same things // twice we're going to "Eigenupdate" from our own directory; // UpdateManager will operate in bootstrap mode and create a // local directory for us. // // Then, we create a *new* UpdateManager whose target is the normal // update URL - we can then apply delta updates against the bundled // NuGet package to get up to vCurrent. The reason we go through // this rigamarole is so that developers don't have to rebuild the // installer as often (never, technically). return Observable.Start(() => { var fxVersion = determineFxVersionFromPackage(bundledPackageMetadata); var eigenUpdater = new UpdateManager(currentAssemblyDir, bundledPackageMetadata.Id, fxVersion, TargetRootDirectory); var eigenCheckProgress = new Subject<int>(); var eigenCopyFileProgress = new Subject<int>(); var eigenApplyProgress = new Subject<int>(); var realCheckProgress = new Subject<int>(); var realCopyFileProgress = new Subject<int>(); var realApplyProgress = new Subject<int>(); // The real update takes longer than the eigenupdate because we're // downloading from the Internet instead of doing everything // locally, so give it more weight Observable.Concat( Observable.Concat(eigenCheckProgress, eigenCopyFileProgress, eigenCopyFileProgress).Select(x => (x / 3.0) * 0.33), Observable.Concat(realCheckProgress, realCopyFileProgress, realApplyProgress).Select(x => (x / 3.0) * 0.67)) .Select(x => (int)x) .Subscribe(progress); List<string> ret = null; ret = eigenUpdater.CheckForUpdate(progress: eigenCheckProgress) .SelectMany(x => eigenUpdater.DownloadReleases(x.ReleasesToApply, eigenCopyFileProgress).Select(_ => x)) .SelectMany(x => eigenUpdater.ApplyReleases(x, eigenApplyProgress)) .First(); eigenUpdater.Dispose(); var updateUrl = bundledPackageMetadata.ProjectUrl != null ? bundledPackageMetadata.ProjectUrl.ToString() : null; updateUrl = null; //XXX REMOVE ME if (updateUrl == null) { realCheckProgress.OnNext(100); realCheckProgress.OnCompleted(); realCopyFileProgress.OnNext(100); realCopyFileProgress.OnCompleted(); realApplyProgress.OnNext(100); realApplyProgress.OnCompleted(); return ret; } var realUpdater = new UpdateManager(updateUrl, bundledPackageMetadata.Id, fxVersion, TargetRootDirectory); realUpdater.CheckForUpdate(progress: realCheckProgress) .SelectMany(x => realUpdater.DownloadReleases(x.ReleasesToApply, realCopyFileProgress).Select(_ => x)) .SelectMany(x => realUpdater.ApplyReleases(x, realApplyProgress)) .LoggedCatch<List<string>, InstallManager, Exception>(this, ex => { // NB: If we don't do this, we won't Collapse the Wave // Function(tm) below on 'progress' and it will never complete realCheckProgress.OnError(ex); return Observable.Return(Enumerable.Empty<string>().ToList()); }, "Failed to update to latest remote version") .First(); realUpdater.Dispose(); return ret; }).ObserveOn(RxApp.DeferredScheduler); }
public WixUiBootstrapper(IWiXEvents wixEvents, TinyIoCContainer testKernel = null, IRoutingState router = null) { Kernel = testKernel ?? createDefaultKernel(); Kernel.Register<IWixUiBootstrapper>(this).AsSingleton(); Kernel.Register<IScreen>(this); Kernel.Register(wixEvents); Router = router ?? new RoutingState(); WiXEvents = wixEvents; _BundledRelease = new Lazy<ReleaseEntry>(readBundledReleasesFile); registerExtensionDlls(Kernel); RxApp.ConfigureServiceLocator( (type, contract) => Kernel.Resolve(type, contract), (type, contract) => Kernel.ResolveAll(type), (c, t, s) => Kernel.Register(t, c, s)); UserError.RegisterHandler(ex => { if (wixEvents.Command.Display != Display.Full) { this.Log().Error(ex.ErrorMessage); wixEvents.ShouldQuit(); } var errorVm = RxApp.GetService<IErrorViewModel>(); errorVm.Error = ex; Router.Navigate.Execute(errorVm); return Observable.Return(RecoveryOptionResult.CancelOperation); }); var bundledPackageMetadata = openBundledPackage(); wixEvents.DetectPackageCompleteObs.Subscribe(eventArgs => { var error = convertHResultToError(eventArgs.Status); if (error != null) { UserError.Throw(error); return; } // TODO: If the app is already installed, run it and bail if (wixEvents.Command.Action == LaunchAction.Uninstall) { var updateManager = new UpdateManager("http://lol", BundledRelease.PackageName, FrameworkVersion.Net40); var updateLock = updateManager.AcquireUpdateLock(); updateManager.FullUninstall() .ObserveOn(RxApp.DeferredScheduler) .Log(this, "Failed uninstall") .Finally(updateLock.Dispose) .Subscribe( _ => wixEvents.Engine.Plan(LaunchAction.Uninstall), ex => UserError.Throw("Failed to uninstall application", ex), () => wixEvents.ShouldQuit()); return; } if (wixEvents.Command.Action == LaunchAction.Install) { if (wixEvents.Command.Display != Display.Full) { wixEvents.Engine.Plan(LaunchAction.Install); return; } var welcomeVm = RxApp.GetService<IWelcomeViewModel>(); welcomeVm.PackageMetadata = bundledPackageMetadata; welcomeVm.ShouldProceed.Subscribe(_ => wixEvents.Engine.Plan(LaunchAction.Install)); Router.Navigate.Execute(welcomeVm); } }); wixEvents.PlanCompleteObs.Subscribe(eventArgs => { var error = convertHResultToError(eventArgs.Status); if (error != null) { UserError.Throw(error); return; } if (wixEvents.Command.Action != LaunchAction.Install) { wixEvents.Engine.Apply(wixEvents.MainWindowHwnd); return; } // NB: Create a dummy subject to receive progress if we're in silent mode IObserver<int> progress = new Subject<int>(); if (wixEvents.Command.Display == Display.Full) { var installingVm = RxApp.GetService<IInstallingViewModel>(); progress = installingVm.ProgressValue; installingVm.PackageMetadata = bundledPackageMetadata; Router.Navigate.Execute(installingVm); } // NB: This bit of code is a bit clever. The binaries that WiX // has installed *itself* meets the qualifications for being a // Shimmer update directory (a RELEASES file and the corresponding // NuGet packages). // // So, in order to reuse some code and not write the same things // twice we're going to "Eigenupdate" from our own directory; // UpdateManager will operate in bootstrap mode and create a // local directory for us. // // Then, we create a *new* UpdateManager whose target is the normal // update URL - we can then apply delta updates against the bundled // NuGet package to get up to vCurrent. The reason we go through // this rigamarole is so that developers don't have to rebuild the // installer as often (never, technically). var fxVersion = determineFxVersionFromPackage(bundledPackageMetadata); var eigenUpdater = new UpdateManager(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), BundledRelease.PackageName, fxVersion); var eigenLock = eigenUpdater.AcquireUpdateLock(); var eigenUpdateProgress = eigenUpdater.CheckForUpdate() .SelectMany(x => eigenUpdater.DownloadReleases(x.ReleasesToApply)) .Finally(eigenLock.Dispose) .Select(x => x / 2) .Multicast(new Subject<int>()); eigenUpdateProgress.Subscribe(progress); eigenUpdateProgress.Connect(); var realUpdateProgress = eigenUpdateProgress.TakeLast(1) .SelectMany(_ => { var realUpdateManager = new UpdateManager(bundledPackageMetadata.ProjectUrl.ToString(), BundledRelease.PackageName, fxVersion); return realUpdateManager.CheckForUpdate() .SelectMany(updateInfo => { return Observable.Concat( realUpdateManager.DownloadReleases(updateInfo.ReleasesToApply).Select(x => x * 0.75), realUpdateManager.ApplyReleases(updateInfo).Select(x => x * 0.25 + 75)) .Select(x => (int)x); }).LoggedCatch(this, Observable.Return(100), "Failed to update to latest remote version"); }) .Select(x => x / 2 + 50) .Multicast(new Subject<int>()); realUpdateProgress.Subscribe(progress); realUpdateProgress.Connect(); realUpdateProgress .TakeLast(1) .ObserveOn(RxApp.DeferredScheduler) .Subscribe( _ => wixEvents.Engine.Apply(wixEvents.MainWindowHwnd), ex => UserError.Throw("Failed to install application", ex)); }); wixEvents.ApplyCompleteObs.Subscribe(eventArgs => { var error = convertHResultToError(eventArgs.Status); if (error != null) { UserError.Throw(error); return; } if (wixEvents.Command.Display != Display.Full) { wixEvents.ShouldQuit(); } // TODO: Figure out what the "main app" is and run it }); wixEvents.ErrorObs.Subscribe(eventArgs => UserError.Throw("An installation error has occurred: " + eventArgs.ErrorMessage)); }
async Task<List<string>> executeInstall( string currentAssemblyDir, IPackage bundledPackageMetadata, bool ignoreDeltaUpdates = false, IObserver<int> progress = null) { var fxVersion = bundledPackageMetadata.DetectFrameworkVersion(); var eigenCheckProgress = new Subject<int>(); var eigenCopyFileProgress = new Subject<int>(); var eigenApplyProgress = new Subject<int>(); var realCheckProgress = new Subject<int>(); var realCopyFileProgress = new Subject<int>(); var realApplyProgress = new Subject<int>(); List<string> ret = null; using (var eigenUpdater = new UpdateManager( currentAssemblyDir, bundledPackageMetadata.Id, fxVersion, TargetRootDirectory)) { // The real update takes longer than the eigenupdate because we're // downloading from the Internet instead of doing everything // locally, so give it more weight Observable.Concat( Observable.Concat(eigenCheckProgress, eigenCopyFileProgress, eigenCopyFileProgress) .Select(x => (x/3.0)*0.33), Observable.Concat(realCheckProgress, realCopyFileProgress, realApplyProgress) .Select(x => (x/3.0)*0.67)) .Select(x => (int) x) .Subscribe(progress); var updateInfo = await eigenUpdater.CheckForUpdate(ignoreDeltaUpdates, eigenCheckProgress); log.Info("The checking of releases completed - and there was much rejoicing"); if (!updateInfo.ReleasesToApply.Any()) { var rootDirectory = TargetRootDirectory ?? Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); var version = updateInfo.CurrentlyInstalledVersion; var releaseFolder = String.Format("app-{0}", version.Version); var absoluteFolder = Path.Combine(rootDirectory, version.PackageName, releaseFolder); if (!Directory.Exists(absoluteFolder)) { log.Warn("executeInstall: the directory {0} doesn't exist - cannot find the current app?!!?"); } else { return Directory.GetFiles(absoluteFolder, "*.exe", SearchOption.TopDirectoryOnly) .ToList(); } } foreach (var u in updateInfo.ReleasesToApply) { log.Info("HEY! We should be applying update {0}", u.Filename); } await eigenUpdater.DownloadReleases(updateInfo.ReleasesToApply, eigenCopyFileProgress); log.Info("The downloading of releases completed - and there was much rejoicing"); ret = await eigenUpdater.ApplyReleases(updateInfo, eigenApplyProgress); log.Info("The applying of releases completed - and there was much rejoicing"); } var updateUrl = bundledPackageMetadata.ProjectUrl != null ? bundledPackageMetadata.ProjectUrl.ToString() : null; updateUrl = null; //XXX REMOVE ME if (updateUrl == null) { realCheckProgress.OnNext(100); realCheckProgress.OnCompleted(); realCopyFileProgress.OnNext(100); realCopyFileProgress.OnCompleted(); realApplyProgress.OnNext(100); realApplyProgress.OnCompleted(); return ret; } using(var realUpdater = new UpdateManager( updateUrl, bundledPackageMetadata.Id, fxVersion, TargetRootDirectory)) { try { var updateInfo = await realUpdater.CheckForUpdate(progress: realCheckProgress); await realUpdater.DownloadReleases(updateInfo.ReleasesToApply, realCopyFileProgress); await realUpdater.ApplyReleases(updateInfo, realApplyProgress); } catch (Exception ex) { log.ErrorException("Failed to update to latest remote version", ex); return new List<string>(); } } return ret; }
public void ApplyMultipleDeltaPackagesGeneratesCorrectHash() { var firstRelease = new ReleasePackage(IntegrationTestHelper.GetPath("fixtures", "ShimmerDesktopDemo-1.0.0-full.nupkg"), true); var secondRelease = new ReleasePackage(IntegrationTestHelper.GetPath("fixtures", "ShimmerDesktopDemo-1.1.0-full.nupkg"), true); var thirdRelease = new ReleasePackage(IntegrationTestHelper.GetPath("fixtures", "ShimmerDesktopDemo-1.2.0-full.nupkg"), true); string installDir, releasesDir; using(Utility.WithTempDirectory(out releasesDir)) using (IntegrationTestHelper.WithFakeAlreadyInstalledApp("InstalledShimmerDesktopDemo-1.0.0.zip", out installDir)) { var firstDelta = Path.Combine(releasesDir, "ShimmerDesktopDemo-1.1.0-delta.nupkg"); var secondDelta = Path.Combine(releasesDir, "ShimmerDesktopDemo-1.2.0-delta.nupkg"); new[] { firstRelease, secondRelease, thirdRelease } .ForEach(file => { var packageFile = file.ReleasePackageFile; var fileName = Path.GetFileName(packageFile); File.Copy(packageFile, Path.Combine(releasesDir, fileName)); }); var deltaBuilder = new DeltaPackageBuilder(); deltaBuilder.CreateDeltaPackage(firstRelease, secondRelease, firstDelta); deltaBuilder.CreateDeltaPackage(secondRelease, thirdRelease, secondDelta); ReleaseEntry.BuildReleasesFile(releasesDir); var updateManager = new UpdateManager( releasesDir, "ShimmerDesktopDemo", FrameworkVersion.Net40, installDir); using (updateManager) { var updateInfo = updateManager.CheckForUpdate().First(); Assert.Equal(2, updateInfo.ReleasesToApply.Count()); updateManager.DownloadReleases(updateInfo.ReleasesToApply).Wait(); updateManager.ApplyReleases(updateInfo).Wait(); } string referenceDir; using (IntegrationTestHelper.WithFakeAlreadyInstalledApp("InstalledShimmerDesktopDemo-1.2.0.zip", out referenceDir)) { var referenceVersion = Path.Combine(referenceDir, "ShimmerDesktopDemo", "app-1.2.0"); var installVersion = Path.Combine(installDir, "ShimmerDesktopDemo", "app-1.2.0"); var referenceFiles = Directory.GetFiles(referenceVersion); var actualFiles = Directory.GetFiles(installVersion); Assert.Equal(referenceFiles.Count(), actualFiles.Count()); var invalidFiles = Enumerable.Zip(referenceFiles, actualFiles, (reference, actual) => { var refSha = Utility.CalculateStreamSHA1(File.OpenRead(reference)); var actualSha = Utility.CalculateStreamSHA1(File.OpenRead(actual)); return new { File = actual, Result = refSha == actualSha }; }) .Where(c => !c.Result).ToArray(); Assert.Empty(invalidFiles); } } }