public void NoLocalReleasesFileMeansWeStartFromScratch()
        {
            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>();
            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());
        }
        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>(), 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);
            var result = default(UpdateInfo);

            using (fixture) {
                result = fixture.CheckForUpdate().First();
            }

            Assert.NotNull(result);
            Assert.Equal(1, result.ReleasesToApply.Single().Version.Major);
            Assert.Equal(1, result.ReleasesToApply.Single().Version.Minor);
        }
            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[] {
                        "Squirrel.Core.1.0.0.0-full.nupkg",
                        "Squirrel.Core.1.1.0.0-delta.nupkg",
                        "Squirrel.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 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 ApplyMultipleDeltaPackagesGeneratesCorrectHash()
        {
            var firstRelease = new ReleasePackage(IntegrationTestHelper.GetPath("fixtures", "SquirrelDesktopDemo-1.0.0-full.nupkg"), true);
            var secondRelease = new ReleasePackage(IntegrationTestHelper.GetPath("fixtures", "SquirrelDesktopDemo-1.1.0-full.nupkg"), true);
            var thirdRelease = new ReleasePackage(IntegrationTestHelper.GetPath("fixtures", "SquirrelDesktopDemo-1.2.0-full.nupkg"), true);

            string installDir, releasesDir;
            using(Utility.WithTempDirectory(out releasesDir))
            using (IntegrationTestHelper.WithFakeAlreadyInstalledApp("InstalledSquirrelDesktopDemo-1.0.0.zip", out installDir)) {

                var firstDelta = Path.Combine(releasesDir, "SquirrelDesktopDemo-1.1.0-delta.nupkg");
                var secondDelta = Path.Combine(releasesDir, "SquirrelDesktopDemo-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("InstalledSquirrelDesktopDemo-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.CalculateFileSHA1(reference);
                            var actualSha = Utility.CalculateFileSHA1(actual);

                            return new { File = actual, Result = refSha == actualSha };
                        })
                        .Where(c => !c.Result).ToArray();

                    Assert.Empty(invalidFiles);
                }
            }
        }
Beispiel #6
0
        static int Main(string[] args)
        {
            var command = "";
            var target = "";
            var appName = default(string);
            var showHelp = false;

            var opts = new OptionSet() {
                { "c|command=", "One of 'check' or 'update' to return the latest update information, or to perform the update", v => command = v },
                { "t|update-target=", "The URL or directory to download updates from", v => target = v },
                { "n|app-name=", "(Optional) The name of the application, only necessary if updater.exe isn't in the install directory", v => appName = v},
                { "h|help", "Show this message and exit", v => showHelp = v != null },
            };

            opts.Parse(args);

            if (!new[] { "check", "update", "install", }.Any(x => command.ToLowerInvariant() == x)) {
                Console.Error.WriteLine("Command must be either 'check' or 'update'");
                showHelp = true;
            }

            if (!Directory.Exists(target) && !File.Exists(target) && !isAUrl(target)) {
                Console.Error.WriteLine("Target must be either a directory or a URL to check for updates");
                showHelp = true;
            }

            if (showHelp) {
                Console.WriteLine("\nSquirrel.Updater.exe - Check for updates or update an application");
                Console.WriteLine(@"Usage: Squirrel.Updater.exe [options]");

                Console.WriteLine("Options:");
                foreach(var v in opts) {
                    if (v.GetNames().Length != 2) {
                        Console.WriteLine("  --{0} - {1}", v.GetNames()[0], v.Description);
                    } else {
                        Console.WriteLine("  -{0}/--{1} - {2}", v.GetNames()[0], v.GetNames()[1], v.Description);
                    }
                }

                return 0;
            }

            appName = appName ?? determineAppName();
            using (var mgr = new UpdateManager(target, appName, FrameworkVersion.Net40)) {
                if (command.ToLowerInvariant() == "check") {
                    var updateInfo = default(UpdateInfo);
                    try {
                        updateInfo = mgr.CheckForUpdate().First();

                        if (updateInfo.ReleasesToApply.Count > 0) {
                            mgr.DownloadReleases(updateInfo.ReleasesToApply).First();
                        }
                    } catch (Exception ex) {
                        writeJsonForException(ex, "Failed to check for updates");
                        return -1;
                    }

                    var releaseNotes = new Dictionary<ReleaseEntry, string>();

                    try {
                        releaseNotes = (updateInfo.ReleasesToApply.Count > 0) ? updateInfo.FetchReleaseNotes() : releaseNotes;
                    } catch (Exception ex) {
                        // TODO: Find a way to log this
                    }

                    Console.WriteLine(JsonConvert.SerializeObject(new {
                        UpdateInfo = updateInfo,
                        ReleaseNotes = releaseNotes,
                    }));

                    return 0;
                }

                if (command.ToLowerInvariant() == "update") {
                    var result = default(ReleaseEntry);
                    try {
                        result = mgr.UpdateAppAsync().Result;
                    } catch (Exception ex) {
                        writeJsonForException(ex, "Failed to update application");
                        return -1;
                    }

                    Console.WriteLine(JsonConvert.SerializeObject(result));
                    return 0;
                }

                if (command.ToLowerInvariant() == "install") {
                    var targetRelease = ReleaseEntry.GenerateFromFile(target);
                    mgr.ApplyReleases(UpdateInfo.Create(null, new[] { targetRelease }, Path.GetDirectoryName(target), FrameworkVersion.Net40)).First();
                    return 0;
                }
            }

            throw new Exception("How even did we get here?");
        }
Beispiel #7
0
        private IObservable<UpdateAvailableNotification> CheckForUpdates()
        {
            return Observable.Create<UpdateAvailableNotification>(async obs =>
            {
                string updateUrl = AppModel.GetRegistrySetting("UpdateUrl");
                Log.DebugFormat("UpdateUrl = {0}", updateUrl);
                using (var updateManager = new UpdateManager(updateUrl ?? @"http://bradleygrainger.com/GitBlame/download", "GitBlame", FrameworkVersion.Net45))
                {
                    try
                    {
                        UpdateInfo updateInfo = await updateManager.CheckForUpdate();
                        var releases = updateInfo == null ? new List<ReleaseEntry>() : updateInfo.ReleasesToApply.ToList();
                        if (updateInfo == null)
                            Log.Info("CheckForUpdate returned (null)");
                        else
                            Log.InfoFormat("CheckForUpdate: Current=({0}), Future=({1}), {2} ReleasesToApply", ToLog(updateInfo.CurrentlyInstalledVersion), ToLog(updateInfo.FutureReleaseEntry), releases.Count);

                        if (releases.Count != 0)
                        {
                            await updateManager.DownloadReleases(releases);
                            Log.Info("Downloaded releases");
                            var results = await updateManager.ApplyReleases(updateInfo);
                            Log.InfoFormat("ApplyReleases: {0}", string.Join(", ", results));

                            if (results.Any())
                            {
                                string newPath = results[0];
                                VisualStudioIntegration.ReintegrateWithVisualStudio(newPath);
                                obs.OnNext(new UpdateAvailableNotification(newPath));
                            }
                        }
                    }
                    catch (InvalidOperationException ex)
                    {
                        // Squirrel throws an InvalidOperationException (wrapping the underlying exception) if anything goes wrong
                        Log.ErrorFormat("CheckForUpdates failed: {0}", ex, ex.Message);
                    }
                    catch (TimeoutException ex)
                    {
                        // Failed to check for updates; try again the next time the app is run
                        Log.ErrorFormat("CheckForUpdates timed out: {0}", ex, ex.Message);
                    }
                }
                obs.OnCompleted();
            });
        }
            public void WhenUrlResultsInWebExceptionReturnNull()
            {
                // This should result in a WebException (which gets caught) unless you can actually access http://lol

                var fixture = new UpdateManager("http://lol", "theApp", FrameworkVersion.Net45);

                var updateInfo = fixture.CheckForUpdate().Wait();

                Assert.Null(updateInfo);
            }
            public void WhenUrlTimesOutReturnNull()
            {
                var fixture = new UpdateManager("http://lol", "theApp", FrameworkVersion.Net45, null, null, new TimingOutUrlDownloader());

                var updateInfo = fixture.CheckForUpdate().Wait();

                Assert.Null(updateInfo);
            }
            public void WhenReleasesFileIsBlankReturnNull()
            {
                string tempDir;
                using (Utility.WithTempDirectory(out tempDir)) {
                    var fixture = new UpdateManager(tempDir, "MyAppName", FrameworkVersion.Net40);
                    File.WriteAllText(Path.Combine(tempDir, "RELEASES"), "");

                    using (fixture) {
                        Assert.Null(fixture.CheckForUpdate().Wait());
                    }
                }
            }
            public void WhenReleasesFileDoesntExistThrowACustomError()
            {
                string tempDir;
                using (Utility.WithTempDirectory(out tempDir)) {
                    var fixture = new UpdateManager(tempDir, "MyAppName", FrameworkVersion.Net40);

                    using (fixture) {
                        Assert.Throws<SquirrelConfigurationException>(
                            () => fixture.CheckForUpdate().Wait());
                    }
                }
            }
            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<SquirrelConfigurationException>(
                            () => 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[] {
                        "Squirrel.Core.1.0.0.0-full.nupkg",
                    }.ForEach(x =>
                    {
                        var path = IntegrationTestHelper.GetPath("fixtures", x);
                        File.Copy(path, Path.Combine(localPackages, x));
                    });

                    new[] {
                        "Squirrel.Core.1.0.0.0-full.nupkg",
                        "Squirrel.Core.1.1.0.0-delta.nupkg",
                        "Squirrel.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);
                    }
                }

            }
Beispiel #14
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);
        }