IObservable <UpdateInfo> checkForUpdate(bool ignoreDeltaUpdates = false, IObserver <int> progress = null) { IEnumerable <ReleaseEntry> localReleases = Enumerable.Empty <ReleaseEntry>(); progress = progress ?? new Subject <int>(); try { var file = fileSystem.GetFileInfo(LocalReleaseFile).OpenRead(); // NB: sr disposes file using (var sr = new StreamReader(file, Encoding.UTF8)) { localReleases = ReleaseEntry.ParseReleaseFile(sr.ReadToEnd()); } } catch (Exception ex) { // Something has gone wrong, we'll start from scratch. log.WarnException("Failed to load local release list", ex); initializeClientAppDirectory(); } IObservable <string> releaseFile; // Fetch the remote RELEASES file, whether it's a local dir or an // HTTP URL try { if (isHttpUrl(updateUrlOrPath)) { log.Info("Downloading RELEASES file from {0}", updateUrlOrPath); releaseFile = urlDownloader.DownloadUrl(String.Format("{0}/{1}", updateUrlOrPath, "RELEASES"), progress); } else { log.Info("Reading RELEASES file from {0}", updateUrlOrPath); var fi = fileSystem.GetFileInfo(Path.Combine(updateUrlOrPath, "RELEASES")); using (var sr = new StreamReader(fi.OpenRead(), Encoding.UTF8)) { var text = sr.ReadToEnd(); releaseFile = Observable.Return(text); } progress.OnNext(100); progress.OnCompleted(); } } catch (Exception ex) { progress.OnCompleted(); return(Observable.Throw <UpdateInfo>(ex)); } var ret = releaseFile .Select(ReleaseEntry.ParseReleaseFile) .SelectMany(releases => determineUpdateInfo(localReleases, releases, ignoreDeltaUpdates)) .PublishLast(); ret.Connect(); return(ret); }
IObservable <UpdateInfo> determineUpdateInfo(IEnumerable <ReleaseEntry> localReleases, IEnumerable <ReleaseEntry> remoteReleases, bool ignoreDeltaUpdates) { localReleases = localReleases ?? Enumerable.Empty <ReleaseEntry>(); if (remoteReleases == null) { log.Warn("Release information couldn't be determined due to remote corrupt RELEASES file"); return(Observable.Throw <UpdateInfo>(new Exception("Corrupt remote RELEASES file"))); } if (localReleases.Count() == remoteReleases.Count()) { log.Info("No updates, remote and local are the same"); return(Observable.Return <UpdateInfo>(null)); } if (ignoreDeltaUpdates) { remoteReleases = remoteReleases.Where(x => !x.IsDelta); } if (!localReleases.Any()) { log.Warn("First run or local directory is corrupt, starting from scratch"); var latestFullRelease = findCurrentVersion(remoteReleases); return(Observable.Return(UpdateInfo.Create(findCurrentVersion(localReleases), new[] { latestFullRelease }, PackageDirectory, appFrameworkVersion))); } if (localReleases.Max(x => x.Version) >= remoteReleases.Max(x => x.Version)) { log.Warn("hwhat, local version is greater than remote version"); var latestFullRelease = findCurrentVersion(remoteReleases); return(Observable.Return(UpdateInfo.Create(findCurrentVersion(localReleases), new[] { latestFullRelease }, PackageDirectory, appFrameworkVersion))); } return(Observable.Return(UpdateInfo.Create(findCurrentVersion(localReleases), remoteReleases, PackageDirectory, appFrameworkVersion))); }
IEnumerable <ShortcutCreationRequest> uninstallAppVersion(IAppSetup app, Version ver) { try { app.OnVersionUninstalling(ver); } catch (Exception ex) { log.ErrorException("App threw exception on uninstall: " + app.GetType().FullName, ex); } var shortcuts = Enumerable.Empty <ShortcutCreationRequest>(); try { shortcuts = app.GetAppShortcutList(); } catch (Exception ex) { log.ErrorException("App threw exception on shortcut uninstall: " + app.GetType().FullName, ex); } // Get the list of shortcuts that *should've* been there, but aren't; // this means that the user deleted them by hand and that they should // stay dead return(shortcuts.Aggregate(new List <ShortcutCreationRequest>(), (acc, x) => { var path = x.GetLinkTarget(applicationName); var fi = fileSystem.GetFileInfo(path); if (fi.Exists) { fi.Delete(); log.Info("Deleting shortcut: {0}", fi.FullName); } else { acc.Add(x); log.Info("Shortcut not found: {0}, capturing for future reference", fi.FullName); } return acc; })); }
IObservable <UpdateInfo> checkForUpdate(bool ignoreDeltaUpdates = false, IObserver <int> progress = null) { var localReleases = Enumerable.Empty <ReleaseEntry>(); progress = progress ?? new Subject <int>(); try { var file = fileSystem.GetFileInfo(LocalReleaseFile).OpenRead(); // NB: sr disposes file using (var sr = new StreamReader(file, Encoding.UTF8)) { localReleases = ReleaseEntry.ParseReleaseFile(sr.ReadToEnd()); } } catch (Exception ex) { // Something has gone wrong, we'll start from scratch. log.WarnException("Failed to load local release list", ex); initializeClientAppDirectory(); } IObservable <string> releaseFile; // Fetch the remote RELEASES file, whether it's a local dir or an // HTTP URL try { if (isHttpUrl(updateUrlOrPath)) { log.Info("Downloading RELEASES file from {0}", updateUrlOrPath); releaseFile = urlDownloader.DownloadUrl(String.Format("{0}/{1}", updateUrlOrPath, "RELEASES"), progress) .Catch <string, TimeoutException>(ex => { log.Info("Download timed out (returning blank release list)"); return(Observable.Return(String.Empty)); }) .Catch <string, WebException>(ex => { log.InfoException("Download resulted in WebException (returning blank release list)", ex); return(Observable.Return(String.Empty)); }); } else { log.Info("Reading RELEASES file from {0}", updateUrlOrPath); if (!fileSystem.GetDirectoryInfo(updateUrlOrPath).Exists) { var message = String.Format( "The directory {0} does not exist, something is probably broken with your application", updateUrlOrPath); var ex = new SquirrelConfigurationException(message); return(Observable.Throw <UpdateInfo>(ex)); } var fi = fileSystem.GetFileInfo(Path.Combine(updateUrlOrPath, "RELEASES")); if (!fi.Exists) { var message = String.Format( "The file {0} does not exist, something is probably broken with your application", fi.FullName); log.Warn(message); var packages = fileSystem.GetDirectoryInfo(updateUrlOrPath).GetFiles("*.nupkg"); if (packages.Length == 0) { var ex = new SquirrelConfigurationException(message); return(Observable.Throw <UpdateInfo>(ex)); } // NB: Create a new RELEASES file since we've got a directory of packages ReleaseEntry.WriteReleaseFile( packages.Select(x => ReleaseEntry.GenerateFromFile(x.FullName)), fi.FullName); } using (var sr = new StreamReader(fi.OpenRead(), Encoding.UTF8)) { var text = sr.ReadToEnd(); releaseFile = Observable.Return(text); } progress.OnNext(100); progress.OnCompleted(); } } catch (Exception ex) { progress.OnCompleted(); return(Observable.Throw <UpdateInfo>(ex)); } // Return null if no updates found var ret = releaseFile .Select(ReleaseEntry.ParseReleaseFile) .SelectMany(releases => releases.Any() ? determineUpdateInfo(localReleases, releases, ignoreDeltaUpdates) : Observable.Return <UpdateInfo>(null)) .PublishLast(); ret.Connect(); return(ret); }
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>(); var eigenUpdateObs = Observable.Using(() => new UpdateManager(currentAssemblyDir, bundledPackageMetadata.Id, fxVersion, TargetRootDirectory), eigenUpdater => { // 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 updateInfoObs = eigenUpdater.CheckForUpdate(ignoreDeltaUpdates, eigenCheckProgress); return(updateInfoObs.SelectMany(updateInfo => { 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 Observable.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); } return eigenUpdater.DownloadReleases(updateInfo.ReleasesToApply, eigenCopyFileProgress) .Do(_ => log.Info("The downloading of releases completed - and there was much rejoicing")) .SelectMany(_ => eigenUpdater.ApplyReleases(updateInfo, eigenApplyProgress)) .Do(_ => log.Info("The applying of releases completed - and there was much rejoicing")); })); }); return(eigenUpdateObs.SelectMany(ret => { 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 Observable.Return(ret); } return Observable.Using(() => new UpdateManager(updateUrl, bundledPackageMetadata.Id, fxVersion, TargetRootDirectory), realUpdater => { return realUpdater.CheckForUpdate(progress: realCheckProgress) .SelectMany(x => realUpdater.DownloadReleases(x.ReleasesToApply, realCopyFileProgress).Select(_ => x)) .SelectMany(x => realUpdater.ApplyReleases(x, realApplyProgress)) .Select(_ => ret) .LoggedCatch(this, Observable.Return(new List <string>()), "Failed to update to latest remote version"); }); }).ToTask()); }
async Task <List <string> > executeInstall( string currentAssemblyDir, IPackage bundledPackageMetadata, bool ignoreDeltaUpdates = false, IObserver <int> progress = null) { var fxVersion = determineFxVersionFromPackage(bundledPackageMetadata); 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"); 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); }