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); }
protected UpdateInfo(ReleaseEntry currentlyInstalledVersion, IEnumerable<ReleaseEntry> releasesToApply, string packageDirectory, FrameworkVersion appFrameworkVersion) { // NB: When bootstrapping, CurrentlyInstalledVersion is null! CurrentlyInstalledVersion = currentlyInstalledVersion; ReleasesToApply = (releasesToApply ?? Enumerable.Empty<ReleaseEntry>()).ToList(); FutureReleaseEntry = ReleasesToApply.Any() ? ReleasesToApply.MaxBy(x => x.Version).FirstOrDefault() : null; AppFrameworkVersion = appFrameworkVersion; this.PackageDirectory = packageDirectory; }
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(); } }
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(), fi.Name); // Recursively combine the rest of them return createFullPackagesFromDeltas(releasesToApply.Skip(1), entry); }); }
// // ApplyReleases methods // List<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) { 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. log.Info("Writing files to app directory: {0}", target.FullName); 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); }
void checksumPackage(ReleaseEntry downloadedRelease) { var targetPackage = fileSystem.GetFileInfo( Path.Combine(rootAppDirectory, "packages", downloadedRelease.Filename)); if (!targetPackage.Exists) { 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) { 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); } } }
private static string ToLog(ReleaseEntry entry) { return entry == null ? "null" : entry.EntryAsString; }
public InstallManager(ReleaseEntry bundledRelease, string targetRootDirectory = null) { BundledRelease = bundledRelease; TargetRootDirectory = targetRootDirectory; log = LogManager.GetLogger<InstallManager>(); }