From 3c519629d2249e54af6da09532be21f9d5b02283 Mon Sep 17 00:00:00 2001 From: EduBox Dev Date: Sun, 28 Jun 2026 22:52:45 +0000 Subject: [PATCH] installer: corrections wizard C# (System.Management, AppContext.BaseDirectory), fix script Inno Setup GetDiskFreeSpaceEx, ajout SUIVI_INSTALLER --- agent/installer/SUIVI_INSTALLER.md | 106 ++++++++++++++++++ agent/installer/setup-wizard/MainForm.cs | 8 +- agent/installer/setup-wizard/README.md | 6 +- .../installer/setup-wizard/SetupWizard.csproj | 6 +- agent/installer/studioE5-agent.iss | 11 +- agent/studioE5-agent-setup-wizard.zip | Bin 0 -> 12552 bytes 6 files changed, 128 insertions(+), 9 deletions(-) create mode 100644 agent/installer/SUIVI_INSTALLER.md create mode 100644 agent/studioE5-agent-setup-wizard.zip diff --git a/agent/installer/SUIVI_INSTALLER.md b/agent/installer/SUIVI_INSTALLER.md new file mode 100644 index 0000000..8256b17 --- /dev/null +++ b/agent/installer/SUIVI_INSTALLER.md @@ -0,0 +1,106 @@ +# Feuille de route — Installateur studioE5 Agent + +## Objectif + +Fournir un **installateur professionnel Windows** pour studioE5 Agent, guidé pas à pas, qui gère les prérequis (WSL2 / Podman) et propose une désinstallation complète. + +## Architecture choisie + +- **Wizard C# Windows Forms (.NET 8)** : `setup-wizard/` + - Détecte les prérequis. + - Installe WSL2 si besoin (avec reprise après redémarrage via `RunOnce`). + - Installe Podman depuis le MSI officiel. + - Initialise et démarre la machine Podman. + - Lance le package Inno Setup de studioE5 Agent. + - Mode désinstallation via `/uninstall`. +- **Package agent (Inno Setup)** : `studioE5-agent.iss` + - Installe `studioE5-agent.exe` + binaires Tailscale. + - Crée les raccourcis. + - Gère la désinstallation. + +## État actuel + +### ✅ Réalisé + +- Wizard C# avec 7 étapes guidées. +- Détection des prérequis : Windows, RAM, disque, WSL2, Podman. +- Installation WSL2 avec redémarrage + reprise automatique. +- Installation Podman via MSI bundlé. +- Configuration Podman (`machine init` + `machine start`). +- Lancement du package Inno Setup agent. +- Mode désinstallation complet. +- Script Inno Setup de base pour l’agent. + +### 🔄 En cours / À tester + +- Compilation et test du wizard sur Windows. +- Packaging final (wizard + MSI Podman + setup agent) en un seul dossier distribuable. + +### ⏳ À venir + +- Signature de l’exécutable pour éviter les alertes SmartScreen. +- Support macOS et Linux. +- Installateur silencieux possible pour déploiement GPO. + +## Build du wizard + +### Prérequis + +- Windows 10/11 +- .NET 8 SDK +- Inno Setup 6 (pour générer `studioE5-agent-setup.exe`) + +### Fichiers à placer + +Dans `setup-wizard/Resources/` : + +```text +podman-installer-windows-amd64.msi +studioE5-agent-setup.exe +``` + +### Commande + +```powershell +cd setup-wizard +dotnet publish -c Release -r win-x64 --self-contained true /p:PublishSingleFile=true +``` + +### Sortie + +```text +setup-wizard\bin\Release\net8.0-windows\win-x64\publish\StudioE5-SetupWizard.exe +``` + +## Build du package agent (Inno Setup) + +Structure attendue : + +```text +agent/ +├── studioE5-agent.exe +├── tailscale-bin/ +│ └── windows/ +│ ├── tailscale.exe +│ ├── tailscaled.exe +│ └── wintun.dll +└── installer/ + └── studioE5-agent.iss +``` + +Ouvrir `studioE5-agent.iss` avec Inno Setup Compiler et compiler (`Ctrl+F9`). + +Le fichier généré se trouve dans `installer-output/`. + +## Notes importantes + +- Le wizard doit être exécuté **en administrateur**. +- L’installation de WSL2 nécessite un **redémarrage** de l’ordinateur. +- Le MSI Podman officiel pour Windows est `podman-installer-windows-amd64.msi`. +- Pour la désinstallation, le MSI Podman doit être présent dans `Resources/`. + +## Liens utiles + +- Releases Podman : +- Inno Setup : +- .NET 8 SDK : diff --git a/agent/installer/setup-wizard/MainForm.cs b/agent/installer/setup-wizard/MainForm.cs index 5e9a5c4..128c5bd 100644 --- a/agent/installer/setup-wizard/MainForm.cs +++ b/agent/installer/setup-wizard/MainForm.cs @@ -412,9 +412,9 @@ public partial class MainForm : Form try { - var msiPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? ".", "Resources", "podman-setup.msi"); + var msiPath = Path.Combine(AppContext.BaseDirectory, "Resources", "podman-installer-windows-amd64.msi"); if (!File.Exists(msiPath)) - throw new FileNotFoundException("Le fichier podman-setup.msi est introuvable. Vérifiez qu'il est bien inclus dans le package."); + throw new FileNotFoundException("Le fichier podman-installer-windows-amd64.msi est introuvable. Vérifiez qu'il est bien inclus dans le package."); RunCommand("msiexec.exe", $"/i \"{msiPath}\" /qn /norestart", "Installation de Podman en cours..."); _state.PodmanInstalled = true; @@ -506,7 +506,7 @@ public partial class MainForm : Form { try { - var setupPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? ".", "Resources", "studioE5-agent-setup.exe"); + var setupPath = Path.Combine(AppContext.BaseDirectory, "Resources", "studioE5-agent-setup.exe"); if (!File.Exists(setupPath)) throw new FileNotFoundException("Le fichier studioE5-agent-setup.exe est introuvable. Vérifiez qu'il est bien inclus dans le package."); @@ -600,7 +600,7 @@ public partial class MainForm : Form } // 4. Uninstall Podman - var podmanMsiPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? ".", "Resources", "podman-setup.msi"); + var podmanMsiPath = Path.Combine(AppContext.BaseDirectory, "Resources", "podman-installer-windows-amd64.msi"); if (File.Exists(podmanMsiPath)) { RunCommand("msiexec.exe", $"/x \"{podmanMsiPath}\" /qn /norestart", "Désinstallation de Podman..."); diff --git a/agent/installer/setup-wizard/README.md b/agent/installer/setup-wizard/README.md index 0d19ecd..2f84b41 100644 --- a/agent/installer/setup-wizard/README.md +++ b/agent/installer/setup-wizard/README.md @@ -30,8 +30,8 @@ setup-wizard/ ├── InstallerState.cs ├── PrerequisiteChecker.cs └── Resources/ - ├── podman-setup.msi # MSI officiel Podman pour Windows - └── studioE5-agent-setup.exe # Package Inno Setup de l'agent + ├── podman-installer-windows-amd64.msi # MSI officiel Podman pour Windows + └── studioE5-agent-setup.exe # Package Inno Setup de l'agent ``` ## Build @@ -58,7 +58,7 @@ bin\Release\net8.0-windows\win-x64\publish\StudioE5-SetupWizard.exe 1. Télécharger le MSI Podman Windows : -2. Le renommer en `podman-setup.msi` et le placer dans `Resources/`. +2. Le renommer en `podman-installer-windows-amd64.msi` et le placer dans `Resources/`. 3. Générer le package Inno Setup de l’agent (`studioE5-agent-setup.exe`) et le placer dans `Resources/`. 4. Builder et publier le wizard. diff --git a/agent/installer/setup-wizard/SetupWizard.csproj b/agent/installer/setup-wizard/SetupWizard.csproj index c21bb9c..43d914f 100644 --- a/agent/installer/setup-wizard/SetupWizard.csproj +++ b/agent/installer/setup-wizard/SetupWizard.csproj @@ -11,7 +11,11 @@ - + + + + + PreserveNewest diff --git a/agent/installer/studioE5-agent.iss b/agent/installer/studioE5-agent.iss index 73e9f9c..c46802a 100644 --- a/agent/installer/studioE5-agent.iss +++ b/agent/installer/studioE5-agent.iss @@ -84,11 +84,20 @@ begin Result := Exec('podman.exe', 'machine list', '', SW_HIDE, ewWaitUntilTerminated, ResultCode) and (ResultCode = 0); end; +function GetDiskFreeSpaceEx( + lpDirectoryName: string; + var lpFreeBytesAvailableToCaller: Int64; + var lpTotalNumberOfBytes: Int64; + var lpTotalNumberOfFreeBytes: Int64 +): Boolean; +external 'GetDiskFreeSpaceExW@kernel32.dll stdcall'; + function GetFreeDiskSpaceMB(const Path: string): Cardinal; var FreeBytes, TotalBytes: Int64; + Dummy: Int64; begin - if GetDiskFreeSpaceEx(Path, FreeBytes, TotalBytes, nil) then + if GetDiskFreeSpaceEx(Path, FreeBytes, TotalBytes, Dummy) then Result := Cardinal(FreeBytes div (1024 * 1024)) else Result := 0; diff --git a/agent/studioE5-agent-setup-wizard.zip b/agent/studioE5-agent-setup-wizard.zip new file mode 100644 index 0000000000000000000000000000000000000000..a9bf299bc1bbec8e7c34b7961e2fbce0b49511d2 GIT binary patch literal 12552 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