Replaced SemanticVersion.cs with the unmodified SemVersion.cs from original author.
This commit is contained in:
parent
09d362eac1
commit
64a2c35c0c
@ -40,8 +40,8 @@
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Katteker\SemanticVersion.cs">
|
||||
<Link>SemanticVersion.cs</Link>
|
||||
<Compile Include="..\Katteker\Common\SemVersion.cs">
|
||||
<Link>SemVersion.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
|
@ -5,7 +5,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Windows.Forms;
|
||||
using Katteker;
|
||||
using Semver;
|
||||
|
||||
namespace AppStub
|
||||
{
|
||||
@ -26,13 +26,13 @@ namespace AppStub
|
||||
var files = directory.EnumerateFiles(location.Name, SearchOption.AllDirectories)
|
||||
.Where(x => x.Directory?.Name.StartsWith("app-") == true);
|
||||
|
||||
var entries = new SortedList<SemanticVersion, FileInfo>();
|
||||
var entries = new SortedList<SemVersion, FileInfo>();
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
var version = SemanticVersion.TryParse(file.Directory?.Name.Replace("app-", ""), out var value)
|
||||
var version = SemVersion.TryParse(file.Directory?.Name.Replace("app-", ""), out var value)
|
||||
? value
|
||||
: new SemanticVersion(0);
|
||||
: new SemVersion(0);
|
||||
entries.Add(version, file);
|
||||
}
|
||||
|
||||
@ -58,7 +58,7 @@ namespace AppStub
|
||||
}
|
||||
}
|
||||
|
||||
private static void DeleteOldVersionDirectories(IEnumerable<KeyValuePair<SemanticVersion, FileInfo>> dirEntries)
|
||||
private static void DeleteOldVersionDirectories(IEnumerable<KeyValuePair<SemVersion, FileInfo>> dirEntries)
|
||||
{
|
||||
#if !DEBUG
|
||||
foreach (var directoryInfo in dirEntries)
|
||||
|
@ -1,268 +0,0 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace AppStub
|
||||
{
|
||||
/// <summary>
|
||||
/// A hybrid implementation of SemVer that supports semantic versioning as described at http://semver.org while not
|
||||
/// strictly enforcing it to
|
||||
/// allow older 4-digit versioning schemes to continue working.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
//[TypeConverter(typeof(SemanticVersionTypeConverter))]
|
||||
public sealed class SemanticVersion : IComparable, IComparable<SemanticVersion>, IEquatable<SemanticVersion>
|
||||
{
|
||||
private const RegexOptions Flags =
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture;
|
||||
|
||||
private static readonly Regex SemanticVersionRegex =
|
||||
new Regex(@"^(?<Version>\d+(\s*\.\s*\d+){0,3})(?<Release>-[a-z][0-9a-z-]*)?$", Flags);
|
||||
|
||||
private static readonly Regex StrictSemanticVersionRegex =
|
||||
new Regex(@"^(?<Version>\d+(\.\d+){2})(?<Release>-[a-z][0-9a-z-]*)?$", Flags);
|
||||
|
||||
private readonly string _originalString;
|
||||
|
||||
public SemanticVersion(string version)
|
||||
: this(Parse(version))
|
||||
{
|
||||
// The constructor normalizes the version string so that it we do not need to normalize it every time we need to operate on it.
|
||||
// The original string represents the original form in which the version is represented to be used when printing.
|
||||
_originalString = version;
|
||||
}
|
||||
|
||||
public SemanticVersion(int major, int minor, int build, int revision)
|
||||
: this(new Version(major, minor, build, revision))
|
||||
{
|
||||
}
|
||||
|
||||
public SemanticVersion(int major, int minor, int build, string specialVersion)
|
||||
: this(new Version(major, minor, build), specialVersion)
|
||||
{
|
||||
}
|
||||
|
||||
public SemanticVersion(Version version)
|
||||
: this(version, string.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
public SemanticVersion(Version version, string specialVersion)
|
||||
: this(version, specialVersion, null)
|
||||
{
|
||||
}
|
||||
|
||||
private SemanticVersion(Version version, string specialVersion, string originalString)
|
||||
{
|
||||
if (version == null)
|
||||
throw new ArgumentNullException(nameof(version));
|
||||
Version = NormalizeVersionValue(version);
|
||||
SpecialVersion = specialVersion ?? string.Empty;
|
||||
_originalString = string.IsNullOrEmpty(originalString)
|
||||
? version + (!string.IsNullOrEmpty(specialVersion) ? '-' + specialVersion : null)
|
||||
: originalString;
|
||||
}
|
||||
|
||||
internal SemanticVersion(SemanticVersion semVer)
|
||||
{
|
||||
_originalString = semVer.ToString();
|
||||
Version = semVer.Version;
|
||||
SpecialVersion = semVer.SpecialVersion;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the normalized version portion.
|
||||
/// </summary>
|
||||
public Version Version { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the optional special version.
|
||||
/// </summary>
|
||||
public string SpecialVersion { get; }
|
||||
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
if (ReferenceEquals(obj, null))
|
||||
return 1;
|
||||
var other = obj as SemanticVersion;
|
||||
if (other == null)
|
||||
throw new ArgumentException("Type Must Be A Semantic Version", nameof(obj));
|
||||
return CompareTo(other);
|
||||
}
|
||||
|
||||
public int CompareTo(SemanticVersion other)
|
||||
{
|
||||
if (ReferenceEquals(other, null))
|
||||
return 1;
|
||||
|
||||
var result = Version.CompareTo(other.Version);
|
||||
|
||||
if (result != 0)
|
||||
return result;
|
||||
|
||||
var empty = string.IsNullOrEmpty(SpecialVersion);
|
||||
var otherEmpty = string.IsNullOrEmpty(other.SpecialVersion);
|
||||
if (empty && otherEmpty)
|
||||
return 0;
|
||||
if (empty)
|
||||
return 1;
|
||||
if (otherEmpty)
|
||||
return -1;
|
||||
return StringComparer.OrdinalIgnoreCase.Compare(SpecialVersion, other.SpecialVersion);
|
||||
}
|
||||
|
||||
public bool Equals(SemanticVersion other)
|
||||
{
|
||||
return !ReferenceEquals(null, other) &&
|
||||
Version.Equals(other.Version) &&
|
||||
SpecialVersion.Equals(other.SpecialVersion, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public string[] GetOriginalVersionComponents()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_originalString))
|
||||
{
|
||||
// search the start of the SpecialVersion part, if any
|
||||
var dashIndex = _originalString.IndexOf('-');
|
||||
var original = dashIndex != -1 ? _originalString.Substring(0, dashIndex) : _originalString;
|
||||
|
||||
return SplitAndPadVersionString(original);
|
||||
}
|
||||
return SplitAndPadVersionString(Version.ToString());
|
||||
}
|
||||
|
||||
private static string[] SplitAndPadVersionString(string version)
|
||||
{
|
||||
var a = version.Split('.');
|
||||
if (a.Length == 4)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
// if 'a' has less than 4 elements, we pad the '0' at the end
|
||||
// to make it 4.
|
||||
var b = new string[4] {"0", "0", "0", "0"};
|
||||
Array.Copy(a, 0, b, 0, a.Length);
|
||||
return b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a version string using loose semantic versioning rules that allows 2-4 version components followed by an
|
||||
/// optional special version.
|
||||
/// </summary>
|
||||
public static SemanticVersion Parse(string version)
|
||||
{
|
||||
if (string.IsNullOrEmpty(version))
|
||||
throw new ArgumentException(nameof(version));
|
||||
|
||||
if (!TryParse(version, out var semVer))
|
||||
throw new ArgumentException("Invalid Version String", nameof(version));
|
||||
return semVer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a version string using loose semantic versioning rules that allows 2-4 version components followed by an
|
||||
/// optional special version.
|
||||
/// </summary>
|
||||
public static bool TryParse(string version, out SemanticVersion value)
|
||||
{
|
||||
return TryParseInternal(version, SemanticVersionRegex, out value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a version string using strict semantic versioning rules that allows exactly 3 components and an optional
|
||||
/// special version.
|
||||
/// </summary>
|
||||
public static bool TryParseStrict(string version, out SemanticVersion value)
|
||||
{
|
||||
return TryParseInternal(version, StrictSemanticVersionRegex, out value);
|
||||
}
|
||||
|
||||
private static bool TryParseInternal(string version, Regex regex, out SemanticVersion semVer)
|
||||
{
|
||||
semVer = null;
|
||||
if (string.IsNullOrEmpty(version))
|
||||
return false;
|
||||
|
||||
var match = regex.Match(version.Trim());
|
||||
if (!match.Success || !Version.TryParse(match.Groups["Version"].Value, out var versionValue))
|
||||
return false;
|
||||
|
||||
semVer = new SemanticVersion(NormalizeVersionValue(versionValue),
|
||||
match.Groups["Release"].Value.TrimStart('-'), version.Replace(" ", ""));
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to parse the version token as a SemanticVersion.
|
||||
/// </summary>
|
||||
/// <returns>An instance of SemanticVersion if it parses correctly, null otherwise.</returns>
|
||||
public static SemanticVersion ParseOptionalVersion(string version)
|
||||
{
|
||||
TryParse(version, out var semVer);
|
||||
return semVer;
|
||||
}
|
||||
|
||||
private static Version NormalizeVersionValue(Version version)
|
||||
{
|
||||
return new Version(version.Major,
|
||||
version.Minor,
|
||||
Math.Max(version.Build, 0),
|
||||
Math.Max(version.Revision, 0));
|
||||
}
|
||||
|
||||
public static bool operator ==(SemanticVersion version1, SemanticVersion version2)
|
||||
{
|
||||
if (ReferenceEquals(version1, null))
|
||||
return ReferenceEquals(version2, null);
|
||||
return version1.Equals(version2);
|
||||
}
|
||||
|
||||
public static bool operator !=(SemanticVersion version1, SemanticVersion version2)
|
||||
{
|
||||
return !(version1 == version2);
|
||||
}
|
||||
|
||||
public static bool operator <(SemanticVersion version1, SemanticVersion version2)
|
||||
{
|
||||
if (version1 == null)
|
||||
throw new ArgumentNullException(nameof(version1));
|
||||
return version1.CompareTo(version2) < 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(SemanticVersion version1, SemanticVersion version2)
|
||||
{
|
||||
return version1 == version2 || version1 < version2;
|
||||
}
|
||||
|
||||
public static bool operator >(SemanticVersion version1, SemanticVersion version2)
|
||||
{
|
||||
if (version1 == null)
|
||||
throw new ArgumentNullException(nameof(version1));
|
||||
return version2 < version1;
|
||||
}
|
||||
|
||||
public static bool operator >=(SemanticVersion version1, SemanticVersion version2)
|
||||
{
|
||||
return version1 == version2 || version1 > version2;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return _originalString;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
var semVer = obj as SemanticVersion;
|
||||
return !ReferenceEquals(null, semVer) && Equals(semVer);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hashCode = Version.GetHashCode();
|
||||
if (SpecialVersion != null)
|
||||
hashCode = hashCode * 4567 + SpecialVersion.GetHashCode();
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,268 +0,0 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Katteker.AppStub
|
||||
{
|
||||
/// <summary>
|
||||
/// A hybrid implementation of SemVer that supports semantic versioning as described at http://semver.org while not
|
||||
/// strictly enforcing it to
|
||||
/// allow older 4-digit versioning schemes to continue working.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
//[TypeConverter(typeof(SemanticVersionTypeConverter))]
|
||||
public sealed class SemanticVersion : IComparable, IComparable<SemanticVersion>, IEquatable<SemanticVersion>
|
||||
{
|
||||
private const RegexOptions Flags =
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture;
|
||||
|
||||
private static readonly Regex SemanticVersionRegex =
|
||||
new Regex(@"^(?<Version>\d+(\s*\.\s*\d+){0,3})(?<Release>-[a-z][0-9a-z-]*)?$", Flags);
|
||||
|
||||
private static readonly Regex StrictSemanticVersionRegex =
|
||||
new Regex(@"^(?<Version>\d+(\.\d+){2})(?<Release>-[a-z][0-9a-z-]*)?$", Flags);
|
||||
|
||||
private readonly string _originalString;
|
||||
|
||||
public SemanticVersion(string version)
|
||||
: this(Parse(version))
|
||||
{
|
||||
// The constructor normalizes the version string so that it we do not need to normalize it every time we need to operate on it.
|
||||
// The original string represents the original form in which the version is represented to be used when printing.
|
||||
_originalString = version;
|
||||
}
|
||||
|
||||
public SemanticVersion(int major, int minor, int build, int revision)
|
||||
: this(new Version(major, minor, build, revision))
|
||||
{
|
||||
}
|
||||
|
||||
public SemanticVersion(int major, int minor, int build, string specialVersion)
|
||||
: this(new Version(major, minor, build), specialVersion)
|
||||
{
|
||||
}
|
||||
|
||||
public SemanticVersion(Version version)
|
||||
: this(version, string.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
public SemanticVersion(Version version, string specialVersion)
|
||||
: this(version, specialVersion, null)
|
||||
{
|
||||
}
|
||||
|
||||
private SemanticVersion(Version version, string specialVersion, string originalString)
|
||||
{
|
||||
if (version == null)
|
||||
throw new ArgumentNullException(nameof(version));
|
||||
Version = NormalizeVersionValue(version);
|
||||
SpecialVersion = specialVersion ?? string.Empty;
|
||||
_originalString = string.IsNullOrEmpty(originalString)
|
||||
? version + (!string.IsNullOrEmpty(specialVersion) ? '-' + specialVersion : null)
|
||||
: originalString;
|
||||
}
|
||||
|
||||
internal SemanticVersion(SemanticVersion semVer)
|
||||
{
|
||||
_originalString = semVer.ToString();
|
||||
Version = semVer.Version;
|
||||
SpecialVersion = semVer.SpecialVersion;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the normalized version portion.
|
||||
/// </summary>
|
||||
public Version Version { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the optional special version.
|
||||
/// </summary>
|
||||
public string SpecialVersion { get; }
|
||||
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
if (ReferenceEquals(obj, null))
|
||||
return 1;
|
||||
var other = obj as SemanticVersion;
|
||||
if (other == null)
|
||||
throw new ArgumentException("Type Must Be A Semantic Version", nameof(obj));
|
||||
return CompareTo(other);
|
||||
}
|
||||
|
||||
public int CompareTo(SemanticVersion other)
|
||||
{
|
||||
if (ReferenceEquals(other, null))
|
||||
return 1;
|
||||
|
||||
var result = Version.CompareTo(other.Version);
|
||||
|
||||
if (result != 0)
|
||||
return result;
|
||||
|
||||
var empty = string.IsNullOrEmpty(SpecialVersion);
|
||||
var otherEmpty = string.IsNullOrEmpty(other.SpecialVersion);
|
||||
if (empty && otherEmpty)
|
||||
return 0;
|
||||
if (empty)
|
||||
return 1;
|
||||
if (otherEmpty)
|
||||
return -1;
|
||||
return StringComparer.OrdinalIgnoreCase.Compare(SpecialVersion, other.SpecialVersion);
|
||||
}
|
||||
|
||||
public bool Equals(SemanticVersion other)
|
||||
{
|
||||
return !ReferenceEquals(null, other) &&
|
||||
Version.Equals(other.Version) &&
|
||||
SpecialVersion.Equals(other.SpecialVersion, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public string[] GetOriginalVersionComponents()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_originalString))
|
||||
{
|
||||
// search the start of the SpecialVersion part, if any
|
||||
var dashIndex = _originalString.IndexOf('-');
|
||||
var original = dashIndex != -1 ? _originalString.Substring(0, dashIndex) : _originalString;
|
||||
|
||||
return SplitAndPadVersionString(original);
|
||||
}
|
||||
return SplitAndPadVersionString(Version.ToString());
|
||||
}
|
||||
|
||||
private static string[] SplitAndPadVersionString(string version)
|
||||
{
|
||||
var a = version.Split('.');
|
||||
if (a.Length == 4)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
// if 'a' has less than 4 elements, we pad the '0' at the end
|
||||
// to make it 4.
|
||||
var b = new string[4] {"0", "0", "0", "0"};
|
||||
Array.Copy(a, 0, b, 0, a.Length);
|
||||
return b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a version string using loose semantic versioning rules that allows 2-4 version components followed by an
|
||||
/// optional special version.
|
||||
/// </summary>
|
||||
public static SemanticVersion Parse(string version)
|
||||
{
|
||||
if (string.IsNullOrEmpty(version))
|
||||
throw new ArgumentException(nameof(version));
|
||||
|
||||
if (!TryParse(version, out var semVer))
|
||||
throw new ArgumentException("Invalid Version String", nameof(version));
|
||||
return semVer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a version string using loose semantic versioning rules that allows 2-4 version components followed by an
|
||||
/// optional special version.
|
||||
/// </summary>
|
||||
public static bool TryParse(string version, out SemanticVersion value)
|
||||
{
|
||||
return TryParseInternal(version, SemanticVersionRegex, out value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a version string using strict semantic versioning rules that allows exactly 3 components and an optional
|
||||
/// special version.
|
||||
/// </summary>
|
||||
public static bool TryParseStrict(string version, out SemanticVersion value)
|
||||
{
|
||||
return TryParseInternal(version, StrictSemanticVersionRegex, out value);
|
||||
}
|
||||
|
||||
private static bool TryParseInternal(string version, Regex regex, out SemanticVersion semVer)
|
||||
{
|
||||
semVer = null;
|
||||
if (string.IsNullOrEmpty(version))
|
||||
return false;
|
||||
|
||||
var match = regex.Match(version.Trim());
|
||||
if (!match.Success || !Version.TryParse(match.Groups["Version"].Value, out var versionValue))
|
||||
return false;
|
||||
|
||||
semVer = new SemanticVersion(NormalizeVersionValue(versionValue),
|
||||
match.Groups["Release"].Value.TrimStart('-'), version.Replace(" ", ""));
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to parse the version token as a SemanticVersion.
|
||||
/// </summary>
|
||||
/// <returns>An instance of SemanticVersion if it parses correctly, null otherwise.</returns>
|
||||
public static SemanticVersion ParseOptionalVersion(string version)
|
||||
{
|
||||
TryParse(version, out var semVer);
|
||||
return semVer;
|
||||
}
|
||||
|
||||
private static Version NormalizeVersionValue(Version version)
|
||||
{
|
||||
return new Version(version.Major,
|
||||
version.Minor,
|
||||
Math.Max(version.Build, 0),
|
||||
Math.Max(version.Revision, 0));
|
||||
}
|
||||
|
||||
public static bool operator ==(SemanticVersion version1, SemanticVersion version2)
|
||||
{
|
||||
if (ReferenceEquals(version1, null))
|
||||
return ReferenceEquals(version2, null);
|
||||
return version1.Equals(version2);
|
||||
}
|
||||
|
||||
public static bool operator !=(SemanticVersion version1, SemanticVersion version2)
|
||||
{
|
||||
return !(version1 == version2);
|
||||
}
|
||||
|
||||
public static bool operator <(SemanticVersion version1, SemanticVersion version2)
|
||||
{
|
||||
if (version1 == null)
|
||||
throw new ArgumentNullException(nameof(version1));
|
||||
return version1.CompareTo(version2) < 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(SemanticVersion version1, SemanticVersion version2)
|
||||
{
|
||||
return version1 == version2 || version1 < version2;
|
||||
}
|
||||
|
||||
public static bool operator >(SemanticVersion version1, SemanticVersion version2)
|
||||
{
|
||||
if (version1 == null)
|
||||
throw new ArgumentNullException(nameof(version1));
|
||||
return version2 < version1;
|
||||
}
|
||||
|
||||
public static bool operator >=(SemanticVersion version1, SemanticVersion version2)
|
||||
{
|
||||
return version1 == version2 || version1 > version2;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return _originalString;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
var semVer = obj as SemanticVersion;
|
||||
return !ReferenceEquals(null, semVer) && Equals(semVer);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hashCode = Version.GetHashCode();
|
||||
if (SpecialVersion != null)
|
||||
hashCode = hashCode * 4567 + SpecialVersion.GetHashCode();
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Katteker;
|
||||
using Semver;
|
||||
using TsudaKageyu;
|
||||
using Vestris.ResourceLib;
|
||||
|
||||
@ -42,7 +43,7 @@ namespace KattekerCreator
|
||||
|
||||
public string AssemblyIconPath { get; }
|
||||
|
||||
public SemanticVersion AssemblyVersion { get; private set; }
|
||||
public SemVersion AssemblyVersion { get; private set; }
|
||||
|
||||
public string Company => _company ?? string.Empty;
|
||||
|
||||
@ -82,13 +83,13 @@ namespace KattekerCreator
|
||||
return applicationIcon;
|
||||
}
|
||||
|
||||
private static SemanticVersion GetSemanticVersion(string productVersion)
|
||||
private static SemVersion GetSemanticVersion(string productVersion)
|
||||
{
|
||||
productVersion = productVersion.Replace(',', '.');
|
||||
if (SemanticVersion.TryParse(productVersion, out var semanticVersion))
|
||||
if (SemVersion.TryParse(productVersion, out var semanticVersion))
|
||||
return semanticVersion?.Change(build: string.Empty);
|
||||
Version.TryParse(productVersion, out var version);
|
||||
return SemanticVersion.Parse(version.ToString(3));
|
||||
return SemVersion.Parse(version.ToString(3));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,270 +0,0 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace KattekerCreator
|
||||
{
|
||||
/// <summary>
|
||||
/// A hybrid implementation of SemVer that supports semantic versioning as described at http://semver.org while not
|
||||
/// strictly enforcing it to
|
||||
/// allow older 4-digit versioning schemes to continue working.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
//[TypeConverter(typeof(SemanticVersionTypeConverter))]
|
||||
public sealed class SemanticVersion : IComparable, IComparable<SemanticVersion>, IEquatable<SemanticVersion>
|
||||
{
|
||||
private const RegexOptions Flags =
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture;
|
||||
|
||||
private static readonly Regex SemanticVersionRegex =
|
||||
new Regex(@"^(?<Version>\d+(\s*\.\s*\d+){0,3})(?<Release>-[a-z][0-9a-z-]*)?$", Flags);
|
||||
|
||||
private static readonly Regex StrictSemanticVersionRegex =
|
||||
new Regex(@"^(?<Version>\d+(\.\d+){2})(?<Release>-[a-z][0-9a-z-]*)?$", Flags);
|
||||
|
||||
private readonly string _originalString;
|
||||
|
||||
public SemanticVersion(string version)
|
||||
: this(Parse(version))
|
||||
{
|
||||
// The constructor normalizes the version string so that it we do not need to normalize it every time we need to operate on it.
|
||||
// The original string represents the original form in which the version is represented to be used when printing.
|
||||
_originalString = version;
|
||||
}
|
||||
|
||||
public SemanticVersion(int major, int minor, int build, int revision)
|
||||
: this(new Version(major, minor, build, revision))
|
||||
{
|
||||
}
|
||||
|
||||
public SemanticVersion(int major, int minor, int build, string specialVersion)
|
||||
: this(new Version(major, minor, build), specialVersion)
|
||||
{
|
||||
}
|
||||
|
||||
public SemanticVersion(Version version)
|
||||
: this(version, string.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
public SemanticVersion(Version version, string specialVersion)
|
||||
: this(version, specialVersion, null)
|
||||
{
|
||||
}
|
||||
|
||||
private SemanticVersion(Version version, string specialVersion, string originalString)
|
||||
{
|
||||
if (version == null)
|
||||
throw new ArgumentNullException(nameof(version));
|
||||
Version = NormalizeVersionValue(version);
|
||||
SpecialVersion = specialVersion ?? string.Empty;
|
||||
_originalString = string.IsNullOrEmpty(originalString)
|
||||
? version + (!string.IsNullOrEmpty(specialVersion) ? '-' + specialVersion : null)
|
||||
: originalString;
|
||||
}
|
||||
|
||||
internal SemanticVersion(SemanticVersion semVer)
|
||||
{
|
||||
_originalString = semVer.ToString();
|
||||
Version = semVer.Version;
|
||||
SpecialVersion = semVer.SpecialVersion;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the normalized version portion.
|
||||
/// </summary>
|
||||
public Version Version { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the optional special version.
|
||||
/// </summary>
|
||||
public string SpecialVersion { get; }
|
||||
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
if (ReferenceEquals(obj, null))
|
||||
return 1;
|
||||
var other = obj as SemanticVersion;
|
||||
if (other == null)
|
||||
throw new ArgumentException("Type Must Be A Semantic Version", nameof(obj));
|
||||
return CompareTo(other);
|
||||
}
|
||||
|
||||
public int CompareTo(SemanticVersion other)
|
||||
{
|
||||
if (ReferenceEquals(other, null))
|
||||
return 1;
|
||||
|
||||
var result = Version.CompareTo(other.Version);
|
||||
|
||||
if (result != 0)
|
||||
return result;
|
||||
|
||||
var empty = string.IsNullOrEmpty(SpecialVersion);
|
||||
var otherEmpty = string.IsNullOrEmpty(other.SpecialVersion);
|
||||
if (empty && otherEmpty)
|
||||
return 0;
|
||||
if (empty)
|
||||
return 1;
|
||||
if (otherEmpty)
|
||||
return -1;
|
||||
return StringComparer.OrdinalIgnoreCase.Compare(SpecialVersion, other.SpecialVersion);
|
||||
}
|
||||
|
||||
public bool Equals(SemanticVersion other)
|
||||
{
|
||||
return !ReferenceEquals(null, other) &&
|
||||
Version.Equals(other.Version) &&
|
||||
SpecialVersion.Equals(other.SpecialVersion, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public string[] GetOriginalVersionComponents()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_originalString))
|
||||
{
|
||||
// search the start of the SpecialVersion part, if any
|
||||
var dashIndex = _originalString.IndexOf('-');
|
||||
var original = dashIndex != -1 ? _originalString.Substring(0, dashIndex) : _originalString;
|
||||
|
||||
return SplitAndPadVersionString(original);
|
||||
}
|
||||
|
||||
return SplitAndPadVersionString(Version.ToString());
|
||||
}
|
||||
|
||||
private static string[] SplitAndPadVersionString(string version)
|
||||
{
|
||||
var a = version.Split('.');
|
||||
if (a.Length == 4)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
// if 'a' has less than 4 elements, we pad the '0' at the end
|
||||
// to make it 4.
|
||||
var b = new string[4] {"0", "0", "0", "0"};
|
||||
Array.Copy(a, 0, b, 0, a.Length);
|
||||
return b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a version string using loose semantic versioning rules that allows 2-4 version components followed by an
|
||||
/// optional special version.
|
||||
/// </summary>
|
||||
public static SemanticVersion Parse(string version)
|
||||
{
|
||||
if (string.IsNullOrEmpty(version))
|
||||
throw new ArgumentException(nameof(version));
|
||||
|
||||
if (!TryParse(version, out var semVer))
|
||||
throw new ArgumentException("Invalid Version String", nameof(version));
|
||||
return semVer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a version string using loose semantic versioning rules that allows 2-4 version components followed by an
|
||||
/// optional special version.
|
||||
/// </summary>
|
||||
public static bool TryParse(string version, out SemanticVersion value)
|
||||
{
|
||||
return TryParseInternal(version, SemanticVersionRegex, out value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a version string using strict semantic versioning rules that allows exactly 3 components and an optional
|
||||
/// special version.
|
||||
/// </summary>
|
||||
public static bool TryParseStrict(string version, out SemanticVersion value)
|
||||
{
|
||||
return TryParseInternal(version, StrictSemanticVersionRegex, out value);
|
||||
}
|
||||
|
||||
private static bool TryParseInternal(string version, Regex regex, out SemanticVersion semVer)
|
||||
{
|
||||
semVer = null;
|
||||
if (string.IsNullOrEmpty(version))
|
||||
return false;
|
||||
|
||||
var match = regex.Match(version.Trim());
|
||||
if (!match.Success || !Version.TryParse(match.Groups["Version"].Value, out var versionValue))
|
||||
return false;
|
||||
|
||||
semVer = new SemanticVersion(NormalizeVersionValue(versionValue),
|
||||
match.Groups["Release"].Value.TrimStart('-'), version.Replace(" ", ""));
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to parse the version token as a SemanticVersion.
|
||||
/// </summary>
|
||||
/// <returns>An instance of SemanticVersion if it parses correctly, null otherwise.</returns>
|
||||
public static SemanticVersion ParseOptionalVersion(string version)
|
||||
{
|
||||
TryParse(version, out var semVer);
|
||||
return semVer;
|
||||
}
|
||||
|
||||
private static Version NormalizeVersionValue(Version version)
|
||||
{
|
||||
return new Version(version.Major,
|
||||
version.Minor,
|
||||
Math.Max(version.Build, 0),
|
||||
Math.Max(version.Revision, 0));
|
||||
}
|
||||
|
||||
public static bool operator ==(SemanticVersion version1, SemanticVersion version2)
|
||||
{
|
||||
if (ReferenceEquals(version1, null))
|
||||
return ReferenceEquals(version2, null);
|
||||
return version1.Equals(version2);
|
||||
}
|
||||
|
||||
public static bool operator !=(SemanticVersion version1, SemanticVersion version2)
|
||||
{
|
||||
return !(version1 == version2);
|
||||
}
|
||||
|
||||
public static bool operator <(SemanticVersion version1, SemanticVersion version2)
|
||||
{
|
||||
if (version1 == null)
|
||||
throw new ArgumentNullException(nameof(version1));
|
||||
return version1.CompareTo(version2) < 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(SemanticVersion version1, SemanticVersion version2)
|
||||
{
|
||||
return version1 == version2 || version1 < version2;
|
||||
}
|
||||
|
||||
public static bool operator >(SemanticVersion version1, SemanticVersion version2)
|
||||
{
|
||||
if (version1 == null)
|
||||
throw new ArgumentNullException(nameof(version1));
|
||||
return version2 < version1;
|
||||
}
|
||||
|
||||
public static bool operator >=(SemanticVersion version1, SemanticVersion version2)
|
||||
{
|
||||
return version1 == version2 || version1 > version2;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return _originalString;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
var semVer = obj as SemanticVersion;
|
||||
return !ReferenceEquals(null, semVer) && Equals(semVer);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hashCode = Version.GetHashCode();
|
||||
if (SpecialVersion != null)
|
||||
hashCode = hashCode * 4567 + SpecialVersion.GetHashCode();
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Katteker;
|
||||
using Semver;
|
||||
|
||||
namespace KattekerCreator
|
||||
{
|
||||
@ -16,7 +17,7 @@ namespace KattekerCreator
|
||||
public string Executable { get; set; }
|
||||
public string CompanyName { get; set; }
|
||||
public string Description { get; set; }
|
||||
public SemanticVersion Version { get; set; }
|
||||
public SemVersion Version { get; set; }
|
||||
public Version LegacyVersion => Version.ToSystemVersion();
|
||||
public string HelpUrl { get; set; }
|
||||
public long InstallSize { get; set; }
|
||||
|
61
Katteker.Gui/ChangelogHelper.cs
Normal file
61
Katteker.Gui/ChangelogHelper.cs
Normal file
@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Katteker.Gui
|
||||
{
|
||||
internal static class ChangelogHelper
|
||||
{
|
||||
private static string GenerateHtmlifyChangelog(string text, string extension)
|
||||
{
|
||||
string result;
|
||||
switch (extension)
|
||||
{
|
||||
case ".txt":
|
||||
var plainText = WebUtility.HtmlEncode(text);
|
||||
result = plainText.Replace(Environment.NewLine, "<br />");
|
||||
break;
|
||||
case ".md":
|
||||
result = CommonMark.CommonMarkConverter.Convert(text);
|
||||
break;
|
||||
default:
|
||||
result = text;
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static async Task<string> LoadChangelogAsync(string filename, string path)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(filename) || !string.IsNullOrEmpty(path))
|
||||
{
|
||||
if (!path.EndsWith("/", StringComparison.Ordinal))
|
||||
path += "/";
|
||||
var webReq = WebRequest.Create(path + filename);
|
||||
try
|
||||
{
|
||||
using (var response = await webReq.GetResponseAsync().ConfigureAwait(false))
|
||||
using (var sr = new StreamReader(response.GetResponseStream()))
|
||||
{
|
||||
return GenerateHtmlifyChangelog(await sr.ReadToEndAsync().ConfigureAwait(false),
|
||||
Path.GetExtension(filename));
|
||||
}
|
||||
}
|
||||
catch (WebException)
|
||||
{
|
||||
var changelogFilename = Path.GetFileName(filename);
|
||||
if (changelogFilename == null) return GenerateHtmlifyChangelog("Changelog not found", ".txt");
|
||||
var currentChangelogPath = Path.Combine(Environment.CurrentDirectory, changelogFilename);
|
||||
if (File.Exists(currentChangelogPath))
|
||||
{
|
||||
return GenerateHtmlifyChangelog(File.ReadAllText(currentChangelogPath), Path.GetExtension(filename));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return GenerateHtmlifyChangelog("Changelog not found", ".txt");
|
||||
}
|
||||
}
|
||||
}
|
4
Katteker.Gui/packages.config
Normal file
4
Katteker.Gui/packages.config
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="CommonMark.NET" version="0.15.1" targetFramework="net45" />
|
||||
</packages>
|
1779
Katteker/Common/MarkdownSharp.cs
Normal file
1779
Katteker/Common/MarkdownSharp.cs
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,85 +1,100 @@
|
||||
using System;
|
||||
#if !NETSTANDARD
|
||||
using System.Globalization;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Security.Permissions;
|
||||
#endif
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Katteker
|
||||
namespace Semver
|
||||
{
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
/// <summary>
|
||||
/// A semantic version implementation.
|
||||
/// Conforms to v2.0.0 of http://semver.org/
|
||||
/// </summary>
|
||||
#if NETSTANDARD
|
||||
public sealed class SemVersion : IComparable<SemVersion>, IComparable
|
||||
#else
|
||||
[Serializable]
|
||||
public sealed class SemanticVersion : IComparable<SemanticVersion>, IComparable, ISerializable
|
||||
public sealed class SemVersion : IComparable<SemVersion>, IComparable, ISerializable
|
||||
#endif
|
||||
{
|
||||
private static readonly Regex parseEx =
|
||||
new Regex(@"^(?<major>\d+)(\.(?<minor>\d+))?(\.(?<patch>\d+))?(\-(?<pre>[0-9A-Za-z\-\.]+))?(\+(?<build>[0-9A-Za-z\-\.]+))?$",
|
||||
static Regex parseEx =
|
||||
new Regex(@"^(?<major>\d+)" +
|
||||
@"(\.(?<minor>\d+))?" +
|
||||
@"(\.(?<patch>\d+))?" +
|
||||
@"(\-(?<pre>[0-9A-Za-z\-\.]+))?" +
|
||||
@"(\+(?<build>[0-9A-Za-z\-\.]+))?$",
|
||||
#if NETSTANDARD
|
||||
RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture);
|
||||
#else
|
||||
RegexOptions.CultureInvariant | RegexOptions.Compiled | RegexOptions.ExplicitCapture);
|
||||
#endif
|
||||
|
||||
#if !NETSTANDARD
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SemanticVersion" /> class.
|
||||
/// Initializes a new instance of the <see cref="SemVersion" /> class.
|
||||
/// </summary>
|
||||
/// <param name="info"></param>
|
||||
/// <param name="context"></param>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
private SemanticVersion(SerializationInfo info, StreamingContext context)
|
||||
private SemVersion(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
if (info == null) throw new ArgumentNullException(nameof(info));
|
||||
var semVersion = Parse(info.GetString("SemanticVersion"));
|
||||
if (info == null) throw new ArgumentNullException("info");
|
||||
var semVersion = Parse(info.GetString("SemVersion"));
|
||||
Major = semVersion.Major;
|
||||
Minor = semVersion.Minor;
|
||||
Patch = semVersion.Patch;
|
||||
Prerelease = semVersion.Prerelease;
|
||||
Build = semVersion.Build;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SemanticVersion" /> class.
|
||||
/// Initializes a new instance of the <see cref="SemVersion" /> class.
|
||||
/// </summary>
|
||||
/// <param name="major">The major version.</param>
|
||||
/// <param name="minor">The minor version.</param>
|
||||
/// <param name="patch">The patch version.</param>
|
||||
/// <param name="prerelease">The prerelease version (eg. "alpha").</param>
|
||||
/// <param name="build">The build eg ("nightly.232").</param>
|
||||
public SemanticVersion(int major, int minor = 0, int patch = 0, string prerelease = "", string build = "")
|
||||
public SemVersion(int major, int minor = 0, int patch = 0, string prerelease = "", string build = "")
|
||||
{
|
||||
Major = major;
|
||||
Minor = minor;
|
||||
Patch = patch;
|
||||
this.Major = major;
|
||||
this.Minor = minor;
|
||||
this.Patch = patch;
|
||||
|
||||
Prerelease = prerelease ?? "";
|
||||
Build = build ?? "";
|
||||
this.Prerelease = prerelease ?? "";
|
||||
this.Build = build ?? "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SemanticVersion"/> class.
|
||||
/// Initializes a new instance of the <see cref="SemVersion"/> class.
|
||||
/// </summary>
|
||||
/// <param name="version">The <see cref="System.Version"/> that is used to initialize
|
||||
/// the Major, Minor, Patch and Build properties.</param>
|
||||
public SemanticVersion(Version version)
|
||||
public SemVersion(Version version)
|
||||
{
|
||||
if (version == null)
|
||||
throw new ArgumentNullException(nameof(version));
|
||||
throw new ArgumentNullException("version");
|
||||
|
||||
Major = version.Major;
|
||||
Minor = version.Minor;
|
||||
this.Major = version.Major;
|
||||
this.Minor = version.Minor;
|
||||
|
||||
if (version.Revision >= 0)
|
||||
{
|
||||
Patch = version.Revision;
|
||||
this.Patch = version.Revision;
|
||||
}
|
||||
|
||||
Prerelease = string.Empty;
|
||||
this.Prerelease = String.Empty;
|
||||
|
||||
if (version.Build > 0)
|
||||
{
|
||||
Build = version.Build.ToString();
|
||||
this.Build = version.Build.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
Build = string.Empty;
|
||||
this.Build = String.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
@ -88,21 +103,29 @@ namespace Katteker
|
||||
/// </summary>
|
||||
/// <param name="version">The version string.</param>
|
||||
/// <param name="strict">If set to <c>true</c> minor and patch version are required, else they default to 0.</param>
|
||||
/// <returns>The SemanticVersion object.</returns>
|
||||
/// <returns>The SemVersion object.</returns>
|
||||
/// <exception cref="System.InvalidOperationException">When a invalid version string is passed.</exception>
|
||||
public static SemanticVersion Parse(string version, bool strict = false)
|
||||
public static SemVersion Parse(string version, bool strict = false)
|
||||
{
|
||||
var match = parseEx.Match(version);
|
||||
if (!match.Success)
|
||||
throw new ArgumentException("Invalid version.", nameof(version));
|
||||
throw new ArgumentException("Invalid version.", "version");
|
||||
|
||||
#if NETSTANDARD
|
||||
var major = int.Parse(match.Groups["major"].Value);
|
||||
#else
|
||||
var major = int.Parse(match.Groups["major"].Value, CultureInfo.InvariantCulture);
|
||||
#endif
|
||||
|
||||
var minorMatch = match.Groups["minor"];
|
||||
var minor = 0;
|
||||
int minor = 0;
|
||||
if (minorMatch.Success)
|
||||
{
|
||||
#if NETSTANDARD
|
||||
minor = int.Parse(minorMatch.Value);
|
||||
#else
|
||||
minor = int.Parse(minorMatch.Value, CultureInfo.InvariantCulture);
|
||||
#endif
|
||||
}
|
||||
else if (strict)
|
||||
{
|
||||
@ -110,10 +133,14 @@ namespace Katteker
|
||||
}
|
||||
|
||||
var patchMatch = match.Groups["patch"];
|
||||
var patch = 0;
|
||||
int patch = 0;
|
||||
if (patchMatch.Success)
|
||||
{
|
||||
#if NETSTANDARD
|
||||
patch = int.Parse(patchMatch.Value);
|
||||
#else
|
||||
patch = int.Parse(patchMatch.Value, CultureInfo.InvariantCulture);
|
||||
#endif
|
||||
}
|
||||
else if (strict)
|
||||
{
|
||||
@ -123,19 +150,19 @@ namespace Katteker
|
||||
var prerelease = match.Groups["pre"].Value;
|
||||
var build = match.Groups["build"].Value;
|
||||
|
||||
return new SemanticVersion(major, minor, patch, prerelease, build);
|
||||
return new SemVersion(major, minor, patch, prerelease, build);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the specified string to a semantic version.
|
||||
/// </summary>
|
||||
/// <param name="version">The version string.</param>
|
||||
/// <param name="semver">When the method returns, contains a SemanticVersion instance equivalent
|
||||
/// <param name="semver">When the method returns, contains a SemVersion instance equivalent
|
||||
/// to the version string passed in, if the version string was valid, or <c>null</c> if the
|
||||
/// version string was not valid.</param>
|
||||
/// <param name="strict">If set to <c>true</c> minor and patch version are required, else they default to 0.</param>
|
||||
/// <returns><c>False</c> when a invalid version string is passed, otherwise <c>true</c>.</returns>
|
||||
public static bool TryParse(string version, out SemanticVersion semver, bool strict = false)
|
||||
public static bool TryParse(string version, out SemVersion semver, bool strict = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -155,7 +182,7 @@ namespace Katteker
|
||||
/// <param name="versionA">The first version.</param>
|
||||
/// <param name="versionB">The second version.</param>
|
||||
/// <returns>If versionA is equal to versionB <c>true</c>, else <c>false</c>.</returns>
|
||||
public static bool Equals(SemanticVersion versionA, SemanticVersion versionB)
|
||||
public static bool Equals(SemVersion versionA, SemVersion versionB)
|
||||
{
|
||||
if (ReferenceEquals(versionA, null))
|
||||
return ReferenceEquals(versionB, null);
|
||||
@ -169,7 +196,7 @@ namespace Katteker
|
||||
/// <param name="versionB">The version to compare against.</param>
|
||||
/// <returns>If versionA < versionB <c>< 0</c>, if versionA > versionB <c>> 0</c>,
|
||||
/// if versionA is equal to versionB <c>0</c>.</returns>
|
||||
public static int Compare(SemanticVersion versionA, SemanticVersion versionB)
|
||||
public static int Compare(SemVersion versionA, SemVersion versionB)
|
||||
{
|
||||
if (ReferenceEquals(versionA, null))
|
||||
return ReferenceEquals(versionB, null) ? 0 : -1;
|
||||
@ -185,15 +212,15 @@ namespace Katteker
|
||||
/// <param name="prerelease">The prerelease text.</param>
|
||||
/// <param name="build">The build text.</param>
|
||||
/// <returns>The new version object.</returns>
|
||||
public SemanticVersion Change(int? major = null, int? minor = null, int? patch = null,
|
||||
public SemVersion Change(int? major = null, int? minor = null, int? patch = null,
|
||||
string prerelease = null, string build = null)
|
||||
{
|
||||
return new SemanticVersion(
|
||||
major ?? Major,
|
||||
minor ?? Minor,
|
||||
patch ?? Patch,
|
||||
prerelease ?? Prerelease,
|
||||
build ?? Build);
|
||||
return new SemVersion(
|
||||
major ?? this.Major,
|
||||
minor ?? this.Minor,
|
||||
patch ?? this.Patch,
|
||||
prerelease ?? this.Prerelease,
|
||||
build ?? this.Build);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -245,9 +272,9 @@ namespace Katteker
|
||||
public override string ToString()
|
||||
{
|
||||
var version = "" + Major + "." + Minor + "." + Patch;
|
||||
if (!string.IsNullOrEmpty(Prerelease))
|
||||
if (!String.IsNullOrEmpty(Prerelease))
|
||||
version += "-" + Prerelease;
|
||||
if (!string.IsNullOrEmpty(Build))
|
||||
if (!String.IsNullOrEmpty(Build))
|
||||
version += "+" + Build;
|
||||
return version;
|
||||
}
|
||||
@ -267,7 +294,7 @@ namespace Katteker
|
||||
/// </returns>
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
return CompareTo((SemanticVersion)obj);
|
||||
return CompareTo((SemVersion)obj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -283,16 +310,16 @@ namespace Katteker
|
||||
/// Zero This instance occurs in the same position in the sort order as <paramref name="other" />. i
|
||||
/// Greater than zero This instance follows <paramref name="other" /> in the sort order.
|
||||
/// </returns>
|
||||
public int CompareTo(SemanticVersion other)
|
||||
public int CompareTo(SemVersion other)
|
||||
{
|
||||
if (ReferenceEquals(other, null))
|
||||
return 1;
|
||||
|
||||
var r = CompareByPrecedence(other);
|
||||
var r = this.CompareByPrecedence(other);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
r = CompareComponent(Build, other.Build);
|
||||
r = CompareComponent(this.Build, other.Build);
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -301,7 +328,7 @@ namespace Katteker
|
||||
/// </summary>
|
||||
/// <param name="other">The semantic version.</param>
|
||||
/// <returns><c>true</c> if the version precedence matches.</returns>
|
||||
public bool PrecedenceMatches(SemanticVersion other)
|
||||
public bool PrecedenceMatches(SemVersion other)
|
||||
{
|
||||
return CompareByPrecedence(other) == 0;
|
||||
}
|
||||
@ -317,28 +344,28 @@ namespace Katteker
|
||||
/// Zero This instance has the same precedence as <paramref name="other" />. i
|
||||
/// Greater than zero This instance has creater precedence as <paramref name="other" />.
|
||||
/// </returns>
|
||||
public int CompareByPrecedence(SemanticVersion other)
|
||||
public int CompareByPrecedence(SemVersion other)
|
||||
{
|
||||
if (ReferenceEquals(other, null))
|
||||
return 1;
|
||||
|
||||
var r = Major.CompareTo(other.Major);
|
||||
var r = this.Major.CompareTo(other.Major);
|
||||
if (r != 0) return r;
|
||||
|
||||
r = Minor.CompareTo(other.Minor);
|
||||
r = this.Minor.CompareTo(other.Minor);
|
||||
if (r != 0) return r;
|
||||
|
||||
r = Patch.CompareTo(other.Patch);
|
||||
r = this.Patch.CompareTo(other.Patch);
|
||||
if (r != 0) return r;
|
||||
|
||||
r = CompareComponent(Prerelease, other.Prerelease, true);
|
||||
r = CompareComponent(this.Prerelease, other.Prerelease, true);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int CompareComponent(string a, string b, bool lower = false)
|
||||
{
|
||||
var aEmpty = string.IsNullOrEmpty(a);
|
||||
var bEmpty = string.IsNullOrEmpty(b);
|
||||
var aEmpty = String.IsNullOrEmpty(a);
|
||||
var bEmpty = String.IsNullOrEmpty(b);
|
||||
if (aEmpty && bEmpty)
|
||||
return 0;
|
||||
|
||||
@ -351,13 +378,13 @@ namespace Katteker
|
||||
var bComps = b.Split('.');
|
||||
|
||||
var minLen = Math.Min(aComps.Length, bComps.Length);
|
||||
for (var i = 0; i < minLen; i++)
|
||||
for (int i = 0; i < minLen; i++)
|
||||
{
|
||||
var ac = aComps[i];
|
||||
var bc = bComps[i];
|
||||
int anum, bnum;
|
||||
var isanum = int.TryParse(ac, out anum);
|
||||
var isbnum = int.TryParse(bc, out bnum);
|
||||
var isanum = Int32.TryParse(ac, out anum);
|
||||
var isbnum = Int32.TryParse(bc, out bnum);
|
||||
int r;
|
||||
if (isanum && isbnum)
|
||||
{
|
||||
@ -370,7 +397,7 @@ namespace Katteker
|
||||
return -1;
|
||||
if (isbnum)
|
||||
return 1;
|
||||
r = string.CompareOrdinal(ac, bc);
|
||||
r = String.CompareOrdinal(ac, bc);
|
||||
if (r != 0)
|
||||
return r;
|
||||
}
|
||||
@ -394,13 +421,13 @@ namespace Katteker
|
||||
if (ReferenceEquals(this, obj))
|
||||
return true;
|
||||
|
||||
var other = (SemanticVersion)obj;
|
||||
var other = (SemVersion)obj;
|
||||
|
||||
return Major == other.Major &&
|
||||
Minor == other.Minor &&
|
||||
Patch == other.Patch &&
|
||||
string.Equals(Prerelease, other.Prerelease, StringComparison.Ordinal) &&
|
||||
string.Equals(Build, other.Build, StringComparison.Ordinal);
|
||||
return this.Major == other.Major &&
|
||||
this.Minor == other.Minor &&
|
||||
this.Patch == other.Patch &&
|
||||
string.Equals(this.Prerelease, other.Prerelease, StringComparison.Ordinal) &&
|
||||
string.Equals(this.Build, other.Build, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -413,11 +440,11 @@ namespace Katteker
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
var result = Major.GetHashCode();
|
||||
result = result * 31 + Minor.GetHashCode();
|
||||
result = result * 31 + Patch.GetHashCode();
|
||||
result = result * 31 + Prerelease.GetHashCode();
|
||||
result = result * 31 + Build.GetHashCode();
|
||||
int result = this.Major.GetHashCode();
|
||||
result = result * 31 + this.Minor.GetHashCode();
|
||||
result = result * 31 + this.Patch.GetHashCode();
|
||||
result = result * 31 + this.Prerelease.GetHashCode();
|
||||
result = result * 31 + this.Build.GetHashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@ -426,19 +453,19 @@ namespace Katteker
|
||||
[SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
|
||||
public void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
if (info == null) throw new ArgumentNullException(nameof(info));
|
||||
info.AddValue("SemanticVersion", ToString());
|
||||
if (info == null) throw new ArgumentNullException("info");
|
||||
info.AddValue("SemVersion", ToString());
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Implicit conversion from string to SemanticVersion.
|
||||
/// Implicit conversion from string to SemVersion.
|
||||
/// </summary>
|
||||
/// <param name="version">The semantic version.</param>
|
||||
/// <returns>The SemanticVersion object.</returns>
|
||||
public static implicit operator SemanticVersion(string version)
|
||||
/// <returns>The SemVersion object.</returns>
|
||||
public static implicit operator SemVersion(string version)
|
||||
{
|
||||
return Parse(version);
|
||||
return SemVersion.Parse(version);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -447,9 +474,9 @@ namespace Katteker
|
||||
/// <param name="left">The left value.</param>
|
||||
/// <param name="right">The right value.</param>
|
||||
/// <returns>If left is equal to right <c>true</c>, else <c>false</c>.</returns>
|
||||
public static bool operator ==(SemanticVersion left, SemanticVersion right)
|
||||
public static bool operator ==(SemVersion left, SemVersion right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
return SemVersion.Equals(left, right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -458,9 +485,9 @@ namespace Katteker
|
||||
/// <param name="left">The left value.</param>
|
||||
/// <param name="right">The right value.</param>
|
||||
/// <returns>If left is not equal to right <c>true</c>, else <c>false</c>.</returns>
|
||||
public static bool operator !=(SemanticVersion left, SemanticVersion right)
|
||||
public static bool operator !=(SemVersion left, SemVersion right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
return !SemVersion.Equals(left, right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -469,9 +496,9 @@ namespace Katteker
|
||||
/// <param name="left">The left value.</param>
|
||||
/// <param name="right">The right value.</param>
|
||||
/// <returns>If left is greater than right <c>true</c>, else <c>false</c>.</returns>
|
||||
public static bool operator >(SemanticVersion left, SemanticVersion right)
|
||||
public static bool operator >(SemVersion left, SemVersion right)
|
||||
{
|
||||
return Compare(left, right) > 0;
|
||||
return SemVersion.Compare(left, right) > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -480,7 +507,7 @@ namespace Katteker
|
||||
/// <param name="left">The left value.</param>
|
||||
/// <param name="right">The right value.</param>
|
||||
/// <returns>If left is greater than or equal to right <c>true</c>, else <c>false</c>.</returns>
|
||||
public static bool operator >=(SemanticVersion left, SemanticVersion right)
|
||||
public static bool operator >=(SemVersion left, SemVersion right)
|
||||
{
|
||||
return left == right || left > right;
|
||||
}
|
||||
@ -491,9 +518,9 @@ namespace Katteker
|
||||
/// <param name="left">The left value.</param>
|
||||
/// <param name="right">The right value.</param>
|
||||
/// <returns>If left is less than right <c>true</c>, else <c>false</c>.</returns>
|
||||
public static bool operator <(SemanticVersion left, SemanticVersion right)
|
||||
public static bool operator <(SemVersion left, SemVersion right)
|
||||
{
|
||||
return Compare(left, right) < 0;
|
||||
return SemVersion.Compare(left, right) < 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -502,10 +529,9 @@ namespace Katteker
|
||||
/// <param name="left">The left value.</param>
|
||||
/// <param name="right">The right value.</param>
|
||||
/// <returns>If left is less than or equal to right <c>true</c>, else <c>false</c>.</returns>
|
||||
public static bool operator <=(SemanticVersion left, SemanticVersion right)
|
||||
public static bool operator <=(SemVersion left, SemVersion right)
|
||||
{
|
||||
return left == right || left < right;
|
||||
}
|
||||
}
|
||||
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
|
||||
}
|
@ -20,7 +20,7 @@
|
||||
/// <summary>
|
||||
/// File of the releases.
|
||||
/// </summary>
|
||||
public const string RELEASE = "RELEASE";
|
||||
public const string Release = "RELEASE";
|
||||
|
||||
/// <summary>
|
||||
/// Name of the
|
||||
|
@ -39,10 +39,10 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ChangelogHelper.cs" />
|
||||
<Compile Include="Common\SemVersion.cs" />
|
||||
<Compile Include="Constants.cs" />
|
||||
<Compile Include="KattekerConfig.cs" />
|
||||
<Compile Include="MarkdownSharp.cs" />
|
||||
<Compile Include="SemanticVersion.cs" />
|
||||
<Compile Include="Common\MarkdownSharp.cs" />
|
||||
<Compile Include="UpdateInfo.cs" />
|
||||
<Compile Include="UpdateManager.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
|
@ -1,16 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using Semver;
|
||||
|
||||
namespace Katteker
|
||||
{
|
||||
public class ReleaseEntry : IComparable<ReleaseEntry>, IComparable
|
||||
{
|
||||
public const string FilenameRegex = @"(^.*)-((?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(?:\+[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*)?).*-(full|delta)";
|
||||
private const string FilenameRegex = @"(^.*)-((?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(?:\+[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*)?).*-(full|delta)";
|
||||
|
||||
private const char Seperator = '|';
|
||||
|
||||
public ReleaseEntry(string filename, SemanticVersion version, long fileSize, bool isDelta, string sha1)
|
||||
public ReleaseEntry(string filename, SemVersion version, long fileSize, bool isDelta, string sha1)
|
||||
{
|
||||
Filename = filename;
|
||||
Version = version;
|
||||
@ -29,17 +30,17 @@ namespace Katteker
|
||||
var fileSegments = Regex.Match(Filename, FilenameRegex);
|
||||
if (fileSegments.Groups.Count < 3) throw new ArgumentOutOfRangeException("Filename is not compilant.");
|
||||
ApplicationName = fileSegments.Groups[1].Value;
|
||||
Version = SemanticVersion.Parse(fileSegments.Groups[2].Value);
|
||||
Version = SemVersion.Parse(fileSegments.Groups[2].Value);
|
||||
IsDelta = fileSegments.Groups[3].Value != "full";
|
||||
}
|
||||
|
||||
public ReleaseEntry(string applicationName, SemanticVersion version)
|
||||
public ReleaseEntry(string applicationName, SemVersion version)
|
||||
{
|
||||
ApplicationName = applicationName;
|
||||
Version = version;
|
||||
}
|
||||
|
||||
public SemanticVersion Version { get; }
|
||||
public SemVersion Version { get; }
|
||||
public string SHA1 { get; }
|
||||
public string Filename { get; }
|
||||
public long Filesize { get; }
|
||||
@ -53,8 +54,8 @@ namespace Katteker
|
||||
public int CompareTo(ReleaseEntry other)
|
||||
{
|
||||
if (ReferenceEquals(this, other)) return 0;
|
||||
if (ReferenceEquals(null, other)) return 1;
|
||||
var versionComparison = Comparer<SemanticVersion>.Default.Compare(Version, other.Version);
|
||||
if (other is null) return 1;
|
||||
var versionComparison = Comparer<SemVersion>.Default.Compare(Version, other.Version);
|
||||
if (versionComparison != 0) return versionComparison;
|
||||
return string.Compare(Filename, other.Filename, StringComparison.Ordinal);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Semver;
|
||||
|
||||
namespace Katteker
|
||||
{
|
||||
@ -9,16 +10,16 @@ namespace Katteker
|
||||
{
|
||||
private readonly string _filePath;
|
||||
|
||||
private SortedList<SemanticVersion, ReleaseEntry> ReleaseEntries { get; }
|
||||
private SortedList<SemVersion, ReleaseEntry> ReleaseEntries { get; }
|
||||
|
||||
private Releases()
|
||||
{
|
||||
ReleaseEntries = new SortedList<SemanticVersion, ReleaseEntry>();
|
||||
ReleaseEntries = new SortedList<SemVersion, ReleaseEntry>();
|
||||
}
|
||||
|
||||
public Releases(string path) : this()
|
||||
{
|
||||
_filePath = Path.Combine(path, Constants.RELEASE);
|
||||
_filePath = Path.Combine(path, Constants.Release);
|
||||
if (!File.Exists(_filePath)) return;
|
||||
AddRange(File.ReadAllLines(_filePath));
|
||||
}
|
||||
@ -48,7 +49,7 @@ namespace Katteker
|
||||
/// <param name="setupFilePath">Path to setup-file</param>
|
||||
/// <param name="version">Version of setup-file</param>
|
||||
/// <returns>Returns true if newest version.</returns>
|
||||
public ReleaseEntry Add(string setupFilePath, SemanticVersion version)
|
||||
public ReleaseEntry Add(string setupFilePath, SemVersion version)
|
||||
{
|
||||
var sha1 = Utility.ComputeFileHash(setupFilePath);
|
||||
var setupFile = new FileInfo(setupFilePath);
|
||||
|
@ -4,18 +4,12 @@ namespace Katteker
|
||||
{
|
||||
public class UpdateInfo
|
||||
{
|
||||
private readonly Releases _releases;
|
||||
public ReleaseEntry CurrentlyInstalledVersion { get; protected set; }
|
||||
|
||||
public ReleaseEntry FutureReleaseEntry { get; protected set; }
|
||||
|
||||
public List<ReleaseEntry> ReleasesToApply { get; protected set; } = new List<ReleaseEntry>();
|
||||
|
||||
public ReleaseEntry CurrentlyInstalledVersion { get; }
|
||||
|
||||
public List<ReleaseEntry> ReleasesToApply { get; } = new List<ReleaseEntry>();
|
||||
|
||||
public UpdateInfo(string applicationName, Releases releases)
|
||||
{
|
||||
_releases = releases;
|
||||
CurrentlyInstalledVersion = new ReleaseEntry(applicationName, VersionExtension.GetCurrentVersion);
|
||||
foreach (var release in releases)
|
||||
{
|
||||
|
@ -123,7 +123,7 @@ namespace Katteker
|
||||
private static async Task<Releases> DownloadIndexAsync(string urlOrPath)
|
||||
{
|
||||
var url = urlOrPath.TrimEnd('/');
|
||||
url += "/" + Constants.RELEASE;
|
||||
url += "/" + Constants.Release;
|
||||
var content = await new WebClient().DownloadStringTaskAsync(url).ConfigureAwait(false);
|
||||
var lines = content.Split(new[] {'\r', '\n'}, StringSplitOptions.RemoveEmptyEntries);
|
||||
return new Releases(lines);
|
||||
|
@ -5,6 +5,9 @@ using System.Security.Cryptography;
|
||||
|
||||
namespace Katteker
|
||||
{
|
||||
/// <summary>
|
||||
/// Utilities.
|
||||
/// </summary>
|
||||
public static class Utility
|
||||
{
|
||||
internal static string GetLocalAppDataDirectory()
|
||||
@ -17,12 +20,23 @@ namespace Katteker
|
||||
return Assembly.GetEntryAssembly().GetName().Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is true if the url is a web-address. False otherwise.
|
||||
/// </summary>
|
||||
/// <param name="urlOrPath"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsWebUrl(string urlOrPath)
|
||||
{
|
||||
var uri = new Uri(urlOrPath);
|
||||
return uri.Scheme == "http" || uri.Scheme == "https";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute file hash with SHA1.
|
||||
/// If the file is greater than 5 MB, the hash will only computed with the first 5 MB.
|
||||
/// </summary>
|
||||
/// <param name="filename"></param>
|
||||
/// <returns></returns>
|
||||
public static string ComputeFileHash(string filename)
|
||||
{
|
||||
string sha1;
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Semver;
|
||||
|
||||
namespace Katteker
|
||||
{
|
||||
@ -11,20 +12,25 @@ namespace Katteker
|
||||
/// <summary>
|
||||
/// Get the current Version of Application.
|
||||
/// </summary>
|
||||
public static SemanticVersion GetCurrentVersion
|
||||
public static SemVersion GetCurrentVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
var assemblyVersion = Assembly.GetEntryAssembly().GetName().Version.ToString(3);
|
||||
var getCurrentVersion = SemanticVersion.Parse(assemblyVersion);
|
||||
var getCurrentVersion = SemVersion.Parse(assemblyVersion);
|
||||
var informalVersion = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
|
||||
if (informalVersion != null && SemanticVersion.TryParse(informalVersion, out var semVersion))
|
||||
if (informalVersion != null && SemVersion.TryParse(informalVersion, out var semVersion))
|
||||
return semVersion;
|
||||
return getCurrentVersion;
|
||||
}
|
||||
}
|
||||
|
||||
public static Version ToSystemVersion(this SemanticVersion value) => new Version(value.Major, value.Minor, value.Patch, 0);
|
||||
/// <summary>
|
||||
/// Convert to cenventional System.Version instance.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static Version ToSystemVersion(this SemVersion value) => new Version(value.Major, value.Minor, value.Patch, 0);
|
||||
|
||||
/// <summary>
|
||||
/// Get the current Version of Application.
|
||||
|
12
README.md
12
README.md
@ -1,20 +1,16 @@
|
||||
Katteker
|
||||
SquirrelKiller (aka Wombat)
|
||||
=============
|
||||
Aktuell:
|
||||
|
||||
Ziele:
|
||||
-------------
|
||||
|
||||
- Automatische Updates, auch für nicht verwaltete Software.
|
||||
- Popup unten rechts mit Informationen über Update.
|
||||
- Paket erstellt mit NSIS.
|
||||
- schnellere Verteilung.
|
||||
- Schnellere Paketerstellung ohne Umwege.
|
||||
- AppStub übernimmt die Resourcen der Executable. (Icon, Name, Version, Copyright, Beschreibung, etc)
|
||||
[ResourceLib C# File Resource Management Library](https://github.com/dblock/resourcelib)
|
||||
|
||||
Zukunft:
|
||||
-------------
|
||||
- Automatische Updates, auch für nicht verwaltete Software.
|
||||
- Popup unten rechts mit Informationen über Update.
|
||||
|
||||
Creator:
|
||||
-------------
|
||||
- Informationen der Anwendung auslesen
|
||||
|
Loading…
x
Reference in New Issue
Block a user