Little refactoring and improved error handling.

This commit is contained in:
Holger Boerchers 2018-07-30 15:07:48 +02:00
parent 0ed81d68c8
commit aa70a24a26
7 changed files with 90 additions and 54 deletions

View File

@ -1,4 +1,3 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;

View File

@ -1,6 +1 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
</startup>
</configuration>
{}

View File

@ -43,6 +43,7 @@
<Compile Include="Constants.cs" />
<Compile Include="KattekerConfig.cs" />
<Compile Include="Common\MarkdownSharp.cs" />
<Compile Include="KattekerUpdateException.cs" />
<Compile Include="UpdateInfo.cs" />
<Compile Include="UpdateManager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />

View File

@ -0,0 +1,24 @@
using System;
namespace Katteker
{
/// <summary>
/// Represent an error message in the Katteker update process.
/// </summary>
public class KattekerUpdateException : Exception
{
/// <summary>
/// Creates a new instance of the <see cref="KattekerUpdateException"/> class with the error message.
/// </summary>
public KattekerUpdateException(string message) : base(message)
{
}
/// <summary>
/// Creates a new instance of the <see cref="KattekerUpdateException"/> with the error message and inner exception.
/// </summary>
public KattekerUpdateException(string message, Exception innerException) : base(message, innerException)
{
}
}
}

View File

@ -6,6 +6,7 @@ using System.Text.RegularExpressions;
namespace Katteker
{
/// <inheritdoc cref="IComparable" />
/// <summary>
/// Entry of a Release.
/// </summary>
@ -16,7 +17,7 @@ namespace Katteker
private const char Seperator = '|';
/// <summary>
/// Construct a new release entry.
/// Creat an instance of <see cref="ReleaseEntry"/>.
/// </summary>
public ReleaseEntry(string filename, SemVersion version, long fileSize, bool isDelta, string sha1)
{
@ -28,18 +29,25 @@ namespace Katteker
}
/// <summary>
/// Construct release entry from string.
/// Create an instance of <see cref="ReleaseEntry"/> from string.
/// </summary>
/// <exception cref="FileLoadException"></exception>
/// <exception cref="ArgumentOutOfRangeException">Argument 'line' has not the right format.</exception>
/// <exception cref="ArgumentNullException">Argument 'line' is null.</exception>
/// <exception cref="FileLoadException">Filename is not compliant.</exception>
public ReleaseEntry(string line)
{
var elements = line?.Split(Seperator);
if (elements?.Length != 3) return;
if (line == null) throw new ArgumentNullException(nameof(line));
var elements = line.Split(Seperator);
if (elements.Length != 3)
{
throw new ArgumentOutOfRangeException(nameof(line), elements.Length, "The release entry has not the right format.");
}
SHA1 = elements[0];
Filename = elements[1];
Filesize = long.Parse(elements[2]);
var fileSegments = Regex.Match(Filename, FilenameRegex);
if (fileSegments.Groups.Count < 3) throw new FileLoadException("Filename not compilant.");
if (fileSegments.Groups.Count < 3) throw new FileLoadException("Filename is not compilant.");
ApplicationName = fileSegments.Groups[1].Value;
Version = SemVersion.Parse(fileSegments.Groups[2].Value);
IsDelta = fileSegments.Groups[3].Value != "full";

View File

@ -13,11 +13,11 @@ namespace Katteker
{
private readonly string _filePath;
private SortedList<SemVersion, ReleaseEntry> ReleaseEntries { get; }
private SortedDictionary<SemVersion, ReleaseEntry> ReleaseEntries { get; }
private Releases()
{
ReleaseEntries = new SortedList<SemVersion, ReleaseEntry>();
ReleaseEntries = new SortedDictionary<SemVersion, ReleaseEntry>();
}
/// <summary>
@ -52,7 +52,7 @@ namespace Katteker
/// </summary>
public void WriteReleaseFile()
{
File.WriteAllLines(_filePath, ReleaseEntries.Select(x => x.Value.EntryAsString));
File.WriteAllLines(_filePath, ReleaseEntries.Values.Select(x => x.EntryAsString));
}
/// <summary>
@ -67,16 +67,8 @@ namespace Katteker
var setupFile = new FileInfo(setupFilePath);
var entry = new ReleaseEntry(setupFile.Name, version, setupFile.Length, false, sha1);
if (!ReleaseEntries.ContainsValue(entry))
{
if (ReleaseEntries.ContainsKey(version))
{
ReleaseEntries.Remove(version);
}
ReleaseEntries.Add(version, entry);
}
if (ReleaseEntries.ContainsValue(entry)) return entry;
ReleaseEntries[version] = entry;
return entry;
}

View File

@ -20,12 +20,11 @@ namespace Katteker
private readonly string _applicationName;
private readonly string _packageDir;
private readonly string _rootAppDirectory;
private readonly string _urlOrPath;
private Releases _releases;
private UpdateManager(string urlOrPath, string applicationName, string rootDirectory)
{
_urlOrPath = urlOrPath;
UrlOrPath = urlOrPath;
_applicationName = applicationName;
_rootAppDirectory = rootDirectory;
var packageDir = Path.Combine(rootDirectory, Constants.PackageFolder);
@ -41,7 +40,7 @@ namespace Katteker
/// <summary>
/// Url or path where the update files are located.
/// </summary>
public string UrlOrPath => _urlOrPath;
public string UrlOrPath { get; }
/// <summary>
/// Create the update manager.
@ -50,7 +49,8 @@ namespace Katteker
/// <param name="applicationName">name of the application to update.</param>
/// <param name="rootDirectory">root directory.</param>
/// <returns>the update manager.</returns>
public static UpdateManager Create(string urlOrPath = null, string applicationName = null, string rootDirectory = null)
public static UpdateManager Create(string urlOrPath = null, string applicationName = null,
string rootDirectory = null)
{
_config = ReadConfigFile();
urlOrPath = urlOrPath ?? _config.Publish;
@ -67,7 +67,8 @@ namespace Katteker
/// <param name="applicationName">name of the application to update.</param>
/// <param name="rootDirectory">root directory.</param>
/// <returns>true if the creation success, false otherwise.</returns>
public static bool TryCreate(out UpdateManager manager, string urlOrPath = null, string applicationName = null, string rootDirectory = null)
public static bool TryCreate(out UpdateManager manager, string urlOrPath = null, string applicationName = null,
string rootDirectory = null)
{
try
{
@ -87,7 +88,21 @@ namespace Katteker
/// <returns></returns>
public async Task<IEnumerable<ReleaseEntry>> CheckForUpdateAsync()
{
_releases = Utility.IsWebUrl(_urlOrPath) ? await DownloadIndexAsync(_urlOrPath).ConfigureAwait(false) : GetFromFilesystem(_urlOrPath);
try
{
return await CheckForUpdateImplAsync().ConfigureAwait(false);
}
catch (Exception e)
{
throw new KattekerUpdateException("Error at checking for available update.", e);
}
}
private async Task<IEnumerable<ReleaseEntry>> CheckForUpdateImplAsync()
{
_releases = Utility.IsWebUrl(UrlOrPath)
? await DownloadIndexAsync(UrlOrPath).ConfigureAwait(false)
: GetFromFilesystem(UrlOrPath);
var updateInfo = new UpdateInfo(_applicationName, _releases);
return updateInfo.ReleasesToApply;
}
@ -113,6 +128,8 @@ namespace Katteker
/// <param name="progress">The updating process.</param>
/// <returns></returns>
public async Task<bool> UpdateAppAsync(IProgress<int> progress = null)
{
try
{
progress?.Report(0);
var updateEntries = (await CheckForUpdateAsync().ConfigureAwait(false)).ToArray();
@ -121,6 +138,11 @@ namespace Katteker
progress?.Report(30);
return await UpdateAppImplAsync(update, progress).ConfigureAwait(false);
}
catch (Exception e)
{
throw new KattekerUpdateException("Error at updating application.", e);
}
}
private static async Task<Releases> DownloadIndexAsync(string urlOrPath)
{
@ -151,11 +173,9 @@ namespace Katteker
foreach (var process in Process.GetProcessesByName(_applicationName))
{
var path = Path.GetDirectoryName(process.MainModule.FileName);
if (_rootAppDirectory.Equals(path))
{
if (!_rootAppDirectory.Equals(path)) continue;
process.Kill();
return Task.Delay(500);
}
return Task.Delay(100);
}
return Task.FromResult(true);
@ -165,41 +185,38 @@ namespace Katteker
{
var targetFile = Path.Combine(_packageDir, filename);
File.Delete(targetFile);
if (Utility.IsWebUrl(_urlOrPath))
if (Utility.IsWebUrl(UrlOrPath))
{
var url = _urlOrPath.TrimEnd('/');
var url = UrlOrPath.TrimEnd('/');
url += "/" + filename;
await new WebClient().DownloadFileTaskAsync(new Uri(url), targetFile).ConfigureAwait(false);
}
else
{
File.Copy(Path.Combine(_urlOrPath, filename), targetFile);
File.Copy(Path.Combine(UrlOrPath, filename), targetFile);
}
}
private async Task<bool> UpdateAppImplAsync(ReleaseEntry lastEntry, IProgress<int> progress)
{
if (lastEntry == null) throw new ArgumentNullException(nameof(lastEntry));
var targetFile = Path.Combine(_packageDir, lastEntry.Filename);
//download file.
await PutFileInPackageFolderAsync(lastEntry.Filename).ConfigureAwait(false);
progress?.Report(60);
if (!VerifyFileChecksum(targetFile, lastEntry.SHA1)) throw new FileLoadException();
if (!VerifyFileChecksum(targetFile, lastEntry.SHA1)) throw new FileLoadException("Checksum missmatch.");
progress?.Report(70);
await KillAppStubAsync().ConfigureAwait(false);
progress?.Report(80);
using (var updater = new Process())
using (var updater = Process.Start(targetFile, "/S"))
{
updater.StartInfo = new ProcessStartInfo(targetFile, "/S");
updater.Start();
updater.WaitForExit();
updater?.WaitForExit();
}
progress?.Report(100);
return true;
}
}