Get CheckForUpdates into its own inner class

This commit is contained in:
Paul Betts
2014-08-06 17:21:51 +02:00
parent a40085812d
commit 0501ed8f93
5 changed files with 136 additions and 94 deletions

View File

@@ -43,6 +43,12 @@ namespace Squirrel
/// <param name="progress">A Observer which can be used to report Progress -
/// will return values from 0-100 and Complete, or Throw</param>
Task ApplyReleases(UpdateInfo updateInfo, Action<int> progress = null);
/// <summary>
/// Completely uninstalls the targeted app
/// </summary>
/// <returns>Completion</returns>
async Task FullUninstall();
}
public static class EasyModeMixin

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Splat;
@@ -12,7 +13,22 @@ namespace Squirrel
{
class CheckForUpdates : IEnableLogger
{
public async Task<UpdateInfo> CheckForUpdate(bool ignoreDeltaUpdates = false, Action<int> progress = null)
readonly string rootAppDirectory;
// TODO: rip this out
readonly FrameworkVersion appFrameworkVersion = FrameworkVersion.Net45;
public CheckForUpdates(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 ?? (_ => { });
@@ -20,7 +36,7 @@ namespace Squirrel
bool shouldInitialize = false;
try {
var file = File.OpenRead(LocalReleaseFile);
var file = File.OpenRead(localReleaseFile);
// NB: sr disposes file
using (var sr = new StreamReader(file, Encoding.UTF8)) {
@@ -38,7 +54,7 @@ namespace Squirrel
// Fetch the remote RELEASES file, whether it's a local dir or an
// HTTP URL
if (isHttpUrl(updateUrlOrPath)) {
if (Utility.IsHttpUrl(updateUrlOrPath)) {
this.Log().Info("Downloading RELEASES file from {0}", updateUrlOrPath);
try {
@@ -87,73 +103,74 @@ namespace Squirrel
var remoteReleases = ReleaseEntry.ParseReleaseFile(releaseFile);
progress(66);
if (!remoteReleases.IsEmpty()) {
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);
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);
}
Directory.CreateDirectory(pkgDir);
}
UpdateInfo determineUpdateInfo(IEnumerable<ReleaseEntry> localReleases, IEnumerable<ReleaseEntry> remoteReleases, bool ignoreDeltaUpdates)
{
var packageDirectory = Utility.PackageDirectoryForAppDir(rootAppDirectory);
localReleases = localReleases ?? Enumerable.Empty<ReleaseEntry>();
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 (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);
}
if (localReleases.Count() == remoteReleases.Count()) {
this.Log().Info("No updates, remote and local are the same");
static ReleaseEntry findCurrentVersion(IEnumerable<ReleaseEntry> localReleases)
{
if (!localReleases.Any()) {
return null;
}
var latestFullRelease = findCurrentVersion(remoteReleases);
var currentRelease = findCurrentVersion(localReleases);
var info = UpdateInfo.Create(currentRelease, new[] {latestFullRelease}, PackageDirectory,appFrameworkVersion);
return info;
return localReleases.MaxBy(x => x.Version).SingleOrDefault(x => !x.IsDelta);
}
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);
}
}
}

View File

@@ -9,7 +9,7 @@ namespace Squirrel
{
public sealed partial class UpdateManager
{
class DownloadReleases : IEnableLogger
class DownloadReleases : IEnableLogger
{
public async Task DownloadReleases(IEnumerable<ReleaseEntry> releasesToDownload, Action<int> progress = null)
{
@@ -33,16 +33,6 @@ namespace Squirrel
});
}
}
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)
{

View File

@@ -41,21 +41,26 @@ namespace Squirrel
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 FullUninstall(Version version = null)
public Task<UpdateInfo> CheckForUpdate(bool ignoreDeltaUpdates, Action<int> progress = null)
{
version = version ?? new Version(255, 255, 255, 255);
this.Log().Info("Uninstalling version '{0}'", version);
var checkForUpdate = new CheckForUpdates(rootAppDirectory);
return checkForUpdate.CheckForUpdate(Utility.LocalReleaseFileForAppDir(rootAppDirectory), updateUrlOrPath, ignoreDeltaUpdates, progress, urlDownloader);
}
public Task DownloadReleases(IEnumerable<ReleaseEntry> releasesToDownload, Action<int> progress = null)
{
throw new NotImplementedException();
}
public Task ApplyReleases(UpdateInfo updateInfo, Action<int> progress = null)
{
throw new NotImplementedException();
}
public async Task FullUninstall()
{
await acquireUpdateLock();
await fullUninstall(version);
await fullUninstall();
}
IEnumerable<DirectoryInfo> getReleases()
@@ -75,18 +80,22 @@ namespace Squirrel
.ToArray();
}
async Task fullUninstall(Version version)
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 => deleteDirectoryWithFallbackToNextReboot(x));
await directoriesToDelete.ForEachAsync(x => Utility.DeleteDirectoryWithFallbackToNextReboot(x));
if (!getReleases().Any()) {
await deleteDirectoryWithFallbackToNextReboot(rootAppDirectory);
await Utility.DeleteDirectoryWithFallbackToNextReboot(rootAppDirectory);
}
}
@@ -143,17 +152,5 @@ namespace Squirrel
{
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);
}
}
}
}

View File

@@ -17,7 +17,7 @@ using System.Collections.Concurrent;
namespace Squirrel
{
public static class Utility
static class Utility
{
public static IEnumerable<FileInfo> GetAllFilesRecursively(this DirectoryInfo rootPath)
{
@@ -201,6 +201,16 @@ namespace Squirrel
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)
{
TAcc acc = initialValue;
@@ -213,6 +223,28 @@ namespace Squirrel
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)
{
var di = new DirectoryInfo(directoryPath);