Пример #1
0
        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);
                    }
                }
        }
Пример #2
0
        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");
            }
        }
Пример #3
0
        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);
        }
Пример #5
0
        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);
                }
        }
Пример #6
0
        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);
                }
        }
Пример #7
0
        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);
        }
Пример #8
0
        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"));
        }
Пример #9
0
        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);
        }
Пример #10
0
        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);
            }
        }
Пример #11
0
        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))));
        }
Пример #13
0
        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);
            }
        }
Пример #18
0
        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")));
                }
        }
Пример #19
0
        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);
        }
Пример #20
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);
            }
Пример #22
0
        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);
            }
        }
Пример #24
0
        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();
            }
        }
Пример #25
0
        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");
            }
        }
Пример #26
0
        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);
        }