diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index dc3c2a6..b15744a 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -28,6 +28,7 @@ jobs:
main:
uses: Tyrrrz/.github/.github/workflows/nuget.yml@master
with:
+ windows-only: true
deploy: ${{ inputs.deploy || github.ref_type == 'tag' }}
package-version: ${{ inputs.package-version || (github.ref_type == 'tag' && github.ref_name) || format('0.0.0-ci-{0}', github.sha) }}
dotnet-version: 8.0.x
diff --git a/Onova.Tests.Dummy/Onova.Tests.Dummy.csproj b/Onova.Tests.Dummy/Onova.Tests.Dummy.csproj
index 36736c8..2f4dcf8 100644
--- a/Onova.Tests.Dummy/Onova.Tests.Dummy.csproj
+++ b/Onova.Tests.Dummy/Onova.Tests.Dummy.csproj
@@ -2,11 +2,11 @@
Exe
- net8.0
+ net8.0-windows
-
+
diff --git a/Onova.Tests/Onova.Tests.csproj b/Onova.Tests/Onova.Tests.csproj
index 81c4d05..fd77e25 100644
--- a/Onova.Tests/Onova.Tests.csproj
+++ b/Onova.Tests/Onova.Tests.csproj
@@ -1,19 +1,23 @@
- net8.0
+ net8.0-windows
-
-
-
+
+
+
+
+
+
+
-
+
-
-
+
+
@@ -21,8 +25,4 @@
-
-
-
-
\ No newline at end of file
diff --git a/Onova.Tests/UpdateSpecs.cs b/Onova.Tests/UpdateSpecs.cs
index 1aaf954..e57ed51 100644
--- a/Onova.Tests/UpdateSpecs.cs
+++ b/Onova.Tests/UpdateSpecs.cs
@@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Linq;
+using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using FluentAssertions;
@@ -45,7 +46,11 @@ public void Dispose()
public async Task I_can_check_for_updates_and_get_a_higher_version_if_it_is_available()
{
// Arrange
- var updatee = new AssemblyMetadata("TestUpdatee", Version.Parse("1.0"), "");
+ var updatee = new AssemblyMetadata(
+ "TestUpdatee",
+ Version.Parse("1.0"),
+ Assembly.GetExecutingAssembly().Location
+ );
// Cleanup storage directory (TODO: move this to API)
DirectoryEx.DeleteIfExists(
@@ -82,7 +87,11 @@ public async Task I_can_check_for_updates_and_get_a_higher_version_if_it_is_avai
public async Task I_can_check_for_updates_and_get_nothing_if_there_is_no_higher_version_available()
{
// Arrange
- var updatee = new AssemblyMetadata("TestUpdatee", Version.Parse("3.0"), "");
+ var updatee = new AssemblyMetadata(
+ "TestUpdatee",
+ Version.Parse("3.0"),
+ Assembly.GetExecutingAssembly().Location
+ );
// Cleanup storage directory (TODO: move this to API)
DirectoryEx.DeleteIfExists(
@@ -119,7 +128,11 @@ public async Task I_can_check_for_updates_and_get_nothing_if_there_is_no_higher_
public async Task I_can_check_for_updates_and_get_nothing_if_the_package_source_contains_no_packages()
{
// Arrange
- var updatee = new AssemblyMetadata("TestUpdatee", Version.Parse("1.0"), "");
+ var updatee = new AssemblyMetadata(
+ "TestUpdatee",
+ Version.Parse("1.0"),
+ Assembly.GetExecutingAssembly().Location
+ );
// Cleanup storage directory (TODO: move this to API)
DirectoryEx.DeleteIfExists(
@@ -151,7 +164,11 @@ public async Task I_can_check_for_updates_and_get_nothing_if_the_package_source_
public async Task I_can_prepare_an_update_so_that_it_can_be_installed()
{
// Arrange
- var updatee = new AssemblyMetadata("TestUpdatee", Version.Parse("1.0"), "");
+ var updatee = new AssemblyMetadata(
+ "TestUpdatee",
+ Version.Parse("1.0"),
+ Assembly.GetExecutingAssembly().Location
+ );
// Cleanup storage directory (TODO: move this to API)
DirectoryEx.DeleteIfExists(
@@ -188,7 +205,11 @@ public async Task I_can_prepare_an_update_so_that_it_can_be_installed()
public async Task I_can_get_a_list_of_updates_which_are_already_prepared_to_install()
{
// Arrange
- var updatee = new AssemblyMetadata("TestUpdatee", Version.Parse("1.0"), "");
+ var updatee = new AssemblyMetadata(
+ "TestUpdatee",
+ Version.Parse("1.0"),
+ Assembly.GetExecutingAssembly().Location
+ );
// Cleanup storage directory (TODO: move this to API)
DirectoryEx.DeleteIfExists(
diff --git a/Onova.Tests/Utils/DummyEnvironment.cs b/Onova.Tests/Utils/DummyEnvironment.cs
index 86df8bc..696b145 100644
--- a/Onova.Tests/Utils/DummyEnvironment.cs
+++ b/Onova.Tests/Utils/DummyEnvironment.cs
@@ -11,7 +11,7 @@
namespace Onova.Tests.Utils;
-internal class DummyEnvironment : IDisposable
+internal class DummyEnvironment(string rootDirPath) : IDisposable
{
private static readonly Assembly DummyAssembly = typeof(Dummy.Program).Assembly;
private static readonly string DummyAssemblyFileName = Path.GetFileName(DummyAssembly.Location);
@@ -19,19 +19,9 @@ internal class DummyEnvironment : IDisposable
DummyAssembly.Location
)!;
- private readonly string _rootDirPath;
+ private string DummyFilePath { get; } = Path.Combine(rootDirPath, DummyAssemblyFileName);
- private string DummyFilePath { get; }
-
- private string DummyPackagesDirPath { get; }
-
- public DummyEnvironment(string rootDirPath)
- {
- _rootDirPath = rootDirPath;
-
- DummyFilePath = Path.Combine(_rootDirPath, DummyAssemblyFileName);
- DummyPackagesDirPath = Path.Combine(_rootDirPath, "Packages");
- }
+ private string DummyPackagesDirPath { get; } = Path.Combine(rootDirPath, "Packages");
private void SetAssemblyVersion(string filePath, Version version)
{
@@ -45,13 +35,13 @@ private void SetAssemblyVersion(string filePath, Version version)
private void CreateBase(Version version)
{
// Create dummy directory
- Directory.CreateDirectory(_rootDirPath);
+ Directory.CreateDirectory(rootDirPath);
// Copy files
foreach (var filePath in Directory.EnumerateFiles(DummyAssemblyDirPath))
{
var fileName = Path.GetFileName(filePath);
- File.Copy(filePath, Path.Combine(_rootDirPath, fileName));
+ File.Copy(filePath, Path.Combine(rootDirPath, fileName));
}
// Change base dummy version
@@ -64,7 +54,7 @@ private void CreatePackage(Version version)
Directory.CreateDirectory(DummyPackagesDirPath);
// Temporarily copy the dummy
- var dummyTempFilePath = Path.Combine(_rootDirPath, $"{DummyAssemblyFileName}.{version}");
+ var dummyTempFilePath = Path.Combine(rootDirPath, $"{DummyAssemblyFileName}.{version}");
File.Copy(DummyFilePath, dummyTempFilePath);
// Change dummy version
@@ -91,7 +81,7 @@ private void Cleanup()
{
try
{
- DirectoryEx.DeleteIfExists(_rootDirPath);
+ DirectoryEx.DeleteIfExists(rootDirPath);
break;
}
catch (UnauthorizedAccessException) when (retriesRemaining > 0)
@@ -113,7 +103,7 @@ public void Setup(Version baseVersion, IReadOnlyList availableVersions)
public string[] GetLastRunArguments(Version version)
{
- var filePath = Path.Combine(_rootDirPath, $"lastrun-{version}.txt");
+ var filePath = Path.Combine(rootDirPath, $"lastrun-{version}.txt");
return File.Exists(filePath) ? File.ReadAllLines(filePath) : Array.Empty();
}
diff --git a/Onova.Updater/Onova.Updater.csproj b/Onova.Updater/Onova.Updater.csproj
index bd1820a..4b47952 100644
--- a/Onova.Updater/Onova.Updater.csproj
+++ b/Onova.Updater/Onova.Updater.csproj
@@ -7,8 +7,8 @@
-
-
+
+
\ No newline at end of file
diff --git a/Onova.Updater/Updater.cs b/Onova.Updater/Updater.cs
index f2b05fb..5fc9159 100644
--- a/Onova.Updater/Updater.cs
+++ b/Onova.Updater/Updater.cs
@@ -8,35 +8,23 @@
namespace Onova.Updater;
-public class Updater : IDisposable
+public class Updater(
+ string updateeFilePath,
+ string packageContentDirPath,
+ bool restartUpdatee,
+ string routedArgs
+) : IDisposable
{
- private readonly string _updateeFilePath;
- private readonly string _packageContentDirPath;
- private readonly bool _restartUpdatee;
- private readonly string _routedArgs;
-
private readonly TextWriter _log = File.CreateText(
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Log.txt")
);
- public Updater(
- string updateeFilePath,
- string packageContentDirPath,
- bool restartUpdatee,
- string routedArgs
- )
- {
- _updateeFilePath = updateeFilePath;
- _packageContentDirPath = packageContentDirPath;
- _restartUpdatee = restartUpdatee;
- _routedArgs = routedArgs;
- }
-
private void WriteLog(string content)
{
- var date = DateTimeOffset
- .Now
- .ToString("dd-MMM-yyyy HH:mm:ss.fff", CultureInfo.InvariantCulture);
+ var date = DateTimeOffset.Now.ToString(
+ "dd-MMM-yyyy HH:mm:ss.fff",
+ CultureInfo.InvariantCulture
+ );
var entry = $"{date}> {content}";
@@ -51,7 +39,7 @@ private void WaitForUpdateeExit()
for (var retriesRemaining = 15; retriesRemaining > 0; retriesRemaining--)
{
- if (FileEx.CheckWriteAccess(_updateeFilePath))
+ if (FileEx.CheckWriteAccess(updateeFilePath))
return;
Thread.Sleep(1000);
@@ -63,12 +51,12 @@ private void WaitForUpdateeExit()
private void ApplyUpdate()
{
WriteLog("Copying package contents from storage to the updatee directory...");
- DirectoryEx.Copy(_packageContentDirPath, Path.GetDirectoryName(_updateeFilePath)!);
+ DirectoryEx.Copy(packageContentDirPath, Path.GetDirectoryName(updateeFilePath)!);
try
{
WriteLog("Deleting package contents from storage...");
- Directory.Delete(_packageContentDirPath, true);
+ Directory.Delete(packageContentDirPath, true);
}
catch (Exception ex)
{
@@ -79,40 +67,38 @@ private void ApplyUpdate()
private void StartUpdatee()
{
- using var process = new Process
+ using var process = new Process();
+ process.StartInfo = new ProcessStartInfo
{
- StartInfo = new ProcessStartInfo
- {
- WorkingDirectory = Path.GetDirectoryName(_updateeFilePath),
- Arguments = _routedArgs,
- // Don't let the child process inherit the current console window
- UseShellExecute = true
- }
+ WorkingDirectory = Path.GetDirectoryName(updateeFilePath),
+ Arguments = routedArgs,
+ // Don't let the child process inherit the current console window
+ UseShellExecute = true
};
// If the updatee is an .exe file, start it directly.
// This covers self-contained .NET Core apps and legacy .NET Framework apps.
if (
string.Equals(
- Path.GetExtension(_updateeFilePath),
+ Path.GetExtension(updateeFilePath),
".exe",
StringComparison.OrdinalIgnoreCase
)
)
{
- process.StartInfo.FileName = _updateeFilePath;
+ process.StartInfo.FileName = updateeFilePath;
}
// Otherwise, locate the apphost by looking for the .exe file with the same name.
// This covers framework-dependent .NET Core apps.
- else if (File.Exists(Path.ChangeExtension(_updateeFilePath, ".exe")))
+ else if (File.Exists(Path.ChangeExtension(updateeFilePath, ".exe")))
{
- process.StartInfo.FileName = Path.ChangeExtension(_updateeFilePath, ".exe");
+ process.StartInfo.FileName = Path.ChangeExtension(updateeFilePath, ".exe");
}
// As a fallback, try to run the updatee through the .NET CLI
else
{
process.StartInfo.FileName = "dotnet";
- process.StartInfo.Arguments = $"{_updateeFilePath} {_routedArgs}";
+ process.StartInfo.Arguments = $"{updateeFilePath} {routedArgs}";
}
WriteLog(
@@ -131,17 +117,17 @@ public void Run()
WriteLog(
$"""
Onova Updater v{updaterVersion} started with the following arguments:
- - UpdateeFilePath = {_updateeFilePath}
- - PackageContentDirPath = {_packageContentDirPath}
- - RestartUpdatee = {_restartUpdatee}
- - RoutedArgs = {_routedArgs}
+ - UpdateeFilePath = {updateeFilePath}
+ - PackageContentDirPath = {packageContentDirPath}
+ - RestartUpdatee = {restartUpdatee}
+ - RoutedArgs = {routedArgs}
"""
);
WaitForUpdateeExit();
ApplyUpdate();
- if (_restartUpdatee)
+ if (restartUpdatee)
StartUpdatee();
WriteLog("Update completed successfully.");
diff --git a/Onova/Exceptions/LockFileNotAcquiredException.cs b/Onova/Exceptions/LockFileNotAcquiredException.cs
index 9494181..7e6557f 100644
--- a/Onova/Exceptions/LockFileNotAcquiredException.cs
+++ b/Onova/Exceptions/LockFileNotAcquiredException.cs
@@ -5,13 +5,7 @@ namespace Onova.Exceptions;
///
/// Thrown when an attempt to acquire a lock file failed.
///
-public class LockFileNotAcquiredException : Exception
-{
- ///
- /// Initializes an instance of .
- ///
- public LockFileNotAcquiredException()
- : base(
- "Could not acquire a lock file. Most likely, another instance of this application currently owns the lock file."
- ) { }
-}
+public class LockFileNotAcquiredException()
+ : Exception(
+ "Could not acquire a lock file. Most likely, another instance of this application currently owns the lock file."
+ );
diff --git a/Onova/Exceptions/PackageNotFoundException.cs b/Onova/Exceptions/PackageNotFoundException.cs
index e6a1d56..224e785 100644
--- a/Onova/Exceptions/PackageNotFoundException.cs
+++ b/Onova/Exceptions/PackageNotFoundException.cs
@@ -5,19 +5,11 @@ namespace Onova.Exceptions;
///
/// Thrown when a package of given version was not found by a resolver.
///
-public class PackageNotFoundException : Exception
+public class PackageNotFoundException(Version version)
+ : Exception($"Package version '{version}' was not found by the configured package resolver.")
{
///
/// Package version.
///
- public Version Version { get; }
-
- ///
- /// Initializes an instance of .
- ///
- public PackageNotFoundException(Version version)
- : base($"Package version '{version}' was not found by the configured package resolver.")
- {
- Version = version;
- }
+ public Version Version { get; } = version;
}
diff --git a/Onova/Exceptions/UpdateNotPreparedException.cs b/Onova/Exceptions/UpdateNotPreparedException.cs
index 983fa9c..18bb6b0 100644
--- a/Onova/Exceptions/UpdateNotPreparedException.cs
+++ b/Onova/Exceptions/UpdateNotPreparedException.cs
@@ -5,21 +5,13 @@ namespace Onova.Exceptions;
///
/// Thrown when launching the updater to install an update that was not prepared.
///
-public class UpdateNotPreparedException : Exception
+public class UpdateNotPreparedException(Version version)
+ : Exception(
+ $"Update to version '{version}' is not prepared. Please prepare an update before applying it."
+ )
{
///
/// Package version.
///
- public Version Version { get; }
-
- ///
- /// Initializes an instance of .
- ///
- public UpdateNotPreparedException(Version version)
- : base(
- $"Update to version '{version}' is not prepared. Please prepare an update before applying it."
- )
- {
- Version = version;
- }
+ public Version Version { get; } = version;
}
diff --git a/Onova/Exceptions/UpdaterAlreadyLaunchedException.cs b/Onova/Exceptions/UpdaterAlreadyLaunchedException.cs
index 07f0f56..150b637 100644
--- a/Onova/Exceptions/UpdaterAlreadyLaunchedException.cs
+++ b/Onova/Exceptions/UpdaterAlreadyLaunchedException.cs
@@ -5,13 +5,7 @@ namespace Onova.Exceptions;
///
/// Thrown when launching the updater after it has already been launched.
///
-public class UpdaterAlreadyLaunchedException : Exception
-{
- ///
- /// Initializes an instance of .
- ///
- public UpdaterAlreadyLaunchedException()
- : base(
- "Updater has already been launched, either by this or another instance of the application."
- ) { }
-}
+public class UpdaterAlreadyLaunchedException()
+ : Exception(
+ "Updater has already been launched, either by this or another instance of the application."
+ );
diff --git a/Onova/Models/AssemblyMetadata.cs b/Onova/Models/AssemblyMetadata.cs
index 1e24d83..c1626fc 100644
--- a/Onova/Models/AssemblyMetadata.cs
+++ b/Onova/Models/AssemblyMetadata.cs
@@ -1,5 +1,6 @@
using System;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
@@ -8,48 +9,47 @@ namespace Onova.Models;
///
/// Contains information about an assembly.
///
-public partial class AssemblyMetadata
+public partial class AssemblyMetadata(string name, Version version, string filePath)
{
///
/// Assembly name.
///
- public string Name { get; }
+ public string Name { get; } = name;
///
/// Assembly version.
///
- public Version Version { get; }
+ public Version Version { get; } = version;
///
/// Assembly file path.
///
- public string FilePath { get; }
-
- internal string DirPath => Path.GetDirectoryName(FilePath)!;
+ public string FilePath { get; } = Path.GetFullPath(filePath);
- ///
- /// Initializes a new instance of .
- ///
- public AssemblyMetadata(string name, Version version, string filePath)
- {
- Name = name;
- Version = version;
- FilePath = filePath;
- }
+ internal string DirPath { get; } = Path.GetDirectoryName(filePath)!;
}
public partial class AssemblyMetadata
{
///
- /// Extracts assembly metadata from given assembly.
- /// The specified path is used to override the executable file path in case the assembly is not meant to run directly.
+ /// Extracts assembly metadata from the specified assembly.
+ /// The specified path is used to override the executable file path in case the assembly is not available on disk.
///
public static AssemblyMetadata FromAssembly(Assembly assembly, string assemblyFilePath) =>
- new(assembly.GetName().Name!, assembly.GetName().Version!, assemblyFilePath);
+ new(
+ assembly.GetName().Name
+ ?? throw new InvalidOperationException("Provided assembly's name is ."),
+ assembly.GetName().Version
+ ?? throw new InvalidOperationException("Provided assembly's version is ."),
+ assemblyFilePath
+ );
///
- /// Extracts assembly metadata from given assembly.
+ /// Extracts assembly metadata from the specified assembly.
///
+ [RequiresAssemblyFiles(
+ "This method requires the specified assembly's file path to be available."
+ )]
public static AssemblyMetadata FromAssembly(Assembly assembly)
{
if (string.IsNullOrEmpty(assembly.Location))
@@ -64,22 +64,30 @@ public static AssemblyMetadata FromAssembly(Assembly assembly)
}
///
- /// Extracts assembly metadata from entry assembly.
+ /// Extracts assembly metadata from the entry assembly.
///
+ [UnconditionalSuppressMessage(
+ "SingleFile",
+ "IL3000:Avoid accessing Assembly file path when publishing as a single file",
+ Justification = "The return value of the method is checked to ensure the assembly location is available."
+ )]
public static AssemblyMetadata FromEntryAssembly()
{
// For most applications, the entry assembly is the entry point
var assembly =
Assembly.GetEntryAssembly()
- ?? throw new InvalidOperationException("Can't get entry assembly.");
+ ?? throw new InvalidOperationException("Failed to get the entry assembly.");
if (!string.IsNullOrWhiteSpace(assembly.Location))
return FromAssembly(assembly, assembly.Location);
- // For self-contained applications, the executable is the entry point
+ // For single-file applications, the executable is the entry point
+ using var currentProcess = Process.GetCurrentProcess();
var filePath =
- Process.GetCurrentProcess().MainModule?.FileName
- ?? throw new InvalidOperationException("Can't get current process main module.");
+ currentProcess.MainModule?.FileName
+ ?? throw new InvalidOperationException(
+ "Failed to get the current process's entry point."
+ );
return FromAssembly(assembly, filePath);
}
diff --git a/Onova/Models/CheckForUpdatesResult.cs b/Onova/Models/CheckForUpdatesResult.cs
index dc516e8..ba380d8 100644
--- a/Onova/Models/CheckForUpdatesResult.cs
+++ b/Onova/Models/CheckForUpdatesResult.cs
@@ -6,35 +6,25 @@ namespace Onova.Models;
///
/// Result of checking for updates.
///
-public class CheckForUpdatesResult
+public class CheckForUpdatesResult(
+ IReadOnlyList versions,
+ Version? lastVersion,
+ bool canUpdate
+)
{
///
/// All available package versions.
///
- public IReadOnlyList Versions { get; }
+ public IReadOnlyList Versions { get; } = versions;
///
/// Last available package version.
/// Null if there are no available packages.
///
- public Version? LastVersion { get; }
+ public Version? LastVersion { get; } = lastVersion;
///
/// Whether there is a package with higher version than the current version.
///
- public bool CanUpdate { get; }
-
- ///
- /// Initializes a new instance of .
- ///
- public CheckForUpdatesResult(
- IReadOnlyList versions,
- Version? lastVersion,
- bool canUpdate
- )
- {
- Versions = versions;
- LastVersion = lastVersion;
- CanUpdate = canUpdate;
- }
+ public bool CanUpdate { get; } = canUpdate;
}
diff --git a/Onova/Onova.csproj b/Onova/Onova.csproj
index 166de75..62d12d6 100644
--- a/Onova/Onova.csproj
+++ b/Onova/Onova.csproj
@@ -1,8 +1,10 @@
- netstandard2.0;netcoreapp3.0;net462
+ netstandard2.0;netcoreapp3.0;net462;net8.0
true
+ true
+ true
@@ -22,12 +24,13 @@
-
-
+
+
+
-
-
-
+
+
+
diff --git a/Onova/Services/AggregatePackageResolver.cs b/Onova/Services/AggregatePackageResolver.cs
index 68b1ef0..333079b 100644
--- a/Onova/Services/AggregatePackageResolver.cs
+++ b/Onova/Services/AggregatePackageResolver.cs
@@ -11,18 +11,8 @@ namespace Onova.Services;
///
/// Resolves packages using multiple other package resolvers.
///
-public class AggregatePackageResolver : IPackageResolver
+public class AggregatePackageResolver(IReadOnlyList resolvers) : IPackageResolver
{
- private readonly IReadOnlyList _resolvers;
-
- ///
- /// Initializes an instance of .
- ///
- public AggregatePackageResolver(IReadOnlyList resolvers)
- {
- _resolvers = resolvers;
- }
-
///
/// Initializes an instance of .
///
@@ -37,7 +27,7 @@ public async Task> GetPackageVersionsAsync(
var aggregateVersions = new HashSet();
// Get unique package versions provided by all resolvers
- foreach (var resolver in _resolvers)
+ foreach (var resolver in resolvers)
{
var versions = await resolver.GetPackageVersionsAsync(cancellationToken);
aggregateVersions.AddRange(versions);
@@ -52,7 +42,7 @@ CancellationToken cancellationToken
)
{
// Try to find the first resolver that has this package version
- foreach (var resolver in _resolvers)
+ foreach (var resolver in resolvers)
{
var versions = await resolver.GetPackageVersionsAsync(cancellationToken);
if (versions.Contains(version))
diff --git a/Onova/Services/GithubPackageResolver.cs b/Onova/Services/GithubPackageResolver.cs
index 585baef..5efa59d 100644
--- a/Onova/Services/GithubPackageResolver.cs
+++ b/Onova/Services/GithubPackageResolver.cs
@@ -19,14 +19,14 @@ namespace Onova.Services;
/// Resolves packages from release assets of a GitHub repository.
/// Release names should contain package versions (e.g. "v1.8.3").
///
-public class GithubPackageResolver : IPackageResolver
+public class GithubPackageResolver(
+ HttpClient http,
+ string apiBaseAddress,
+ string repositoryOwner,
+ string repositoryName,
+ string assetNamePattern
+) : IPackageResolver
{
- private readonly HttpClient _httpClient;
- private readonly string _apiBaseAddress;
- private readonly string _repositoryOwner;
- private readonly string _repositoryName;
- private readonly string _assetNamePattern;
-
private EntityTagHeaderValue? _cachedPackageVersionUrlMapETag;
private IReadOnlyDictionary? _cachedPackageVersionUrlMap;
@@ -34,36 +34,13 @@ public class GithubPackageResolver : IPackageResolver
/// Initializes an instance of .
///
public GithubPackageResolver(
- HttpClient httpClient,
- string apiBaseAddress,
- string repositoryOwner,
- string repositoryName,
- string assetNamePattern
- )
- {
- _httpClient = httpClient;
- _apiBaseAddress = apiBaseAddress;
- _repositoryOwner = repositoryOwner;
- _repositoryName = repositoryName;
- _assetNamePattern = assetNamePattern;
- }
-
- ///
- /// Initializes an instance of .
- ///
- public GithubPackageResolver(
- HttpClient httpClient,
+ HttpClient http,
string repositoryOwner,
string repositoryName,
string assetNamePattern
)
- : this(
- httpClient,
- "https://api.github.com",
- repositoryOwner,
- repositoryName,
- assetNamePattern
- ) { }
+ : this(http, "https://api.github.com", repositoryOwner, repositoryName, assetNamePattern)
+ { }
///
/// Initializes an instance of .
@@ -120,12 +97,14 @@ private IReadOnlyDictionary ParsePackageVersionUrlMap(JsonEleme
var assetName = assetJson.GetProperty("name").GetString();
var assetUrl = assetJson.GetProperty("url").GetString();
- // See if name matches
if (
string.IsNullOrWhiteSpace(assetName)
- || !WildcardPattern.IsMatch(assetName, _assetNamePattern)
+ || string.IsNullOrWhiteSpace(assetUrl)
+ || !WildcardPattern.IsMatch(assetName, assetNamePattern)
)
+ {
continue;
+ }
// Add to dictionary
map[version] = assetUrl;
@@ -140,7 +119,7 @@ CancellationToken cancellationToken
)
{
// Get releases
- var url = $"{_apiBaseAddress}/repos/{_repositoryOwner}/{_repositoryName}/releases";
+ var url = $"{apiBaseAddress}/repos/{repositoryOwner}/{repositoryName}/releases";
using var request = new HttpRequestMessage(HttpMethod.Get, url);
// Set If-None-Match header if ETag is available
@@ -148,7 +127,7 @@ CancellationToken cancellationToken
request.Headers.IfNoneMatch.Add(_cachedPackageVersionUrlMapETag);
// Get response
- using var response = await _httpClient.SendAsync(
+ using var response = await http.SendAsync(
request,
HttpCompletionOption.ResponseHeadersRead,
cancellationToken
@@ -202,7 +181,7 @@ public async Task DownloadPackageAsync(
using var request = new HttpRequestMessage(HttpMethod.Get, packageUrl);
request.Headers.Add("Accept", "application/octet-stream"); // required
- using var response = await _httpClient.SendAsync(
+ using var response = await http.SendAsync(
request,
HttpCompletionOption.ResponseHeadersRead,
cancellationToken
diff --git a/Onova/Services/LocalPackageResolver.cs b/Onova/Services/LocalPackageResolver.cs
index a657c92..1fd331f 100644
--- a/Onova/Services/LocalPackageResolver.cs
+++ b/Onova/Services/LocalPackageResolver.cs
@@ -15,34 +15,23 @@ namespace Onova.Services;
/// Resolves packages from a local repository.
/// Package file names should contain package versions (e.g. "MyProject-v1.8.3.onv").
///
-public class LocalPackageResolver : IPackageResolver
+public class LocalPackageResolver(string repositoryDirPath, string fileNamePattern = "*")
+ : IPackageResolver
{
- private readonly string _repositoryDirPath;
- private readonly string _fileNamePattern;
-
- ///
- /// Initializes an instance of .
- ///
- public LocalPackageResolver(string repositoryDirPath, string fileNamePattern = "*")
- {
- _repositoryDirPath = repositoryDirPath;
- _fileNamePattern = fileNamePattern;
- }
-
private IReadOnlyDictionary GetPackageVersionFilePathMap()
{
var map = new Dictionary();
// Check if repository directory exists
- if (!Directory.Exists(_repositoryDirPath))
+ if (!Directory.Exists(repositoryDirPath))
return map;
// Enumerate files in repository directory
- foreach (var filePath in Directory.EnumerateFiles(_repositoryDirPath))
+ foreach (var filePath in Directory.EnumerateFiles(repositoryDirPath))
{
// See if the name matches
var fileName = Path.GetFileName(filePath);
- if (!WildcardPattern.IsMatch(fileName, _fileNamePattern))
+ if (!WildcardPattern.IsMatch(fileName, fileNamePattern))
continue;
// Try to parse version
diff --git a/Onova/Services/NugetPackageExtractor.cs b/Onova/Services/NugetPackageExtractor.cs
index 4b6154c..73098da 100644
--- a/Onova/Services/NugetPackageExtractor.cs
+++ b/Onova/Services/NugetPackageExtractor.cs
@@ -12,18 +12,8 @@ namespace Onova.Services;
///
/// Extracts files from NuGet packages.
///
-public class NugetPackageExtractor : IPackageExtractor
+public class NugetPackageExtractor(string rootDirPath) : IPackageExtractor
{
- private readonly string _rootDirPath;
-
- ///
- /// Initializes an instance of .
- ///
- public NugetPackageExtractor(string rootDirPath)
- {
- _rootDirPath = rootDirPath;
- }
-
///
public async Task ExtractPackageAsync(
string sourceFilePath,
@@ -37,8 +27,9 @@ public async Task ExtractPackageAsync(
// Get entries in the content directory
var entries = archive
- .Entries
- .Where(e => e.FullName.StartsWith(_rootDirPath, StringComparison.OrdinalIgnoreCase))
+ .Entries.Where(e =>
+ e.FullName.StartsWith(rootDirPath, StringComparison.OrdinalIgnoreCase)
+ )
.ToArray();
// For progress reporting
@@ -49,7 +40,7 @@ public async Task ExtractPackageAsync(
foreach (var entry in entries)
{
// Get relative entry path
- var relativeEntryPath = entry.FullName[_rootDirPath.Length..].TrimStart('/', '\\');
+ var relativeEntryPath = entry.FullName[rootDirPath.Length..].TrimStart('/', '\\');
// Get destination paths
var entryDestFilePath = Path.Combine(destDirPath, relativeEntryPath);
@@ -64,7 +55,9 @@ public async Task ExtractPackageAsync(
entry.FullName.Last() == Path.DirectorySeparatorChar
|| entry.FullName.Last() == Path.AltDirectorySeparatorChar
)
+ {
continue;
+ }
// Extract entry
using var input = entry.Open();
diff --git a/Onova/Services/NugetPackageResolver.cs b/Onova/Services/NugetPackageResolver.cs
index 5f5dfb1..7b88131 100644
--- a/Onova/Services/NugetPackageResolver.cs
+++ b/Onova/Services/NugetPackageResolver.cs
@@ -15,36 +15,23 @@ namespace Onova.Services;
///
/// Resolves packages from a NuGet feed.
///
-public class NugetPackageResolver : IPackageResolver
+public class NugetPackageResolver(HttpClient http, string serviceIndexUrl, string packageId)
+ : IPackageResolver
{
- private readonly HttpClient _httpClient;
- private readonly string _serviceIndexUrl;
- private readonly string _packageId;
-
- private string PackageIdNormalized => _packageId.ToLowerInvariant();
-
- ///
- /// Initializes an instance of .
- ///
- public NugetPackageResolver(HttpClient httpClient, string serviceIndexUrl, string packageId)
- {
- _httpClient = httpClient;
- _serviceIndexUrl = serviceIndexUrl;
- _packageId = packageId;
- }
-
///
/// Initializes an instance of .
///
public NugetPackageResolver(string serviceIndexUrl, string packageId)
: this(Http.Client, serviceIndexUrl, packageId) { }
+ private string PackageIdNormalized { get; } = packageId.ToLowerInvariant();
+
private async Task GetPackageBaseAddressResourceUrlAsync(
CancellationToken cancellationToken
)
{
// Get all available resources
- var responseJson = await _httpClient.GetJsonAsync(_serviceIndexUrl, cancellationToken);
+ var responseJson = await http.GetJsonAsync(serviceIndexUrl, cancellationToken);
var resourcesJson = responseJson.GetProperty("resources");
// Get URL of the required resource
@@ -59,7 +46,11 @@ CancellationToken cancellationToken
StringComparison.OrdinalIgnoreCase
)
)
- return resourceJson.GetProperty("@id").GetString();
+ {
+ var url = resourceJson.GetProperty("@id").GetString();
+ if (!string.IsNullOrWhiteSpace(url))
+ return url;
+ }
}
// Resource not found
@@ -76,7 +67,7 @@ public async Task> GetPackageVersionsAsync(
// Get versions
var request = $"{resourceUrl}/{PackageIdNormalized}/index.json";
- var responseJson = await _httpClient.GetJsonAsync(request, cancellationToken);
+ var responseJson = await http.GetJsonAsync(request, cancellationToken);
var versionsJson = responseJson.GetProperty("versions");
var versions = new HashSet();
@@ -107,7 +98,7 @@ public async Task DownloadPackageAsync(
$"{resourceUrl}/{PackageIdNormalized}/{version}/{PackageIdNormalized}.{version}.nupkg";
// Get response
- using var response = await _httpClient.GetAsync(
+ using var response = await http.GetAsync(
packageUrl,
HttpCompletionOption.ResponseHeadersRead,
cancellationToken
diff --git a/Onova/Services/WebPackageResolver.cs b/Onova/Services/WebPackageResolver.cs
index b224449..3872338 100644
--- a/Onova/Services/WebPackageResolver.cs
+++ b/Onova/Services/WebPackageResolver.cs
@@ -15,20 +15,8 @@ namespace Onova.Services;
/// Resolves packages using a manifest served by a web server.
/// Manifest consists of package versions and URLs, separated by space, one line per version.
///
-public class WebPackageResolver : IPackageResolver
+public class WebPackageResolver(HttpClient http, string manifestUrl) : IPackageResolver
{
- private readonly HttpClient _httpClient;
- private readonly string _manifestUrl;
-
- ///
- /// Initializes an instance of .
- ///
- public WebPackageResolver(HttpClient httpClient, string manifestUrl)
- {
- _httpClient = httpClient;
- _manifestUrl = manifestUrl;
- }
-
///
/// Initializes an instance of .
///
@@ -37,7 +25,7 @@ public WebPackageResolver(string manifestUrl)
private string ExpandRelativeUrl(string url)
{
- var manifestUri = new Uri(_manifestUrl);
+ var manifestUri = new Uri(manifestUrl);
var uri = new Uri(manifestUri, url);
return uri.ToString();
@@ -50,7 +38,7 @@ CancellationToken cancellationToken
var map = new Dictionary();
// Get manifest
- var response = await _httpClient.GetStringAsync(_manifestUrl, cancellationToken);
+ var response = await http.GetStringAsync(manifestUrl, cancellationToken);
foreach (var line in response.Split('\n'))
{
@@ -102,7 +90,7 @@ public async Task DownloadPackageAsync(
throw new PackageNotFoundException(version);
// Download
- using var response = await _httpClient.GetAsync(
+ using var response = await http.GetAsync(
packageUrl,
HttpCompletionOption.ResponseHeadersRead,
cancellationToken
diff --git a/Onova/UpdateManager.cs b/Onova/UpdateManager.cs
index c7ae155..87d9fd8 100644
--- a/Onova/UpdateManager.cs
+++ b/Onova/UpdateManager.cs
@@ -5,6 +5,7 @@
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
using System.Threading;
using System.Threading.Tasks;
using Onova.Exceptions;
@@ -18,6 +19,7 @@ namespace Onova;
///
/// Entry point for handling application updates.
///
+[SupportedOSPlatform("windows")]
public class UpdateManager : IUpdateManager
{
private const string UpdaterResourceName = "Onova.Updater.exe";