v0.12.0 — Report tool auto-deploy from PROSERVE ZIP
Each PROSERVE ZIP can ship a _report/ subfolder alongside _migrations/.
After install + DB migration, the launcher copies _report/ to the local
XAMPP htdocs (default C:\xampp\htdocs\ProserveReport\) so the Reports
tab in the sidebar always serves the matching version.
Service (PSLauncher.Core/ReportTool)
- IReportToolDeployer + ReportToolDeployer.
- Atomic deploy: copy to {target}.new/ → rename current to .old/ →
rename .new → final → cleanup .old. Apache never sees a half-state.
- Skip silencieux si _report/ absent du ZIP (PROSERVE release qui ne
touche pas au Report).
- DeployStatus enum (SkippedNoSourceFolder, XamppNotFound, Failed,
Deployed) renvoyé pour gestion UI claire.
Config (LocalConfig)
- ReportToolConfig (HtdocsRoot, FolderName, AutoDeploy). Defaults
matchent l'install standard ASTERION (C:\xampp\htdocs\ProserveReport).
Install pipeline (MainViewModel)
- Étape 6 après extraction + migrations. Progress reporté via le footer
Library : « 📂 Déploiement Report : 47/120 — assets/main.js ».
XAMPP introuvable → dialog avec lien vers Settings → Avancés.
Settings UI (SettingsDialog → Avancés)
- Nouveau bloc « OUTIL REPORT (XAMPP htdocs) » : 2 champs path + checkbox
AutoDeploy + bouton « 📂 Re-déployer maintenant » qui rejoue le deploy
sur la dernière version installée. Utile après changement de chemin
htdocs ou si l'auto-deploy avait foiré (XAMPP éteint).
Versions bumped to 0.12.0 (App + Updater + installer .iss).
Côté release : tu ajoutes _report/ dans le source de PROSERVE, tu zippes,
tu uploades. Les clients récupèrent ZIP + migrations + Report en une
seule install.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,7 @@ using PSLauncher.Core.Localization;
|
||||
using PSLauncher.Core.Manifests;
|
||||
using PSLauncher.Core.Migrations;
|
||||
using PSLauncher.Core.Process;
|
||||
using PSLauncher.Core.ReportTool;
|
||||
using PSLauncher.Core.Updates;
|
||||
using PSLauncher.Models;
|
||||
|
||||
@@ -41,6 +42,7 @@ public sealed partial class MainViewModel : ObservableObject
|
||||
private readonly ILauncherSelfUpdater _selfUpdater;
|
||||
private readonly IToastService _toastService;
|
||||
private readonly IDatabaseMigrationService _migrationService;
|
||||
private readonly IReportToolDeployer _reportDeployer;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly ILogger<MainViewModel> _logger;
|
||||
|
||||
@@ -223,6 +225,7 @@ public sealed partial class MainViewModel : ObservableObject
|
||||
ILauncherSelfUpdater selfUpdater,
|
||||
IToastService toastService,
|
||||
IDatabaseMigrationService migrationService,
|
||||
IReportToolDeployer reportDeployer,
|
||||
IServiceProvider serviceProvider,
|
||||
ILogger<MainViewModel> logger)
|
||||
{
|
||||
@@ -238,6 +241,7 @@ public sealed partial class MainViewModel : ObservableObject
|
||||
_selfUpdater = selfUpdater;
|
||||
_toastService = toastService;
|
||||
_migrationService = migrationService;
|
||||
_reportDeployer = reportDeployer;
|
||||
_serviceProvider = serviceProvider;
|
||||
_logger = logger;
|
||||
|
||||
@@ -745,6 +749,50 @@ public sealed partial class MainViewModel : ObservableObject
|
||||
}
|
||||
}
|
||||
|
||||
// 6) Déploiement de l'outil Report (page web XAMPP).
|
||||
// Cherche _report/ dans le ZIP extrait et copie vers htdocs en
|
||||
// atomic rename. Skip silencieux si _report/ absent du ZIP.
|
||||
if (_config.ReportTool.AutoDeploy)
|
||||
{
|
||||
StatusMessage = Strings.StatusDeployingReport(row.Version);
|
||||
ProgressDetail = null;
|
||||
ProgressPercent = 0;
|
||||
var rdProgress = new Progress<DeployProgress>(dp =>
|
||||
{
|
||||
if (dp.FilesTotal > 0)
|
||||
{
|
||||
var pct = (double)dp.FilesDone / dp.FilesTotal * 100.0;
|
||||
ProgressPercent = pct;
|
||||
row.ProgressPercent = pct;
|
||||
}
|
||||
var label = Strings.ProgressReportDeploy(dp.FilesDone, dp.FilesTotal, dp.CurrentFilename);
|
||||
ProgressDetail = label;
|
||||
row.ProgressDetail = label;
|
||||
});
|
||||
var rdResult = await _reportDeployer.DeployAsync(target, rdProgress, ct);
|
||||
switch (rdResult.Status)
|
||||
{
|
||||
case DeployStatus.Deployed:
|
||||
_logger.LogInformation("Report tool deployed ({N} files) for v{Version}", rdResult.FilesDeployed, row.Version);
|
||||
break;
|
||||
case DeployStatus.SkippedNoSourceFolder:
|
||||
_logger.LogInformation("No _report/ in v{Version} ZIP, report tool unchanged", row.Version);
|
||||
break;
|
||||
case DeployStatus.XamppNotFound:
|
||||
MessageBox.Show(
|
||||
Strings.MsgReportXamppNotFound(_config.ReportTool.HtdocsRoot),
|
||||
Strings.MsgBoxReportDeployFailed,
|
||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
break;
|
||||
case DeployStatus.Failed:
|
||||
MessageBox.Show(
|
||||
Strings.MsgReportDeployFailed(rdResult.ErrorMessage ?? "?"),
|
||||
Strings.MsgBoxReportDeployFailed,
|
||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
StatusMessage = Strings.StatusInstalledSuccess(row.Version);
|
||||
ProgressDetail = null;
|
||||
ProgressPercent = 0;
|
||||
|
||||
@@ -12,6 +12,7 @@ using PSLauncher.Core.Installations;
|
||||
using PSLauncher.Core.Licensing;
|
||||
using PSLauncher.Core.Localization;
|
||||
using PSLauncher.Core.Migrations;
|
||||
using PSLauncher.Core.ReportTool;
|
||||
using PSLauncher.Models;
|
||||
|
||||
namespace PSLauncher.App.ViewModels;
|
||||
@@ -30,6 +31,7 @@ public sealed partial class SettingsViewModel : ObservableObject
|
||||
private readonly ILicenseService _licenseService;
|
||||
private readonly IDownloadStateStore _downloadStore;
|
||||
private readonly IDatabaseMigrationService _migrationService;
|
||||
private readonly IReportToolDeployer _reportDeployer;
|
||||
private readonly IInstallationRegistry _registry;
|
||||
private readonly HttpClient _http;
|
||||
private readonly ILogger<SettingsViewModel> _logger;
|
||||
@@ -57,6 +59,13 @@ public sealed partial class SettingsViewModel : ObservableObject
|
||||
[ObservableProperty] private string? _migrationsReplayStatus;
|
||||
[ObservableProperty] private bool _isReplayingMigrations;
|
||||
|
||||
// ----- Report Tool deploy settings -----
|
||||
[ObservableProperty] private string _reportHtdocsRoot;
|
||||
[ObservableProperty] private string _reportFolderName;
|
||||
[ObservableProperty] private bool _reportAutoDeploy;
|
||||
[ObservableProperty] private string? _reportDeployStatus;
|
||||
[ObservableProperty] private bool _isDeployingReport;
|
||||
|
||||
public IReadOnlyList<LanguageOption> AvailableLanguages { get; } =
|
||||
PSLauncher.Core.Localization.Strings.Available
|
||||
.Select(t => new LanguageOption(t.Code, t.Name))
|
||||
@@ -124,6 +133,7 @@ public sealed partial class SettingsViewModel : ObservableObject
|
||||
ILicenseService licenseService,
|
||||
IDownloadStateStore downloadStore,
|
||||
IDatabaseMigrationService migrationService,
|
||||
IReportToolDeployer reportDeployer,
|
||||
IInstallationRegistry registry,
|
||||
HttpClient http,
|
||||
ILogger<SettingsViewModel> logger)
|
||||
@@ -133,6 +143,7 @@ public sealed partial class SettingsViewModel : ObservableObject
|
||||
_licenseService = licenseService;
|
||||
_downloadStore = downloadStore;
|
||||
_migrationService = migrationService;
|
||||
_reportDeployer = reportDeployer;
|
||||
_registry = registry;
|
||||
_http = http;
|
||||
_logger = logger;
|
||||
@@ -150,6 +161,10 @@ public sealed partial class SettingsViewModel : ObservableObject
|
||||
_dbName = config.Database.Database;
|
||||
_dbAutoApplyMigrations = config.Database.AutoApplyMigrations;
|
||||
|
||||
_reportHtdocsRoot = config.ReportTool.HtdocsRoot;
|
||||
_reportFolderName = config.ReportTool.FolderName;
|
||||
_reportAutoDeploy = config.ReportTool.AutoDeploy;
|
||||
|
||||
LauncherVersion = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "0.0.0";
|
||||
MachineId = licenseService.GetMachineId();
|
||||
LogsDirectory = App.LogsDirectory;
|
||||
@@ -269,6 +284,10 @@ public sealed partial class SettingsViewModel : ObservableObject
|
||||
_config.Database.Database = DbName.Trim();
|
||||
_config.Database.AutoApplyMigrations = DbAutoApplyMigrations;
|
||||
|
||||
_config.ReportTool.HtdocsRoot = ReportHtdocsRoot.Trim();
|
||||
_config.ReportTool.FolderName = ReportFolderName.Trim();
|
||||
_config.ReportTool.AutoDeploy = ReportAutoDeploy;
|
||||
|
||||
_configStore.Save(_config);
|
||||
_logger.LogInformation("Settings saved (language={Lang}, changed={Changed})", _config.Language, languageChanged);
|
||||
|
||||
@@ -334,6 +353,53 @@ public sealed partial class SettingsViewModel : ObservableObject
|
||||
|
||||
private static string FormatSize(long bytes) => Strings.FormatSize(bytes);
|
||||
|
||||
/// <summary>
|
||||
/// Re-déploie l'outil Report depuis la dernière version PROSERVE installée
|
||||
/// vers htdocs. Utile si le déploiement post-install avait échoué (XAMPP
|
||||
/// déplacé, htdocs absent), ou pour resync après changement du chemin htdocs
|
||||
/// dans Settings.
|
||||
/// </summary>
|
||||
[RelayCommand]
|
||||
private async Task RedeployReportAsync()
|
||||
{
|
||||
var installed = _registry.Scan().OrderByDescending(v => v.Version).FirstOrDefault();
|
||||
if (installed is null)
|
||||
{
|
||||
ReportDeployStatus = "Aucune version installée — installe d'abord une version qui contient _report/.";
|
||||
return;
|
||||
}
|
||||
IsDeployingReport = true;
|
||||
ReportDeployStatus = $"Déploiement depuis v{installed.Version}…";
|
||||
try
|
||||
{
|
||||
// Persiste les éventuels changements de chemin avant le deploy
|
||||
Save();
|
||||
var progress = new Progress<DeployProgress>(dp =>
|
||||
{
|
||||
if (dp.FilesTotal > 0)
|
||||
ReportDeployStatus = $"{dp.FilesDone}/{dp.FilesTotal} : {dp.CurrentFilename}";
|
||||
});
|
||||
var result = await _reportDeployer.DeployAsync(installed.FolderPath, progress, CancellationToken.None);
|
||||
ReportDeployStatus = result.Status switch
|
||||
{
|
||||
DeployStatus.Deployed => $"✓ {result.FilesDeployed} fichiers déployés vers {result.TargetPath}",
|
||||
DeployStatus.SkippedNoSourceFolder => $"⚠ La version v{installed.Version} ne contient pas de _report/ — rien à déployer",
|
||||
DeployStatus.XamppNotFound => $"✗ {result.ErrorMessage}",
|
||||
DeployStatus.Failed => $"✗ {result.ErrorMessage}",
|
||||
_ => "?",
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Redeploy report failed");
|
||||
ReportDeployStatus = $"✗ {ex.Message}";
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsDeployingReport = false;
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== DB MIGRATIONS =====================
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user