Пример #1
0
        public static UpdateInfo Create(ReleaseEntry currentVersion, IEnumerable<ReleaseEntry> availableReleases, string packageDirectory, FrameworkVersion appFrameworkVersion)
        {
            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(currentVersion, new[] { latestFull }, packageDirectory, appFrameworkVersion);
            }

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

            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, appFrameworkVersion)
                : new UpdateInfo(currentVersion, new[] { latestFull }, packageDirectory, appFrameworkVersion);
        }
Пример #2
0
        protected UpdateInfo(ReleaseEntry currentlyInstalledVersion, IEnumerable<ReleaseEntry> releasesToApply, string packageDirectory)
        {
            // NB: When bootstrapping, CurrentlyInstalledVersion is null!
            CurrentlyInstalledVersion = currentlyInstalledVersion;
            ReleasesToApply = releasesToApply ?? Enumerable.Empty<ReleaseEntry>();
            FutureReleaseEntry = ReleasesToApply.MaxBy(x => x.Version).FirstOrDefault();

            this.packageDirectory = packageDirectory;
        }
Пример #3
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);
            using (var of = File.Create(targetFile.FullName + ".diff")) {
                BinaryPatchUtility.Create(oldData, newData, of);

                var rl = ReleaseEntry.GenerateFromFile(new MemoryStream(newData), targetFile.Name + ".shasum");
                File.WriteAllText(targetFile.FullName + ".shasum", rl.EntryAsString, Encoding.UTF8);
                targetFile.Delete();
            }
        }
Пример #4
0
        //
        // ApplyReleases methods
        //
        List<string> installPackageToAppDir(UpdateInfo updateInfo, ReleaseEntry release)
        {
            var pkg = new ZipPackage(Path.Combine(rootAppDirectory, "packages", release.Filename));
            var target = getDirectoryForRelease(release.Version);

            // NB: This might happen if we got killed partially through applying the release
            if (target.Exists) {
                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.
            pkg.GetFiles().Where(x => pathIsInFrameworkProfile(x, appFrameworkVersion)).OrderBy(x => x.Path)
                .ForEach(x => {
                    var targetPath = Path.Combine(target.FullName, Path.GetFileName(x.Path));

                    var fi = fileSystem.GetFileInfo(targetPath);
                    if (fi.Exists) fi.Delete();

                    using (var inf = x.GetStream())
                    using (var of = fi.Open(FileMode.CreateNew, FileAccess.Write)) {
                        log.Info("Writing {0} to app directory", targetPath);
                        inf.CopyTo(of);
                    }
                });

            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.
            return runPostInstallAndCleanup(newCurrentVersion, updateInfo.IsBootstrapping);
        }
Пример #5
0
        IObservable<ReleaseEntry> createFullPackagesFromDeltas(IEnumerable<ReleaseEntry> releasesToApply, ReleaseEntry currentVersion)
        {
            Contract.Requires(releasesToApply != null);

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

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

            // Smash together our base full package and the nearest delta
            var ret = Observable.Start(() => {
                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();

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

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

            return ret.SelectMany(x => {
                var fi = fileSystem.GetFileInfo(x.InputPackageFile);
                var entry = ReleaseEntry.GenerateFromFile(fi.OpenRead(), x.InputPackageFile);

                // Recursively combine the rest of them
                return createFullPackagesFromDeltas(releasesToApply.Skip(1), entry);
            });
        }
Пример #6
0
        void checksumPackage(ReleaseEntry downloadedRelease)
        {
            var targetPackage = fileSystem.GetFileInfo(
                Path.Combine(rootAppDirectory, "packages", downloadedRelease.Filename));

            if (!targetPackage.Exists) {
                log.Error("File should exist but doesn't", targetPackage.FullName);
                throw new Exception("Checksummed file doesn't exist: " + targetPackage.FullName);
            }

            if (targetPackage.Length != downloadedRelease.Filesize) {
                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)) {
                    log.Error("File SHA1 should be {0}, is {1}", downloadedRelease.SHA1, hash);
                    targetPackage.Delete();
                    throw new Exception("Checksum doesn't match: " + targetPackage.FullName);
                }
            }
        }
Пример #7
0
 public InstallManager(ReleaseEntry bundledRelease, string targetRootDirectory = null)
 {
     BundledRelease = bundledRelease;
     TargetRootDirectory = targetRootDirectory;
     log = LogManager.GetLogger<InstallManager>();
 }
Пример #8
0
        //
        // ApplyReleases methods
        //

        List<string> installPackageToAppDir(UpdateInfo updateInfo, ReleaseEntry release)
        {
            var pkg = new ZipPackage(Path.Combine(rootAppDirectory, "packages", release.Filename));
            var target = getDirectoryForRelease(release.Version);

            // NB: This might happen if we got killed partially through applying the release
            if (target.Exists) {
                Utility.DeleteDirectory(target.FullName).Wait();
            }
            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.
            pkg.GetLibFiles().Where(x => pathIsInFrameworkProfile(x, appFrameworkVersion))
                             .OrderBy(x => x.Path)
                             .ForEach(x => CopyFileToLocation(target, x));

            pkg.GetContentFiles().ForEach(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.
            return runPostInstallAndCleanup(newCurrentVersion, updateInfo.IsBootstrapping);
        }
Пример #9
0
 public InstallManager(ReleaseEntry bundledRelease, string targetRootDirectory = null)
 {
     BundledRelease = bundledRelease;
     TargetRootDirectory = targetRootDirectory;
 }
Пример #10
0
        public ReleasePackage CreateDeltaPackage(ReleasePackage baseFixture, string outputFile)
        {
            Contract.Requires(baseFixture != null && baseFixture.ReleasePackageFile != null);
            Contract.Requires(!String.IsNullOrEmpty(outputFile) && !File.Exists(outputFile));

            string baseTempPath = null;
            string tempPath     = null;

            using (Utility.WithTempDirectory(out baseTempPath))
                using (Utility.WithTempDirectory(out tempPath)) {
                    var baseTempInfo = new DirectoryInfo(baseTempPath);
                    var tempInfo     = new DirectoryInfo(tempPath);

                    using (var zf = new ZipFile(baseFixture.ReleasePackageFile)) {
                        zf.ExtractAll(baseTempInfo.FullName);
                    }

                    using (var zf = new ZipFile(ReleasePackageFile)) {
                        zf.ExtractAll(tempInfo.FullName);
                    }

                    // Collect a list of relative paths under 'lib' and map them
                    // to their full name. We'll use this later to determine in
                    // the new version of the package whether the file exists or
                    // not.
                    var baseLibFiles = baseTempInfo.GetAllFilesRecursively()
                                       .Where(x => x.FullName.ToLowerInvariant().Contains("lib" + Path.DirectorySeparatorChar))
                                       .ToDictionary(k => k.FullName.Replace(baseTempInfo.FullName, ""), v => v.FullName);

                    var newLibDir = tempInfo.GetDirectories().First(x => x.Name.ToLowerInvariant() == "lib");

                    // 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
                    newLibDir.GetAllFilesRecursively().ForEach(libFile => {
                        var relativePath = libFile.FullName.Replace(tempInfo.FullName, "");

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

                        var oldData = File.ReadAllBytes(baseLibFiles[relativePath]);
                        var newData = File.ReadAllBytes(libFile.FullName);

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

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

                        this.Log().Info("Delta patching {0} => {1}", baseLibFiles[relativePath], libFile.FullName);
                        using (var of = File.Create(libFile.FullName + ".diff")) {
                            BinaryPatchUtility.Create(oldData, newData, of);

                            var rl = ReleaseEntry.GenerateFromFile(new MemoryStream(newData), libFile.Name + ".shasum");
                            File.WriteAllText(libFile.FullName + ".shasum", rl.EntryAsString, Encoding.UTF8);
                            libFile.Delete();
                        }
                    });

                    addDeltaFilesToContentTypes(tempInfo.FullName);

                    using (var zf = new ZipFile(outputFile)) {
                        zf.AddDirectory(tempInfo.FullName);
                        zf.Save();
                    }
                }

            return(new ReleasePackage(outputFile));
        }