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()));
                }
            });
        }
Exemple #5
0
        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);
            }
Exemple #9
0
        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);
        }
Exemple #14
0
        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));
        }
Exemple #15
0
        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;
        }
Exemple #16
0
        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);
                }
            }
        }