mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Merge pull request #15 from Squirrel/refactor-and-cleanup
Enable UpdateManager to support Install / Uninstall
This commit is contained in:
@@ -20,7 +20,7 @@ namespace Squirrel
|
|||||||
/// will return values from 0-100 and Complete, or Throw</param>
|
/// will return values from 0-100 and Complete, or Throw</param>
|
||||||
/// <returns>An UpdateInfo object representing the updates to install.
|
/// <returns>An UpdateInfo object representing the updates to install.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
Task<UpdateInfo> CheckForUpdate(bool ignoreDeltaUpdates, Action<int> progress = null);
|
Task<UpdateInfo> CheckForUpdate(bool ignoreDeltaUpdates = false, Action<int> progress = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Download a list of releases into the local package directory.
|
/// Download a list of releases into the local package directory.
|
||||||
@@ -43,6 +43,18 @@ namespace Squirrel
|
|||||||
/// <param name="progress">A Observer which can be used to report Progress -
|
/// <param name="progress">A Observer which can be used to report Progress -
|
||||||
/// will return values from 0-100 and Complete, or Throw</param>
|
/// will return values from 0-100 and Complete, or Throw</param>
|
||||||
Task ApplyReleases(UpdateInfo updateInfo, Action<int> progress = null);
|
Task ApplyReleases(UpdateInfo updateInfo, Action<int> progress = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Completely Installs a targeted app
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Completion</returns>
|
||||||
|
Task FullInstall();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Completely uninstalls the targeted app
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Completion</returns>
|
||||||
|
Task FullUninstall();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class EasyModeMixin
|
public static class EasyModeMixin
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ namespace Squirrel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void BuildReleasesFile(string releasePackagesDir)
|
public static List<ReleaseEntry> BuildReleasesFile(string releasePackagesDir)
|
||||||
{
|
{
|
||||||
var packagesDir = new DirectoryInfo(releasePackagesDir);
|
var packagesDir = new DirectoryInfo(releasePackagesDir);
|
||||||
|
|
||||||
@@ -176,6 +176,7 @@ namespace Squirrel
|
|||||||
}
|
}
|
||||||
|
|
||||||
File.Move(tempFile, target);
|
File.Move(tempFile, target);
|
||||||
|
return entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool filenameIsDeltaFile(string filename)
|
static bool filenameIsDeltaFile(string filename)
|
||||||
|
|||||||
@@ -79,11 +79,14 @@
|
|||||||
<Compile Include="ShellFile.cs" />
|
<Compile Include="ShellFile.cs" />
|
||||||
<Compile Include="TaskbarHelper.cs" />
|
<Compile Include="TaskbarHelper.cs" />
|
||||||
<Compile Include="UpdateInfo.cs" />
|
<Compile Include="UpdateInfo.cs" />
|
||||||
|
<Compile Include="UpdateManager.CheckForUpdates.cs" />
|
||||||
<Compile Include="UpdateManager.cs" />
|
<Compile Include="UpdateManager.cs" />
|
||||||
<Compile Include="Utility.cs" />
|
<Compile Include="Utility.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
|
<Compile Include="UpdateManager.DownloadReleases.cs" />
|
||||||
|
<Compile Include="UpdateManager.ApplyReleases.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
|||||||
339
src/Squirrel/UpdateManager.ApplyReleases.cs
Normal file
339
src/Squirrel/UpdateManager.ApplyReleases.cs
Normal file
@@ -0,0 +1,339 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.Contracts;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using NuGet;
|
||||||
|
using Splat;
|
||||||
|
|
||||||
|
namespace Squirrel
|
||||||
|
{
|
||||||
|
public sealed partial class UpdateManager
|
||||||
|
{
|
||||||
|
internal class ApplyReleasesImpl : IEnableLogger
|
||||||
|
{
|
||||||
|
// TODO: Kill this entire concept
|
||||||
|
readonly FrameworkVersion appFrameworkVersion = FrameworkVersion.Net45;
|
||||||
|
|
||||||
|
readonly string rootAppDirectory;
|
||||||
|
|
||||||
|
public ApplyReleasesImpl(string rootAppDirectory)
|
||||||
|
{
|
||||||
|
this.rootAppDirectory = rootAppDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ApplyReleases(UpdateInfo updateInfo, Action<int> progress = null)
|
||||||
|
{
|
||||||
|
progress = progress ?? (_ => { });
|
||||||
|
|
||||||
|
var release = await createFullPackagesFromDeltas(updateInfo.ReleasesToApply, updateInfo.CurrentlyInstalledVersion);
|
||||||
|
progress(10);
|
||||||
|
|
||||||
|
await installPackageToAppDir(updateInfo, release);
|
||||||
|
progress(50);
|
||||||
|
|
||||||
|
var currentReleases = await updateLocalReleasesFile();
|
||||||
|
progress(75);
|
||||||
|
|
||||||
|
await cleanDeadVersions(currentReleases.MaxBy(x => x.Version).First().Version);
|
||||||
|
progress(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task FullUninstall(Version version = null)
|
||||||
|
{
|
||||||
|
version = version ?? new Version(255, 255, 255, 255);
|
||||||
|
this.Log().Info("Uninstalling version '{0}'", version);
|
||||||
|
|
||||||
|
// find all the old releases (and this one)
|
||||||
|
var directoriesToDelete = getOldReleases(version)
|
||||||
|
.Concat(new [] { getDirectoryForRelease(version) })
|
||||||
|
.Where(d => d.Exists)
|
||||||
|
.Select(d => d.FullName);
|
||||||
|
|
||||||
|
await directoriesToDelete.ForEachAsync(x => Utility.DeleteDirectoryWithFallbackToNextReboot(x));
|
||||||
|
|
||||||
|
if (!getReleases().Any()) {
|
||||||
|
await Utility.DeleteDirectoryWithFallbackToNextReboot(rootAppDirectory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task 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) {
|
||||||
|
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.
|
||||||
|
runPostInstallAndCleanup(newCurrentVersion, updateInfo.IsBootstrapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CopyFileToLocation(FileSystemInfo target, IPackageFile x)
|
||||||
|
{
|
||||||
|
var targetPath = Path.Combine(target.FullName, x.EffectivePath);
|
||||||
|
|
||||||
|
var fi = new FileInfo(targetPath);
|
||||||
|
if (fi.Exists) fi.Delete();
|
||||||
|
|
||||||
|
var dir = new DirectoryInfo(Path.GetDirectoryName(targetPath));
|
||||||
|
if (!dir.Exists) dir.Create();
|
||||||
|
|
||||||
|
using (var inf = x.GetStream())
|
||||||
|
using (var of = fi.Open(FileMode.CreateNew, FileAccess.Write)) {
|
||||||
|
inf.CopyTo(of);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void runPostInstallAndCleanup(Version newCurrentVersion, bool isBootstrapping)
|
||||||
|
{
|
||||||
|
fixPinnedExecutables(newCurrentVersion);
|
||||||
|
|
||||||
|
this.Log().Info("runPostInstallAndCleanup: finished fixPinnedExecutables");
|
||||||
|
cleanUpOldVersions(newCurrentVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool pathIsInFrameworkProfile(IPackageFile packageFile, FrameworkVersion appFrameworkVersion)
|
||||||
|
{
|
||||||
|
if (!packageFile.Path.StartsWith("lib", StringComparison.InvariantCultureIgnoreCase)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (appFrameworkVersion == FrameworkVersion.Net40
|
||||||
|
&& packageFile.Path.StartsWith("lib\\net45", StringComparison.InvariantCultureIgnoreCase)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task<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 releasesToApply.MaxBy(x => x.Version).First();
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanUpOldVersions(Version newCurrentVersion)
|
||||||
|
{
|
||||||
|
var directory = new DirectoryInfo(rootAppDirectory);
|
||||||
|
if (!directory.Exists) {
|
||||||
|
this.Log().Warn("cleanUpOldVersions: the directory '{0}' does not exist", rootAppDirectory);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var v in getOldReleases(newCurrentVersion)) {
|
||||||
|
Utility.DeleteDirectoryAtNextReboot(v.FullName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fixPinnedExecutables(Version newCurrentVersion)
|
||||||
|
{
|
||||||
|
if (Environment.OSVersion.Version < new Version(6, 1)) {
|
||||||
|
this.Log().Warn("fixPinnedExecutables: Found OS Version '{0}', exiting...", Environment.OSVersion.VersionString);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var newCurrentFolder = "app-" + newCurrentVersion;
|
||||||
|
var oldAppDirectories = (new DirectoryInfo(rootAppDirectory)).GetDirectories()
|
||||||
|
.Where(x => x.Name.StartsWith("app-", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
.Where(x => x.Name != newCurrentFolder)
|
||||||
|
.Select(x => x.FullName)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
if (!oldAppDirectories.Any()) {
|
||||||
|
this.Log().Info("fixPinnedExecutables: oldAppDirectories is empty, this is pointless");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var newAppPath = Path.Combine(rootAppDirectory, newCurrentFolder);
|
||||||
|
|
||||||
|
var taskbarPath = Path.Combine(
|
||||||
|
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||||
|
"Microsoft\\Internet Explorer\\Quick Launch\\User Pinned\\TaskBar");
|
||||||
|
|
||||||
|
Func<FileInfo, ShellLink> resolveLink = file => {
|
||||||
|
try {
|
||||||
|
return new ShellLink(file.FullName);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
var message = String.Format("File '{0}' could not be converted into a valid ShellLink", file.FullName);
|
||||||
|
this.Log().WarnException(message, ex);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var shellLinks = (new DirectoryInfo(taskbarPath)).GetFiles("*.lnk")
|
||||||
|
.Select(resolveLink)
|
||||||
|
.Where(x => x != null)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
foreach (var shortcut in shellLinks) {
|
||||||
|
try {
|
||||||
|
updateLink(shortcut, oldAppDirectories, newAppPath);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
var message = String.Format("fixPinnedExecutables: shortcut failed: {0}", shortcut.Target);
|
||||||
|
this.Log().ErrorException(message, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateLink(ShellLink shortcut, string[] oldAppDirectories, string newAppPath)
|
||||||
|
{
|
||||||
|
this.Log().Info("Processing shortcut '{0}'", shortcut.Target);
|
||||||
|
|
||||||
|
foreach (var oldAppDirectory in oldAppDirectories) {
|
||||||
|
if (!shortcut.Target.StartsWith(oldAppDirectory, StringComparison.OrdinalIgnoreCase)) {
|
||||||
|
this.Log().Info("Does not match '{0}', continuing to next directory", oldAppDirectory);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace old app path with new app path and check, if executable still exists
|
||||||
|
var newTarget = Path.Combine(newAppPath, shortcut.Target.Substring(oldAppDirectory.Length + 1));
|
||||||
|
|
||||||
|
if (File.Exists(newTarget)) {
|
||||||
|
shortcut.Target = newTarget;
|
||||||
|
|
||||||
|
// replace working directory too if appropriate
|
||||||
|
if (shortcut.WorkingDirectory.StartsWith(oldAppDirectory, StringComparison.OrdinalIgnoreCase)) {
|
||||||
|
this.Log().Info("Changing new directory to '{0}'", newAppPath);
|
||||||
|
shortcut.WorkingDirectory = Path.Combine(newAppPath,
|
||||||
|
shortcut.WorkingDirectory.Substring(oldAppDirectory.Length + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
shortcut.Save();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.Log().Info("Unpinning {0} from taskbar", shortcut.Target);
|
||||||
|
TaskbarHelper.UnpinFromTaskbar(shortcut.Target);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 currentVersion)
|
||||||
|
{
|
||||||
|
if (currentVersion == null) return;
|
||||||
|
|
||||||
|
var di = new DirectoryInfo(rootAppDirectory);
|
||||||
|
if (!di.Exists) return;
|
||||||
|
|
||||||
|
this.Log().Info("cleanDeadVersions: for version {0}", currentVersion);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
await toCleanup.ForEachAsync(async x => {
|
||||||
|
try {
|
||||||
|
await Utility.DeleteDirectoryWithFallbackToNextReboot(x.FullName);
|
||||||
|
} catch (UnauthorizedAccessException ex) {
|
||||||
|
this.Log().WarnException("Couldn't delete directory: " + x.FullName, ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
internal async Task<List<ReleaseEntry>> updateLocalReleasesFile()
|
||||||
|
{
|
||||||
|
return await Task.Run(() => ReleaseEntry.BuildReleasesFile(Utility.PackageDirectoryForAppDir(rootAppDirectory)));
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable<DirectoryInfo> getReleases()
|
||||||
|
{
|
||||||
|
var rootDirectory = new DirectoryInfo(rootAppDirectory);
|
||||||
|
|
||||||
|
if (!rootDirectory.Exists) return Enumerable.Empty<DirectoryInfo>();
|
||||||
|
|
||||||
|
return rootDirectory.GetDirectories()
|
||||||
|
.Where(x => x.Name.StartsWith("app-", StringComparison.InvariantCultureIgnoreCase));
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable<DirectoryInfo> getOldReleases(Version version)
|
||||||
|
{
|
||||||
|
return getReleases()
|
||||||
|
.Where(x => x.Name.ToVersion() < version)
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
DirectoryInfo getDirectoryForRelease(Version releaseVersion)
|
||||||
|
{
|
||||||
|
return new DirectoryInfo(Path.Combine(rootAppDirectory, "app-" + releaseVersion));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
176
src/Squirrel/UpdateManager.CheckForUpdates.cs
Normal file
176
src/Squirrel/UpdateManager.CheckForUpdates.cs
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Splat;
|
||||||
|
|
||||||
|
namespace Squirrel
|
||||||
|
{
|
||||||
|
public sealed partial class UpdateManager
|
||||||
|
{
|
||||||
|
internal class CheckForUpdateImpl : IEnableLogger
|
||||||
|
{
|
||||||
|
readonly string rootAppDirectory;
|
||||||
|
|
||||||
|
// TODO: rip this out
|
||||||
|
readonly FrameworkVersion appFrameworkVersion = FrameworkVersion.Net45;
|
||||||
|
|
||||||
|
public CheckForUpdateImpl(string rootAppDirectory)
|
||||||
|
{
|
||||||
|
this.rootAppDirectory = rootAppDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
var file = File.OpenRead(localReleaseFile);
|
||||||
|
|
||||||
|
// NB: sr disposes file
|
||||||
|
using (var sr = new StreamReader(file, Encoding.UTF8)) {
|
||||||
|
localReleases = ReleaseEntry.ParseReleaseFile(sr.ReadToEnd());
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
// Something has gone wrong, we'll start from scratch.
|
||||||
|
this.Log().WarnException("Failed to load local release list", 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task initializeClientAppDirectory()
|
||||||
|
{
|
||||||
|
// On bootstrap, we won't have any of our directories, create them
|
||||||
|
var pkgDir = Path.Combine(rootAppDirectory, "packages");
|
||||||
|
if (Directory.Exists(pkgDir)) {
|
||||||
|
await Utility.DeleteDirectory(pkgDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
Directory.CreateDirectory(pkgDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateInfo determineUpdateInfo(IEnumerable<ReleaseEntry> localReleases, IEnumerable<ReleaseEntry> remoteReleases, bool ignoreDeltaUpdates)
|
||||||
|
{
|
||||||
|
var packageDirectory = Utility.PackageDirectoryForAppDir(rootAppDirectory);
|
||||||
|
localReleases = localReleases ?? Enumerable.Empty<ReleaseEntry>();
|
||||||
|
|
||||||
|
if (remoteReleases == null) {
|
||||||
|
this.Log().Warn("Release information couldn't be determined due to remote corrupt RELEASES file");
|
||||||
|
throw new Exception("Corrupt remote RELEASES file");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localReleases.Count() == remoteReleases.Count()) {
|
||||||
|
this.Log().Info("No updates, remote and local are the same");
|
||||||
|
|
||||||
|
var latestFullRelease = findCurrentVersion(remoteReleases);
|
||||||
|
var currentRelease = findCurrentVersion(localReleases);
|
||||||
|
|
||||||
|
var info = UpdateInfo.Create(currentRelease, new[] {latestFullRelease}, packageDirectory, appFrameworkVersion);
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ignoreDeltaUpdates) {
|
||||||
|
remoteReleases = remoteReleases.Where(x => !x.IsDelta);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!localReleases.Any()) {
|
||||||
|
this.Log().Warn("First run or local directory is corrupt, starting from scratch");
|
||||||
|
|
||||||
|
var latestFullRelease = findCurrentVersion(remoteReleases);
|
||||||
|
return UpdateInfo.Create(findCurrentVersion(localReleases), new[] {latestFullRelease}, packageDirectory, appFrameworkVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localReleases.Max(x => x.Version) > remoteReleases.Max(x => x.Version)) {
|
||||||
|
this.Log().Warn("hwhat, local version is greater than remote version");
|
||||||
|
|
||||||
|
var latestFullRelease = findCurrentVersion(remoteReleases);
|
||||||
|
return UpdateInfo.Create(findCurrentVersion(localReleases), new[] {latestFullRelease}, packageDirectory, appFrameworkVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
return UpdateInfo.Create(findCurrentVersion(localReleases), remoteReleases, packageDirectory, appFrameworkVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ReleaseEntry findCurrentVersion(IEnumerable<ReleaseEntry> localReleases)
|
||||||
|
{
|
||||||
|
if (!localReleases.Any()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return localReleases.MaxBy(x => x.Version).SingleOrDefault(x => !x.IsDelta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
82
src/Squirrel/UpdateManager.DownloadReleases.cs
Normal file
82
src/Squirrel/UpdateManager.DownloadReleases.cs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Splat;
|
||||||
|
|
||||||
|
namespace Squirrel
|
||||||
|
{
|
||||||
|
public sealed partial class UpdateManager
|
||||||
|
{
|
||||||
|
internal class DownloadReleasesImpl : IEnableLogger
|
||||||
|
{
|
||||||
|
readonly string rootAppDirectory;
|
||||||
|
|
||||||
|
public DownloadReleasesImpl(string rootAppDirectory)
|
||||||
|
{
|
||||||
|
this.rootAppDirectory = rootAppDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DownloadReleases(string updateUrlOrPath, IEnumerable<ReleaseEntry> releasesToDownload, Action<int> progress = null, IFileDownloader urlDownloader = null)
|
||||||
|
{
|
||||||
|
progress = progress ?? (_ => { });
|
||||||
|
urlDownloader = urlDownloader ?? new FileDownloader();
|
||||||
|
|
||||||
|
int current = 0;
|
||||||
|
int toIncrement = (int)(100.0 / releasesToDownload.Count());
|
||||||
|
|
||||||
|
if (Utility.IsHttpUrl(updateUrlOrPath)) {
|
||||||
|
await releasesToDownload.ForEachAsync(async x => {
|
||||||
|
await urlDownloader.DownloadFile(
|
||||||
|
String.Format("{0}/{1}", updateUrlOrPath, x.Filename),
|
||||||
|
Path.Combine(rootAppDirectory, "packages", x.Filename));
|
||||||
|
lock (progress) progress(current += toIncrement);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await releasesToDownload.ForEachAsync(x => {
|
||||||
|
File.Copy(
|
||||||
|
Path.Combine(updateUrlOrPath, x.Filename),
|
||||||
|
Path.Combine(rootAppDirectory, "packages", x.Filename));
|
||||||
|
lock (progress) progress(current += toIncrement);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Task checksumAllPackages(IEnumerable<ReleaseEntry> releasesDownloaded)
|
||||||
|
{
|
||||||
|
return releasesDownloaded.ForEachAsync(x => checksumPackage(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ using Splat;
|
|||||||
|
|
||||||
namespace Squirrel
|
namespace Squirrel
|
||||||
{
|
{
|
||||||
public sealed class UpdateManager : IUpdateManager, IEnableLogger
|
public sealed partial class UpdateManager : IUpdateManager, IEnableLogger
|
||||||
{
|
{
|
||||||
readonly string rootAppDirectory;
|
readonly string rootAppDirectory;
|
||||||
readonly string applicationName;
|
readonly string applicationName;
|
||||||
@@ -41,187 +41,43 @@ namespace Squirrel
|
|||||||
this.urlDownloader = urlDownloader ?? new FileDownloader();
|
this.urlDownloader = urlDownloader ?? new FileDownloader();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string PackageDirectory {
|
|
||||||
get { return Path.Combine(rootAppDirectory, "packages"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public string LocalReleaseFile {
|
|
||||||
get { return Path.Combine(PackageDirectory, "RELEASES"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<UpdateInfo> CheckForUpdate(bool ignoreDeltaUpdates = false, Action<int> progress = null)
|
public async Task<UpdateInfo> CheckForUpdate(bool ignoreDeltaUpdates = false, Action<int> progress = null)
|
||||||
{
|
{
|
||||||
progress = progress ?? (_ => { });
|
var checkForUpdate = new CheckForUpdateImpl(rootAppDirectory);
|
||||||
|
|
||||||
await acquireUpdateLock();
|
await acquireUpdateLock();
|
||||||
|
return await checkForUpdate.CheckForUpdate(Utility.LocalReleaseFileForAppDir(rootAppDirectory), updateUrlOrPath, ignoreDeltaUpdates, progress, urlDownloader);
|
||||||
var localReleases = Enumerable.Empty<ReleaseEntry>();
|
|
||||||
|
|
||||||
bool shouldInitialize = false;
|
|
||||||
try {
|
|
||||||
var file = File.OpenRead(LocalReleaseFile);
|
|
||||||
|
|
||||||
// NB: sr disposes file
|
|
||||||
using (var sr = new StreamReader(file, Encoding.UTF8)) {
|
|
||||||
localReleases = ReleaseEntry.ParseReleaseFile(sr.ReadToEnd());
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
|
||||||
// Something has gone wrong, we'll start from scratch.
|
|
||||||
this.Log().WarnException("Failed to load local release list", 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 (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.IsEmpty()) {
|
|
||||||
ret = determineUpdateInfo(localReleases, remoteReleases, ignoreDeltaUpdates);
|
|
||||||
}
|
|
||||||
|
|
||||||
progress(100);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DownloadReleases(IEnumerable<ReleaseEntry> releasesToDownload, Action<int> progress = null)
|
public async Task DownloadReleases(IEnumerable<ReleaseEntry> releasesToDownload, Action<int> progress = null)
|
||||||
{
|
{
|
||||||
progress = progress ?? (_ => { });
|
var downloadReleases = new DownloadReleasesImpl(rootAppDirectory);
|
||||||
int current = 0;
|
|
||||||
int toIncrement = (int)(100.0 / releasesToDownload.Count());
|
|
||||||
|
|
||||||
await acquireUpdateLock();
|
await acquireUpdateLock();
|
||||||
|
|
||||||
if (isHttpUrl(updateUrlOrPath)) {
|
await downloadReleases.DownloadReleases(updateUrlOrPath, releasesToDownload, progress, urlDownloader);
|
||||||
await releasesToDownload.ForEachAsync(async x => {
|
|
||||||
await urlDownloader.DownloadFile(
|
|
||||||
String.Format("{0}/{1}", updateUrlOrPath, x.Filename),
|
|
||||||
Path.Combine(rootAppDirectory, "packages", x.Filename));
|
|
||||||
lock (progress) progress(current += toIncrement);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
await releasesToDownload.ForEachAsync(x => {
|
|
||||||
File.Copy(
|
|
||||||
Path.Combine(updateUrlOrPath, x.Filename),
|
|
||||||
Path.Combine(rootAppDirectory, "packages", x.Filename));
|
|
||||||
lock (progress) progress(current += toIncrement);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ApplyReleases(UpdateInfo updateInfo, Action<int> progress = null)
|
public async Task ApplyReleases(UpdateInfo updateInfo, Action<int> progress = null)
|
||||||
{
|
{
|
||||||
progress = progress ?? (_ => { });
|
var applyReleases = new ApplyReleasesImpl(rootAppDirectory);
|
||||||
|
|
||||||
await acquireUpdateLock();
|
await acquireUpdateLock();
|
||||||
|
|
||||||
await cleanDeadVersions(updateInfo.CurrentlyInstalledVersion != null ? updateInfo.CurrentlyInstalledVersion.Version : null);
|
await applyReleases.ApplyReleases(updateInfo, progress);
|
||||||
progress(10);
|
|
||||||
|
|
||||||
var release = await createFullPackagesFromDeltas(updateInfo.ReleasesToApply, updateInfo.CurrentlyInstalledVersion);
|
|
||||||
progress(50);
|
|
||||||
|
|
||||||
await installPackageToAppDir(updateInfo, release);
|
|
||||||
progress(95);
|
|
||||||
|
|
||||||
await UpdateLocalReleasesFile();
|
|
||||||
progress(100);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateLocalReleasesFile()
|
public async Task FullInstall()
|
||||||
{
|
{
|
||||||
|
var updateInfo = await CheckForUpdate();
|
||||||
|
await DownloadReleases(updateInfo.ReleasesToApply);
|
||||||
|
await ApplyReleases(updateInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task FullUninstall()
|
||||||
|
{
|
||||||
|
var applyReleases = new ApplyReleasesImpl(rootAppDirectory);
|
||||||
await acquireUpdateLock();
|
await acquireUpdateLock();
|
||||||
await Task.Run(() => ReleaseEntry.BuildReleasesFile(PackageDirectory));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task FullUninstall(Version version = null)
|
await applyReleases.FullUninstall();
|
||||||
{
|
|
||||||
version = version ?? new Version(255, 255, 255, 255);
|
|
||||||
this.Log().Info("Uninstalling version '{0}'", version);
|
|
||||||
|
|
||||||
await acquireUpdateLock();
|
|
||||||
await fullUninstall(version);
|
|
||||||
}
|
|
||||||
|
|
||||||
IEnumerable<DirectoryInfo> getReleases()
|
|
||||||
{
|
|
||||||
var rootDirectory = new DirectoryInfo(rootAppDirectory);
|
|
||||||
|
|
||||||
if (!rootDirectory.Exists) return Enumerable.Empty<DirectoryInfo>();
|
|
||||||
|
|
||||||
return rootDirectory.GetDirectories()
|
|
||||||
.Where(x => x.Name.StartsWith("app-", StringComparison.InvariantCultureIgnoreCase));
|
|
||||||
}
|
|
||||||
|
|
||||||
IEnumerable<DirectoryInfo> getOldReleases(Version version)
|
|
||||||
{
|
|
||||||
return getReleases()
|
|
||||||
.Where(x => x.Name.ToVersion() < version)
|
|
||||||
.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task fullUninstall(Version version)
|
|
||||||
{
|
|
||||||
// find all the old releases (and this one)
|
|
||||||
var directoriesToDelete = getOldReleases(version)
|
|
||||||
.Concat(new [] { getDirectoryForRelease(version) })
|
|
||||||
.Where(d => d.Exists)
|
|
||||||
.Select(d => d.FullName);
|
|
||||||
|
|
||||||
await directoriesToDelete.ForEachAsync(x => deleteDirectoryWithFallbackToNextReboot(x));
|
|
||||||
|
|
||||||
if (!getReleases().Any()) {
|
|
||||||
await deleteDirectoryWithFallbackToNextReboot(rootAppDirectory);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
@@ -272,388 +128,5 @@ namespace Squirrel
|
|||||||
{
|
{
|
||||||
return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||||
}
|
}
|
||||||
|
|
||||||
DirectoryInfo getDirectoryForRelease(Version releaseVersion)
|
|
||||||
{
|
|
||||||
return new DirectoryInfo(Path.Combine(rootAppDirectory, "app-" + releaseVersion));
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task deleteDirectoryWithFallbackToNextReboot(string dir)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
await Utility.DeleteDirectory(dir);
|
|
||||||
} catch (UnauthorizedAccessException ex) {
|
|
||||||
var message = String.Format("Uninstall failed to delete dir '{0}', punting to next reboot", dir);
|
|
||||||
this.Log().WarnException(message, ex);
|
|
||||||
|
|
||||||
Utility.DeleteDirectoryAtNextReboot(dir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// CheckForUpdate methods
|
|
||||||
//
|
|
||||||
|
|
||||||
async Task initializeClientAppDirectory()
|
|
||||||
{
|
|
||||||
// On bootstrap, we won't have any of our directories, create them
|
|
||||||
var pkgDir = Path.Combine(rootAppDirectory, "packages");
|
|
||||||
if (Directory.Exists(pkgDir)) {
|
|
||||||
await Utility.DeleteDirectory(pkgDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
Directory.CreateDirectory(pkgDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateInfo determineUpdateInfo(IEnumerable<ReleaseEntry> localReleases, IEnumerable<ReleaseEntry> remoteReleases, bool ignoreDeltaUpdates)
|
|
||||||
{
|
|
||||||
localReleases = localReleases ?? Enumerable.Empty<ReleaseEntry>();
|
|
||||||
|
|
||||||
if (remoteReleases == null) {
|
|
||||||
this.Log().Warn("Release information couldn't be determined due to remote corrupt RELEASES file");
|
|
||||||
throw new Exception("Corrupt remote RELEASES file");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (localReleases.Count() == remoteReleases.Count()) {
|
|
||||||
this.Log().Info("No updates, remote and local are the same");
|
|
||||||
|
|
||||||
var latestFullRelease = findCurrentVersion(remoteReleases);
|
|
||||||
var currentRelease = findCurrentVersion(localReleases);
|
|
||||||
|
|
||||||
var info = UpdateInfo.Create(currentRelease, new[] {latestFullRelease}, PackageDirectory,appFrameworkVersion);
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ignoreDeltaUpdates) {
|
|
||||||
remoteReleases = remoteReleases.Where(x => !x.IsDelta);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!localReleases.Any()) {
|
|
||||||
this.Log().Warn("First run or local directory is corrupt, starting from scratch");
|
|
||||||
|
|
||||||
var latestFullRelease = findCurrentVersion(remoteReleases);
|
|
||||||
return UpdateInfo.Create(findCurrentVersion(localReleases), new[] {latestFullRelease}, PackageDirectory, appFrameworkVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (localReleases.Max(x => x.Version) > remoteReleases.Max(x => x.Version)) {
|
|
||||||
this.Log().Warn("hwhat, local version is greater than remote version");
|
|
||||||
|
|
||||||
var latestFullRelease = findCurrentVersion(remoteReleases);
|
|
||||||
return UpdateInfo.Create(findCurrentVersion(localReleases), new[] {latestFullRelease}, PackageDirectory, appFrameworkVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
return UpdateInfo.Create(findCurrentVersion(localReleases), remoteReleases, PackageDirectory, appFrameworkVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ReleaseEntry findCurrentVersion(IEnumerable<ReleaseEntry> localReleases)
|
|
||||||
{
|
|
||||||
if (!localReleases.Any()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return localReleases.MaxBy(x => x.Version).SingleOrDefault(x => !x.IsDelta);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// DownloadReleases methods
|
|
||||||
//
|
|
||||||
|
|
||||||
static bool isHttpUrl(string urlOrPath)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
var url = new Uri(urlOrPath);
|
|
||||||
return new[] {"https", "http"}.Contains(url.Scheme.ToLowerInvariant());
|
|
||||||
} catch (Exception) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Task checksumAllPackages(IEnumerable<ReleaseEntry> releasesDownloaded)
|
|
||||||
{
|
|
||||||
return releasesDownloaded.ForEachAsync(x => checksumPackage(x));
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// ApplyReleases methods
|
|
||||||
//
|
|
||||||
|
|
||||||
async Task 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) {
|
|
||||||
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.
|
|
||||||
runPostInstallAndCleanup(newCurrentVersion, updateInfo.IsBootstrapping);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CopyFileToLocation(FileSystemInfo target, IPackageFile x)
|
|
||||||
{
|
|
||||||
var targetPath = Path.Combine(target.FullName, x.EffectivePath);
|
|
||||||
|
|
||||||
var fi = new FileInfo(targetPath);
|
|
||||||
if (fi.Exists) fi.Delete();
|
|
||||||
|
|
||||||
var dir = new DirectoryInfo(Path.GetDirectoryName(targetPath));
|
|
||||||
if (!dir.Exists) dir.Create();
|
|
||||||
|
|
||||||
using (var inf = x.GetStream())
|
|
||||||
using (var of = fi.Open(FileMode.CreateNew, FileAccess.Write)) {
|
|
||||||
inf.CopyTo(of);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void runPostInstallAndCleanup(Version newCurrentVersion, bool isBootstrapping)
|
|
||||||
{
|
|
||||||
fixPinnedExecutables(newCurrentVersion);
|
|
||||||
|
|
||||||
this.Log().Info("runPostInstallAndCleanup: finished fixPinnedExecutables");
|
|
||||||
cleanUpOldVersions(newCurrentVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool pathIsInFrameworkProfile(IPackageFile packageFile, FrameworkVersion appFrameworkVersion)
|
|
||||||
{
|
|
||||||
if (!packageFile.Path.StartsWith("lib", StringComparison.InvariantCultureIgnoreCase)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appFrameworkVersion == FrameworkVersion.Net40
|
|
||||||
&& packageFile.Path.StartsWith("lib\\net45", StringComparison.InvariantCultureIgnoreCase)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task<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 releasesToApply.MaxBy(x => x.Version).First();
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cleanUpOldVersions(Version newCurrentVersion)
|
|
||||||
{
|
|
||||||
var directory = new DirectoryInfo(rootAppDirectory);
|
|
||||||
if (!directory.Exists) {
|
|
||||||
this.Log().Warn("cleanUpOldVersions: the directory '{0}' does not exist", rootAppDirectory);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var v in getOldReleases(newCurrentVersion)) {
|
|
||||||
Utility.DeleteDirectoryAtNextReboot(v.FullName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void fixPinnedExecutables(Version newCurrentVersion)
|
|
||||||
{
|
|
||||||
if (Environment.OSVersion.Version < new Version(6, 1)) {
|
|
||||||
this.Log().Warn("fixPinnedExecutables: Found OS Version '{0}', exiting...", Environment.OSVersion.VersionString);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var newCurrentFolder = "app-" + newCurrentVersion;
|
|
||||||
var oldAppDirectories = (new DirectoryInfo(rootAppDirectory)).GetDirectories()
|
|
||||||
.Where(x => x.Name.StartsWith("app-", StringComparison.InvariantCultureIgnoreCase))
|
|
||||||
.Where(x => x.Name != newCurrentFolder)
|
|
||||||
.Select(x => x.FullName)
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
if (!oldAppDirectories.Any()) {
|
|
||||||
this.Log().Info("fixPinnedExecutables: oldAppDirectories is empty, this is pointless");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var newAppPath = Path.Combine(rootAppDirectory, newCurrentFolder);
|
|
||||||
|
|
||||||
var taskbarPath = Path.Combine(
|
|
||||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
|
||||||
"Microsoft\\Internet Explorer\\Quick Launch\\User Pinned\\TaskBar");
|
|
||||||
|
|
||||||
Func<FileInfo, ShellLink> resolveLink = file => {
|
|
||||||
try {
|
|
||||||
return new ShellLink(file.FullName);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
var message = String.Format("File '{0}' could not be converted into a valid ShellLink", file.FullName);
|
|
||||||
this.Log().WarnException(message, ex);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var shellLinks = (new DirectoryInfo(taskbarPath)).GetFiles("*.lnk")
|
|
||||||
.Select(resolveLink)
|
|
||||||
.Where(x => x != null)
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
foreach (var shortcut in shellLinks) {
|
|
||||||
try {
|
|
||||||
updateLink(shortcut, oldAppDirectories, newAppPath);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
var message = String.Format("fixPinnedExecutables: shortcut failed: {0}", shortcut.Target);
|
|
||||||
this.Log().ErrorException(message, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateLink(ShellLink shortcut, string[] oldAppDirectories, string newAppPath)
|
|
||||||
{
|
|
||||||
this.Log().Info("Processing shortcut '{0}'", shortcut.Target);
|
|
||||||
|
|
||||||
foreach (var oldAppDirectory in oldAppDirectories) {
|
|
||||||
if (!shortcut.Target.StartsWith(oldAppDirectory, StringComparison.OrdinalIgnoreCase)) {
|
|
||||||
this.Log().Info("Does not match '{0}', continuing to next directory", oldAppDirectory);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// replace old app path with new app path and check, if executable still exists
|
|
||||||
var newTarget = Path.Combine(newAppPath, shortcut.Target.Substring(oldAppDirectory.Length + 1));
|
|
||||||
|
|
||||||
if (File.Exists(newTarget)) {
|
|
||||||
shortcut.Target = newTarget;
|
|
||||||
|
|
||||||
// replace working directory too if appropriate
|
|
||||||
if (shortcut.WorkingDirectory.StartsWith(oldAppDirectory, StringComparison.OrdinalIgnoreCase)) {
|
|
||||||
this.Log().Info("Changing new directory to '{0}'", newAppPath);
|
|
||||||
shortcut.WorkingDirectory = Path.Combine(newAppPath,
|
|
||||||
shortcut.WorkingDirectory.Substring(oldAppDirectory.Length + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
shortcut.Save();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.Log().Info("Unpinning {0} from taskbar", shortcut.Target);
|
|
||||||
TaskbarHelper.UnpinFromTaskbar(shortcut.Target);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 currentVersion)
|
|
||||||
{
|
|
||||||
if (currentVersion == null) return;
|
|
||||||
|
|
||||||
var di = new DirectoryInfo(rootAppDirectory);
|
|
||||||
if (!di.Exists) return;
|
|
||||||
|
|
||||||
this.Log().Info("cleanDeadVersions: for version {0}", currentVersion);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
await toCleanup.ForEachAsync(async x => {
|
|
||||||
try {
|
|
||||||
await Utility.DeleteDirectory(x.FullName);
|
|
||||||
} catch (UnauthorizedAccessException ex) {
|
|
||||||
this.Log().WarnException("Couldn't delete directory: " + x.FullName, ex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,7 +17,7 @@ using System.Collections.Concurrent;
|
|||||||
|
|
||||||
namespace Squirrel
|
namespace Squirrel
|
||||||
{
|
{
|
||||||
public static class Utility
|
static class Utility
|
||||||
{
|
{
|
||||||
public static IEnumerable<FileInfo> GetAllFilesRecursively(this DirectoryInfo rootPath)
|
public static IEnumerable<FileInfo> GetAllFilesRecursively(this DirectoryInfo rootPath)
|
||||||
{
|
{
|
||||||
@@ -201,6 +201,16 @@ namespace Squirrel
|
|||||||
return Tuple.Create(path, (Stream) File.OpenWrite(path));
|
return Tuple.Create(path, (Stream) File.OpenWrite(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string PackageDirectoryForAppDir(string rootAppDirectory)
|
||||||
|
{
|
||||||
|
return Path.Combine(rootAppDirectory, "packages");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string LocalReleaseFileForAppDir(string rootAppDirectory)
|
||||||
|
{
|
||||||
|
return Path.Combine(PackageDirectoryForAppDir(rootAppDirectory), "RELEASES");
|
||||||
|
}
|
||||||
|
|
||||||
static TAcc scan<T, TAcc>(this IEnumerable<T> This, TAcc initialValue, Func<TAcc, T, TAcc> accFunc)
|
static TAcc scan<T, TAcc>(this IEnumerable<T> This, TAcc initialValue, Func<TAcc, T, TAcc> accFunc)
|
||||||
{
|
{
|
||||||
TAcc acc = initialValue;
|
TAcc acc = initialValue;
|
||||||
@@ -213,6 +223,28 @@ namespace Squirrel
|
|||||||
return acc;
|
return acc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsHttpUrl(string urlOrPath)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
var url = new Uri(urlOrPath);
|
||||||
|
return new[] {"https", "http"}.Contains(url.Scheme.ToLowerInvariant());
|
||||||
|
} catch (Exception) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task DeleteDirectoryWithFallbackToNextReboot(string dir)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
await Utility.DeleteDirectory(dir);
|
||||||
|
} catch (UnauthorizedAccessException ex) {
|
||||||
|
var message = String.Format("Uninstall failed to delete dir '{0}', punting to next reboot", dir);
|
||||||
|
LogHost.Default.WarnException(message, ex);
|
||||||
|
|
||||||
|
Utility.DeleteDirectoryAtNextReboot(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void DeleteDirectoryAtNextReboot(string directoryPath)
|
public static void DeleteDirectoryAtNextReboot(string directoryPath)
|
||||||
{
|
{
|
||||||
var di = new DirectoryInfo(directoryPath);
|
var di = new DirectoryInfo(directoryPath);
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ namespace Squirrel.Tests
|
|||||||
{
|
{
|
||||||
string tempDir;
|
string tempDir;
|
||||||
using (Utility.WithTempDirectory(out tempDir)) {
|
using (Utility.WithTempDirectory(out tempDir)) {
|
||||||
Directory.CreateDirectory(Path.Combine(tempDir, "theApp"));
|
var appDir = Directory.CreateDirectory(Path.Combine(tempDir, "theApp"));
|
||||||
var packages = Path.Combine(tempDir, "theApp", "packages");
|
var packages = Path.Combine(appDir.FullName, "packages");
|
||||||
Directory.CreateDirectory(packages);
|
Directory.CreateDirectory(packages);
|
||||||
|
|
||||||
var package = "Squirrel.Core.1.0.0.0-full.nupkg";
|
var package = "Squirrel.Core.1.0.0.0-full.nupkg";
|
||||||
@@ -55,8 +55,8 @@ namespace Squirrel.Tests
|
|||||||
string tempDir;
|
string tempDir;
|
||||||
using (Utility.WithTempDirectory(out tempDir))
|
using (Utility.WithTempDirectory(out tempDir))
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(Path.Combine(tempDir, "theApp"));
|
var appDir = Directory.CreateDirectory(Path.Combine(tempDir, "theApp"));
|
||||||
var packages = Path.Combine(tempDir, "theApp", "packages");
|
var packages = Path.Combine(appDir.FullName, "packages");
|
||||||
Directory.CreateDirectory(packages);
|
Directory.CreateDirectory(packages);
|
||||||
|
|
||||||
var baseFile = "Squirrel.Core.1.0.0.0-full.nupkg";
|
var baseFile = "Squirrel.Core.1.0.0.0-full.nupkg";
|
||||||
@@ -82,7 +82,8 @@ namespace Squirrel.Tests
|
|||||||
string tempDir;
|
string tempDir;
|
||||||
|
|
||||||
using (Utility.WithTempDirectory(out tempDir)) {
|
using (Utility.WithTempDirectory(out tempDir)) {
|
||||||
string packagesDir = Path.Combine(tempDir, "theApp", "packages");
|
string appDir = Path.Combine(tempDir, "theApp");
|
||||||
|
string packagesDir = Path.Combine(appDir, "packages");
|
||||||
Directory.CreateDirectory(packagesDir);
|
Directory.CreateDirectory(packagesDir);
|
||||||
|
|
||||||
new[] {
|
new[] {
|
||||||
@@ -90,7 +91,7 @@ namespace Squirrel.Tests
|
|||||||
"Squirrel.Core.1.1.0.0-full.nupkg",
|
"Squirrel.Core.1.1.0.0-full.nupkg",
|
||||||
}.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(packagesDir, x)));
|
}.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(packagesDir, x)));
|
||||||
|
|
||||||
var fixture = new UpdateManager("http://lol", "theApp", FrameworkVersion.Net40, tempDir, new FakeUrlDownloader());
|
var fixture = new UpdateManager.ApplyReleasesImpl(appDir);
|
||||||
|
|
||||||
var baseEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "Squirrel.Core.1.0.0.0-full.nupkg"));
|
var baseEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "Squirrel.Core.1.0.0.0-full.nupkg"));
|
||||||
var latestFullEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "Squirrel.Core.1.1.0.0-full.nupkg"));
|
var latestFullEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "Squirrel.Core.1.1.0.0-full.nupkg"));
|
||||||
@@ -98,16 +99,14 @@ namespace Squirrel.Tests
|
|||||||
var updateInfo = UpdateInfo.Create(baseEntry, new[] { latestFullEntry }, packagesDir, FrameworkVersion.Net40);
|
var updateInfo = UpdateInfo.Create(baseEntry, new[] { latestFullEntry }, packagesDir, FrameworkVersion.Net40);
|
||||||
updateInfo.ReleasesToApply.Contains(latestFullEntry).ShouldBeTrue();
|
updateInfo.ReleasesToApply.Contains(latestFullEntry).ShouldBeTrue();
|
||||||
|
|
||||||
using (fixture) {
|
var progress = new List<int>();
|
||||||
var progress = new List<int>();
|
|
||||||
|
|
||||||
await fixture.ApplyReleases(updateInfo, progress.Add);
|
await fixture.ApplyReleases(updateInfo, progress.Add);
|
||||||
this.Log().Info("Progress: [{0}]", String.Join(",", progress));
|
this.Log().Info("Progress: [{0}]", String.Join(",", progress));
|
||||||
|
|
||||||
progress
|
progress
|
||||||
.Aggregate(0, (acc, x) => { x.ShouldBeGreaterThan(acc); return x; })
|
.Aggregate(0, (acc, x) => { x.ShouldBeGreaterThan(acc); return x; })
|
||||||
.ShouldEqual(100);
|
.ShouldEqual(100);
|
||||||
}
|
|
||||||
|
|
||||||
var filesToFind = new[] {
|
var filesToFind = new[] {
|
||||||
new {Name = "NLog.dll", Version = new Version("2.0.0.0")},
|
new {Name = "NLog.dll", Version = new Version("2.0.0.0")},
|
||||||
@@ -133,7 +132,8 @@ namespace Squirrel.Tests
|
|||||||
string tempDir;
|
string tempDir;
|
||||||
|
|
||||||
using (Utility.WithTempDirectory(out tempDir)) {
|
using (Utility.WithTempDirectory(out tempDir)) {
|
||||||
string packagesDir = Path.Combine(tempDir, "theApp", "packages");
|
string appDir = Path.Combine(tempDir, "theApp");
|
||||||
|
string packagesDir = Path.Combine(appDir, "packages");
|
||||||
Directory.CreateDirectory(packagesDir);
|
Directory.CreateDirectory(packagesDir);
|
||||||
|
|
||||||
new[] {
|
new[] {
|
||||||
@@ -141,7 +141,7 @@ namespace Squirrel.Tests
|
|||||||
"Squirrel.Core.1.2.0.0-full.nupkg",
|
"Squirrel.Core.1.2.0.0-full.nupkg",
|
||||||
}.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(packagesDir, x)));
|
}.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(packagesDir, x)));
|
||||||
|
|
||||||
var fixture = new UpdateManager("http://lol", "theApp", FrameworkVersion.Net40, tempDir, new FakeUrlDownloader());
|
var fixture = new UpdateManager.ApplyReleasesImpl(appDir);
|
||||||
|
|
||||||
var baseEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "Squirrel.Core.1.1.0.0-full.nupkg"));
|
var baseEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "Squirrel.Core.1.1.0.0-full.nupkg"));
|
||||||
var latestFullEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "Squirrel.Core.1.2.0.0-full.nupkg"));
|
var latestFullEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "Squirrel.Core.1.2.0.0-full.nupkg"));
|
||||||
@@ -149,15 +149,13 @@ namespace Squirrel.Tests
|
|||||||
var updateInfo = UpdateInfo.Create(baseEntry, new[] { latestFullEntry }, packagesDir, FrameworkVersion.Net40);
|
var updateInfo = UpdateInfo.Create(baseEntry, new[] { latestFullEntry }, packagesDir, FrameworkVersion.Net40);
|
||||||
updateInfo.ReleasesToApply.Contains(latestFullEntry).ShouldBeTrue();
|
updateInfo.ReleasesToApply.Contains(latestFullEntry).ShouldBeTrue();
|
||||||
|
|
||||||
using (fixture) {
|
var progress = new List<int>();
|
||||||
var progress = new List<int>();
|
await fixture.ApplyReleases(updateInfo, progress.Add);
|
||||||
await fixture.ApplyReleases(updateInfo, progress.Add);
|
this.Log().Info("Progress: [{0}]", String.Join(",", progress));
|
||||||
this.Log().Info("Progress: [{0}]", String.Join(",", progress));
|
|
||||||
|
|
||||||
progress
|
progress
|
||||||
.Aggregate(0, (acc, x) => { x.ShouldBeGreaterThan(acc); return x; })
|
.Aggregate(0, (acc, x) => { x.ShouldBeGreaterThan(acc); return x; })
|
||||||
.ShouldEqual(100);
|
.ShouldEqual(100);
|
||||||
}
|
|
||||||
|
|
||||||
var rootDirectory = Path.Combine(tempDir, "theApp", "app-1.2.0.0");
|
var rootDirectory = Path.Combine(tempDir, "theApp", "app-1.2.0.0");
|
||||||
|
|
||||||
@@ -183,7 +181,8 @@ namespace Squirrel.Tests
|
|||||||
|
|
||||||
using (Utility.WithTempDirectory(out tempDir))
|
using (Utility.WithTempDirectory(out tempDir))
|
||||||
{
|
{
|
||||||
string packagesDir = Path.Combine(tempDir, "theApp", "packages");
|
string appDir = Path.Combine(tempDir, "theApp");
|
||||||
|
string packagesDir = Path.Combine(appDir, "packages");
|
||||||
Directory.CreateDirectory(packagesDir);
|
Directory.CreateDirectory(packagesDir);
|
||||||
|
|
||||||
new[] {
|
new[] {
|
||||||
@@ -191,7 +190,7 @@ namespace Squirrel.Tests
|
|||||||
"Squirrel.Core.1.3.0.0-full.nupkg",
|
"Squirrel.Core.1.3.0.0-full.nupkg",
|
||||||
}.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(packagesDir, x)));
|
}.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(packagesDir, x)));
|
||||||
|
|
||||||
var fixture = new UpdateManager("http://lol", "theApp", FrameworkVersion.Net40, tempDir, new FakeUrlDownloader());
|
var fixture = new UpdateManager.ApplyReleasesImpl(appDir);
|
||||||
|
|
||||||
var baseEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "Squirrel.Core.1.1.0.0-full.nupkg"));
|
var baseEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "Squirrel.Core.1.1.0.0-full.nupkg"));
|
||||||
var latestFullEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "Squirrel.Core.1.3.0.0-full.nupkg"));
|
var latestFullEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "Squirrel.Core.1.3.0.0-full.nupkg"));
|
||||||
@@ -199,15 +198,13 @@ namespace Squirrel.Tests
|
|||||||
var updateInfo = UpdateInfo.Create(baseEntry, new[] { latestFullEntry }, packagesDir, FrameworkVersion.Net40);
|
var updateInfo = UpdateInfo.Create(baseEntry, new[] { latestFullEntry }, packagesDir, FrameworkVersion.Net40);
|
||||||
updateInfo.ReleasesToApply.Contains(latestFullEntry).ShouldBeTrue();
|
updateInfo.ReleasesToApply.Contains(latestFullEntry).ShouldBeTrue();
|
||||||
|
|
||||||
using (fixture) {
|
var progress = new List<int>();
|
||||||
var progress = new List<int>();
|
await fixture.ApplyReleases(updateInfo, progress.Add);
|
||||||
await fixture.ApplyReleases(updateInfo, progress.Add);
|
this.Log().Info("Progress: [{0}]", String.Join(",", progress));
|
||||||
this.Log().Info("Progress: [{0}]", String.Join(",", progress));
|
|
||||||
|
|
||||||
progress
|
progress
|
||||||
.Aggregate(0, (acc, x) => { x.ShouldBeGreaterThan(acc); return x; })
|
.Aggregate(0, (acc, x) => { x.ShouldBeGreaterThan(acc); return x; })
|
||||||
.ShouldEqual(100);
|
.ShouldEqual(100);
|
||||||
}
|
|
||||||
|
|
||||||
var rootDirectory = Path.Combine(tempDir, "theApp", "app-1.3.0.0");
|
var rootDirectory = Path.Combine(tempDir, "theApp", "app-1.3.0.0");
|
||||||
|
|
||||||
@@ -234,7 +231,8 @@ namespace Squirrel.Tests
|
|||||||
string tempDir;
|
string tempDir;
|
||||||
|
|
||||||
using (Utility.WithTempDirectory(out tempDir)) {
|
using (Utility.WithTempDirectory(out tempDir)) {
|
||||||
string packagesDir = Path.Combine(tempDir, "theApp", "packages");
|
string appDir = Path.Combine(tempDir, "theApp");
|
||||||
|
string packagesDir = Path.Combine(appDir, "packages");
|
||||||
Directory.CreateDirectory(packagesDir);
|
Directory.CreateDirectory(packagesDir);
|
||||||
|
|
||||||
new[] {
|
new[] {
|
||||||
@@ -243,7 +241,7 @@ namespace Squirrel.Tests
|
|||||||
"Squirrel.Core.1.1.0.0-full.nupkg",
|
"Squirrel.Core.1.1.0.0-full.nupkg",
|
||||||
}.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(packagesDir, x)));
|
}.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(packagesDir, x)));
|
||||||
|
|
||||||
var fixture = new UpdateManager("http://lol", "theApp", FrameworkVersion.Net40, tempDir, new FakeUrlDownloader());
|
var fixture = new UpdateManager.ApplyReleasesImpl(appDir);
|
||||||
|
|
||||||
var baseEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "Squirrel.Core.1.0.0.0-full.nupkg"));
|
var baseEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "Squirrel.Core.1.0.0.0-full.nupkg"));
|
||||||
var deltaEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "Squirrel.Core.1.1.0.0-delta.nupkg"));
|
var deltaEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "Squirrel.Core.1.1.0.0-delta.nupkg"));
|
||||||
@@ -252,16 +250,14 @@ namespace Squirrel.Tests
|
|||||||
var updateInfo = UpdateInfo.Create(baseEntry, new[] { deltaEntry, latestFullEntry }, packagesDir, FrameworkVersion.Net40);
|
var updateInfo = UpdateInfo.Create(baseEntry, new[] { deltaEntry, latestFullEntry }, packagesDir, FrameworkVersion.Net40);
|
||||||
updateInfo.ReleasesToApply.Contains(deltaEntry).ShouldBeTrue();
|
updateInfo.ReleasesToApply.Contains(deltaEntry).ShouldBeTrue();
|
||||||
|
|
||||||
using (fixture) {
|
var progress = new List<int>();
|
||||||
var progress = new List<int>();
|
|
||||||
|
|
||||||
await fixture.ApplyReleases(updateInfo, progress.Add);
|
await fixture.ApplyReleases(updateInfo, progress.Add);
|
||||||
this.Log().Info("Progress: [{0}]", String.Join(",", progress));
|
this.Log().Info("Progress: [{0}]", String.Join(",", progress));
|
||||||
|
|
||||||
progress
|
progress
|
||||||
.Aggregate(0, (acc, x) => { x.ShouldBeGreaterThan(acc); return x; })
|
.Aggregate(0, (acc, x) => { x.ShouldBeGreaterThan(acc); return x; })
|
||||||
.ShouldEqual(100);
|
.ShouldEqual(100);
|
||||||
}
|
|
||||||
|
|
||||||
var filesToFind = new[] {
|
var filesToFind = new[] {
|
||||||
new {Name = "NLog.dll", Version = new Version("2.0.0.0")},
|
new {Name = "NLog.dll", Version = new Version("2.0.0.0")},
|
||||||
@@ -286,7 +282,9 @@ namespace Squirrel.Tests
|
|||||||
{
|
{
|
||||||
string tempDir;
|
string tempDir;
|
||||||
using (Utility.WithTempDirectory(out tempDir)) {
|
using (Utility.WithTempDirectory(out tempDir)) {
|
||||||
Directory.CreateDirectory(Path.Combine(tempDir, "theApp", "packages"));
|
string appDir = Path.Combine(tempDir, "theApp");
|
||||||
|
string packagesDir = Path.Combine(appDir, "packages");
|
||||||
|
Directory.CreateDirectory(packagesDir);
|
||||||
|
|
||||||
new[] {
|
new[] {
|
||||||
"Squirrel.Core.1.0.0.0-full.nupkg",
|
"Squirrel.Core.1.0.0.0-full.nupkg",
|
||||||
@@ -294,17 +292,17 @@ namespace Squirrel.Tests
|
|||||||
}.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(tempDir, "theApp", "packages", x)));
|
}.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(tempDir, "theApp", "packages", x)));
|
||||||
|
|
||||||
var urlDownloader = new FakeUrlDownloader();
|
var urlDownloader = new FakeUrlDownloader();
|
||||||
using (var fixture = new UpdateManager("http://lol", "theApp", FrameworkVersion.Net40, tempDir, urlDownloader)) {
|
var fixture = new UpdateManager.ApplyReleasesImpl(appDir);
|
||||||
var baseEntry = ReleaseEntry.GenerateFromFile(Path.Combine(tempDir, "theApp", "packages", "Squirrel.Core.1.0.0.0-full.nupkg"));
|
|
||||||
var deltaEntry = ReleaseEntry.GenerateFromFile(Path.Combine(tempDir, "theApp", "packages", "Squirrel.Core.1.1.0.0-delta.nupkg"));
|
|
||||||
|
|
||||||
var resultObs = (Task<ReleaseEntry>)fixture.GetType().GetMethod("createFullPackagesFromDeltas", BindingFlags.NonPublic | BindingFlags.Instance)
|
var baseEntry = ReleaseEntry.GenerateFromFile(Path.Combine(tempDir, "theApp", "packages", "Squirrel.Core.1.0.0.0-full.nupkg"));
|
||||||
.Invoke(fixture, new object[] { new[] {deltaEntry}, baseEntry });
|
var deltaEntry = ReleaseEntry.GenerateFromFile(Path.Combine(tempDir, "theApp", "packages", "Squirrel.Core.1.1.0.0-delta.nupkg"));
|
||||||
|
|
||||||
var result = await resultObs;
|
var resultObs = (Task<ReleaseEntry>)fixture.GetType().GetMethod("createFullPackagesFromDeltas", BindingFlags.NonPublic | BindingFlags.Instance)
|
||||||
var zp = new ZipPackage(Path.Combine(tempDir, "theApp", "packages", result.Filename));
|
.Invoke(fixture, new object[] { new[] {deltaEntry}, baseEntry });
|
||||||
zp.Version.ToString().ShouldEqual("1.1.0.0");
|
|
||||||
}
|
var result = await resultObs;
|
||||||
|
var zp = new ZipPackage(Path.Combine(tempDir, "theApp", "packages", result.Filename));
|
||||||
|
zp.Version.ToString().ShouldEqual("1.1.0.0");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ namespace Squirrel.Tests
|
|||||||
{
|
{
|
||||||
string tempDir;
|
string tempDir;
|
||||||
using (Utility.WithTempDirectory(out tempDir)) {
|
using (Utility.WithTempDirectory(out tempDir)) {
|
||||||
var packageDir = Directory.CreateDirectory(Path.Combine(tempDir, "theApp", "packages"));
|
var appDir = Path.Combine(tempDir, "theApp");
|
||||||
|
var packageDir = Directory.CreateDirectory(Path.Combine(appDir, "packages"));
|
||||||
|
|
||||||
new[] {
|
new[] {
|
||||||
"Squirrel.Core.1.0.0.0-full.nupkg",
|
"Squirrel.Core.1.0.0.0-full.nupkg",
|
||||||
@@ -27,11 +28,9 @@ namespace Squirrel.Tests
|
|||||||
"Squirrel.Core.1.1.0.0-full.nupkg",
|
"Squirrel.Core.1.1.0.0-full.nupkg",
|
||||||
}.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(tempDir, "theApp", "packages", x)));
|
}.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(tempDir, "theApp", "packages", x)));
|
||||||
|
|
||||||
var fixture = new UpdateManager("http://lol", "theApp", FrameworkVersion.Net40, tempDir, new FakeUrlDownloader());
|
var fixture = new UpdateManager.ApplyReleasesImpl(appDir);
|
||||||
|
|
||||||
using (fixture) {
|
await fixture.updateLocalReleasesFile();
|
||||||
await fixture.UpdateLocalReleasesFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
var releasePath = Path.Combine(packageDir.FullName, "RELEASES");
|
var releasePath = Path.Combine(packageDir.FullName, "RELEASES");
|
||||||
File.Exists(releasePath).ShouldBeTrue();
|
File.Exists(releasePath).ShouldBeTrue();
|
||||||
@@ -41,13 +40,43 @@ namespace Squirrel.Tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task InitialInstallSmokeTest()
|
||||||
|
{
|
||||||
|
string tempDir;
|
||||||
|
using (Utility.WithTempDirectory(out tempDir)) {
|
||||||
|
var remotePackageDir = Directory.CreateDirectory(Path.Combine(tempDir, "remotePackages"));
|
||||||
|
var localAppDir = Path.Combine(tempDir, "theApp");
|
||||||
|
|
||||||
|
new[] {
|
||||||
|
"Squirrel.Core.1.0.0.0-full.nupkg",
|
||||||
|
}.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(remotePackageDir.FullName, x)));
|
||||||
|
|
||||||
|
using (var fixture = new UpdateManager(remotePackageDir.FullName, "theApp", FrameworkVersion.Net45, tempDir)) {
|
||||||
|
await fixture.FullInstall();
|
||||||
|
}
|
||||||
|
|
||||||
|
var releasePath = Path.Combine(localAppDir, "packages", "RELEASES");
|
||||||
|
File.Exists(releasePath).ShouldBeTrue();
|
||||||
|
|
||||||
|
var entries = ReleaseEntry.ParseReleaseFile(File.ReadAllText(releasePath, Encoding.UTF8));
|
||||||
|
entries.Count().ShouldEqual(1);
|
||||||
|
|
||||||
|
new[] {
|
||||||
|
"ReactiveUI.dll",
|
||||||
|
"NSync.Core.dll",
|
||||||
|
}.ForEach(x => File.Exists(Path.Combine(localAppDir, "app-1.0.0.0", x)).ShouldBeTrue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task WhenBothFilesAreInSyncNoUpdatesAreApplied()
|
public async Task WhenBothFilesAreInSyncNoUpdatesAreApplied()
|
||||||
{
|
{
|
||||||
string tempDir;
|
string tempDir;
|
||||||
using (Utility.WithTempDirectory(out tempDir))
|
using (Utility.WithTempDirectory(out tempDir))
|
||||||
{
|
{
|
||||||
var localPackages = Path.Combine(tempDir, "theApp", "packages");
|
var appDir = Path.Combine(tempDir, "theApp");
|
||||||
|
var localPackages = Path.Combine(appDir, "packages");
|
||||||
var remotePackages = Path.Combine(tempDir, "releases");
|
var remotePackages = Path.Combine(tempDir, "releases");
|
||||||
Directory.CreateDirectory(localPackages);
|
Directory.CreateDirectory(localPackages);
|
||||||
Directory.CreateDirectory(remotePackages);
|
Directory.CreateDirectory(remotePackages);
|
||||||
@@ -63,17 +92,16 @@ namespace Squirrel.Tests
|
|||||||
File.Copy(path, Path.Combine(remotePackages, x));
|
File.Copy(path, Path.Combine(remotePackages, x));
|
||||||
});
|
});
|
||||||
|
|
||||||
var fixture = new UpdateManager(remotePackages, "theApp", FrameworkVersion.Net40, tempDir, new FakeUrlDownloader());
|
var fixture = new UpdateManager.ApplyReleasesImpl(appDir);
|
||||||
|
|
||||||
|
// sync both release files
|
||||||
|
await fixture.updateLocalReleasesFile();
|
||||||
|
ReleaseEntry.BuildReleasesFile(remotePackages);
|
||||||
|
|
||||||
|
// check for an update
|
||||||
UpdateInfo updateInfo;
|
UpdateInfo updateInfo;
|
||||||
using (fixture)
|
using (var mgr = new UpdateManager(remotePackages, "theApp", FrameworkVersion.Net40, tempDir, new FakeUrlDownloader())) {
|
||||||
{
|
updateInfo = await mgr.CheckForUpdate();
|
||||||
// sync both release files
|
|
||||||
await fixture.UpdateLocalReleasesFile();
|
|
||||||
ReleaseEntry.BuildReleasesFile(remotePackages);
|
|
||||||
|
|
||||||
// check for an update
|
|
||||||
updateInfo = await fixture.CheckForUpdate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.NotNull(updateInfo);
|
Assert.NotNull(updateInfo);
|
||||||
@@ -87,7 +115,8 @@ namespace Squirrel.Tests
|
|||||||
string tempDir;
|
string tempDir;
|
||||||
using (Utility.WithTempDirectory(out tempDir))
|
using (Utility.WithTempDirectory(out tempDir))
|
||||||
{
|
{
|
||||||
var localPackages = Path.Combine(tempDir, "theApp", "packages");
|
var appDir = Path.Combine(tempDir, "theApp");
|
||||||
|
var localPackages = Path.Combine(appDir, "packages");
|
||||||
var remotePackages = Path.Combine(tempDir, "releases");
|
var remotePackages = Path.Combine(tempDir, "releases");
|
||||||
Directory.CreateDirectory(localPackages);
|
Directory.CreateDirectory(localPackages);
|
||||||
Directory.CreateDirectory(remotePackages);
|
Directory.CreateDirectory(remotePackages);
|
||||||
@@ -111,17 +140,15 @@ namespace Squirrel.Tests
|
|||||||
File.Copy(path, Path.Combine(remotePackages, x));
|
File.Copy(path, Path.Combine(remotePackages, x));
|
||||||
});
|
});
|
||||||
|
|
||||||
var fixture = new UpdateManager(remotePackages, "theApp", FrameworkVersion.Net40, tempDir, new FakeUrlDownloader());
|
var fixture = new UpdateManager.ApplyReleasesImpl(appDir);
|
||||||
|
|
||||||
|
// sync both release files
|
||||||
|
await fixture.updateLocalReleasesFile();
|
||||||
|
ReleaseEntry.BuildReleasesFile(remotePackages);
|
||||||
|
|
||||||
UpdateInfo updateInfo;
|
UpdateInfo updateInfo;
|
||||||
using (fixture)
|
using (var mgr = new UpdateManager(remotePackages, "theApp", FrameworkVersion.Net40, tempDir, new FakeUrlDownloader())) {
|
||||||
{
|
updateInfo = await mgr.CheckForUpdate();
|
||||||
// sync both release files
|
|
||||||
await fixture.UpdateLocalReleasesFile();
|
|
||||||
ReleaseEntry.BuildReleasesFile(remotePackages);
|
|
||||||
|
|
||||||
// check for an update
|
|
||||||
updateInfo = await fixture.CheckForUpdate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.NotNull(updateInfo);
|
Assert.NotNull(updateInfo);
|
||||||
@@ -135,7 +162,8 @@ namespace Squirrel.Tests
|
|||||||
string tempDir;
|
string tempDir;
|
||||||
using (Utility.WithTempDirectory(out tempDir))
|
using (Utility.WithTempDirectory(out tempDir))
|
||||||
{
|
{
|
||||||
var localPackages = Path.Combine(tempDir, "theApp", "packages");
|
var appDir = Path.Combine(tempDir, "theApp");
|
||||||
|
var localPackages = Path.Combine(appDir, "packages");
|
||||||
var remotePackages = Path.Combine(tempDir, "releases");
|
var remotePackages = Path.Combine(tempDir, "releases");
|
||||||
Directory.CreateDirectory(localPackages);
|
Directory.CreateDirectory(localPackages);
|
||||||
Directory.CreateDirectory(remotePackages);
|
Directory.CreateDirectory(remotePackages);
|
||||||
@@ -154,25 +182,21 @@ namespace Squirrel.Tests
|
|||||||
File.Copy(path, Path.Combine(remotePackages, x));
|
File.Copy(path, Path.Combine(remotePackages, x));
|
||||||
});
|
});
|
||||||
|
|
||||||
var fixture = new UpdateManager(remotePackages, "theApp", FrameworkVersion.Net40, tempDir, new FakeUrlDownloader());
|
var fixture = new UpdateManager.ApplyReleasesImpl(appDir);
|
||||||
|
|
||||||
UpdateInfo updateInfo;
|
// sync both release files
|
||||||
using (fixture)
|
await fixture.updateLocalReleasesFile();
|
||||||
{
|
ReleaseEntry.BuildReleasesFile(remotePackages);
|
||||||
// sync both release files
|
|
||||||
await fixture.UpdateLocalReleasesFile();
|
|
||||||
ReleaseEntry.BuildReleasesFile(remotePackages);
|
|
||||||
|
|
||||||
updateInfo = await fixture.CheckForUpdate();
|
|
||||||
|
|
||||||
|
using (var mgr = new UpdateManager(remotePackages, "theApp", FrameworkVersion.Net40, tempDir, new FakeUrlDownloader())) {
|
||||||
|
UpdateInfo updateInfo;
|
||||||
|
updateInfo = await mgr.CheckForUpdate();
|
||||||
Assert.True(updateInfo.ReleasesToApply.First().IsDelta);
|
Assert.True(updateInfo.ReleasesToApply.First().IsDelta);
|
||||||
|
|
||||||
updateInfo = await fixture.CheckForUpdate(ignoreDeltaUpdates: true);
|
updateInfo = await mgr.CheckForUpdate(ignoreDeltaUpdates: true);
|
||||||
|
|
||||||
Assert.False(updateInfo.ReleasesToApply.First().IsDelta);
|
Assert.False(updateInfo.ReleasesToApply.First().IsDelta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -220,11 +244,8 @@ namespace Squirrel.Tests
|
|||||||
public async Task WhenUrlResultsInWebExceptionReturnNull()
|
public async Task WhenUrlResultsInWebExceptionReturnNull()
|
||||||
{
|
{
|
||||||
// This should result in a WebException (which gets caught) unless you can actually access http://lol
|
// This should result in a WebException (which gets caught) unless you can actually access http://lol
|
||||||
|
|
||||||
var fixture = new UpdateManager("http://lol", "theApp", FrameworkVersion.Net45);
|
var fixture = new UpdateManager("http://lol", "theApp", FrameworkVersion.Net45);
|
||||||
|
|
||||||
var updateInfo = await fixture.CheckForUpdate();
|
var updateInfo = await fixture.CheckForUpdate();
|
||||||
|
|
||||||
Assert.Null(updateInfo);
|
Assert.Null(updateInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user