public async Task CleanInstallRunsSquirrelAwareAppsWithInstallFlag() { string tempDir; string remotePkgDir; using (Utility.WithTempDirectory(out tempDir)) using (Utility.WithTempDirectory(out remotePkgDir)) { IntegrationTestHelper.CreateFakeInstalledApp("0.1.0", remotePkgDir); var pkgs = ReleaseEntry.BuildReleasesFile(remotePkgDir); ReleaseEntry.WriteReleaseFile(pkgs, Path.Combine(remotePkgDir, "RELEASES")); using (var fixture = new UpdateManager(remotePkgDir, "theApp", tempDir)) { await fixture.FullInstall(); // NB: We execute the Squirrel-aware apps, so we need to give // them a minute to settle or else the using statement will // try to blow away a running process await Task.Delay(1000); Assert.False(File.Exists(Path.Combine(tempDir, "theApp", "app-0.1.0", "args2.txt"))); Assert.True(File.Exists(Path.Combine(tempDir, "theApp", "app-0.1.0", "args.txt"))); var text = File.ReadAllText(Path.Combine(tempDir, "theApp", "app-0.1.0", "args.txt"), Encoding.UTF8); Assert.Contains("firstrun", text); } } }
public async Task Install(bool silentInstall, ProgressSource progressSource, string sourceDirectory = null) { sourceDirectory = sourceDirectory ?? Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); var releasesPath = Path.Combine(sourceDirectory, "RELEASES"); this.Log().Info("Starting install, writing to {0}", sourceDirectory); if (!File.Exists(releasesPath)) { this.Log().Info("RELEASES doesn't exist, creating it at " + releasesPath); var nupkgs = (new DirectoryInfo(sourceDirectory)).GetFiles() .Where(x => x.Name.EndsWith(".nupkg", StringComparison.OrdinalIgnoreCase)) .Select(x => ReleaseEntry.GenerateFromFile(x.FullName)); ReleaseEntry.WriteReleaseFile(nupkgs, releasesPath); } var ourAppName = ReleaseEntry.ParseReleaseFile(File.ReadAllText(releasesPath, Encoding.UTF8)) .First().PackageName; var rootDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); using (var mgr = new UpdateManager(sourceDirectory, ourAppName, FrameworkVersion.Net45, rootDir)) { Directory.CreateDirectory(mgr.RootAppDirectory); var updateTarget = Path.Combine(mgr.RootAppDirectory, "Update.exe"); this.ErrorIfThrows(() => File.Copy(Assembly.GetExecutingAssembly().Location, updateTarget, true), "Failed to copy Update.exe to " + updateTarget); await mgr.FullInstall(silentInstall, progressSource.Raise); await this.ErrorIfThrows(() => mgr.CreateUninstallerRegistryEntry(), "Failed to create uninstaller registry entry"); } }
public void WhenReleasesAreOutOfOrderSortByVersion() { var path = Path.GetTempFileName(); var firstVersion = new Version("1.0.0"); var secondVersion = new Version("1.1.0"); var thirdVersion = new Version("1.2.0"); var releaseEntries = new[] { ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.2.0-delta.nupkg")), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.1.0-delta.nupkg")), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.1.0-full.nupkg")), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.2.0-full.nupkg")), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.0.0-full.nupkg")) }; ReleaseEntry.WriteReleaseFile(releaseEntries, path); var releases = ReleaseEntry.ParseReleaseFile(File.ReadAllText(path)).ToArray(); Assert.Equal(firstVersion, releases[0].Version); Assert.Equal(secondVersion, releases[1].Version); Assert.Equal(true, releases[1].IsDelta); Assert.Equal(secondVersion, releases[2].Version); Assert.Equal(false, releases[2].IsDelta); Assert.Equal(thirdVersion, releases[3].Version); Assert.Equal(true, releases[3].IsDelta); Assert.Equal(thirdVersion, releases[4].Version); Assert.Equal(false, releases[4].IsDelta); }
public void WhenPreReleasesAreOutOfOrderSortByNumericSuffix() { var path = Path.GetTempFileName(); var firstVersion = new SemanticVersion("1.1.9-beta105"); var secondVersion = new SemanticVersion("1.2.0-beta9"); var thirdVersion = new SemanticVersion("1.2.0-beta10"); var fourthVersion = new SemanticVersion("1.2.0-beta100"); var releaseEntries = new[] { ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.2.0-beta1-full.nupkg")), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.2.0-beta9-full.nupkg")), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.2.0-beta100-full.nupkg")), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.1.9-beta105-full.nupkg")), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.2.0-beta10-full.nupkg")) }; ReleaseEntry.WriteReleaseFile(releaseEntries, path); var releases = ReleaseEntry.ParseReleaseFile(File.ReadAllText(path)).ToArray(); Assert.Equal(firstVersion, releases[0].Version); Assert.Equal(secondVersion, releases[2].Version); Assert.Equal(thirdVersion, releases[3].Version); Assert.Equal(fourthVersion, releases[4].Version); }
public async Task UpgradeRunsSquirrelAwareAppsWithUpgradeFlag() { string tempDir; string remotePkgDir; using (Utility.WithTempDirectory(out tempDir)) using (Utility.WithTempDirectory(out remotePkgDir)) { IntegrationTestHelper.CreateFakeInstalledApp("0.1.0", remotePkgDir); var pkgs = ReleaseEntry.BuildReleasesFile(remotePkgDir); ReleaseEntry.WriteReleaseFile(pkgs, Path.Combine(remotePkgDir, "RELEASES")); using (var fixture = new UpdateManager(remotePkgDir, "theApp", tempDir)) { await fixture.FullInstall(); } await Task.Delay(1000); IntegrationTestHelper.CreateFakeInstalledApp("0.2.0", remotePkgDir); pkgs = ReleaseEntry.BuildReleasesFile(remotePkgDir); ReleaseEntry.WriteReleaseFile(pkgs, Path.Combine(remotePkgDir, "RELEASES")); using (var fixture = new UpdateManager(remotePkgDir, "theApp", tempDir)) { await fixture.UpdateApp(); } await Task.Delay(1000); Assert.False(File.Exists(Path.Combine(tempDir, "theApp", "app-0.2.0", "args2.txt"))); Assert.True(File.Exists(Path.Combine(tempDir, "theApp", "app-0.2.0", "args.txt"))); var text = File.ReadAllText(Path.Combine(tempDir, "theApp", "app-0.2.0", "args.txt"), Encoding.UTF8); Assert.Contains("updated|0.2.0", text); } }
public async Task RunningUpgradeAppTwiceDoesntCrash() { string tempDir; string remotePkgDir; using (Utility.WithTempDirectory(out tempDir)) using (Utility.WithTempDirectory(out remotePkgDir)) { IntegrationTestHelper.CreateFakeInstalledApp("0.1.0", remotePkgDir); var pkgs = ReleaseEntry.BuildReleasesFile(remotePkgDir); ReleaseEntry.WriteReleaseFile(pkgs, Path.Combine(remotePkgDir, "RELEASES")); using (var fixture = new UpdateManager(remotePkgDir, "theApp", tempDir)) { await fixture.FullInstall(); } await Task.Delay(1000); IntegrationTestHelper.CreateFakeInstalledApp("0.2.0", remotePkgDir); pkgs = ReleaseEntry.BuildReleasesFile(remotePkgDir); ReleaseEntry.WriteReleaseFile(pkgs, Path.Combine(remotePkgDir, "RELEASES")); using (var fixture = new UpdateManager(remotePkgDir, "theApp", tempDir)) { await fixture.UpdateApp(); } await Task.Delay(1000); // NB: The 2nd time we won't have any updates to apply. We should just do nothing! using (var fixture = new UpdateManager(remotePkgDir, "theApp", tempDir)) { await fixture.UpdateApp(); } await Task.Delay(1000); } }
private static void ReleasifyElectron(string package, string targetDir = null, string baseUrl = null) { // check that package is valid new ZipPackage(package).GetType(); if (baseUrl != null) { if (!Utility.IsHttpUrl(baseUrl)) { throw new Exception($"Invalid --baseUrl '{baseUrl}'. A base URL must start with http or https and be a valid URI."); } if (!baseUrl.EndsWith("/")) { baseUrl += "/"; } } targetDir = targetDir ?? Path.Combine(".", "Releases"); var di = new DirectoryInfo(targetDir); var releaseFilePath = Path.Combine(di.FullName, "RELEASES"); var previousReleases = new List <ReleaseEntry>(); if (File.Exists(releaseFilePath)) { previousReleases.AddRange(ReleaseEntry.ParseReleaseFile(File.ReadAllText(releaseFilePath, Encoding.UTF8))); } var processed = new List <string>(); var rp = new ReleasePackage(package, true); processed.Add(package); var prev = ReleaseEntry.GetPreviousRelease(previousReleases, rp, targetDir); if (prev != null) { var deltaBuilder = new DeltaPackageBuilder(); var dp = deltaBuilder.CreateDeltaPackage(prev, rp, Path.Combine(di.FullName, rp.SuggestedReleaseFileName.Replace("full", "delta"))); processed.Insert(0, dp.InputPackageFile); } var newReleaseEntries = processed .Select(packageFilename => ReleaseEntry.GenerateFromFile(packageFilename, baseUrl)) .ToList(); var distinctPreviousReleases = previousReleases .Where(x => !newReleaseEntries.Select(e => e.Version).Contains(x.Version)); var releaseEntries = distinctPreviousReleases.Concat(newReleaseEntries).ToList(); ReleaseEntry.WriteReleaseFile(releaseEntries, releaseFilePath); var newestFullRelease = releaseEntries.MaxBy(x => x.Version).First(x => !x.IsDelta); Console.Out.WriteLine(ReleaseEntry.GenerateFromFile(Path.Combine(di.FullName, newestFullRelease.Filename)).EntryAsString); }
public static async Task SyncFromGitHub(string repoUrl, string token, DirectoryInfo releaseDirectoryInfo) { var repoUri = new Uri(repoUrl); var userAgent = new ProductHeaderValue("SyncReleases", Assembly.GetExecutingAssembly().GetName().Version.ToString()); var client = new GitHubClient(userAgent, repoUri); if (token != null) { client.Credentials = new Credentials(token); } var nwo = nwoFromRepoUrl(repoUrl); var releases = (await client.Release.GetAll(nwo.Item1, nwo.Item2)) .OrderByDescending(x => x.PublishedAt) .Take(5); await releases.ForEachAsync(async release => { // NB: Why do I have to double-fetch the release assets? It's already in GetAll var assets = await client.Release.GetAssets(nwo.Item1, nwo.Item2, release.Id); await assets .Where(x => x.Name.EndsWith(".nupkg", StringComparison.OrdinalIgnoreCase)) .Where(x => { var fi = new FileInfo(Path.Combine(releaseDirectoryInfo.FullName, x.Name)); return(!(fi.Exists && fi.Length == x.Size)); }) .ForEachAsync(async x => { var target = new FileInfo(Path.Combine(releaseDirectoryInfo.FullName, x.Name)); if (target.Exists) { target.Delete(); } await retryAsync(3, async() => { var hc = new HttpClient(); var rq = new HttpRequestMessage(HttpMethod.Get, x.Url); rq.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/octet-stream")); rq.Headers.UserAgent.Add(new System.Net.Http.Headers.ProductInfoHeaderValue(userAgent.Name, userAgent.Version)); rq.Headers.Add("Authorization", "Bearer " + token); var resp = await hc.SendAsync(rq); resp.EnsureSuccessStatusCode(); using (var from = await resp.Content.ReadAsStreamAsync()) using (var to = File.OpenWrite(target.FullName)) { await from.CopyToAsync(to); } }); }); }); var entries = releaseDirectoryInfo.GetFiles("*.nupkg") .AsParallel() .Select(x => ReleaseEntry.GenerateFromFile(x.FullName)); ReleaseEntry.WriteReleaseFile(entries, Path.Combine(releaseDirectoryInfo.FullName, "RELEASES")); }
public static IDisposable WithFakeInstallDirectory(string packageFileName, out string path) { var ret = Utility.WithTempDirectory(out path); File.Copy(GetPath("fixtures", packageFileName), Path.Combine(path, packageFileName)); var rp = ReleaseEntry.GenerateFromFile(Path.Combine(path, packageFileName)); ReleaseEntry.WriteReleaseFile(new[] { rp }, Path.Combine(path, "RELEASES")); return(ret); }
async Task <string> createSetupEmbeddedZip(string fullPackage, string releasesDir, string backgroundGif, string signingOpts, string setupIcon) { string tempPath; this.Log().Info("Building embedded zip file for Setup.exe"); using (Utility.WithTempDirectory(out tempPath, null)) { this.ErrorIfThrows(() => { File.Copy(Assembly.GetEntryAssembly().Location.Replace("-Mono.exe", ".exe"), Path.Combine(tempPath, "Update.exe")); File.Copy(fullPackage, Path.Combine(tempPath, Path.GetFileName(fullPackage))); }, "Failed to write package files to temp dir: " + tempPath); if (!String.IsNullOrWhiteSpace(backgroundGif)) { this.ErrorIfThrows(() => { File.Copy(backgroundGif, Path.Combine(tempPath, "background.gif")); }, "Failed to write animated GIF to temp dir: " + tempPath); } if (!String.IsNullOrWhiteSpace(setupIcon)) { this.ErrorIfThrows(() => { File.Copy(setupIcon, Path.Combine(tempPath, "setupIcon.ico")); }, "Failed to write icon to temp dir: " + tempPath); } var releases = new[] { ReleaseEntry.GenerateFromFile(fullPackage) }; ReleaseEntry.WriteReleaseFile(releases, Path.Combine(tempPath, "RELEASES")); var target = Path.GetTempFileName(); File.Delete(target); // Sign Update.exe so that virus scanners don't think we're // pulling one over on them if (signingOpts != null) { var di = new DirectoryInfo(tempPath); var files = di.EnumerateFiles() .Where(x => x.Name.ToLowerInvariant().EndsWith(".exe")) .Select(x => x.FullName); await files.ForEachAsync(x => signPEFile(x, signingOpts)); } this.ErrorIfThrows(() => ZipFile.CreateFromDirectory(tempPath, target, CompressionLevel.Optimal, false), "Failed to create Zip file from directory: " + tempPath); return(target); } }
public async Task Install(bool silentInstall, ProgressSource progressSource, string sourceDirectory = null) { sourceDirectory = sourceDirectory ?? Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); var releasesPath = Path.Combine(sourceDirectory, "RELEASES"); this.Log().Info("Starting install, writing to {0}", sourceDirectory); if (!File.Exists(releasesPath)) { this.Log().Info("RELEASES doesn't exist, creating it at " + releasesPath); var nupkgs = (new DirectoryInfo(sourceDirectory)).GetFiles() .Where(x => x.Name.EndsWith(".nupkg", StringComparison.OrdinalIgnoreCase)) .Select(x => ReleaseEntry.GenerateFromFile(x.FullName)); ReleaseEntry.WriteReleaseFile(nupkgs, releasesPath); } var ourAppName = ReleaseEntry.ParseReleaseFile(File.ReadAllText(releasesPath, Encoding.UTF8)) .First().PackageName; using (var mgr = new UpdateManager(sourceDirectory, ourAppName)) { this.Log().Info("About to install to: " + mgr.RootAppDirectory); if (Directory.Exists(mgr.RootAppDirectory)) { this.Log().Warn("Install path {0} already exists, burning it to the ground", mgr.RootAppDirectory); mgr.KillAllExecutablesBelongingToPackage(); await Task.Delay(500); await this.ErrorIfThrows(() => Utility.DeleteDirectory(mgr.RootAppDirectory), "Failed to remove existing directory on full install, is the app still running???"); this.ErrorIfThrows(() => Utility.Retry(() => Directory.CreateDirectory(mgr.RootAppDirectory), 3), "Couldn't recreate app directory, perhaps Antivirus is blocking it"); } Directory.CreateDirectory(mgr.RootAppDirectory); var updateTarget = Path.Combine(mgr.RootAppDirectory, "Update.exe"); this.ErrorIfThrows(() => File.Copy(Assembly.GetExecutingAssembly().Location, updateTarget, true), "Failed to copy Update.exe to " + updateTarget); await mgr.FullInstall(silentInstall, progressSource.Raise); await this.ErrorIfThrows(() => mgr.CreateUninstallerRegistryEntry(), "Failed to create uninstaller registry entry"); } }
public static IDisposable WithFakeInstallDirectory(string packageFileName, out string path) { var ret = Utility.WithTempDirectory(out path); File.Copy(GetPath("fixtures", packageFileName), Path.Combine(path, packageFileName)); var rp = ReleaseEntry.GenerateFromFile(Path.Combine(path, packageFileName)); ReleaseEntry.WriteReleaseFile(new[] { rp }, Path.Combine(path, "RELEASES")); // NB: This is a temporary hack. The reason we serialize the tests // like this, is to make sure that we don't have two tests registering // their Service Locators with RxApp. Monitor.Enter(gate); return(new CompositeDisposable(ret, Disposable.Create(() => Monitor.Exit(gate)))); }
public ReleaseEntry GetReleaseEntry(string sourceDirectory) { var releasesPath = Path.Combine(sourceDirectory, "RELEASES"); if (!File.Exists(releasesPath)) { this.Log().Info("RELEASES doesn't exist, creating it at " + releasesPath); var nupkgs = (new DirectoryInfo(sourceDirectory)).GetFiles() .Where(x => x.Name.EndsWith(".nupkg", StringComparison.OrdinalIgnoreCase)) .Select(x => ReleaseEntry.GenerateFromFile(x.FullName)); ReleaseEntry.WriteReleaseFile(nupkgs, releasesPath); } return(ReleaseEntry.ParseReleaseFile(File.ReadAllText(releasesPath, Encoding.UTF8)) .First()); }
public void LuckyUsersGetBetaSoftware(string inputGuid) { var path = Path.GetTempFileName(); var ourGuid = Guid.ParseExact(inputGuid, "B"); var releaseEntries = new[] { ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.2.0-full.nupkg", 0.25f)), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.1.0-full.nupkg")), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.0.0-full.nupkg")) }; ReleaseEntry.WriteReleaseFile(releaseEntries, path); var releases = ReleaseEntry.ParseReleaseFileAndApplyStaging(File.ReadAllText(path), ourGuid).ToArray(); Assert.Equal(3, releases.Length); }
public void BorkedUsersGetProductionSoftware() { var path = Path.GetTempFileName(); var ourGuid = default(Guid?); var releaseEntries = new[] { ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.2.0-full.nupkg", 0.1f)), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.1.0-full.nupkg")), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.0.0-full.nupkg")) }; ReleaseEntry.WriteReleaseFile(releaseEntries, path); var releases = ReleaseEntry.ParseReleaseFileAndApplyStaging(File.ReadAllText(path), ourGuid).ToArray(); Assert.Equal(2, releases.Length); }
public void StagingUsersGetBetaSoftware() { // NB: We're kind of using a hack here, in that we know that the // last 4 bytes are used as the percentage, and the percentage // effectively measures, "How close are you to zero". Guid.Empty // is v close to zero, because it is zero. var path = Path.GetTempFileName(); var ourGuid = Guid.Empty; var releaseEntries = new[] { ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.2.0-full.nupkg", 0.1f)), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.1.0-full.nupkg")), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.0.0-full.nupkg")) }; ReleaseEntry.WriteReleaseFile(releaseEntries, path); var releases = ReleaseEntry.ParseReleaseFileAndApplyStaging(File.ReadAllText(path), ourGuid).ToArray(); Assert.Equal(3, releases.Length); }
string createSetupEmbeddedZip(string fullPackage, string releasesDir) { string tempPath; this.Log().Info("Building embedded zip file for Setup.exe"); using (Utility.WithTempDirectory(out tempPath)) { this.ErrorIfThrows(() => { File.Copy(Assembly.GetEntryAssembly().Location, Path.Combine(tempPath, "Update.exe")); File.Copy(fullPackage, Path.Combine(tempPath, Path.GetFileName(fullPackage))); }, "Failed to write package files to temp dir: " + tempPath); var releases = new[] { ReleaseEntry.GenerateFromFile(fullPackage) }; ReleaseEntry.WriteReleaseFile(releases, Path.Combine(tempPath, "RELEASES")); var target = Path.GetTempFileName(); File.Delete(target); this.ErrorIfThrows(() => ZipFile.CreateFromDirectory(tempPath, target, CompressionLevel.Optimal, false), "Failed to create Zip file from directory: " + tempPath); return(target); } }
public async Task FullUninstallRemovesAllVersions() { string tempDir; string remotePkgDir; using (Utility.WithTempDirectory(out tempDir)) using (Utility.WithTempDirectory(out remotePkgDir)) { IntegrationTestHelper.CreateFakeInstalledApp("0.1.0", remotePkgDir); var pkgs = ReleaseEntry.BuildReleasesFile(remotePkgDir); ReleaseEntry.WriteReleaseFile(pkgs, Path.Combine(remotePkgDir, "RELEASES")); using (var fixture = new UpdateManager(remotePkgDir, "theApp", tempDir)) { await fixture.FullInstall(); } await Task.Delay(1000); IntegrationTestHelper.CreateFakeInstalledApp("0.2.0", remotePkgDir); pkgs = ReleaseEntry.BuildReleasesFile(remotePkgDir); ReleaseEntry.WriteReleaseFile(pkgs, Path.Combine(remotePkgDir, "RELEASES")); using (var fixture = new UpdateManager(remotePkgDir, "theApp", tempDir)) { await fixture.UpdateApp(); } await Task.Delay(1000); using (var fixture = new UpdateManager(remotePkgDir, "theApp", tempDir)) { await fixture.FullUninstall(); } Assert.False(File.Exists(Path.Combine(tempDir, "theApp", "app-0.1.0", "args.txt"))); Assert.False(File.Exists(Path.Combine(tempDir, "theApp", "app-0.2.0", "args.txt"))); Assert.True(File.Exists(Path.Combine(tempDir, "theApp", "app-0.2.0", ".dead"))); } }
async Task <int> main(string[] args) { using (var logger = new SetupLogLogger(false) { Level = Splat.LogLevel.Info }) { Splat.Locator.CurrentMutable.Register(() => logger, typeof(Splat.ILogger)); var releaseDir = default(string); var repoUrl = default(string); var token = default(string); opts = new OptionSet() { "Usage: SyncGitHubReleases.exe command [OPTS]", "Builds a Releases directory from releases on GitHub", "", "Options:", { "h|?|help", "Display Help and exit", _ => {} }, { "r=|releaseDir=", "Path to a release directory to download to", v => releaseDir = v }, { "u=|repoUrl=", "The URL to the repository root page", v => repoUrl = v }, { "t=|token=", "The OAuth token to use as login credentials", v => token = v }, }; opts.Parse(args); if (token == null || repoUrl == null || repoUrl.StartsWith("http", true, CultureInfo.InvariantCulture) == false) { ShowHelp(); return(-1); } var releaseDirectoryInfo = new DirectoryInfo(releaseDir ?? Path.Combine(".", "Releases")); if (!releaseDirectoryInfo.Exists) { releaseDirectoryInfo.Create(); } var repoUri = new Uri(repoUrl); var userAgent = new ProductHeaderValue("SyncGitHubReleases", Assembly.GetExecutingAssembly().GetName().Version.ToString()); var client = new GitHubClient(userAgent, repoUri) { Credentials = new Credentials(token) }; var nwo = nwoFromRepoUrl(repoUrl); var releases = await client.Release.GetAll(nwo.Item1, nwo.Item2); await releases.ForEachAsync(async release => { // NB: Why do I have to double-fetch the release assets? It's already in GetAll var assets = await client.Release.GetAssets(nwo.Item1, nwo.Item2, release.Id); await assets .Where(x => x.Name.EndsWith(".nupkg", StringComparison.OrdinalIgnoreCase)) .Where(x => { var fi = new FileInfo(Path.Combine(releaseDirectoryInfo.FullName, x.Name)); return(!(fi.Exists && fi.Length == x.Size)); }) .ForEachAsync(async x => { var target = new FileInfo(Path.Combine(releaseDirectoryInfo.FullName, x.Name)); if (target.Exists) { target.Delete(); } var hc = new HttpClient(); var rq = new HttpRequestMessage(HttpMethod.Get, x.Url); rq.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/octet-stream")); rq.Headers.UserAgent.Add(new System.Net.Http.Headers.ProductInfoHeaderValue(userAgent.Name, userAgent.Version)); rq.Headers.Add("Authorization", "Bearer " + token); var resp = await hc.SendAsync(rq); resp.EnsureSuccessStatusCode(); using (var from = await resp.Content.ReadAsStreamAsync()) using (var to = File.OpenWrite(target.FullName)) { await from.CopyToAsync(to); } }); }); var entries = releaseDirectoryInfo.GetFiles("*.nupkg") .AsParallel() .Select(x => ReleaseEntry.GenerateFromFile(x.FullName)); ReleaseEntry.WriteReleaseFile(entries, Path.Combine(releaseDirectoryInfo.FullName, "RELEASES")); } return(0); }
public async Task <UpdateInfo> CheckForUpdate( string localReleaseFile, string updateUrlOrPath, bool ignoreDeltaUpdates = false, Action <int> progress = null, IFileDownloader urlDownloader = null) { progress = progress ?? (_ => { }); var localReleases = Enumerable.Empty <ReleaseEntry>(); var stagingId = GetOrCreateStagedUserId(); var shouldInitialize = false; try { localReleases = Utility.LoadLocalReleases(localReleaseFile); } catch (Exception ex) { // Something has gone pear-shaped, let's start from scratch Log.Warn("Failed to load local releases, starting from scratch", ex); shouldInitialize = true; } if (shouldInitialize) { await InitializeClientAppDirectory(); } string releaseFile; var latestLocalRelease = localReleases.Count() > 0 ? localReleases.MaxBy(x => x.Version).First() : default; // Fetch the remote RELEASES file, whether it's a local dir or an // HTTP URL if (Utility.IsHttpUrl(updateUrlOrPath)) { if (updateUrlOrPath.EndsWith("/")) { updateUrlOrPath = updateUrlOrPath.Substring(0, updateUrlOrPath.Length - 1); } Log.InfoFormat("Downloading RELEASES file from {0}", updateUrlOrPath); var retries = 3; retry: try { var uri = Utility.AppendPathToUri(new Uri(updateUrlOrPath), "RELEASES"); if (latestLocalRelease != null) { uri = Utility.AddQueryParamsToUri( uri, new Dictionary <string, string> { { "id", latestLocalRelease.PackageName }, { "localVersion", latestLocalRelease.Version.ToString() }, { "arch", Environment.Is64BitOperatingSystem ? "amd64" : "x86" } }); } var data = await urlDownloader.DownloadUrl(uri.ToString()); releaseFile = Encoding.UTF8.GetString(data); } catch (WebException ex) { Log.Info("Download resulted in WebException (returning blank release list)", ex); if (retries <= 0) { throw; } retries--; goto retry; } progress(33); } else { Log.InfoFormat("Reading RELEASES file from {0}", updateUrlOrPath); if (!Directory.Exists(updateUrlOrPath)) { var message = $"The directory {updateUrlOrPath} does not exist, something is probably broken with your application"; throw new Exception(message); } var fi = new FileInfo(Path.Combine(updateUrlOrPath, "RELEASES")); if (!fi.Exists) { var message = $"The file {fi.FullName} does not exist, something is probably broken with your application"; Log.WarnFormat(message); var packages = new DirectoryInfo(updateUrlOrPath).GetFiles("*.nupkg"); if (packages.Length == 0) { throw new Exception(message); } // 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); } releaseFile = File.ReadAllText(fi.FullName, Encoding.UTF8); progress(33); } var ret = default(UpdateInfo); var remoteReleases = ReleaseEntry.ParseReleaseFileAndApplyStaging(releaseFile, stagingId); progress(66); if (!remoteReleases.Any()) { throw new Exception("Remote release File is empty or corrupted"); } ret = DetermineUpdateInfo(localReleases, remoteReleases, ignoreDeltaUpdates); progress(100); return(ret); }
// NB: Once we uninstall the old version of the app, we try to schedule // it to be deleted at next reboot. Unfortunately, depending on whether // the user has admin permissions, this can fail. So as a failsafe, // before we try to apply any update, we assume previous versions in the // directory are "dead" (i.e. already uninstalled, but not deleted), and // we blow them away. This is to make sure that we don't attempt to run // an uninstaller on an already-uninstalled version. private async Task CleanDeadVersions(SemanticVersion originalVersion, SemanticVersion currentVersion, bool forceUninstall = false) { if (currentVersion == null) { return; } var di = new DirectoryInfo(rootAppDirectory); if (!di.Exists) { return; } Log.InfoFormat("cleanDeadVersions: for version {0}", currentVersion); string originalVersionFolder = null; if (originalVersion != null) { originalVersionFolder = GetDirectoryForRelease(originalVersion).Name; Log.InfoFormat("cleanDeadVersions: exclude folder {0}", originalVersionFolder); } string currentVersionFolder = null; if (currentVersion != null) { currentVersionFolder = GetDirectoryForRelease(currentVersion).Name; Log.InfoFormat("cleanDeadVersions: exclude folder {0}", currentVersionFolder); } // NB: If we try to access a directory that has already been // scheduled for deletion by MoveFileEx it throws what seems like // NT's only error code, ERROR_ACCESS_DENIED. Squelch errors that // come from here. var toCleanup = di.GetDirectories() .Where(x => x.Name.ToLowerInvariant().Contains("app-")) .Where(x => x.Name != currentVersionFolder && x.Name != originalVersionFolder) .Where(x => !IsAppFolderDead(x.FullName)); if (forceUninstall == false) { await toCleanup.ForEachAsync( async x => { var squirrelApps = SquirrelAwareExecutableDetector.GetAllSquirrelAwareApps(x.FullName); var args = $"--squirrel-obsolete {x.Name.Replace("app-", "")}"; if (squirrelApps.Count > 0) { // For each app, run the install command in-order and wait await squirrelApps.ForEachAsync( async exe => { using (var cts = new CancellationTokenSource()) { cts.CancelAfter(10 * 1000); try { await Utility.InvokeProcessAsync(exe, args, cts.Token); } catch (Exception ex) { Log.Error("Coudln't run Squirrel hook, continuing: " + exe, ex); } } }, 1 /* at a time */); } }); } // Include dead folders in folders to :fire: toCleanup = di.GetDirectories() .Where(x => x.Name.ToLowerInvariant().Contains("app-")) .Where(x => x.Name != currentVersionFolder && x.Name != originalVersionFolder); // Get the current process list in an attempt to not burn // directories which have running processes var runningProcesses = UnsafeUtility.EnumerateProcesses(); // Finally, clean up the app-X.Y.Z directories await toCleanup.ForEachAsync( async x => { try { if (runningProcesses.All(p => p.Item1 == null || !p.Item1.StartsWith(x.FullName, StringComparison.OrdinalIgnoreCase))) { await Utility.DeleteDirectoryOrJustGiveUp(x.FullName); } if (Directory.Exists(x.FullName)) { // NB: If we cannot clean up a directory, we need to make // sure that anyone finding it later won't attempt to run // Squirrel events on it. We'll mark it with a .dead file MarkAppFolderAsDead(x.FullName); } } catch (UnauthorizedAccessException ex) { Log.Warn("Couldn't delete directory: " + x.FullName, ex); // NB: Same deal as above MarkAppFolderAsDead(x.FullName); } }); // Clean up the packages directory too var releasesFile = Utility.LocalReleaseFileForAppDir(rootAppDirectory); var entries = ReleaseEntry.ParseReleaseFile(File.ReadAllText(releasesFile, Encoding.UTF8)); var pkgDir = Utility.PackageDirectoryForAppDir(rootAppDirectory); var releaseEntry = default(ReleaseEntry); foreach (var entry in entries) { if (entry.Version == currentVersion) { releaseEntry = ReleaseEntry.GenerateFromFile(Path.Combine(pkgDir, entry.Filename)); continue; } File.Delete(Path.Combine(pkgDir, entry.Filename)); } ReleaseEntry.WriteReleaseFile(new[] { releaseEntry }, releasesFile); }
public void Releasify(string package, string targetDir = null, string packagesDir = null, string bootstrapperExe = null, string backgroundGif = null, string signingOpts = null, string baseUrl = null, string setupIcon = null, bool generateMsi = true) { if (baseUrl != null) { if (!Utility.IsHttpUrl(baseUrl)) { throw new Exception(string.Format("Invalid --baseUrl '{0}'. A base URL must start with http or https and be a valid URI.", baseUrl)); } if (!baseUrl.EndsWith("/")) { baseUrl += "/"; } } targetDir = targetDir ?? Path.Combine(".", "Releases"); packagesDir = packagesDir ?? "."; bootstrapperExe = bootstrapperExe ?? Path.Combine(".", "Setup.exe"); if (!Directory.Exists(targetDir)) { Directory.CreateDirectory(targetDir); } if (!File.Exists(bootstrapperExe)) { bootstrapperExe = Path.Combine( Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Setup.exe"); } this.Log().Info("Bootstrapper EXE found at:" + bootstrapperExe); var di = new DirectoryInfo(targetDir); File.Copy(package, Path.Combine(di.FullName, Path.GetFileName(package)), true); var allNuGetFiles = di.EnumerateFiles() .Where(x => x.Name.EndsWith(".nupkg", StringComparison.OrdinalIgnoreCase)); var toProcess = allNuGetFiles.Where(x => !x.Name.Contains("-delta") && !x.Name.Contains("-full")); var processed = new List <string>(); var releaseFilePath = Path.Combine(di.FullName, "RELEASES"); var previousReleases = new List <ReleaseEntry>(); if (File.Exists(releaseFilePath)) { previousReleases.AddRange(ReleaseEntry.ParseReleaseFile(File.ReadAllText(releaseFilePath, Encoding.UTF8))); } foreach (var file in toProcess) { this.Log().Info("Creating release package: " + file.FullName); var rp = new ReleasePackage(file.FullName); rp.CreateReleasePackage(Path.Combine(di.FullName, rp.SuggestedReleaseFileName), packagesDir, contentsPostProcessHook: pkgPath => { new DirectoryInfo(pkgPath).GetAllFilesRecursively() .Where(x => x.Name.ToLowerInvariant().EndsWith(".exe")) .Where(x => !x.Name.ToLowerInvariant().Contains("squirrel.exe")) .Where(x => Utility.ExecutableUsesWin32Subsystem(x.FullName)) .ForEachAsync(x => createExecutableStubForExe(x.FullName)) .Wait(); if (signingOpts == null) { return; } new DirectoryInfo(pkgPath).GetAllFilesRecursively() .Where(x => Utility.FileIsLikelyPEImage(x.Name)) .ForEachAsync(async x => { if (isPEFileSigned(x.FullName)) { this.Log().Info("{0} is already signed, skipping", x.FullName); return; } this.Log().Info("About to sign {0}", x.FullName); await signPEFile(x.FullName, signingOpts); }) .Wait(); }); processed.Add(rp.ReleasePackageFile); var prev = ReleaseEntry.GetPreviousRelease(previousReleases, rp, targetDir); if (prev != null) { var deltaBuilder = new DeltaPackageBuilder(null); var dp = deltaBuilder.CreateDeltaPackage(prev, rp, Path.Combine(di.FullName, rp.SuggestedReleaseFileName.Replace("full", "delta"))); processed.Insert(0, dp.InputPackageFile); } } foreach (var file in toProcess) { File.Delete(file.FullName); } var newReleaseEntries = processed .Select(packageFilename => ReleaseEntry.GenerateFromFile(packageFilename, baseUrl)) .ToList(); var distinctPreviousReleases = previousReleases .Where(x => !newReleaseEntries.Select(e => e.Version).Contains(x.Version)); var releaseEntries = distinctPreviousReleases.Concat(newReleaseEntries).ToList(); ReleaseEntry.WriteReleaseFile(releaseEntries, releaseFilePath); var targetSetupExe = Path.Combine(di.FullName, "Setup.exe"); var newestFullRelease = releaseEntries.MaxBy(x => x.Version).Where(x => !x.IsDelta).First(); File.Copy(bootstrapperExe, targetSetupExe, true); var zipPath = createSetupEmbeddedZip(Path.Combine(di.FullName, newestFullRelease.Filename), di.FullName, backgroundGif, signingOpts).Result; var writeZipToSetup = findExecutable("WriteZipToSetup.exe"); try { var result = Utility.InvokeProcessAsync(writeZipToSetup, String.Format("\"{0}\" \"{1}\"", targetSetupExe, zipPath), CancellationToken.None).Result; if (result.Item1 != 0) { throw new Exception("Failed to write Zip to Setup.exe!\n\n" + result.Item2); } } catch (Exception ex) { this.Log().ErrorException("Failed to update Setup.exe with new Zip file", ex); } finally { File.Delete(zipPath); } Utility.Retry(() => setPEVersionInfoAndIcon(targetSetupExe, new ZipPackage(package), setupIcon).Wait()); if (signingOpts != null) { signPEFile(targetSetupExe, signingOpts).Wait(); } if (generateMsi) { createMsiPackage(targetSetupExe, new ZipPackage(package)).Wait(); if (signingOpts != null) { signPEFile(targetSetupExe.Replace(".exe", ".msi"), signingOpts).Wait(); } } }
public void Releasify(string package, string targetDir = null, string packagesDir = null, string bootstrapperExe = null) { targetDir = targetDir ?? ".\\Releases"; packagesDir = packagesDir ?? "."; bootstrapperExe = bootstrapperExe ?? ".\\Setup.exe"; if (!Directory.Exists(targetDir)) { Directory.CreateDirectory(targetDir); } if (!File.Exists(bootstrapperExe)) { bootstrapperExe = Path.Combine( Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Setup.exe"); } this.Log().Info("Bootstrapper EXE found at:" + bootstrapperExe); var di = new DirectoryInfo(targetDir); File.Copy(package, Path.Combine(di.FullName, Path.GetFileName(package)), true); var allNuGetFiles = di.EnumerateFiles() .Where(x => x.Name.EndsWith(".nupkg", StringComparison.OrdinalIgnoreCase)); var toProcess = allNuGetFiles.Where(x => !x.Name.Contains("-delta") && !x.Name.Contains("-full")); var releaseFilePath = Path.Combine(di.FullName, "RELEASES"); var previousReleases = Enumerable.Empty <ReleaseEntry>(); if (File.Exists(releaseFilePath)) { previousReleases = ReleaseEntry.ParseReleaseFile(File.ReadAllText(releaseFilePath, Encoding.UTF8)); } foreach (var file in toProcess) { this.Log().Info("Creating release package: " + file.FullName); var rp = new ReleasePackage(file.FullName); rp.CreateReleasePackage(Path.Combine(di.FullName, rp.SuggestedReleaseFileName), packagesDir); var prev = ReleaseEntry.GetPreviousRelease(previousReleases, rp, targetDir); if (prev != null) { var deltaBuilder = new DeltaPackageBuilder(); deltaBuilder.CreateDeltaPackage(prev, rp, Path.Combine(di.FullName, rp.SuggestedReleaseFileName.Replace("full", "delta"))); } } foreach (var file in toProcess) { File.Delete(file.FullName); } var releaseEntries = allNuGetFiles.Select(x => ReleaseEntry.GenerateFromFile(x.FullName)); ReleaseEntry.WriteReleaseFile(releaseEntries, releaseFilePath); var targetSetupExe = Path.Combine(di.FullName, "Setup.exe"); var newestFullRelease = releaseEntries.MaxBy(x => x.Version).Where(x => !x.IsDelta).First(); File.Copy(bootstrapperExe, targetSetupExe, true); var zipPath = createSetupEmbeddedZip(Path.Combine(di.FullName, newestFullRelease.Filename), di.FullName); try { var zip = File.ReadAllBytes(zipPath); IntPtr handle = NativeMethods.BeginUpdateResource(targetSetupExe, false); if (handle == IntPtr.Zero) { throw new Win32Exception(); } if (!NativeMethods.UpdateResource(handle, "DATA", new IntPtr(131), 0x0409, zip, zip.Length)) { throw new Win32Exception(); } if (!NativeMethods.EndUpdateResource(handle, false)) { throw new Win32Exception(); } } catch (Exception ex) { this.Log().ErrorException("Failed to update Setup.exe with new Zip file", ex); } finally { File.Delete(zipPath); } }
public void Releasify(string package, string targetDir = null, string packagesDir = null, string bootstrapperExe = null, string backgroundGif = null, string signingOpts = null, string baseUrl = null, string setupIcon = null) { if (baseUrl != null) { if (!Utility.IsHttpUrl(baseUrl)) { throw new Exception(string.Format("Invalid --baseUrl '{0}'. A base URL must start with http or https and be a valid URI.", baseUrl)); } if (!baseUrl.EndsWith("/")) { baseUrl += "/"; } } targetDir = targetDir ?? ".\\Releases"; packagesDir = packagesDir ?? "."; bootstrapperExe = bootstrapperExe ?? ".\\Setup.exe"; if (!Directory.Exists(targetDir)) { Directory.CreateDirectory(targetDir); } if (!File.Exists(bootstrapperExe)) { bootstrapperExe = Path.Combine( Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Setup.exe"); } this.Log().Info("Bootstrapper EXE found at:" + bootstrapperExe); var di = new DirectoryInfo(targetDir); File.Copy(package, Path.Combine(di.FullName, Path.GetFileName(package)), true); var allNuGetFiles = di.EnumerateFiles() .Where(x => x.Name.EndsWith(".nupkg", StringComparison.OrdinalIgnoreCase)); var toProcess = allNuGetFiles.Where(x => !x.Name.Contains("-delta") && !x.Name.Contains("-full")); var processed = new List <string>(); var releaseFilePath = Path.Combine(di.FullName, "RELEASES"); var previousReleases = Enumerable.Empty <ReleaseEntry>(); if (File.Exists(releaseFilePath)) { previousReleases = ReleaseEntry.ParseReleaseFile(File.ReadAllText(releaseFilePath, Encoding.UTF8)); } foreach (var file in toProcess) { this.Log().Info("Creating release package: " + file.FullName); var rp = new ReleasePackage(file.FullName); rp.CreateReleasePackage(Path.Combine(di.FullName, rp.SuggestedReleaseFileName), packagesDir, contentsPostProcessHook: pkgPath => { if (signingOpts == null) { return; } new DirectoryInfo(pkgPath).GetAllFilesRecursively() .Where(x => x.Name.ToLowerInvariant().EndsWith(".exe")) .ForEachAsync(x => signPEFile(x.FullName, signingOpts)) .Wait(); }); processed.Add(rp.ReleasePackageFile); var prev = ReleaseEntry.GetPreviousRelease(previousReleases, rp, targetDir); if (prev != null) { var deltaBuilder = new DeltaPackageBuilder(); var dp = deltaBuilder.CreateDeltaPackage(prev, rp, Path.Combine(di.FullName, rp.SuggestedReleaseFileName.Replace("full", "delta"))); processed.Insert(0, dp.InputPackageFile); } } foreach (var file in toProcess) { File.Delete(file.FullName); } var releaseEntries = previousReleases.Concat(processed.Select(packageFilename => ReleaseEntry.GenerateFromFile(packageFilename, baseUrl))); ReleaseEntry.WriteReleaseFile(releaseEntries, releaseFilePath); var targetSetupExe = Path.Combine(di.FullName, "Setup.exe"); var newestFullRelease = releaseEntries.MaxBy(x => x.Version).Where(x => !x.IsDelta).First(); File.Copy(bootstrapperExe, targetSetupExe, true); var zipPath = createSetupEmbeddedZip(Path.Combine(di.FullName, newestFullRelease.Filename), di.FullName, backgroundGif, signingOpts).Result; try { var zip = File.ReadAllBytes(zipPath); IntPtr handle = NativeMethods.BeginUpdateResource(targetSetupExe, false); if (handle == IntPtr.Zero) { throw new Win32Exception(); } if (!NativeMethods.UpdateResource(handle, "DATA", new IntPtr(131), 0x0409, zip, zip.Length)) { throw new Win32Exception(); } if (!NativeMethods.EndUpdateResource(handle, false)) { throw new Win32Exception(); } } catch (Exception ex) { this.Log().ErrorException("Failed to update Setup.exe with new Zip file", ex); } finally { File.Delete(zipPath); } Utility.Retry(() => setPEVersionInfoAndIcon(targetSetupExe, new ZipPackage(package), setupIcon).Wait()); if (signingOpts != null) { signPEFile(targetSetupExe, signingOpts).Wait(); } }
public async Task Install(bool silentInstall, ProgressSource progressSource, string sourceDirectory = null) { sourceDirectory = sourceDirectory ?? Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); var releasesPath = Path.Combine(sourceDirectory, "RELEASES"); this.Log().Info("Starting install, writing to {0}", sourceDirectory); if (!File.Exists(releasesPath)) { this.Log().Info("RELEASES doesn't exist, creating it at " + releasesPath); var nupkgs = (new DirectoryInfo(sourceDirectory)).GetFiles() .Where(x => x.Name.EndsWith(".nupkg", StringComparison.OrdinalIgnoreCase)) .Select(x => ReleaseEntry.GenerateFromFile(x.FullName)); ReleaseEntry.WriteReleaseFile(nupkgs, releasesPath); } var ourAppName = ReleaseEntry.ParseReleaseFile(File.ReadAllText(releasesPath, Encoding.UTF8)) .First().PackageName; this.Log().Warn("Preparing to install"); string tempSettingsFile = null; string destSettingsFile = null; using (var mgr = new UpdateManager(sourceDirectory, ourAppName)) { this.Log().Info("About to install to: " + mgr.RootAppDirectory); if (Directory.Exists(mgr.RootAppDirectory)) { this.Log().Warn("Install path {0} already exists, burning it to the ground", mgr.RootAppDirectory); var settingsName = "UiPath.settings"; var settingsFile = Path.Combine(mgr.RootAppDirectory, settingsName); tempSettingsFile = File.Exists(settingsFile) ? Path.GetTempPath() + Path.GetRandomFileName() : null; if (tempSettingsFile != null) { this.Log().Warn("Backup uipath.settings"); destSettingsFile = settingsFile; File.Copy(settingsFile, tempSettingsFile); } bool success = false; int maxRetries = 3; do { var killed = mgr.KillAllExecutablesBelongingToPackage(); await Task.Delay(500); this.Log().Warn($"Deleted {killed} processes"); try { await this.ErrorIfThrows(() => Utility.DeleteDirectory(mgr.RootAppDirectory), "Failed to remove existing directory on full install, is the app still running???"); success = true; } catch (Exception ex) { this.Log().Warn($"Failed to delete whole folder. Reason {ex.ToString()}"); this.Log().Warn($"Attempts left {maxRetries}"); if (maxRetries == 0) { throw; } } }while (!success || maxRetries-- > 0); this.ErrorIfThrows(() => Utility.Retry(() => Directory.CreateDirectory(mgr.RootAppDirectory), 3), "Couldn't recreate app directory, perhaps Antivirus is blocking it"); } Directory.CreateDirectory(mgr.RootAppDirectory); var updateTarget = Path.Combine(mgr.RootAppDirectory, "Update.exe"); this.ErrorIfThrows(() => File.Copy(Assembly.GetExecutingAssembly().Location, updateTarget, true), "Failed to copy Update.exe to " + updateTarget); await mgr.FullInstall(silentInstall, progressSource.Raise); if (destSettingsFile != null && tempSettingsFile != null) { File.Copy(tempSettingsFile, destSettingsFile); } await this.ErrorIfThrows(() => mgr.CreateUninstallerRegistryEntry(), "Failed to create uninstaller registry entry"); } }
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); }