V1
This commit is contained in:
commit
ab17ec1e48
4 files changed
+520
No files matched your search
+83
@@ -0,0 +1,83 @@
|
|||||||
|
# Installation de wsl-manage
|
||||||
|
|
||||||
|
## 1. Placer les fichiers
|
||||||
|
|
||||||
|
Conservez les fichiers suivants dans un dossier permanent, par exemple :
|
||||||
|
|
||||||
|
```text
|
||||||
|
E:\Drive\Apps\WSL\
|
||||||
|
|-- wsl-manage.cmd
|
||||||
|
|-- wsl-manage-core.ps1
|
||||||
|
|-- README.md
|
||||||
|
`-- Install.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Ne placez pas ce dossier dans un emplacement temporaire.
|
||||||
|
|
||||||
|
## 2. Ajouter le dossier au PATH
|
||||||
|
|
||||||
|
Ouvrez PowerShell et executez :
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$userPath = [Environment]::GetEnvironmentVariable('Path', 'User')
|
||||||
|
$installPath = 'E:\Drive\Apps\WSL'
|
||||||
|
|
||||||
|
if (($userPath -split ';') -notcontains $installPath) {
|
||||||
|
[Environment]::SetEnvironmentVariable(
|
||||||
|
'Path',
|
||||||
|
(($userPath.TrimEnd(';') + ';' + $installPath).Trim(';')),
|
||||||
|
'User'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Fermez puis ouvrez un nouveau terminal PowerShell pour que le changement soit
|
||||||
|
pris en compte.
|
||||||
|
|
||||||
|
## 3. Verifier l'installation
|
||||||
|
|
||||||
|
Executez :
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Get-Command wsl-manage
|
||||||
|
```
|
||||||
|
|
||||||
|
La commande doit pointer vers :
|
||||||
|
|
||||||
|
```text
|
||||||
|
E:\Drive\Apps\WSL\wsl-manage.cmd
|
||||||
|
```
|
||||||
|
|
||||||
|
Affichez ensuite l'aide :
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
wsl-manage -?
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. Utiliser le script
|
||||||
|
|
||||||
|
Sauvegarde par defaut :
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
wsl-manage
|
||||||
|
```
|
||||||
|
|
||||||
|
Restauration :
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
wsl-manage restore
|
||||||
|
```
|
||||||
|
|
||||||
|
## Depannage
|
||||||
|
|
||||||
|
Si PowerShell tente de charger directement un fichier `.ps1`, verifiez que le
|
||||||
|
fichier public s'appelle bien `wsl-manage.cmd` et que seul le dossier
|
||||||
|
d'installation est present dans le `PATH`.
|
||||||
|
|
||||||
|
Le lanceur utilise `-ExecutionPolicy Bypass` uniquement pour cette execution.
|
||||||
|
Il n'est normalement pas necessaire de modifier la politique d'execution
|
||||||
|
PowerShell du systeme.
|
||||||
|
|
||||||
|
L'execution en administrateur n'est pas requise pour une sauvegarde standard.
|
||||||
|
Elle peut etre necessaire pour acceder a certains fichiers VHDX ou les
|
||||||
|
compacter.
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
# wsl-manage
|
||||||
|
|
||||||
|
Outil PowerShell interactif de sauvegarde et de restauration des distributions WSL.
|
||||||
|
Il gere aussi les donnees Docker Desktop lorsque la distribution `docker` ou
|
||||||
|
`docker-desktop` est selectionnee.
|
||||||
|
|
||||||
|
## Fichiers
|
||||||
|
|
||||||
|
- `wsl-manage.cmd` : commande a lancer.
|
||||||
|
- `wsl-manage-core.ps1` : implementation PowerShell.
|
||||||
|
|
||||||
|
Le dossier contenant ces fichiers doit etre dans le `PATH` Windows pour pouvoir
|
||||||
|
lancer `wsl-manage` depuis n'importe quel terminal.
|
||||||
|
|
||||||
|
## Prerequis
|
||||||
|
|
||||||
|
- Windows avec WSL installe et accessible par `wsl.exe`.
|
||||||
|
- PowerShell.
|
||||||
|
- Docker Desktop doit etre completement arrete lorsqu'une sauvegarde ou une
|
||||||
|
restauration Docker est concernee.
|
||||||
|
- Le lecteur de destination doit etre accessible et disposer de suffisamment
|
||||||
|
d'espace libre.
|
||||||
|
|
||||||
|
L'execution en administrateur n'est normalement pas necessaire. Elle peut etre
|
||||||
|
necessaire pour acceder a certains fichiers VHDX ou effectuer un compactage.
|
||||||
|
|
||||||
|
## Sauvegarde
|
||||||
|
|
||||||
|
Lancer sans argument :
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
wsl-manage
|
||||||
|
```
|
||||||
|
|
||||||
|
Le script :
|
||||||
|
|
||||||
|
1. Liste les distributions WSL disponibles.
|
||||||
|
2. Demande de sauvegarder toutes les distributions ou une seule.
|
||||||
|
3. Demande le lecteur de destination et affiche son espace libre.
|
||||||
|
4. Demande confirmation.
|
||||||
|
5. Arrete WSL avec `wsl --shutdown`.
|
||||||
|
6. Exporte les distributions selectionnees au format `.tar`.
|
||||||
|
7. Affiche une progression estimee et le temps ecoule.
|
||||||
|
|
||||||
|
Les sauvegardes sont rangees dans un dossier date sous `WSL-Backup` :
|
||||||
|
|
||||||
|
```text
|
||||||
|
D:\WSL-Backup\2026-09-16_20-05-42\
|
||||||
|
|-- Ubuntu.tar
|
||||||
|
|-- Debian.tar
|
||||||
|
`-- ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Le pourcentage affiche est une estimation basee sur l'espace utilise par la
|
||||||
|
distribution. `wsl.exe --export` ne fournit pas de progression native.
|
||||||
|
|
||||||
|
## Donnees Docker Desktop
|
||||||
|
|
||||||
|
Si `docker` ou `docker-desktop` est selectionne, le script :
|
||||||
|
|
||||||
|
1. Verifie que Docker Desktop est arrete.
|
||||||
|
2. Demande le chemin des donnees Docker.
|
||||||
|
3. Utilise par defaut :
|
||||||
|
|
||||||
|
```text
|
||||||
|
E:\WSL\Docker\desktop-data
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Verifie la presence des dossiers `disk` et `main`.
|
||||||
|
5. Copie ces dossiers dans :
|
||||||
|
|
||||||
|
```text
|
||||||
|
D:\WSL-Backup\2026-09-16_20-05-42\Docker\
|
||||||
|
|-- disk\
|
||||||
|
`-- main\
|
||||||
|
```
|
||||||
|
|
||||||
|
La distribution `docker-desktop` exportee en `.tar` contient principalement
|
||||||
|
les fichiers systeme. Les images, conteneurs et volumes Docker sont dans les
|
||||||
|
donnees `disk` et `main`.
|
||||||
|
|
||||||
|
## Restauration
|
||||||
|
|
||||||
|
Lancer avec l'argument `restore` :
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
wsl-manage restore
|
||||||
|
```
|
||||||
|
|
||||||
|
Le script :
|
||||||
|
|
||||||
|
1. Demande le lecteur contenant `WSL-Backup`.
|
||||||
|
2. Demande la date de sauvegarde.
|
||||||
|
3. Demande une distribution ou `Toutes les machines`.
|
||||||
|
4. Demande confirmation apres recapitulatif.
|
||||||
|
5. Arrete WSL.
|
||||||
|
6. Importe les distributions sous `WSL-Install\DATE`.
|
||||||
|
7. Restaure les donnees Docker uniquement si `docker` ou `docker-desktop` est
|
||||||
|
selectionne.
|
||||||
|
|
||||||
|
Une distribution restauree porte le nom de son fichier sans extension :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Ubuntu.tar -> Ubuntu
|
||||||
|
```
|
||||||
|
|
||||||
|
Le dossier d'installation d'une distribution ne doit pas deja exister.
|
||||||
|
|
||||||
|
## Conseils
|
||||||
|
|
||||||
|
- Quitter Docker Desktop depuis son icone de notification avant une operation
|
||||||
|
Docker.
|
||||||
|
- Ne pas interrompre la copie des fichiers VHDX.
|
||||||
|
- Conserver une copie de secours des donnees Docker avant restauration.
|
||||||
|
- Les anciennes sauvegardes ne contenant pas de dossier `Docker` ne permettent
|
||||||
|
pas de restaurer les donnees Docker.
|
||||||
@@ -0,0 +1,318 @@
|
|||||||
|
# Gestion interactive des distributions WSL.
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[ValidateSet('backup', 'restore')]
|
||||||
|
[string]$Mode = 'backup'
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
function Stop-WithMessage {
|
||||||
|
param([string]$Message)
|
||||||
|
Write-Error $Message
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Get-Command wsl.exe -ErrorAction SilentlyContinue)) {
|
||||||
|
Stop-WithMessage 'wsl.exe est introuvable. Lancez ce script depuis Windows avec WSL installe.'
|
||||||
|
}
|
||||||
|
|
||||||
|
function Invoke-Wsl {
|
||||||
|
param([string[]]$Arguments)
|
||||||
|
|
||||||
|
$output = & wsl.exe @Arguments 2>&1
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
$details = (($output | ForEach-Object { $_.ToString() }) -join ' ').Trim()
|
||||||
|
if (-not $details) { $details = 'aucun detail fourni par wsl.exe' }
|
||||||
|
throw ('wsl.exe a retourne le code {0} : {1}' -f $LASTEXITCODE, $details)
|
||||||
|
}
|
||||||
|
return $output
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-Distros {
|
||||||
|
@(Invoke-Wsl @('--list', '--quiet') | ForEach-Object {
|
||||||
|
($_.ToString() -replace '\x00', '').Trim()
|
||||||
|
} | Where-Object { $_ })
|
||||||
|
}
|
||||||
|
|
||||||
|
function Select-Drive {
|
||||||
|
$drives = @(Get-PSDrive -PSProvider FileSystem | Where-Object {
|
||||||
|
$_.Root -and (Test-Path -LiteralPath $_.Root)
|
||||||
|
} | Sort-Object Name)
|
||||||
|
|
||||||
|
if ($drives.Count -eq 0) {
|
||||||
|
Stop-WithMessage 'Aucun disque accessible n''a ete trouve.'
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host 'Disques disponibles :' -ForegroundColor Cyan
|
||||||
|
for ($i = 0; $i -lt $drives.Count; $i++) {
|
||||||
|
$drive = New-Object -TypeName System.IO.DriveInfo -ArgumentList $drives[$i].Root
|
||||||
|
$free = '{0:N2} Go libres' -f ($drive.AvailableFreeSpace / 1GB)
|
||||||
|
Write-Host ('[{0}] {1} ({2}) - {3}' -f ($i + 1), $drives[$i].Name, $drives[$i].Root, $free)
|
||||||
|
}
|
||||||
|
|
||||||
|
$selection = Read-Host ('Choisissez le disque (1-{0})' -f $drives.Count)
|
||||||
|
$driveIndex = 0
|
||||||
|
if (-not [int]::TryParse($selection, [ref]$driveIndex) -or
|
||||||
|
$driveIndex -lt 1 -or $driveIndex -gt $drives.Count) {
|
||||||
|
Stop-WithMessage 'Selection de disque invalide.'
|
||||||
|
}
|
||||||
|
return $drives[$driveIndex - 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
function Confirm-Action {
|
||||||
|
param([string]$Message)
|
||||||
|
$answer = Read-Host "$Message (o/n)"
|
||||||
|
return $answer -match '^(o|oui|y|yes)$'
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-DockerDataPath {
|
||||||
|
param([bool]$RequireData)
|
||||||
|
$defaultPath = 'E:\WSL\Docker\desktop-data'
|
||||||
|
$path = Read-Host ('Chemin des donnees Docker Desktop [{0}]' -f $defaultPath)
|
||||||
|
if (-not $path) { $path = $defaultPath }
|
||||||
|
$path = [IO.Path]::GetFullPath($path)
|
||||||
|
if ($RequireData -and (-not (Test-Path -LiteralPath (Join-Path $path 'disk')) -or
|
||||||
|
-not (Test-Path -LiteralPath (Join-Path $path 'main')))) {
|
||||||
|
Stop-WithMessage ('Le chemin Docker doit contenir les dossiers disk et main : {0}' -f $path)
|
||||||
|
}
|
||||||
|
if (-not $RequireData -and -not (Test-Path -LiteralPath $path)) {
|
||||||
|
New-Item -ItemType Directory -Path $path -Force | Out-Null
|
||||||
|
}
|
||||||
|
return $path
|
||||||
|
}
|
||||||
|
|
||||||
|
function Assert-DockerStopped {
|
||||||
|
$dockerProcesses = @(Get-Process -ErrorAction SilentlyContinue | Where-Object {
|
||||||
|
$_.ProcessName -in @('Docker Desktop', 'com.docker.backend', 'com.docker.proxy', 'com.docker.build')
|
||||||
|
})
|
||||||
|
if ($dockerProcesses.Count -gt 0) {
|
||||||
|
Stop-WithMessage 'Docker Desktop est encore en cours d''execution. Quittez-le completement puis relancez la commande.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Copy-DockerData {
|
||||||
|
param([string]$SourceRoot, [string]$DestinationRoot)
|
||||||
|
New-Item -ItemType Directory -Path $DestinationRoot -Force | Out-Null
|
||||||
|
foreach ($folder in @('disk', 'main')) {
|
||||||
|
$source = Join-Path $SourceRoot $folder
|
||||||
|
$destination = Join-Path $DestinationRoot $folder
|
||||||
|
New-Item -ItemType Directory -Path $destination -Force | Out-Null
|
||||||
|
& robocopy.exe $source $destination /E /COPY:DAT /DCOPY:DAT /R:2 /W:2 /XJ
|
||||||
|
if ($LASTEXITCODE -gt 7) {
|
||||||
|
throw ('La copie Docker a echoue pour {0} (code robocopy {1}).' -f $folder, $LASTEXITCODE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-DistroSize {
|
||||||
|
param([string]$Distro)
|
||||||
|
try {
|
||||||
|
$sizeOutput = & wsl.exe --distribution $Distro -- df -B1 --output=used / 2>$null
|
||||||
|
$value = @($sizeOutput | ForEach-Object { $_.ToString() -replace '\x00', '' } |
|
||||||
|
Where-Object { $_ -match '^\s*\d+\s*$' } | Select-Object -Last 1)
|
||||||
|
if ($value) { return [int64]$value.Trim() }
|
||||||
|
} catch { }
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-Distro {
|
||||||
|
param(
|
||||||
|
[string]$Distro,
|
||||||
|
[string]$Archive,
|
||||||
|
[int64]$EstimatedBytes
|
||||||
|
)
|
||||||
|
|
||||||
|
$psi = New-Object System.Diagnostics.ProcessStartInfo
|
||||||
|
$psi.FileName = 'wsl.exe'
|
||||||
|
$psi.Arguments = '--export "' + $Distro.Replace('"', '\"') + '" "' + $Archive.Replace('"', '\"') + '"'
|
||||||
|
$psi.UseShellExecute = $false
|
||||||
|
$psi.CreateNoWindow = $true
|
||||||
|
$psi.RedirectStandardOutput = $true
|
||||||
|
$psi.RedirectStandardError = $true
|
||||||
|
$process = New-Object System.Diagnostics.Process
|
||||||
|
$process.StartInfo = $psi
|
||||||
|
[void]$process.Start()
|
||||||
|
$stdoutTask = $process.StandardOutput.ReadToEndAsync()
|
||||||
|
$stderrTask = $process.StandardError.ReadToEndAsync()
|
||||||
|
$started = Get-Date
|
||||||
|
do {
|
||||||
|
Start-Sleep -Milliseconds 500
|
||||||
|
$percent = 0
|
||||||
|
if ($EstimatedBytes -gt 0 -and (Test-Path -LiteralPath $Archive)) {
|
||||||
|
$percent = [math]::Min(99, [int](100 * ((Get-Item -LiteralPath $Archive).Length / $EstimatedBytes)))
|
||||||
|
}
|
||||||
|
$elapsed = ((Get-Date) - $started).ToString('hh\:mm\:ss')
|
||||||
|
Write-Progress -Activity ('Export de {0}' -f $Distro) -Status ('{0}% - {1} ecoule' -f $percent, $elapsed) -PercentComplete $percent
|
||||||
|
} while (-not $process.HasExited)
|
||||||
|
$process.WaitForExit()
|
||||||
|
Write-Progress -Activity ('Export de {0}' -f $Distro) -Completed
|
||||||
|
if ($process.ExitCode -ne 0) {
|
||||||
|
$details = $stderrTask.Result.Trim()
|
||||||
|
if (-not $details) { $details = 'aucun detail fourni par wsl.exe' }
|
||||||
|
throw ('wsl.exe a retourne le code {0} : {1}' -f $process.ExitCode, $details)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($Mode -eq 'restore') {
|
||||||
|
$drive = Select-Drive
|
||||||
|
$backupRoot = Join-Path $drive.Root 'WSL-Backup'
|
||||||
|
$dates = @(Get-ChildItem -LiteralPath $backupRoot -Directory -ErrorAction SilentlyContinue |
|
||||||
|
Sort-Object Name -Descending)
|
||||||
|
if ($dates.Count -eq 0) {
|
||||||
|
Stop-WithMessage ('Aucune sauvegarde trouvee dans {0}.' -f $backupRoot)
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host 'Sauvegardes disponibles :' -ForegroundColor Cyan
|
||||||
|
for ($i = 0; $i -lt $dates.Count; $i++) {
|
||||||
|
Write-Host ('[{0}] {1}' -f ($i + 1), $dates[$i].Name)
|
||||||
|
}
|
||||||
|
$dateSelection = Read-Host ('Choisissez une date (1-{0})' -f $dates.Count)
|
||||||
|
$dateIndex = 0
|
||||||
|
if (-not [int]::TryParse($dateSelection, [ref]$dateIndex) -or
|
||||||
|
$dateIndex -lt 1 -or $dateIndex -gt $dates.Count) {
|
||||||
|
Stop-WithMessage 'Selection de date invalide.'
|
||||||
|
}
|
||||||
|
$dateFolder = $dates[$dateIndex - 1]
|
||||||
|
$dockerBackupPath = Join-Path $dateFolder.FullName 'Docker'
|
||||||
|
$archives = @(Get-ChildItem -LiteralPath $dateFolder.FullName -File |
|
||||||
|
Where-Object { $_.Name -match '\.tar(\.gz|\.xz)?$' } | Sort-Object Name)
|
||||||
|
if ($archives.Count -eq 0) {
|
||||||
|
Stop-WithMessage 'Cette sauvegarde ne contient aucune archive exploitable.'
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host 'Machines disponibles :' -ForegroundColor Cyan
|
||||||
|
Write-Host '[1] Toutes les machines'
|
||||||
|
for ($i = 0; $i -lt $archives.Count; $i++) {
|
||||||
|
Write-Host ('[{0}] {1}' -f ($i + 2), $archives[$i].Name)
|
||||||
|
}
|
||||||
|
$machineSelection = Read-Host ('Choisissez une machine (1-{0})' -f ($archives.Count + 1))
|
||||||
|
$machineIndex = 0
|
||||||
|
if (-not [int]::TryParse($machineSelection, [ref]$machineIndex) -or
|
||||||
|
$machineIndex -lt 1 -or $machineIndex -gt ($archives.Count + 1)) {
|
||||||
|
Stop-WithMessage 'Selection de machine invalide.'
|
||||||
|
}
|
||||||
|
if ($machineIndex -eq 1) { $selectedArchives = $archives } else { $selectedArchives = @($archives[$machineIndex - 2]) }
|
||||||
|
|
||||||
|
$restoreDocker = @($selectedArchives | Where-Object {
|
||||||
|
(($_.Name -replace '\.tar(\.gz|\.xz)?$', '') -ieq 'docker' -or
|
||||||
|
($_.Name -replace '\.tar(\.gz|\.xz)?$', '') -ieq 'docker-desktop')
|
||||||
|
}).Count -gt 0
|
||||||
|
if ($restoreDocker) {
|
||||||
|
if (-not (Test-Path -LiteralPath (Join-Path $dockerBackupPath 'disk')) -or
|
||||||
|
-not (Test-Path -LiteralPath (Join-Path $dockerBackupPath 'main'))) {
|
||||||
|
Stop-WithMessage 'Cette sauvegarde ne contient pas les donnees Docker (disk et main).'
|
||||||
|
}
|
||||||
|
Assert-DockerStopped
|
||||||
|
$dockerDataPath = Get-DockerDataPath $false
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host 'Recapitulatif :' -ForegroundColor Cyan
|
||||||
|
Write-Host (' Date : {0}' -f $dateFolder.Name)
|
||||||
|
Write-Host (' Machine(s) : {0}' -f (($selectedArchives | ForEach-Object { $_.BaseName -replace '\.tar$' }) -join ', '))
|
||||||
|
if ($restoreDocker) { Write-Host (' Donnees Docker : {0}' -f $dockerDataPath) }
|
||||||
|
Write-Host (' Lecteur : {0}' -f $drive.Root)
|
||||||
|
if (-not (Confirm-Action 'Lancer la restauration')) { Write-Host 'Operation annulee.'; exit 0 }
|
||||||
|
|
||||||
|
try { Invoke-Wsl @('--shutdown') | Out-Null } catch { Stop-WithMessage ('Impossible d''arreter WSL : {0}' -f $_.Exception.Message) }
|
||||||
|
if ($restoreDocker) { try {
|
||||||
|
Copy-DockerData -SourceRoot $dockerBackupPath -DestinationRoot $dockerDataPath
|
||||||
|
Write-Host 'Donnees Docker restaurees.' -ForegroundColor Green
|
||||||
|
} catch { Stop-WithMessage ('Restauration Docker impossible : {0}' -f $_.Exception.Message) } }
|
||||||
|
$failed = @()
|
||||||
|
$installRoot = Join-Path $drive.Root ('WSL-Install\' + $dateFolder.Name)
|
||||||
|
New-Item -ItemType Directory -Path $installRoot -Force | Out-Null
|
||||||
|
foreach ($archive in $selectedArchives) {
|
||||||
|
$name = $archive.Name -replace '\.tar(\.gz|\.xz)?$', ''
|
||||||
|
$installPath = Join-Path $installRoot $name
|
||||||
|
try {
|
||||||
|
if (Test-Path -LiteralPath $installPath) {
|
||||||
|
throw ('Le dossier d''installation existe deja : {0}' -f $installPath)
|
||||||
|
}
|
||||||
|
Invoke-Wsl @('--import', $name, $installPath, $archive.FullName) | Out-Null
|
||||||
|
Write-Host ('Restauration OK : {0}' -f $name) -ForegroundColor Green
|
||||||
|
} catch { $failed += $name; Write-Warning ('Echec pour {0} : {1}' -f $name, $_.Exception.Message) }
|
||||||
|
}
|
||||||
|
if ($failed.Count -gt 0) { Stop-WithMessage ('Restauration terminee avec erreurs : {0}' -f ($failed -join ', ')) }
|
||||||
|
Write-Host 'Restauration terminee avec succes.' -ForegroundColor Green
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
$distros = Get-Distros
|
||||||
|
|
||||||
|
if ($distros.Count -eq 0) {
|
||||||
|
Stop-WithMessage 'Aucune distribution WSL n''a ete trouvee.'
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host 'Distributions a sauvegarder :' -ForegroundColor Cyan
|
||||||
|
Write-Host '[1] Toutes les distributions'
|
||||||
|
for ($i = 0; $i -lt $distros.Count; $i++) {
|
||||||
|
Write-Host ('[{0}] {1}' -f ($i + 2), $distros[$i])
|
||||||
|
}
|
||||||
|
$distroSelection = Read-Host ('Choisissez une option (1-{0})' -f ($distros.Count + 1))
|
||||||
|
$distroIndex = 0
|
||||||
|
if (-not [int]::TryParse($distroSelection, [ref]$distroIndex) -or
|
||||||
|
$distroIndex -lt 1 -or $distroIndex -gt ($distros.Count + 1)) {
|
||||||
|
Stop-WithMessage 'Selection de distribution invalide.'
|
||||||
|
}
|
||||||
|
if ($distroIndex -eq 1) { $selectedDistros = $distros } else { $selectedDistros = @($distros[$distroIndex - 2]) }
|
||||||
|
|
||||||
|
$includeDockerData = @($selectedDistros | Where-Object {
|
||||||
|
$_ -ieq 'docker' -or $_ -ieq 'docker-desktop'
|
||||||
|
}).Count -gt 0
|
||||||
|
if ($includeDockerData) {
|
||||||
|
Assert-DockerStopped
|
||||||
|
$dockerDataPath = Get-DockerDataPath $true
|
||||||
|
}
|
||||||
|
|
||||||
|
$drive = Select-Drive
|
||||||
|
$backupRoot = Join-Path -Path $drive.Root -ChildPath 'WSL-Backup'
|
||||||
|
$backupFolder = Join-Path -Path $backupRoot -ChildPath (Get-Date -Format 'yyyy-MM-dd_HH-mm-ss')
|
||||||
|
New-Item -ItemType Directory -Path $backupFolder -Force | Out-Null
|
||||||
|
Write-Host ('Sauvegarde dans : {0}' -f $backupFolder) -ForegroundColor Green
|
||||||
|
Write-Host ('Distributions : {0}' -f ($selectedDistros -join ', '))
|
||||||
|
$freeSpace = New-Object -TypeName System.IO.DriveInfo -ArgumentList $drive.Root
|
||||||
|
Write-Host ('Espace libre : {0:N2} Go' -f ($freeSpace.AvailableFreeSpace / 1GB)) -ForegroundColor Cyan
|
||||||
|
$estimatedSizes = @{}
|
||||||
|
foreach ($distro in $selectedDistros) { $estimatedSizes[$distro] = Get-DistroSize $distro }
|
||||||
|
if (-not (Confirm-Action 'Lancer la sauvegarde')) { Remove-Item -LiteralPath $backupFolder -Force; Write-Host 'Operation annulee.'; exit 0 }
|
||||||
|
|
||||||
|
try { Invoke-Wsl @('--shutdown') | Out-Null } catch { Stop-WithMessage ('Impossible d''arreter WSL : {0}' -f $_.Exception.Message) }
|
||||||
|
|
||||||
|
if ($includeDockerData) {
|
||||||
|
$dockerBackupPath = Join-Path $backupFolder 'Docker'
|
||||||
|
try {
|
||||||
|
Copy-DockerData -SourceRoot $dockerDataPath -DestinationRoot $dockerBackupPath
|
||||||
|
Write-Host ('Donnees Docker sauvegardees dans : {0}' -f $dockerBackupPath) -ForegroundColor Green
|
||||||
|
} catch {
|
||||||
|
Stop-WithMessage ('Sauvegarde Docker impossible : {0}' -f $_.Exception.Message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$failed = @()
|
||||||
|
foreach ($distro in $selectedDistros) {
|
||||||
|
$archive = Join-Path -Path $backupFolder -ChildPath ($distro + '.tar')
|
||||||
|
Write-Host ('Export de {0}...' -f $distro) -ForegroundColor Yellow
|
||||||
|
|
||||||
|
try {
|
||||||
|
Export-Distro -Distro $distro -Archive $archive -EstimatedBytes $estimatedSizes[$distro]
|
||||||
|
Write-Host (' OK : {0}' -f $archive) -ForegroundColor Green
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
$failed += $distro
|
||||||
|
Write-Warning ('Echec pour {0} : {1}' -f $distro, $_.Exception.Message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($failed.Count -gt 0) {
|
||||||
|
Write-Warning ('Sauvegarde terminee avec des erreurs : {0}' -f ($failed -join ', '))
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host 'Sauvegarde terminee avec succes.' -ForegroundColor Green
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 65001 >nul
|
||||||
|
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0wsl-manage-core.ps1" %*
|
||||||
Reference in new issue
Block a user