Пример #1
0
        public static UpdateInfo Create(ReleaseEntry currentVersion, IEnumerable<ReleaseEntry> availableReleases, string packageDirectory)
        {
            Contract.Requires(availableReleases != null);
            Contract.Requires(!String.IsNullOrEmpty(packageDirectory));

            var latestFull = availableReleases.MaxBy(x => x.Version).FirstOrDefault(x => !x.IsDelta);
            if (latestFull == null) {
                throw new Exception("There should always be at least one full release");
            }

            if (currentVersion == null) {
                return new UpdateInfo(null, new[] { latestFull }, packageDirectory);
            }

            if (currentVersion.Version == latestFull.Version) {
                return new UpdateInfo(currentVersion, Enumerable.Empty<ReleaseEntry>(), packageDirectory);
            }

            var newerThanUs = availableReleases
                .Where(x => x.Version > currentVersion.Version)
                .OrderBy(v => v.Version);

            var deltasSize = newerThanUs.Where(x => x.IsDelta).Sum(x => x.Filesize);

            return (deltasSize < latestFull.Filesize && deltasSize > 0) ? 
                new UpdateInfo(currentVersion, newerThanUs.Where(x => x.IsDelta).ToArray(), packageDirectory) : 
                new UpdateInfo(currentVersion, new[] { latestFull }, packageDirectory);
        }
            void checksumPackage(ReleaseEntry downloadedRelease)
            {
                var targetPackage = new FileInfo(
                    Path.Combine(rootAppDirectory, "packages", downloadedRelease.Filename));

                if (!targetPackage.Exists) {
                    this.Log().Error("File {0} should exist but doesn't", targetPackage.FullName);

                    throw new Exception("Checksummed file doesn't exist: " + targetPackage.FullName);
                }

                if (targetPackage.Length != downloadedRelease.Filesize) {
                    this.Log().Error("File Length should be {0}, is {1}", downloadedRelease.Filesize, targetPackage.Length);
                    targetPackage.Delete();

                    throw new Exception("Checksummed file size doesn't match: " + targetPackage.FullName);
                }

                using (var file = targetPackage.OpenRead()) {
                    var hash = Utility.CalculateStreamSHA1(file);

                    if (!hash.Equals(downloadedRelease.SHA1,StringComparison.OrdinalIgnoreCase)) {
                        this.Log().Error("File SHA1 should be {0}, is {1}", downloadedRelease.SHA1, hash);
                        targetPackage.Delete();
                        throw new Exception("Checksum doesn't match: " + targetPackage.FullName);
                    }
                }
            }
Пример #3
0
 private void OnNewVersionAvailable(ReleaseEntry release)
 {
     _logger.Info("New version {0} available!", release.Version);
     System.Windows.Application.Current.Dispatcher.Invoke(delegate {
         _newVersionAvailable.OnNext(release);
     });
 }
            Task downloadRelease(string updateBaseUrl, ReleaseEntry releaseEntry, IFileDownloader urlDownloader, string targetFile)
            {
                if (!updateBaseUrl.EndsWith("/")) {
                    updateBaseUrl += '/';
                }

                var sourceFileUrl = new Uri(new Uri(updateBaseUrl), releaseEntry.BaseUrl + releaseEntry.Filename).AbsoluteUri;
                File.Delete(targetFile);

                return urlDownloader.DownloadFile(sourceFileUrl, targetFile);
            }
Пример #5
0
        protected UpdateInfo(ReleaseEntry currentlyInstalledVersion, IEnumerable<ReleaseEntry> releasesToApply, string packageDirectory)
        {
            // NB: When bootstrapping, CurrentlyInstalledVersion is null!
            CurrentlyInstalledVersion = currentlyInstalledVersion;
            ReleasesToApply = (releasesToApply ?? Enumerable.Empty<ReleaseEntry>()).ToList();
            FutureReleaseEntry = ReleasesToApply.Any() ?
                ReleasesToApply.MaxBy(x => x.Version).FirstOrDefault() :
                CurrentlyInstalledVersion;

            this.PackageDirectory = packageDirectory;
        }
            Task downloadRelease(string updateBaseUrl, ReleaseEntry releaseEntry, IFileDownloader urlDownloader, string targetFile, Action<int> progress)
            {
                var baseUri = Utility.EnsureTrailingSlash(new Uri(updateBaseUrl));

                var releaseEntryUrl = releaseEntry.BaseUrl + releaseEntry.Filename;
                if (!String.IsNullOrEmpty(releaseEntry.Query)) {
                    releaseEntryUrl += releaseEntry.Query;
                }
                var sourceFileUrl = new Uri(baseUri, releaseEntryUrl).AbsoluteUri;
                File.Delete(targetFile);

                return urlDownloader.DownloadFile(sourceFileUrl, targetFile, progress);
            }
            Task downloadRelease(string updateBaseUrl, ReleaseEntry releaseEntry, IFileDownloader urlDownloader, string targetFile, Action<int> progress)
            {
                if (!updateBaseUrl.EndsWith("/")) {
                    updateBaseUrl += '/';
                }

                var releaseEntryUrl = releaseEntry.BaseUrl + releaseEntry.Filename;
                if (!String.IsNullOrEmpty(releaseEntry.Query)) {
                    releaseEntryUrl += releaseEntry.Query;
                }
                var sourceFileUrl = new Uri(new Uri(updateBaseUrl), releaseEntryUrl).AbsoluteUri;
                File.Delete(targetFile);

                return urlDownloader.DownloadFile(sourceFileUrl, targetFile, progress);
            }
Пример #8
0
        void createDeltaForSingleFile(FileInfo targetFile, DirectoryInfo workingDirectory, Dictionary <string, string> baseFileListing)
        {
            // NB: There are three cases here that we'll handle:
            //
            // 1. Exists only in new => leave it alone, we'll use it directly.
            // 2. Exists in both old and new => write a dummy file so we know
            //    to keep it.
            // 3. Exists in old but changed in new => create a delta file
            //
            // The fourth case of "Exists only in old => delete it in new"
            // is handled when we apply the delta package
            var relativePath = targetFile.FullName.Replace(workingDirectory.FullName, "");

            if (!baseFileListing.ContainsKey(relativePath))
            {
                this.Log().Info("{0} not found in base package, marking as new", relativePath);
                return;
            }

            var oldData = File.ReadAllBytes(baseFileListing[relativePath]);
            var newData = File.ReadAllBytes(targetFile.FullName);

            if (bytesAreIdentical(oldData, newData))
            {
                this.Log().Info("{0} hasn't changed, writing dummy file", relativePath);

                File.Create(targetFile.FullName + ".diff").Dispose();
                File.Create(targetFile.FullName + ".shasum").Dispose();
                targetFile.Delete();
                return;
            }

            this.Log().Info("Delta patching {0} => {1}", baseFileListing[relativePath], targetFile.FullName);
            var msDelta = new MsDeltaCompression();

            try {
                msDelta.CreateDelta(baseFileListing[relativePath], targetFile.FullName, targetFile.FullName + ".diff");
            } catch (Win32Exception ex) {
                this.Log().Warn("We couldn't create a delta for {0}, writing full file", targetFile.Name);
                return;
            }

            var rl = ReleaseEntry.GenerateFromFile(new MemoryStream(newData), targetFile.Name + ".shasum");

            File.WriteAllText(targetFile.FullName + ".shasum", rl.EntryAsString, Encoding.UTF8);
            targetFile.Delete();
        }
            async Task <string> installPackageToAppDir(UpdateInfo updateInfo, ReleaseEntry release)
            {
                string tmpDir             = default(string);
                bool   shouldDeleteTmpDir = findShortTemporaryDir(out tmpDir);

                var fs     = new PhysicalFileSystem(tmpDir);
                var pkg    = new OptimizedZipPackage(fs, Path.Combine(updateInfo.PackageDirectory, release.Filename));
                var target = getDirectoryForRelease(release.Version);

                // NB: This might happen if we got killed partially through applying the release
                if (target.Exists)
                {
                    this.Log().Warn("Found partially applied release folder, killing it: " + target.FullName);
                    await Utility.DeleteDirectory(target.FullName);
                }

                target.Create();

                // Copy all of the files out of the lib/ dirs in the NuGet package
                // into our target App directory.
                //
                // NB: We sort this list in order to guarantee that if a Net20
                // and a Net40 version of a DLL get shipped, we always end up
                // with the 4.0 version.
                this.Log().Info("Writing files to app directory: {0}", target.FullName);

                var toWrite = pkg.GetLibFiles().Where(x => pathIsInFrameworkProfile(x, appFrameworkVersion))
                              .OrderBy(x => x.Path)
                              .ToList();

                // NB: Because of the above NB, we cannot use ForEachAsync here, we
                // have to copy these files in-order. Once we fix assembly resolution,
                // we can kill both of these NBs.
                await Task.Run(() => toWrite.ForEach(x => copyFileToLocation(target, x)));

                await pkg.GetContentFiles().ForEachAsync(x => copyFileToLocation(target, x));

                if (shouldDeleteTmpDir)
                {
                    await Utility.DeleteDirectory(tmpDir);
                }

                return(target.FullName);
            }
Пример #10
0
            async Task <ReleaseEntry> createFullPackagesFromDeltas(IEnumerable <ReleaseEntry> releasesToApply, ReleaseEntry currentVersion)
            {
                Contract.Requires(releasesToApply != null);

                // If there are no remote releases at all, bail
                if (!releasesToApply.Any())
                {
                    return(null);
                }

                // If there are no deltas in our list, we're already done
                if (releasesToApply.All(x => !x.IsDelta))
                {
                    return(releasesToApply.MaxBy(x => x.Version).FirstOrDefault());
                }

                if (!releasesToApply.All(x => x.IsDelta))
                {
                    throw new Exception("Cannot apply combinations of delta and full packages");
                }

                // Smash together our base full package and the nearest delta
                var ret = await Task.Run(() =>
                {
                    var basePkg  = new ReleasePackage(Path.Combine(rootAppDirectory, "packages", currentVersion.Filename));
                    var deltaPkg = new ReleasePackage(Path.Combine(rootAppDirectory, "packages", releasesToApply.First().Filename));

                    var deltaBuilder = new DeltaPackageBuilder(Directory.GetParent(this.rootAppDirectory).FullName);

                    return(deltaBuilder.ApplyDeltaPackage(basePkg, deltaPkg,
                                                          Regex.Replace(deltaPkg.InputPackageFile, @"-delta.nupkg$", ".nupkg", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)));
                });

                if (releasesToApply.Count() == 1)
                {
                    return(ReleaseEntry.GenerateFromFile(ret.InputPackageFile));
                }

                var fi    = new FileInfo(ret.InputPackageFile);
                var entry = ReleaseEntry.GenerateFromFile(fi.OpenRead(), fi.Name);

                // Recursively combine the rest of them
                return(await createFullPackagesFromDeltas(releasesToApply.Skip(1), entry));
            }
Пример #11
0
            Task downloadRelease(string updateBaseUrl, ReleaseEntry releaseEntry, IFileDownloader urlDownloader, string targetFile, Action <int> progress)
            {
                if (!updateBaseUrl.EndsWith("/"))
                {
                    updateBaseUrl += '/';
                }

                var releaseEntryUrl = releaseEntry.BaseUrl + releaseEntry.Filename;

                if (!String.IsNullOrEmpty(releaseEntry.Query))
                {
                    releaseEntryUrl += releaseEntry.Query;
                }
                var sourceFileUrl = new Uri(new Uri(updateBaseUrl), releaseEntryUrl).AbsoluteUri;

                File.Delete(targetFile);

                return(urlDownloader.DownloadFile(sourceFileUrl, targetFile, progress));
            }
Пример #12
0
            async Task <string> installPackageToAppDir(UpdateInfo updateInfo, ReleaseEntry release)
            {
                var pkg    = new ZipPackage(Path.Combine(updateInfo.PackageDirectory, release.Filename));
                var target = getDirectoryForRelease(release.Version);

                // NB: This might happen if we got killed partially through applying the release
                if (target.Exists)
                {
                    this.Log().Warn("Found partially applied release folder, killing it: " + target.FullName);
                    await Utility.DeleteDirectory(target.FullName);
                }

                target.Create();

                // Copy all of the files out of the lib/ dirs in the NuGet package
                // into our target App directory.
                //
                // NB: We sort this list in order to guarantee that if a Net20
                // and a Net40 version of a DLL get shipped, we always end up
                // with the 4.0 version.
                this.Log().Info("Writing files to app directory: {0}", target.FullName);

                var toWrite = pkg.GetLibFiles().Where(x => pathIsInFrameworkProfile(x, appFrameworkVersion))
                              .OrderBy(x => x.Path)
                              .ToList();

                // NB: Because of the above NB, we cannot use ForEachAsync here, we
                // have to copy these files in-order. Once we fix assembly resolution,
                // we can kill both of these NBs.
                await Task.Run(() => toWrite.ForEach(x => copyFileToLocation(target, x)));

                await pkg.GetContentFiles().ForEachAsync(x => copyFileToLocation(target, x));

                var newCurrentVersion = updateInfo.FutureReleaseEntry.Version;

                // Perform post-install; clean up the previous version by asking it
                // which shortcuts to install, and nuking them. Then, run the app's
                // post install and set up shortcuts.
                this.ErrorIfThrows(() => runPostInstallAndCleanup(newCurrentVersion, updateInfo.IsBootstrapping));

                return(target.FullName);
            }
Пример #13
0
            bool isPackageOk(ReleaseEntry downloadedRelease)
            {
                var targetPackage = new FileInfo(
                    Path.Combine(rootAppDirectory, "packages", downloadedRelease.Filename));

                if (!targetPackage.Exists)
                {
                    return(false);
                }

                if (targetPackage.Length != downloadedRelease.Filesize)
                {
                    return(false);
                }

                using (var file = targetPackage.OpenRead()) {
                    var hash = Utility.CalculateStreamSHA1(file);
                    return(hash.Equals(downloadedRelease.SHA1, StringComparison.OrdinalIgnoreCase));
                }
            }
Пример #14
0
        private void verifyPatchedFile(string relativeFilePath, string inputFile, string tempTargetFile)
        {
            ReleaseEntry entry  = ReleaseEntry.ParseReleaseEntry(File.ReadAllText(Regex.Replace(inputFile, @"\.(bs)?diff$", ".shasum"), Encoding.UTF8));
            ReleaseEntry entry2 = ReleaseEntry.GenerateFromFile(tempTargetFile, null);

            if (entry.Filesize != entry2.Filesize)
            {
                this.Log <DeltaPackageBuilder>().Warn <string, long, long>("Patched file {0} has incorrect size, expected {1}, got {2}", relativeFilePath, entry.Filesize, entry2.Filesize);
                ChecksumFailedException exception1 = new ChecksumFailedException();
                exception1.Filename = relativeFilePath;
                throw exception1;
            }
            if (entry.SHA1 != entry2.SHA1)
            {
                this.Log <DeltaPackageBuilder>().Warn <string, string, string>("Patched file {0} has incorrect SHA1, expected {1}, got {2}", relativeFilePath, entry.SHA1, entry2.SHA1);
                ChecksumFailedException exception2 = new ChecksumFailedException();
                exception2.Filename = relativeFilePath;
                throw exception2;
            }
        }
            async Task<string> installPackageToAppDir(UpdateInfo updateInfo, ReleaseEntry release)
            {
                var pkg = new ZipPackage(Path.Combine(updateInfo.PackageDirectory, release.Filename));
                var target = getDirectoryForRelease(release.Version);

                // NB: This might happen if we got killed partially through applying the release
                if (target.Exists) {
                    this.Log().Warn("Found partially applied release folder, killing it: " + target.FullName);
                    await Utility.DeleteDirectory(target.FullName);
                }

                target.Create();

                // Copy all of the files out of the lib/ dirs in the NuGet package
                // into our target App directory.
                //
                // NB: We sort this list in order to guarantee that if a Net20
                // and a Net40 version of a DLL get shipped, we always end up
                // with the 4.0 version.
                this.Log().Info("Writing files to app directory: {0}", target.FullName);

                var toWrite = pkg.GetLibFiles().Where(x => pathIsInFrameworkProfile(x, appFrameworkVersion))
                    .OrderBy(x => x.Path)
                    .ToList();

                // NB: Because of the above NB, we cannot use ForEachAsync here, we 
                // have to copy these files in-order. Once we fix assembly resolution, 
                // we can kill both of these NBs.
                await Task.Run(() => toWrite.ForEach(x => copyFileToLocation(target, x)));

                await pkg.GetContentFiles().ForEachAsync(x => copyFileToLocation(target, x));

                var newCurrentVersion = updateInfo.FutureReleaseEntry.Version;

                // Perform post-install; clean up the previous version by asking it
                // which shortcuts to install, and nuking them. Then, run the app's
                // post install and set up shortcuts.
                this.ErrorIfThrows(() => runPostInstallAndCleanup(newCurrentVersion, updateInfo.IsBootstrapping));

                return target.FullName;
            }
            Task <string> installPackageToAppDir(UpdateInfo updateInfo, ReleaseEntry release, Action <int> progressCallback)
            {
                return(Task.Run(async() => {
                    var target = getDirectoryForRelease(release.Version);

                    // NB: This might happen if we got killed partially through applying the release
                    if (target.Exists)
                    {
                        this.Log().Warn("Found partially applied release folder, killing it: " + target.FullName);
                        await Utility.DeleteDirectory(target.FullName);
                    }

                    target.Create();
                    try
                    {
                        this.Log().Info("Writing files to app directory: {0}", target.FullName);
                        await ReleasePackage.ExtractZipForInstall(
                            Path.Combine(updateInfo.PackageDirectory, release.Filename),
                            target.FullName,
                            rootAppDirectory,
                            progressCallback);
                    }
                    catch (Exception ex)
                    {
                        try
                        {
                            //Don't leave that empty directory there: its existence will prevent the user from even using the previous version.
                            await Utility.DeleteDirectory(target.FullName);
                        }
                        catch (Exception error)
                        {
                            //This is going to be one very stuck user...
                            this.Log().ErrorException("Failed to cleanup new directory after failed install: " + target.FullName, error);
                        }
                        throw ex;
                    }

                    return target.FullName;
                }));
            }
Пример #17
0
            Task <string> installPackageToAppDir(UpdateInfo updateInfo, ReleaseEntry release)
            {
                return(Task.Run(async() => {
                    var target = getDirectoryForRelease(release.Version);

                    // NB: This might happen if we got killed partially through applying the release
                    if (target.Exists)
                    {
                        this.Log().Warn("Found partially applied release folder, killing it: " + target.FullName);
                        await Utility.DeleteDirectory(target.FullName);
                    }

                    target.Create();

                    this.Log().Info("Writing files to app directory: {0}", target.FullName);
                    await ReleasePackage.ExtractZipForInstall(
                        Path.Combine(updateInfo.PackageDirectory, release.Filename),
                        target.FullName);

                    return target.FullName;
                }));
            }
            public void UpdateUninstallRegisty()
            {
                var releaseContent = File.ReadAllText(Path.Combine(rootAppDirectory, "packages", "RELEASES"), Encoding.UTF8);
                var releases       = ReleaseEntry.ParseReleaseFile(releaseContent);
                var latest         = releases.Where(x => !x.IsDelta).OrderByDescending(x => x.Version).First();

                var pkgPath = Path.Combine(rootAppDirectory, "packages", latest.Filename);
                var zp      = new ZipPackage(pkgPath);

                var installInfoFile   = Path.Combine(this.rootAppDirectory, ".installInfo.json");
                var installInfoResult = Utility.GetInstallInfo(installInfoFile);

                if (installInfoResult.Success)
                {
                    var verion      = installInfoResult.InstallVersion as string;
                    var productCode = installInfoResult.ProductCode as string;
                    var appName     = installInfoResult.AppName as string;
                    var arch        = installInfoResult.Arch == "x86" ? "x86" : "x64";
                    var key         = GetUninstallRegKey(productCode, appName, arch);
                    if (key != null)
                    {
                        try {
                            key.SetValue("DisplayVersion", zp.Version.ToString());
                            key.Close();
                            this.Log().Info($"UpdateUninstallRegisty: DisplayVersion updated to {zp.Version.ToString()}");
                        } catch (Exception ex) {
                            this.Log().ErrorException("UpdateUninstallRegisty: Failed to write current version to registry.", ex);
                        }
                    }
                    else
                    {
                        this.Log().Error("UpdateUninstallRegisty: Registry key not found or no write access. Unable to update current version.");
                    }
                }
                else
                {
                    this.Log().Error("UpdateUninstallRegisty: Unable to update current version without installInfo.");
                }
            }
 bool isReleaseExplicitlyHttp(ReleaseEntry x)
 {
     return x.BaseUrl != null && 
         Uri.IsWellFormedUriString(x.BaseUrl, UriKind.Absolute);
 }
Пример #20
0
        void createDeltaForSingleFile(FileInfo targetFile, DirectoryInfo workingDirectory, Dictionary <string, string> baseFileListing)
        {
            // NB: There are three cases here that we'll handle:
            //
            // 1. Exists only in new => leave it alone, we'll use it directly.
            // 2. Exists in both old and new => write a dummy file so we know
            //    to keep it.
            // 3. Exists in old but changed in new => create a delta file
            //
            // The fourth case of "Exists only in old => delete it in new"
            // is handled when we apply the delta package
            var relativePath = targetFile.FullName.Replace(workingDirectory.FullName, "");

            if (!baseFileListing.ContainsKey(relativePath))
            {
                this.Log().Info("{0} not found in base package, marking as new", relativePath);
                return;
            }

            var oldData = File.ReadAllBytes(baseFileListing[relativePath]);
            var newData = File.ReadAllBytes(targetFile.FullName);

            if (bytesAreIdentical(oldData, newData))
            {
                this.Log().Info("{0} hasn't changed, writing dummy file", relativePath);

                File.Create(targetFile.FullName + ".diff").Dispose();
                File.Create(targetFile.FullName + ".shasum").Dispose();
                targetFile.Delete();
                return;
            }

            this.Log().Info("Delta patching {0} => {1}", baseFileListing[relativePath], targetFile.FullName);
            var msDelta = new MsDeltaCompression();

            if (targetFile.Extension.Equals(".exe", StringComparison.OrdinalIgnoreCase) ||
                targetFile.Extension.Equals(".dll", StringComparison.OrdinalIgnoreCase) ||
                targetFile.Extension.Equals(".node", StringComparison.OrdinalIgnoreCase))
            {
                try {
                    msDelta.CreateDelta(baseFileListing[relativePath], targetFile.FullName, targetFile.FullName + ".diff");
                    goto exit;
                } catch (Exception) {
                    this.Log().Warn("We couldn't create a delta for {0}, attempting to create bsdiff", targetFile.Name);
                }
            }

            var of = default(FileStream);

            try {
                of = File.Create(targetFile.FullName + ".bsdiff");
                BinaryPatchUtility.Create(oldData, newData, of);

                // NB: Create a dummy corrupt .diff file so that older
                // versions which don't understand bsdiff will fail out
                // until they get upgraded, instead of seeing the missing
                // file and just removing it.
                File.WriteAllText(targetFile.FullName + ".diff", "1");
            } catch (Exception ex) {
                this.Log().WarnException(String.Format("We really couldn't create a delta for {0}", targetFile.Name), ex);
                return;
            } finally {
                if (of != null)
                {
                    of.Dispose();
                }
            }

exit:

            var rl = ReleaseEntry.GenerateFromFile(new MemoryStream(newData), targetFile.Name + ".shasum");

            File.WriteAllText(targetFile.FullName + ".shasum", rl.EntryAsString, Encoding.UTF8);
            targetFile.Delete();
        }
            async Task <string> installFromDelta(UpdateInfo updateInfo)
            {
                IEnumerable <ReleaseEntry> releasesToApply = updateInfo.ReleasesToApply;
                ReleaseEntry currentVersion   = updateInfo.CurrentlyInstalledVersion;
                var          packageDirectory = updateInfo.PackageDirectory;

                Contract.Requires(releasesToApply != null);

                // If there are no remote releases at all, bail
                if (!releasesToApply.Any())
                {
                    return(null);
                }

                // If there are no deltas in our list, install the last full one
                if (releasesToApply.All(x => !x.IsDelta))
                {
                    return(await installPackageToAppDir(updateInfo, releasesToApply.MaxBy(x => x.Version).FirstOrDefault()));
                }

                if (!releasesToApply.All(x => x.IsDelta))
                {
                    throw new Exception("Cannot apply combinations of delta and full packages");
                }

                string finalOutputPath   = getDirectoryForRelease(releasesToApply.Last().Version).ToString();
                string localAppDirectory = Directory.GetParent(this.rootAppDirectory).FullName;
                string workingPath;

                using (Utility.WithTempDirectory(out workingPath, localAppDirectory))
                {
                    var opts = new ExtractionOptions()
                    {
                        ExtractFullPath = true, Overwrite = true, PreserveFileTime = true
                    };

                    //Extract base file to working folder
                    this.Log().Info("Extracting base file to working folder {0}", currentVersion.Filename);
                    using (var za = ZipArchive.Open(Path.Combine(rootAppDirectory, "packages", currentVersion.Filename)))
                        using (var reader = za.ExtractAllEntries())
                        {
                            reader.WriteAllToDirectory(workingPath, opts);
                        }

                    //Apply each incremental release
                    foreach (var delta in releasesToApply)
                    {
                        this.Log().Info("Applying delta release {0} to working folder", delta.Filename);
                        var deltaPkg     = new ReleasePackage(Path.Combine(rootAppDirectory, "packages", delta.Filename));
                        var deltaBuilder = new DeltaPackageBuilder(Directory.GetParent(this.rootAppDirectory).FullName);
                        deltaBuilder.ApplyDeltaPackageToWorkingDirectory(workingPath, deltaPkg);
                    }

                    //Save the final thing into a local full package
                    var lastDeltaPkgFullPath = Path.Combine(rootAppDirectory, "packages", releasesToApply.Last().Filename);

                    var fullPackageOutput = Regex.Replace(lastDeltaPkgFullPath, @"-delta.nupkg$", ".nupkg", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
                    this.Log().Info("Repacking into full package: {0}", fullPackageOutput);
                    using (var za = ZipArchive.Create())
                        using (var tgt = File.OpenWrite(fullPackageOutput))
                        {
                            za.DeflateCompressionLevel = CompressionLevel.BestSpeed;
                            za.AddAllFromDirectory(workingPath);
                            za.SaveTo(tgt);
                        }

                    //Convert this from NuGet package format to raw folder format
                    ReleasePackage.ConvertNuGetWorkingDirectoryForInstall(workingPath, finalOutputPath, rootAppDirectory);
                }

                return(finalOutputPath);
            }
Пример #22
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>();

                bool shouldInitialize = false;

                try {
                    localReleases = Utility.LoadLocalReleases(localReleaseFile);
                } catch (Exception ex) {
                    // Something has gone pear-shaped, let's start from scratch
                    this.Log().WarnException("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(ReleaseEntry);

                // 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);
                    }

                    this.Log().Info("Downloading RELEASES file from {0}", updateUrlOrPath);

                    int retries = 3;

retry:

                    try {
                        var url = String.Format("{0}/{1}", updateUrlOrPath, "RELEASES");
                        if (latestLocalRelease != null)
                        {
                            url = String.Format("{0}/RELEASES?id={1}&localVersion={2}&arch={3}",
                                                updateUrlOrPath,
                                                Uri.EscapeUriString(latestLocalRelease.PackageName),
                                                Uri.EscapeUriString(latestLocalRelease.Version.ToString()),
                                                Environment.Is64BitOperatingSystem ? "amd64" : "x86");
                        }

                        var data = await urlDownloader.DownloadUrl(url);

                        releaseFile = Encoding.UTF8.GetString(data);
                    } catch (WebException ex) {
                        this.Log().InfoException("Download resulted in WebException (returning blank release list)", ex);

                        if (retries <= 0)
                        {
                            throw;
                        }
                        retries--;
                        goto retry;
                    }

                    progress(33);
                }
                else
                {
                    this.Log().Info("Reading RELEASES file from {0}", updateUrlOrPath);

                    if (!Directory.Exists(updateUrlOrPath))
                    {
                        var message = String.Format(
                            "The directory {0} does not exist, something is probably broken with your application",
                            updateUrlOrPath);

                        throw new Exception(message);
                    }

                    var fi = new FileInfo(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);

                        this.Log().Warn(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.ParseReleaseFile(releaseFile);

                progress(66);

                if (!remoteReleases.Any())
                {
                    throw new Exception("Remote release File is empty or corrupted");
                }

                ret = determineUpdateInfo(localReleases, remoteReleases, ignoreDeltaUpdates);

                progress(100);
                return(ret);
            }
            Task<string> installPackageToAppDir(UpdateInfo updateInfo, ReleaseEntry release)
            {
                return Task.Run(async () => {
                    var target = getDirectoryForRelease(release.Version);

                    // NB: This might happen if we got killed partially through applying the release
                    if (target.Exists) {
                        this.Log().Warn("Found partially applied release folder, killing it: " + target.FullName);
                        await Utility.DeleteDirectory(target.FullName);
                    }

                    target.Create();

                    this.Log().Info("Writing files to app directory: {0}", target.FullName);
                    await ReleasePackage.ExtractZipForInstall(
                        Path.Combine(updateInfo.PackageDirectory, release.Filename),
                        target.FullName);

                    return target.FullName;
                });
            }
            // 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.
            async Task cleanDeadVersions(SemanticVersion originalVersion, SemanticVersion currentVersion, bool forceUninstall = false)
            {
                if (currentVersion == null)
                {
                    return;
                }

                var di = new DirectoryInfo(rootAppDirectory);

                if (!di.Exists)
                {
                    return;
                }

                this.Log().Info("cleanDeadVersions: for version {0}", currentVersion);

                string originalVersionFolder = null;

                if (originalVersion != null)
                {
                    originalVersionFolder = getDirectoryForRelease(originalVersion).Name;
                    this.Log().Info("cleanDeadVersions: exclude folder {0}", originalVersionFolder);
                }

                string currentVersionFolder = null;

                if (currentVersion != null)
                {
                    currentVersionFolder = getDirectoryForRelease(currentVersion).Name;
                    this.Log().Info("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         = String.Format("--squirrel-obsolete {0}", 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) {
                                        this.Log().ErrorException("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.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) {
                        this.Log().WarnException("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 async Task <RegistryKey> CreateUninstallerRegistryEntry(string uninstallCmd, string quietSwitch)
            {
                var releaseContent = File.ReadAllText(Path.Combine(rootAppDirectory, "packages", "RELEASES"), Encoding.UTF8);
                var releases       = ReleaseEntry.ParseReleaseFile(releaseContent);
                var latest         = releases.Where(x => !x.IsDelta).OrderByDescending(x => x.Version).First();

                // Download the icon and PNG => ICO it. If this doesn't work, who cares
                var pkgPath = Path.Combine(rootAppDirectory, "packages", latest.Filename);
                var zp      = new ZipPackage(pkgPath);

                var targetPng = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".png");
                var targetIco = Path.Combine(rootAppDirectory, "app.ico");

                // NB: Sometimes the Uninstall key doesn't exist
                using (var parentKey =
                           RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default)
                           .CreateSubKey("Uninstall", RegistryKeyPermissionCheck.ReadWriteSubTree)) {; }

                var key = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default)
                          .CreateSubKey(uninstallRegSubKey + "\\" + applicationName, RegistryKeyPermissionCheck.ReadWriteSubTree);

                if (zp.IconUrl != null && !File.Exists(targetIco))
                {
                    try {
                        using (var wc = Utility.CreateWebClient()) {
                            await wc.DownloadFileTaskAsync(zp.IconUrl, targetPng);

                            using (var fs = new FileStream(targetIco, FileMode.Create)) {
                                if (zp.IconUrl.AbsolutePath.EndsWith("ico"))
                                {
                                    var bytes = File.ReadAllBytes(targetPng);
                                    fs.Write(bytes, 0, bytes.Length);
                                }
                                else
                                {
                                    using (var bmp = (Bitmap)Image.FromFile(targetPng))
                                        using (var ico = Icon.FromHandle(bmp.GetHicon())) {
                                            ico.Save(fs);
                                        }
                                }

                                key.SetValue("DisplayIcon", targetIco, RegistryValueKind.String);
                            }
                        }
                    } catch (Exception ex) {
                        this.Log().InfoException("Couldn't write uninstall icon, don't care", ex);
                    } finally {
                        File.Delete(targetPng);
                    }
                }

                var stringsToWrite = new[] {
                    new { Key = "DisplayName", Value = zp.Title ?? zp.Description ?? zp.Summary },
                    new { Key = "DisplayVersion", Value = zp.Version.ToString() },
                    new { Key = "InstallDate", Value = DateTime.Now.ToString("yyyyMMdd") },
                    new { Key = "InstallLocation", Value = rootAppDirectory },
                    new { Key = "Publisher", Value = String.Join(",", zp.Authors) },
                    new { Key = "QuietUninstallString", Value = String.Format("{0} {1}", uninstallCmd, quietSwitch) },
                    new { Key = "UninstallString", Value = uninstallCmd },
                    new { Key = "URLUpdateInfo", Value = zp.ProjectUrl != null?zp.ProjectUrl.ToString() : "", }
                };

                var dwordsToWrite = new[] {
                    new { Key = "EstimatedSize", Value = (int)((new FileInfo(pkgPath)).Length / 1024) },
                    new { Key = "NoModify", Value = 1 },
                    new { Key = "NoRepair", Value = 1 },
                    new { Key = "Language", Value = 0x0409 },
                };

                foreach (var kvp in stringsToWrite)
                {
                    key.SetValue(kvp.Key, kvp.Value, RegistryValueKind.String);
                }
                foreach (var kvp in dwordsToWrite)
                {
                    key.SetValue(kvp.Key, kvp.Value, RegistryValueKind.DWord);
                }

                return(key);
            }
Пример #26
0
            public async Task <UpdateInfo> CheckForUpdate(
                string localReleaseFile,
                string updateUrlOrPath,
                bool ignoreDeltaUpdates       = false,
                Action <int> progress         = null,
                IFileDownloader urlDownloader = null,
                bool startOverIfNone          = false)
            {
                progress = progress ?? (_ => { });

                var localReleases = Enumerable.Empty <ReleaseEntry>();

                bool shouldInitialize = false;

                try {
                    localReleases = Utility.LoadLocalReleases(localReleaseFile);
                } catch (Exception ex) {
                    // Something has gone pear-shaped, let's start from scratch
                    this.Log().WarnException("Failed to load local releases, starting from scratch", ex);
                    shouldInitialize = true;
                }

restart:

                if (shouldInitialize)
                {
                    await initializeClientAppDirectory();
                }

                string releaseFile;

                var latestLocalRelease = localReleases.Count() > 0 ?
                                         localReleases.MaxBy(x => x.Version).First() :
                                         default(ReleaseEntry);

                // 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);
                    }

                    this.Log().Info("Downloading RELEASES file from {0}", updateUrlOrPath);

                    int 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) {
                        this.Log().InfoException("Download resulted in WebException (returning blank release list)", ex);

                        if (retries <= 0)
                        {
                            throw;
                        }
                        retries--;
                        goto retry;
                    }

                    progress(33);
                }
                else
                {
                    this.Log().Info("Reading RELEASES file from {0}", updateUrlOrPath);

                    if (!Directory.Exists(updateUrlOrPath))
                    {
                        var message = String.Format(
                            "The directory {0} does not exist, something is probably broken with your application",
                            updateUrlOrPath);

                        throw new Exception(message);
                    }

                    var fi = new FileInfo(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);

                        this.Log().Warn(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.ParseReleaseFile(releaseFile);

                progress(66);

                if (!remoteReleases.Any())
                {
                    throw new Exception("Remote release File is empty or corrupted");
                }

                ret = determineUpdateInfo(localReleases, remoteReleases, ignoreDeltaUpdates);
                progress(100);
                if (startOverIfNone && !ret.ReleasesToApply.Any())
                {
                    // User has apparently re-run the installer for the version already installed.
                    // Assume the intent is to repair a broken installation.
                    // These rather awkward steps cause it to erase the installed-version directory
                    // and re-create it, much like a first-time install (though it won't run the app
                    // with the special arguments for first-time).
                    shouldInitialize = true;
                    localReleases    = Enumerable.Empty <ReleaseEntry>();
                    goto restart;
                }
                return(ret);
            }
            static long GetTotalUncompressedSize(UpdateInfo updateInfo, ReleaseEntry release)
            {
                long totalSize = 0;
                using (var stream = File.OpenRead(Path.Combine(updateInfo.PackageDirectory, release.Filename)))
                {
                    var zipIn = new ZipInputStream(stream);
                    var zipEntry = zipIn.GetNextEntry();

                    while (zipEntry != null)
                    {
                        var pathParts = zipEntry.Name.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);

                        var foundLib = false;
                        foreach (var part in pathParts)
                        {
                            if (part.Equals("lib", StringComparison.InvariantCulture))
                            {
                                foundLib = true;
                            }
                        }

                        if (!foundLib)
                        {
                            zipEntry = zipIn.GetNextEntry();
                            continue;
                        }

                        totalSize += zipEntry.Size;
                        zipEntry = zipIn.GetNextEntry();
                    }
                }
                return totalSize;
            }
            Task<string> installPackageToAppDir(UpdateInfo updateInfo, ReleaseEntry release, Action<int> progress)
            {
                return Task.Run(async () => 
                {
                    var target = getDirectoryForRelease(release.Version);

                    // NB: This might happen if we got killed partially through applying the release
                    if (target.Exists)
                    {
                        this.Log().Warn("Found partially applied release folder, killing it: " + target.FullName);
                        await Utility.DeleteDirectory(target.FullName);
                    }

                    target.Create();

                    var totalSize = GetTotalUncompressedSize(updateInfo, release);
                    var currentExtractedSize = 0L;
                    using (var stream = File.OpenRead(Path.Combine(updateInfo.PackageDirectory, release.Filename)))
                    {
                       // progress.Report(new InstallerStep("Extracting"));
                       
                        var zipIn = new ZipInputStream(stream);
                        var zipEntry = zipIn.GetNextEntry();

                        while (zipEntry != null)
                        {
                            var entryName = zipEntry.Name;
                            var pathParts = entryName.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);

                            var foundLib = false;
                            foreach (var part in pathParts)
                            {
                                if (part.Equals("lib", StringComparison.InvariantCulture))
                                {
                                    foundLib = true;
                                }
                            }

                            if (!foundLib)
                            {
                                zipEntry = zipIn.GetNextEntry();
                                continue;
                            }

                            var buffer = new byte[4096];
                            var fullZipToPath = Path.Combine(target.FullName, entryName.Replace('/', Path.DirectorySeparatorChar));
                            var directoryName = zipEntry.IsFile ? Path.GetDirectoryName(fullZipToPath) : fullZipToPath;
                            if (!string.IsNullOrEmpty(directoryName))
                                Directory.CreateDirectory(directoryName);
                            
                            using (var streamWriter = File.Create(fullZipToPath))
                            {
                                currentExtractedSize += zipEntry.Size;
                                StreamUtils.Copy(zipIn, streamWriter, buffer);
                                progress((int)((double) currentExtractedSize * 100.0 / (double) totalSize));
                            }

                            zipEntry = zipIn.GetNextEntry();
                        }
                    }

                    this.Log().Info("Writing files to app directory: {0}", target.FullName);

                    // Move all of the files out of the lib/ dirs in the NuGet package
                    // into our target App directory.
                    //
                    // NB: We sort this list in order to guarantee that if a Net20
                    // and a Net40 version of a DLL get shipped, we always end up
                    // with the 4.0 version.
                    var libDir = target.GetDirectories().First(x => x.Name.Equals("lib", StringComparison.OrdinalIgnoreCase));
                    var toMove = libDir.GetDirectories().OrderBy(x => x.Name);

                    toMove.ForEach(ld => {
                        ld.GetDirectories()
                            .ForEachAsync(subdir => subdir.MoveTo(subdir.FullName.Replace(ld.FullName, target.FullName)))
                            .Wait();

                        ld.GetFiles()
                            .ForEachAsync(file => {
                                var tgt = Path.Combine(target.FullName, file.Name);
                                this.Log().Info("Moving file {0} to {1}", file.FullName, tgt);
                                if (File.Exists(tgt)) Utility.DeleteFileHarder(tgt, true);
                                file.MoveTo(tgt);
                            })
                            .Wait();
                    });

                    await Utility.DeleteDirectory(libDir.FullName);
                    return target.FullName;
                });
            }
Пример #29
0
        public ReleasePackage ApplyDeltaPackage(ReleasePackage basePackage, ReleasePackage deltaPackage, string outputFile, Action <int> progress = null)
        {
            Contract.Requires(deltaPackage != null);
            Contract.Requires(!String.IsNullOrEmpty(outputFile) && !File.Exists(outputFile));
            if (progress == null)
            {
                progress = i => { }
            }
            ;

            string workingPath;
            string deltaPath;

            using (Utility.WithTempDirectory(out deltaPath, localAppDirectory))
                using (Utility.WithTempDirectory(out workingPath, localAppDirectory)) {
                    var fz = new FastZip();
                    fz.ExtractZip(deltaPackage.InputPackageFile, deltaPath, null);
                    // The percentages here are somewhat arbitrary and reflect typical Bloom file sizes.
                    // Probably not appropriate to push upstream.
                    // If you are thinking about improving this using FastZip's Progress event, check with JohnT.
                    // I experimented with it and found that the PercentCompleted which it reports is useless,
                    // toggling between 0 and 100% repeatedly...possibly it applies to a particular file?
                    // I also found that it is VERY expensive to turn on progress reporting while unzipping;
                    // seems to slow the process down to the point of making it take two to three times as long.
                    // This may of course improve in a later version of the library.
                    progress(10);
                    fz.ExtractZip(basePackage.InputPackageFile, workingPath, null);
                    progress(35);

                    var pathsVisited = new List <string>();

                    var deltaPathRelativePaths = new DirectoryInfo(deltaPath).GetAllFilesRecursively()
                                                 .Select(x => x.FullName.Replace(deltaPath + Path.DirectorySeparatorChar, ""))
                                                 .ToArray();

                    // Apply all of the .diff files
                    deltaPathRelativePaths
                    .Where(x => x.StartsWith("lib", StringComparison.InvariantCultureIgnoreCase))
                    .Where(x => !x.EndsWith(".shasum", StringComparison.InvariantCultureIgnoreCase))
                    .Where(x => !x.EndsWith(".diff", StringComparison.InvariantCultureIgnoreCase) ||
                           !deltaPathRelativePaths.Contains(x.Replace(".diff", ".bsdiff")))
                    .ForEach(file => {
                        pathsVisited.Add(Regex.Replace(file, @"\.(bs)?diff$", "").ToLowerInvariant());
                        applyDiffToFile(deltaPath, file, workingPath);
                    });

                    // Delete all of the files that were in the old package but
                    // not in the new one.
                    new DirectoryInfo(workingPath).GetAllFilesRecursively()
                    .Select(x => x.FullName.Replace(workingPath + Path.DirectorySeparatorChar, "").ToLowerInvariant())
                    .Where(x => x.StartsWith("lib", StringComparison.InvariantCultureIgnoreCase) && !pathsVisited.Contains(x))
                    .ForEach(x => {
                        this.Log().Info("{0} was in old package but not in new one, deleting", x);
                        File.Delete(Path.Combine(workingPath, x));
                    });

                    // Update all the files that aren't in 'lib' with the delta
                    // package's versions (i.e. the nuspec file, etc etc).
                    deltaPathRelativePaths
                    .Where(x => !x.StartsWith("lib", StringComparison.InvariantCultureIgnoreCase))
                    .ForEach(x => {
                        this.Log().Info("Updating metadata file: {0}", x);
                        File.Copy(Path.Combine(deltaPath, x), Path.Combine(workingPath, x), true);
                    });
                    progress(50);

                    this.Log().Info("Repacking into full package: {0}", outputFile);
                    fz.CreateZip(outputFile, workingPath, true, null);
                    progress(100);
                }

            return(new ReleasePackage(outputFile));
        }

        void createDeltaForSingleFile(FileInfo targetFile, DirectoryInfo workingDirectory, Dictionary <string, string> baseFileListing)
        {
            // NB: There are three cases here that we'll handle:
            //
            // 1. Exists only in new => leave it alone, we'll use it directly.
            // 2. Exists in both old and new => write a dummy file so we know
            //    to keep it.
            // 3. Exists in old but changed in new => create a delta file
            //
            // The fourth case of "Exists only in old => delete it in new"
            // is handled when we apply the delta package
            var relativePath = targetFile.FullName.Replace(workingDirectory.FullName, "");

            if (!baseFileListing.ContainsKey(relativePath))
            {
                this.Log().Info("{0} not found in base package, marking as new", relativePath);
                return;
            }

            var oldData = File.ReadAllBytes(baseFileListing[relativePath]);
            var newData = File.ReadAllBytes(targetFile.FullName);

            if (bytesAreIdentical(oldData, newData))
            {
                this.Log().Info("{0} hasn't changed, writing dummy file", relativePath);

                File.Create(targetFile.FullName + ".diff").Dispose();
                File.Create(targetFile.FullName + ".shasum").Dispose();
                targetFile.Delete();
                return;
            }

            this.Log().Info("Delta patching {0} => {1}", baseFileListing[relativePath], targetFile.FullName);
            var msDelta = new MsDeltaCompression();

            try {
                msDelta.CreateDelta(baseFileListing[relativePath], targetFile.FullName, targetFile.FullName + ".diff");
            } catch (Win32Exception) {
                this.Log().Warn("We couldn't create a delta for {0}, attempting to create bsdiff", targetFile.Name);

                var of = default(FileStream);
                try {
                    of = File.Create(targetFile.FullName + ".bsdiff");
                    BinaryPatchUtility.Create(oldData, newData, of);

                    // NB: Create a dummy corrupt .diff file so that older
                    // versions which don't understand bsdiff will fail out
                    // until they get upgraded, instead of seeing the missing
                    // file and just removing it.
                    File.WriteAllText(targetFile.FullName + ".diff", "1");
                } catch (Exception ex) {
                    this.Log().WarnException(String.Format("We really couldn't create a delta for {0}", targetFile.Name), ex);
                    return;
                } finally {
                    if (of != null)
                    {
                        of.Dispose();
                    }
                }
            }

            var rl = ReleaseEntry.GenerateFromFile(new MemoryStream(newData), targetFile.Name + ".shasum");

            File.WriteAllText(targetFile.FullName + ".shasum", rl.EntryAsString, Encoding.UTF8);
            targetFile.Delete();
        }

        void applyDiffToFile(string deltaPath, string relativeFilePath, string workingDirectory)
        {
            var inputFile   = Path.Combine(deltaPath, relativeFilePath);
            var finalTarget = Path.Combine(workingDirectory, Regex.Replace(relativeFilePath, @"\.(bs)?diff$", ""));

            var tempTargetFile = default(string);

            Utility.WithTempFile(out tempTargetFile, localAppDirectory);

            try {
                // NB: Zero-length diffs indicate the file hasn't actually changed
                if (new FileInfo(inputFile).Length == 0)
                {
                    this.Log().Info("{0} exists unchanged, skipping", relativeFilePath);
                    return;
                }

                if (relativeFilePath.EndsWith(".bsdiff", StringComparison.InvariantCultureIgnoreCase))
                {
                    using (var of = File.OpenWrite(tempTargetFile))
                        using (var inf = File.OpenRead(finalTarget)) {
                            this.Log().Info("Applying BSDiff to {0}", relativeFilePath);
                            BinaryPatchUtility.Apply(inf, () => File.OpenRead(inputFile), of);
                        }

                    verifyPatchedFile(relativeFilePath, inputFile, tempTargetFile);
                }
                else if (relativeFilePath.EndsWith(".diff", StringComparison.InvariantCultureIgnoreCase))
                {
                    this.Log().Info("Applying MSDiff to {0}", relativeFilePath);
                    var msDelta = new MsDeltaCompression();
                    msDelta.ApplyDelta(inputFile, finalTarget, tempTargetFile);

                    verifyPatchedFile(relativeFilePath, inputFile, tempTargetFile);
                }
                else
                {
                    using (var of = File.OpenWrite(tempTargetFile))
                        using (var inf = File.OpenRead(inputFile)) {
                            this.Log().Info("Adding new file: {0}", relativeFilePath);
                            inf.CopyTo(of);
                        }
                }

                if (File.Exists(finalTarget))
                {
                    File.Delete(finalTarget);
                }

                var targetPath = Directory.GetParent(finalTarget);
                if (!targetPath.Exists)
                {
                    targetPath.Create();
                }

                File.Move(tempTargetFile, finalTarget);
            } finally {
                if (File.Exists(tempTargetFile))
                {
                    Utility.DeleteFileHarder(tempTargetFile, true);
                }
            }
        }

        void verifyPatchedFile(string relativeFilePath, string inputFile, string tempTargetFile)
        {
            var shaFile = Regex.Replace(inputFile, @"\.(bs)?diff$", ".shasum");
            var expectedReleaseEntry = ReleaseEntry.ParseReleaseEntry(File.ReadAllText(shaFile, Encoding.UTF8));
            var actualReleaseEntry   = ReleaseEntry.GenerateFromFile(tempTargetFile);

            if (expectedReleaseEntry.Filesize != actualReleaseEntry.Filesize)
            {
                this.Log().Warn("Patched file {0} has incorrect size, expected {1}, got {2}", relativeFilePath,
                                expectedReleaseEntry.Filesize, actualReleaseEntry.Filesize);
                throw new ChecksumFailedException()
                      {
                          Filename = relativeFilePath
                      };
            }

            if (expectedReleaseEntry.SHA1 != actualReleaseEntry.SHA1)
            {
                this.Log().Warn("Patched file {0} has incorrect SHA1, expected {1}, got {2}", relativeFilePath,
                                expectedReleaseEntry.SHA1, actualReleaseEntry.SHA1);
                throw new ChecksumFailedException()
                      {
                          Filename = relativeFilePath
                      };
            }
        }

        bool bytesAreIdentical(byte[] oldData, byte[] newData)
        {
            if (oldData == null || newData == null)
            {
                return(oldData == newData);
            }
            if (oldData.LongLength != newData.LongLength)
            {
                return(false);
            }

            for (long i = 0; i < newData.LongLength; i++)
            {
                if (oldData[i] != newData[i])
                {
                    return(false);
                }
            }

            return(true);
        }
    }
            async Task <ReleaseEntry> createFullPackagesFromDeltas(IEnumerable <ReleaseEntry> releasesToApply, ReleaseEntry currentVersion, ApplyReleasesProgress progress)
            {
                Contract.Requires(releasesToApply != null);

                progress = progress ?? new ApplyReleasesProgress(releasesToApply.Count(), x => { });

                // If there are no remote releases at all, bail
                if (!releasesToApply.Any())
                {
                    return(null);
                }

                // If there are no deltas in our list, we're already done
                if (releasesToApply.All(x => !x.IsDelta))
                {
                    return(releasesToApply.MaxBy(x => x.Version).FirstOrDefault());
                }

                if (!releasesToApply.All(x => x.IsDelta))
                {
                    throw new Exception("Cannot apply combinations of delta and full packages");
                }

                // Progress calculation is "complex" here. We need to known how many releases, and then give each release a similar amount of
                // progress. For example, when applying 5 releases:
                //
                // release 1: 00 => 20
                // release 2: 20 => 40
                // release 3: 40 => 60
                // release 4: 60 => 80
                // release 5: 80 => 100
                //

                // Smash together our base full package and the nearest delta
                var ret = await Task.Run(() => {
                    var basePkg  = new ReleasePackage(Path.Combine(rootAppDirectory, "packages", currentVersion.Filename));
                    var deltaPkg = new ReleasePackage(Path.Combine(rootAppDirectory, "packages", releasesToApply.First().Filename));

                    var deltaBuilder = new DeltaPackageBuilder(Directory.GetParent(this.rootAppDirectory).FullName);

                    return(deltaBuilder.ApplyDeltaPackage(basePkg, deltaPkg,
                                                          Regex.Replace(deltaPkg.InputPackageFile, @"-delta.nupkg$", ".nupkg", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant),
                                                          x => progress.ReportReleaseProgress(x)));
                });

                progress.FinishRelease();

                if (releasesToApply.Count() == 1)
                {
                    return(ReleaseEntry.GenerateFromFile(ret.InputPackageFile));
                }

                var fi    = new FileInfo(ret.InputPackageFile);
                var entry = ReleaseEntry.GenerateFromFile(fi.OpenRead(), fi.Name);

                // Recursively combine the rest of them
                return(await createFullPackagesFromDeltas(releasesToApply.Skip(1), entry, progress));
            }
Пример #31
0
            private static string GetExePath(string rootAppDirectory, string exeName, ReleaseEntry thisRelease)
            {
                var releaseDir = Utility.AppDirForRelease(rootAppDirectory, thisRelease);
                var exePath    = Path.Combine(releaseDir, exeName);

                if (!File.Exists(exePath))
                {
                    var matches = Directory.GetFiles(releaseDir, exeName, SearchOption.AllDirectories);
                    exePath = matches.FirstOrDefault() ?? exePath;
                }
                return(exePath);
            }
Пример #32
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>();

                bool shouldInitialize = false;

                try {
                    localReleases = LoadLocalReleases(localReleaseFile);
                } catch (Exception ex) {
                    // Something has gone pear-shaped, let's start from scratch
                    this.Log().WarnException("Failed to load local releases, starting from scratch", ex);
                    shouldInitialize = true;
                }

                if (shouldInitialize)
                {
                    await initializeClientAppDirectory();
                }

                string releaseFile;

                // Fetch the remote RELEASES file, whether it's a local dir or an
                // HTTP URL
                if (Utility.IsHttpUrl(updateUrlOrPath))
                {
                    this.Log().Info("Downloading RELEASES file from {0}", updateUrlOrPath);

                    try {
                        var data = await urlDownloader.DownloadUrl(String.Format("{0}/{1}", updateUrlOrPath, "RELEASES"));

                        releaseFile = Encoding.UTF8.GetString(data);
                    } catch (WebException ex) {
                        this.Log().InfoException("Download resulted in WebException (returning blank release list)", ex);
                        releaseFile = String.Empty;
                    }

                    progress(33);
                }
                else
                {
                    this.Log().Info("Reading RELEASES file from {0}", updateUrlOrPath);

                    if (!Directory.Exists(updateUrlOrPath))
                    {
                        var message = String.Format(
                            "The directory {0} does not exist, something is probably broken with your application",
                            updateUrlOrPath);

                        throw new Exception(message);
                    }

                    var fi = new FileInfo(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);

                        this.Log().Warn(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.ParseReleaseFile(releaseFile);

                progress(66);

                if (remoteReleases.Any())
                {
                    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.
            async Task cleanDeadVersions(Version originalVersion, Version currentVersion, bool forceUninstall = false)
            {
                if (currentVersion == null)
                {
                    return;
                }

                var di = new DirectoryInfo(rootAppDirectory);

                if (!di.Exists)
                {
                    return;
                }

                this.Log().Info("cleanDeadVersions: for version {0}", currentVersion);

                string originalVersionFolder = null;

                if (originalVersion != null)
                {
                    originalVersionFolder = getDirectoryForRelease(originalVersion).Name;
                    this.Log().Info("cleanDeadVersions: exclude folder {0}", originalVersionFolder);
                }

                string currentVersionFolder = null;

                if (currentVersion != null)
                {
                    currentVersionFolder = getDirectoryForRelease(currentVersion).Name;
                    this.Log().Info("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);

                if (forceUninstall == false)
                {
                    await toCleanup.ForEachAsync(async x => {
                        var squirrelApps = SquirrelAwareExecutableDetector.GetAllSquirrelAwareApps(x.FullName);
                        var args         = String.Format("--squirrel-obsolete {0}", x.Name.Replace("app-", ""));

                        if (squirrelApps.Count > 0)
                        {
                            // For each app, run the install command in-order and wait
                            await squirrelApps.ForEachAsync(exe => Utility.InvokeProcessAsync(exe, args), 1 /* at a time */);
                        }
                    });
                }

                // Finally, clean up the app-X.Y.Z directories
                await toCleanup.ForEachAsync(async x => {
                    try {
                        await Utility.DeleteDirectoryWithFallbackToNextReboot(x.FullName);
                    } catch (UnauthorizedAccessException ex) {
                        this.Log().WarnException("Couldn't delete directory: " + x.FullName, ex);
                    }
                });

                // 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);
            }
Пример #34
0
            Task <string> installPackageToAppDir(UpdateInfo updateInfo, ReleaseEntry release, Action <int> progress)
            {
                return(Task.Run(async() =>
                {
                    var target = getDirectoryForRelease(release.Version);

                    // NB: This might happen if we got killed partially through applying the release
                    if (target.Exists)
                    {
                        this.Log().Warn("Found partially applied release folder, killing it: " + target.FullName);
                        await Utility.DeleteDirectory(target.FullName);
                    }

                    target.Create();

                    var totalSize = GetTotalUncompressedSize(updateInfo, release);
                    var currentExtractedSize = 0L;
                    using (var stream = File.OpenRead(Path.Combine(updateInfo.PackageDirectory, release.Filename)))
                    {
                        // progress.Report(new InstallerStep("Extracting"));

                        var zipIn = new ZipInputStream(stream);
                        var zipEntry = zipIn.GetNextEntry();

                        while (zipEntry != null)
                        {
                            var entryName = zipEntry.Name;
                            var pathParts = entryName.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);

                            var foundLib = false;
                            foreach (var part in pathParts)
                            {
                                if (part.Equals("lib", StringComparison.InvariantCulture))
                                {
                                    foundLib = true;
                                }
                            }

                            if (!foundLib)
                            {
                                zipEntry = zipIn.GetNextEntry();
                                continue;
                            }

                            var buffer = new byte[4096];
                            var fullZipToPath = Path.Combine(target.FullName, entryName.Replace('/', Path.DirectorySeparatorChar));
                            var directoryName = zipEntry.IsFile ? Path.GetDirectoryName(fullZipToPath) : fullZipToPath;
                            if (!string.IsNullOrEmpty(directoryName))
                            {
                                Directory.CreateDirectory(directoryName);
                            }

                            using (var streamWriter = File.Create(fullZipToPath))
                            {
                                currentExtractedSize += zipEntry.Size;
                                StreamUtils.Copy(zipIn, streamWriter, buffer);
                                progress((int)((double)currentExtractedSize * 100.0 / (double)totalSize));
                            }

                            zipEntry = zipIn.GetNextEntry();
                        }
                    }

                    this.Log().Info("Writing files to app directory: {0}", target.FullName);

                    // Move all of the files out of the lib/ dirs in the NuGet package
                    // into our target App directory.
                    //
                    // NB: We sort this list in order to guarantee that if a Net20
                    // and a Net40 version of a DLL get shipped, we always end up
                    // with the 4.0 version.
                    var libDir = target.GetDirectories().First(x => x.Name.Equals("lib", StringComparison.OrdinalIgnoreCase));
                    var toMove = libDir.GetDirectories().OrderBy(x => x.Name);

                    toMove.ForEach(ld => {
                        ld.GetDirectories()
                        .ForEachAsync(subdir => subdir.MoveTo(subdir.FullName.Replace(ld.FullName, target.FullName)))
                        .Wait();

                        ld.GetFiles()
                        .ForEachAsync(file => {
                            var tgt = Path.Combine(target.FullName, file.Name);
                            this.Log().Info("Moving file {0} to {1}", file.FullName, tgt);
                            if (File.Exists(tgt))
                            {
                                Utility.DeleteFileHarder(tgt, true);
                            }
                            file.MoveTo(tgt);
                        })
                        .Wait();
                    });

                    await Utility.DeleteDirectory(libDir.FullName);
                    return target.FullName;
                }));
            }
            Task<string> installPackageToAppDir(UpdateInfo updateInfo, ReleaseEntry release)
            {
                return Task.Run(async () => {
                    var zipper = new FastZip();
                    var target = getDirectoryForRelease(release.Version);

                    // NB: This might happen if we got killed partially through applying the release
                    if (target.Exists) {
                        this.Log().Warn("Found partially applied release folder, killing it: " + target.FullName);
                        await Utility.DeleteDirectory(target.FullName);
                    }

                    target.Create();

                    this.Log().Info("Writing files to app directory: {0}", target.FullName);
                    zipper.ExtractZip(
                        Path.Combine(updateInfo.PackageDirectory, release.Filename),
                        target.FullName, FastZip.Overwrite.Always, (o) => true, null, @"lib", true);

                    // Move all of the files out of the lib/ dirs in the NuGet package
                    // into our target App directory.
                    //
                    // NB: We sort this list in order to guarantee that if a Net20
                    // and a Net40 version of a DLL get shipped, we always end up
                    // with the 4.0 version.
                    var libDir = target.GetDirectories().First(x => x.Name.Equals("lib", StringComparison.OrdinalIgnoreCase));
                    var toMove = libDir.GetDirectories().OrderBy(x => x.Name);

                    toMove.ForEach(ld => {
                        ld.GetDirectories()
                            .ForEachAsync(subdir => subdir.MoveTo(subdir.FullName.Replace(ld.FullName, target.FullName)))
                            .Wait();

                        ld.GetFiles()
                            .ForEachAsync(file => {
                                var tgt = Path.Combine(target.FullName, file.Name);
                                this.Log().Info("Moving file {0} to {1}", file.FullName, tgt);
                                file.MoveTo(tgt);
                            })
                            .Wait();
                    });

                    await Utility.DeleteDirectory(libDir.FullName);
                    return target.FullName;
                });
            }
Пример #36
0
 bool isReleaseExplicitlyHttp(ReleaseEntry x)
 {
     return(x.BaseUrl != null &&
            Uri.IsWellFormedUriString(x.BaseUrl, UriKind.Absolute));
 }
            async Task<string> installPackageToAppDir(UpdateInfo updateInfo, ReleaseEntry release)
            {
                string tmpDir = default(string);
                bool shouldDeleteTmpDir = findShortTemporaryDir(out tmpDir);

                var fs = new PhysicalFileSystem(tmpDir);
                var pkg = new OptimizedZipPackage(fs, Path.Combine(updateInfo.PackageDirectory, release.Filename));
                var target = getDirectoryForRelease(release.Version);

                // NB: This might happen if we got killed partially through applying the release
                if (target.Exists) {
                    this.Log().Warn("Found partially applied release folder, killing it: " + target.FullName);
                    await Utility.DeleteDirectory(target.FullName);
                }

                target.Create();

                // Copy all of the files out of the lib/ dirs in the NuGet package
                // into our target App directory.
                //
                // NB: We sort this list in order to guarantee that if a Net20
                // and a Net40 version of a DLL get shipped, we always end up
                // with the 4.0 version.
                this.Log().Info("Writing files to app directory: {0}", target.FullName);

                var toWrite = pkg.GetLibFiles().Where(x => pathIsInFrameworkProfile(x, appFrameworkVersion))
                    .OrderBy(x => x.Path)
                    .ToList();

                // NB: Because of the above NB, we cannot use ForEachAsync here, we 
                // have to copy these files in-order. Once we fix assembly resolution, 
                // we can kill both of these NBs.
                await Task.Run(() => toWrite.ForEach(x => copyFileToLocation(target, x)));
                await pkg.GetContentFiles().ForEachAsync(x => copyFileToLocation(target, x));

                if (shouldDeleteTmpDir) {
                    await Utility.DeleteDirectory(tmpDir);
                }

                return target.FullName;
            }
 internal async Task <List <ReleaseEntry> > updateLocalReleasesFile()
 {
     return(await Task.Run(() => ReleaseEntry.BuildReleasesFile(Utility.PackageDirectoryForAppDir(rootAppDirectory))));
 }
Пример #39
0
        private void UpdateLogic()
        {
            SquirrelAwareApp.HandleEvents(
                onInitialInstall: v => OnInitialInstall(ref _UpdateManager),
                onAppUpdate: v => OnAppUpdate(ref _UpdateManager),
                onAppUninstall: v => OnAppUninstall(ref _UpdateManager));

            try
            {
                Squirrel.ReleaseEntry releaseEntry = null;
                Task.Run(async() =>
                {
                    try
                    {
                        _UpdateManager = await UpdateManager.GitHubUpdateManager(UpdatePath, null, null, null, CmdArgsStatus.PreRelease);

                        //var updates = await _UpdateManager.CheckForUpdate();
                        //var lastVersion = updates?.ReleasesToApply?.OrderBy(x => x.Version).LastOrDefault();

                        releaseEntry = await _UpdateManager.UpdateApp();
                    }
                    catch (Exception e)
                    {
                        Logger.WriteLog(e);

                        try
                        {
                            if (_UpdateManager != null)
                            {
                                _UpdateManager.Dispose();
                            }
                        }
                        catch (Exception ex)
                        {
                            Logger.WriteLog(ex);
                        }
                    }
                }).Wait();

                string updateInfo = String.Empty;

                try
                {
                    if (releaseEntry != null)
                    {
                        updateInfo  = releaseEntry.BaseUrl + Environment.NewLine;
                        updateInfo += releaseEntry.EntryAsString + Environment.NewLine;
                        updateInfo += releaseEntry.PackageName + Environment.NewLine;
                        updateInfo += releaseEntry.Version + Environment.NewLine;
                        updateInfo += "IsDelta: " + releaseEntry.IsDelta + Environment.NewLine;
                    }
                    else
                    {
                        Logger.WriteLog("No Updates Found");
                    }

                    if (updateInfo.Length > 0)
                    {
                        Logger.WriteLog(updateInfo);
                    }
                }
                catch (Exception ex3)
                {
                    Logger.WriteLog(ex3);
                }
            }
            catch (Exception ex)
            {
                Logger.WriteLog(ex);
            }
        }
            async Task<ReleaseEntry> createFullPackagesFromDeltas(IEnumerable<ReleaseEntry> releasesToApply, ReleaseEntry currentVersion)
            {
                Contract.Requires(releasesToApply != null);

                // If there are no remote releases at all, bail
                if (!releasesToApply.Any()) {
                    return null;
                }

                // If there are no deltas in our list, we're already done
                if (releasesToApply.All(x => !x.IsDelta)) {
                    return releasesToApply.MaxBy(x => x.Version).FirstOrDefault();
                }

                if (!releasesToApply.All(x => x.IsDelta)) {
                    throw new Exception("Cannot apply combinations of delta and full packages");
                }

                // Smash together our base full package and the nearest delta
                var ret = await Task.Run(() => {
                    var basePkg = new ReleasePackage(Path.Combine(rootAppDirectory, "packages", currentVersion.Filename));
                    var deltaPkg = new ReleasePackage(Path.Combine(rootAppDirectory, "packages", releasesToApply.First().Filename));

                    var deltaBuilder = new DeltaPackageBuilder(Directory.GetParent(this.rootAppDirectory).FullName);

                    return deltaBuilder.ApplyDeltaPackage(basePkg, deltaPkg,
                        Regex.Replace(deltaPkg.InputPackageFile, @"-delta.nupkg$", ".nupkg", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant));
                });

                if (releasesToApply.Count() == 1) {
                    return ReleaseEntry.GenerateFromFile(ret.InputPackageFile);
                }

                var fi = new FileInfo(ret.InputPackageFile);
                var entry = ReleaseEntry.GenerateFromFile(fi.OpenRead(), fi.Name);

                // Recursively combine the rest of them
                return await createFullPackagesFromDeltas(releasesToApply.Skip(1), entry);
            }
Пример #41
0
        private void UpdateLogic()
        {
            _UpdateManager = null;

            SquirrelAwareApp.HandleEvents(
                onInitialInstall: v => OnInitialInstall(ref _UpdateManager),
                onAppUpdate: v => OnAppUpdate(ref _UpdateManager),
                onAppUninstall: v => OnAppUninstall(ref _UpdateManager));

            try
            {
                Squirrel.ReleaseEntry releaseEntry = null;
                bool newUpdateExist = false;

                Task.Run(async() =>
                {
                    try
                    {
                        _UpdatingStateChanged.InvokeAsync(new UpdatingState(this)
                        {
                            Text       = String.Empty,
                            UpdateSate = UpdateSate.InitializingUpdater
                        }).Forget();

                        using (_UpdateManager = await UpdateManager.GitHubUpdateManager(UpdatePath, null, null, null, _CheckPrerelease))
                        {
                            try
                            {
                                _UpdatingStateChanged.InvokeAsync(new UpdatingState(this)
                                {
                                    Text       = String.Empty,
                                    UpdateSate = UpdateSate.LookingForUpdates
                                }).Forget(_Logger);

                                var updateInfo = await _UpdateManager.CheckForUpdate();

                                if (updateInfo.ReleasesToApply.Count > 0)
                                {
                                    newUpdateExist = true;
                                }

                                if (newUpdateExist)
                                {
                                    _UpdatingStateChanged.InvokeAsync(new UpdatingState(this)
                                    {
                                        Text       = String.Empty,
                                        UpdateSate = UpdateSate.UpdateFound
                                    }).Forget(_Logger);
                                }
                                else
                                {
                                    _UpdatingStateChanged.InvokeAsync(new UpdatingState(this)
                                    {
                                        Text       = String.Empty,
                                        UpdateSate = UpdateSate.NoUpdatesFound
                                    }).Forget(_Logger);
                                }

                                if (newUpdateExist)
                                {
                                    _UpdatingStateChanged.InvokeAsync(new UpdatingState(this)
                                    {
                                        Text       = String.Empty,
                                        UpdateSate = UpdateSate.DownloadingUpdate
                                    }).Forget(_Logger);
                                }

                                releaseEntry = await _UpdateManager.UpdateApp();

                                if (newUpdateExist)
                                {
                                    _UpdatingStateChanged.InvokeAsync(new UpdatingState(this)
                                    {
                                        Text       = String.Empty,
                                        UpdateSate = UpdateSate.UpdateDownloaded
                                    }).Forget(_Logger);
                                }
                            }
                            catch (Exception e)
                            {
                                _Logger?.WriteLog(e.ToString());
                            }

                            string updateTextInfo = String.Empty;

                            try
                            {
                                if (releaseEntry != null)
                                {
                                    updateTextInfo  = releaseEntry.BaseUrl + Environment.NewLine;
                                    updateTextInfo += releaseEntry.EntryAsString + Environment.NewLine;
                                    updateTextInfo += releaseEntry.PackageName + Environment.NewLine;
                                    updateTextInfo += releaseEntry.Version + Environment.NewLine;
                                    updateTextInfo += "IsDelta: " + releaseEntry.IsDelta + Environment.NewLine;
                                }
                                else
                                {
                                    _Logger?.WriteLog("No Updates Found");
                                }

                                if (updateTextInfo.Length > 0)
                                {
                                    _Logger?.WriteLog(updateTextInfo);
                                }

                                _UpdatingStateChanged.InvokeAsync(new UpdatingState(this)
                                {
                                    Text       = String.Empty,
                                    UpdateSate = UpdateSate.UpdatingFinished
                                }).Forget(_Logger);


                                if (newUpdateExist)
                                {
                                    _UpdatingStateChanged.InvokeAsync(new UpdatingState(this)
                                    {
                                        Text       = String.Empty,
                                        UpdateSate = UpdateSate.ReadyToRestart
                                    }).Forget(_Logger);
                                }
                            }
                            catch (Exception ex3)
                            {
                                _Logger?.WriteLog(ex3.ToString());
                            }
                        }//using
                    }
                    catch (Exception ex0)
                    {
                        _Logger?.WriteLog(ex0.ToString());
                    }
                }).Wait();

                _UpdateManager = null;
            }
            catch (Exception ex)
            {
                _Logger?.WriteLog(ex.ToString());
            }
        }
Пример #42
0
 public static string AppDirForRelease(string rootAppDirectory, ReleaseEntry entry)
 {
     return(Path.Combine(rootAppDirectory, "app-" + entry.Version.ToString()));
 }
Пример #43
0
 private static string ToLog(ReleaseEntry entry)
 {
     return entry == null ? "null" : entry.EntryAsString;
 }