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); } } }
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?"); }
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(); }); }
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 ApplyReleasesWithOneReleaseFile() { string tempDir; using (Utility.WithTempDirectory(out tempDir)) { string packagesDir = Path.Combine(tempDir, "theApp", "packages"); Directory.CreateDirectory(packagesDir); new[] { "Squirrel.Core.1.0.0.0-full.nupkg", "Squirrel.Core.1.1.0.0-full.nupkg", }.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(packagesDir, x))); var fixture = new UpdateManager("http://lol", "theApp", FrameworkVersion.Net40, tempDir, null, new FakeUrlDownloader()); var baseEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "Squirrel.Core.1.0.0.0-full.nupkg")); var latestFullEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "Squirrel.Core.1.1.0.0-full.nupkg")); var updateInfo = UpdateInfo.Create(baseEntry, new[] { latestFullEntry }, packagesDir, FrameworkVersion.Net40); updateInfo.ReleasesToApply.Contains(latestFullEntry).ShouldBeTrue(); using (fixture) { var progress = new ReplaySubject<int>(); fixture.ApplyReleases(updateInfo, progress).First(); this.Log().Info("Progress: [{0}]", String.Join(",", progress)); progress.Buffer(2,1).All(x => x.Count != 2 || x[1] > x[0]).First().ShouldBeTrue(); progress.Last().ShouldEqual(100); } var filesToFind = new[] { new {Name = "NLog.dll", Version = new Version("2.0.0.0")}, new {Name = "NSync.Core.dll", Version = new Version("1.1.0.0")}, new {Name = Path.Combine("sub", "Ionic.Zip.dll"), Version = new Version("1.9.1.8")}, }; filesToFind.ForEach(x => { var path = Path.Combine(tempDir, "theApp", "app-1.1.0.0", x.Name); this.Log().Info("Looking for {0}", path); File.Exists(path).ShouldBeTrue(); var vi = FileVersionInfo.GetVersionInfo(path); var verInfo = new Version(vi.FileVersion ?? "1.0.0.0"); x.Version.ShouldEqual(verInfo); }); } }
public void ExecutablesPinnedToTaskbarShouldPointToNewVersion() { string tempDir; using (Utility.WithTempDirectory(out tempDir)) { string packagesDir = Path.Combine(tempDir, "theApp", "packages"); Directory.CreateDirectory(packagesDir); new[] { "SampleUpdatingApp.1.0.0.0.nupkg", "SampleUpdatingApp.1.1.0.0.nupkg", }.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(packagesDir, x))); var fixture = new UpdateManager("http://lol", "theApp", FrameworkVersion.Net40, tempDir, null, new FakeUrlDownloader()); var baseEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "SampleUpdatingApp.1.0.0.0.nupkg")); var latestFullEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "SampleUpdatingApp.1.1.0.0.nupkg")); var updateInfo = UpdateInfo.Create(null, new[] { baseEntry }, packagesDir, FrameworkVersion.Net40); using (fixture) { fixture.ApplyReleases(updateInfo).ToList().First(); } var oldExecutable = Path.Combine(tempDir, "theApp", "app-1.0.0.0", "SampleUpdatingApp.exe"); File.Exists(oldExecutable).ShouldBeTrue(); TaskbarHelper.PinToTaskbar(oldExecutable); updateInfo = UpdateInfo.Create(baseEntry, new[] { latestFullEntry }, packagesDir, FrameworkVersion.Net40); using (fixture) { fixture.ApplyReleases(updateInfo).ToList().First(); } var newExecutable = Path.Combine(tempDir, "theApp", "app-1.1.0.0", "SampleUpdatingApp.exe"); File.Exists(newExecutable).ShouldBeTrue(); TaskbarHelper.IsPinnedToTaskbar(newExecutable).ShouldBeTrue(); Utility.Retry(() => TaskbarHelper.UnpinFromTaskbar(newExecutable)); } }