v0.13.0 — Report tool : timestamped backups + manual revert

The previous deploy mechanism kept the old version under {target}.old
just long enough to swap, then deleted it — no way back if the new
report had a runtime bug only visible at use time.

Now each deploy keeps the previous active version under
{FolderName}.backup-yyyyMMdd-HHmmss/. The N most recent are kept
(MaxBackups, default 3) and older ones are pruned in best-effort.

UI in Settings → Avancés → Outil Report :
- "Conserver N backups" input (0 = no history, back to v0.12 behavior)
- List of backups with date + size + cryptic folder name + per-row "↶ Revert"
- Confirmation dialog: "Revert to {date}? The currently deployed
  version will itself be saved as a new backup, so you can switch back."

Implementation
- IReportToolDeployer: + ListBackupsAsync, + RevertAsync, + BackupInfo,
  + DeployStatus.BackupNotFound.
- ReportToolDeployer: deploy renames {target} → backup-{ts}; PruneOldBackups
  trims to MaxBackups; RevertAsync atomically swaps current ↔ chosen
  backup, the previous current becoming itself a fresh backup.
- ReportBackupViewModel + DataTemplate for the list rows.
- Strings: backup labels, "↶ Revert", confirmation message.

Caveats documented in the design discussion:
- Backups cover ONLY the report tool files. DB migrations are not reverted
  (irreversible). If a migration broke the schema, that's a separate fix.
- No HTTP-HEAD post-deploy auto-revert: too fragile for false positives.
  Revert stays a deliberate manual action.

Versions bumped to 0.13.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-03 09:15:09 +02:00
parent 67f422539d
commit 562b4e5ed0
9 changed files with 359 additions and 31 deletions

View File

@@ -511,6 +511,36 @@ public static class Strings
"نشر أداة التقارير تلقائياً عند تثبيت نسخة جديدة"
);
public static string SettingsReportRedeploy => T("📂 Re-déployer le Report maintenant", "📂 Re-deploy Report now", "📂 立即重新部署报告", "📂 ปรับใช้รายงานใหม่เดี๋ยวนี้", "📂 إعادة نشر التقرير الآن");
// ---- Backups Report tool ----
public static string SettingsReportBackups => T("Backups disponibles", "Available backups", "可用备份", "สำรองข้อมูลที่มี", "النسخ الاحتياطية المتاحة");
public static string SettingsReportMaxBackups => T("Conserver N backups", "Keep N backups", "保留 N 个备份", "เก็บสำรอง N ชุด", "الاحتفاظ بـ N نسخة احتياطية");
public static string SettingsReportRevert => T("↶ Revert", "↶ Revert", "↶ 还原", "↶ ย้อนกลับ", "↶ استرجاع");
public static string SettingsReportNoBackups => T(
"Aucun backup pour l'instant. Le premier sera créé au prochain deploy.",
"No backups yet. The first one will be created on the next deploy.",
"暂无备份。下次部署时将创建第一个。",
"ยังไม่มีสำรองข้อมูล จะสร้างครั้งแรกในการปรับใช้ครั้งถัดไป",
"لا توجد نسخ احتياطية بعد. سيتم إنشاء أول نسخة عند النشر التالي."
);
public static string SettingsReportBackupSize(string size) => T(
$"taille : {size}", $"size: {size}", $"大小:{size}", $"ขนาด: {size}", $"الحجم: {size}"
);
public static string MsgBoxRevertReport => T("Revert Outil Report", "Revert Report tool", "还原报告工具", "ย้อนกลับเครื่องมือรายงาน", "استرجاع أداة التقارير");
public static string MsgRevertReportConfirm(string date) => T(
$"Revenir à la version du {date} ?\n\nLa version actuellement déployée sera elle-même sauvegardée comme nouveau backup, donc tu pourras y revenir si besoin.",
$"Revert to the version from {date}?\n\nThe currently deployed version will itself be saved as a new backup, so you can switch back if needed.",
$"还原到 {date} 的版本?\n\n当前已部署的版本将作为新备份保存因此您可以在需要时切换回去。",
$"ย้อนกลับไปยังเวอร์ชันของ {date}?\n\nเวอร์ชันที่ปรับใช้อยู่ในปัจจุบันจะถูกบันทึกเป็นสำรองใหม่ ดังนั้นคุณสามารถสลับกลับได้หากต้องการ",
$"العودة إلى نسخة {date}؟\n\nسيتم حفظ النسخة المنشورة حالياً كنسخة احتياطية جديدة، لذا يمكنك التبديل إليها إذا لزم الأمر."
);
public static string MsgRevertReportSuccess(string date) => T(
$"✓ Reverted vers la version du {date}",
$"✓ Reverted to version from {date}",
$"✓ 已还原到 {date} 的版本",
$"✓ ย้อนกลับเป็นเวอร์ชันของ {date} แล้ว",
$"✓ تم الاسترجاع إلى نسخة {date}"
);
public static string MsgMigrationFailed(string detail) => T(
$"Une migration de la base de données a échoué :\n\n{detail}\n\nLa version a été extraite mais la base n'a PAS été modifiée (rollback automatique).\nVérifie que XAMPP est démarré et que les paramètres de connexion sont corrects (Settings → Base de données), puis clique « Rejouer les migrations » dans Settings.",
$"A database migration failed:\n\n{detail}\n\nThe version was extracted but the database was NOT modified (automatic rollback).\nMake sure XAMPP is running and the connection settings are correct (Settings → Database), then click « Replay migrations » in Settings.",