Initial scaffolding: PS_Launcher v0.2
Client (C# / .NET 8 / WPF, MVVM via CommunityToolkit.Mvvm):
- PSLauncher.App: WPF UI dark theme (Epic-style sidebar + hero + big play button)
with UpdateAvailableDialog rendering Markdown release notes via Markdig.Wpf.
- PSLauncher.Core: services for installation registry (scans Proserve v{X.Y.Z}/),
process launcher, manifest fetch, SHA-256 integrity, HTTP download with
progress, ZIP install via .tmp + atomic rename, update orchestrator.
- PSLauncher.Models: RemoteManifest, InstalledVersion, LocalConfig DTOs.
Server (PHP 8 for OVH mutualisé, deployed under www/PS_Launcher/):
- Front controller + routes /manifest and /releasenotes/{version}.
- Static signed-manifest workflow with tools/sign-manifest.php CLI to
recompute SHA-256 and sizeBytes after each ZIP upload.
- .htaccess: HTTPS redirect, rewrite, security headers.
- config.example.php template; real config.php is gitignored.
Cohabiting versions: each release lives in its own Proserve v{version}/ folder
under installRoot. Old versions are never deleted automatically.
Roadmap: v0.3 = HTTP Range resume + Polly retry + state.json,
v0.4 = MySQL license + Ed25519 signatures + DPAPI, v0.5 = settings/UX polish.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
16
src/PSLauncher.Core/Updates/IUpdateChecker.cs
Normal file
16
src/PSLauncher.Core/Updates/IUpdateChecker.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using PSLauncher.Models;
|
||||
|
||||
namespace PSLauncher.Core.Updates;
|
||||
|
||||
public interface IUpdateChecker
|
||||
{
|
||||
Task<UpdateCheckResult> CheckAsync(CancellationToken ct);
|
||||
}
|
||||
|
||||
public sealed record UpdateCheckResult(
|
||||
RemoteManifest? Manifest,
|
||||
VersionManifest? LatestRemote,
|
||||
InstalledVersion? CurrentInstalled,
|
||||
bool IsLatestInstalled,
|
||||
bool IsLatestNewerThanInstalled,
|
||||
string? Error);
|
||||
53
src/PSLauncher.Core/Updates/UpdateChecker.cs
Normal file
53
src/PSLauncher.Core/Updates/UpdateChecker.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PSLauncher.Core.Installations;
|
||||
using PSLauncher.Core.Manifests;
|
||||
using PSLauncher.Models;
|
||||
|
||||
namespace PSLauncher.Core.Updates;
|
||||
|
||||
public sealed class UpdateChecker : IUpdateChecker
|
||||
{
|
||||
private readonly IManifestService _manifestService;
|
||||
private readonly IInstallationRegistry _registry;
|
||||
private readonly ILogger<UpdateChecker> _logger;
|
||||
|
||||
public UpdateChecker(
|
||||
IManifestService manifestService,
|
||||
IInstallationRegistry registry,
|
||||
ILogger<UpdateChecker> logger)
|
||||
{
|
||||
_manifestService = manifestService;
|
||||
_registry = registry;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<UpdateCheckResult> CheckAsync(CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
var manifest = await _manifestService.FetchAsync(ct).ConfigureAwait(false);
|
||||
var latest = manifest.Versions.FirstOrDefault(v => v.Version == manifest.Latest)
|
||||
?? manifest.Versions
|
||||
.OrderByDescending(v => SemVer.Parse(v.Version))
|
||||
.FirstOrDefault();
|
||||
|
||||
var installed = _registry.Scan();
|
||||
var latestInstalled = installed
|
||||
.OrderByDescending(v => SemVer.Parse(v.Version))
|
||||
.FirstOrDefault();
|
||||
|
||||
var isLatestInstalled = latest is not null && latestInstalled is not null
|
||||
&& latestInstalled.Version == latest.Version;
|
||||
var isNewer = latest is not null
|
||||
&& (latestInstalled is null
|
||||
|| SemVer.Parse(latest.Version).CompareTo(SemVer.Parse(latestInstalled.Version)) > 0);
|
||||
|
||||
return new UpdateCheckResult(manifest, latest, latestInstalled, isLatestInstalled, isNewer, null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Update check failed");
|
||||
return new UpdateCheckResult(null, null, null, false, false, ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user