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.ComponentModel;
using System.IO; 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="Constants.cs" />
<Compile Include="KattekerConfig.cs" /> <Compile Include="KattekerConfig.cs" />
<Compile Include="Common\MarkdownSharp.cs" /> <Compile Include="Common\MarkdownSharp.cs" />
<Compile Include="KattekerUpdateException.cs" />
<Compile Include="UpdateInfo.cs" /> <Compile Include="UpdateInfo.cs" />
<Compile Include="UpdateManager.cs" /> <Compile Include="UpdateManager.cs" />
<Compile Include="Properties\AssemblyInfo.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 namespace Katteker
{ {
/// <inheritdoc cref="IComparable" />
/// <summary> /// <summary>
/// Entry of a Release. /// Entry of a Release.
/// </summary> /// </summary>
@ -16,7 +17,7 @@ namespace Katteker
private const char Seperator = '|'; private const char Seperator = '|';
/// <summary> /// <summary>
/// Construct a new release entry. /// Creat an instance of <see cref="ReleaseEntry"/>.
/// </summary> /// </summary>
public ReleaseEntry(string filename, SemVersion version, long fileSize, bool isDelta, string sha1) public ReleaseEntry(string filename, SemVersion version, long fileSize, bool isDelta, string sha1)
{ {
@ -28,18 +29,25 @@ namespace Katteker
} }
/// <summary> /// <summary>
/// Construct release entry from string. /// Create an instance of <see cref="ReleaseEntry"/> from string.
/// </summary> /// </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) public ReleaseEntry(string line)
{ {
var elements = line?.Split(Seperator); if (line == null) throw new ArgumentNullException(nameof(line));
if (elements?.Length != 3) return; 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]; SHA1 = elements[0];
Filename = elements[1]; Filename = elements[1];
Filesize = long.Parse(elements[2]); Filesize = long.Parse(elements[2]);
var fileSegments = Regex.Match(Filename, FilenameRegex); 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; ApplicationName = fileSegments.Groups[1].Value;
Version = SemVersion.Parse(fileSegments.Groups[2].Value); Version = SemVersion.Parse(fileSegments.Groups[2].Value);
IsDelta = fileSegments.Groups[3].Value != "full"; IsDelta = fileSegments.Groups[3].Value != "full";

View File

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

View File

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