From 3494659d41c12c5ffe2677aad8889fae0fcc8814 Mon Sep 17 00:00:00 2001 From: Raz Ben Aharon <181751257+razbenaharon@users.noreply.github.com> Date: Sun, 8 Nov 2026 09:57:34 +0200 Subject: [PATCH] Repair settings paths left behind by a moved tool folder Each source stores its settings file by full path, so renaming the tool folder (BackupCenter -> DevDriveBackup) left a stale path and the app failed with "Cannot find path". When loading profiles, a missing settings path is now repointed to a file of the same name in the jobs folder or beside the profiles file, but only when that file describes the same source folder and plain copy folder. Unreadable or incomplete candidates are skipped. Tests cover the repair and the refusal of another source's file. Co-Authored-By: Claude Opus 5.5 --- DevDriveBackup.Data.psm1 | 16 ++++++++++++++++ Test-DevDriveBackup.ps1 | 8 ++++++++ 2 files changed, 24 insertions(+) diff --git a/DevDriveBackup.Data.psm1 b/DevDriveBackup.Data.psm1 index 178af91..45fb034 100644 --- a/DevDriveBackup.Data.psm1 +++ b/DevDriveBackup.Data.psm1 @@ -78,6 +78,22 @@ function Read-CenterProfiles { if(-not(Test-Path -LiteralPath $p)){return [pscustomobject]@{Version=1;Profiles=@()}} $data=Get-Content -LiteralPath $p -Raw|ConvertFrom-Json $data.Profiles=@($data.Profiles|ForEach-Object {$_.Sources=@($_.Sources);$_}) + # A source's settings file is stored by full path. When the tool folder is moved or renamed, that path + # goes stale; a file of the same name in the jobs folder or beside the profiles file is the same file, moved. + $moved=$false + foreach($s in @($data.Profiles|ForEach-Object {$_.Sources})){ + $old=[string]$s.ConfigPath + if(-not $old -or (Test-Path -LiteralPath $old)){continue} + $leaf=Split-Path $old -Leaf + foreach($dir in @((Get-CenterPaths).Jobs,[IO.Path]::GetDirectoryName($p))){ + $candidate=Join-Path $dir $leaf + if(-not(Test-Path -LiteralPath $candidate -PathType Leaf)){continue} + # Same name is not proof: the file must describe this very source (its folder and its plain copy folder). + $same=try{$job=Get-Content -LiteralPath $candidate -Raw|ConvertFrom-Json;[string]$job.Source -ieq [string]$s.Path -and [string]$job.MirrorFolder -ieq [string]$s.MirrorFolder}catch{$false} + if($same){$s.ConfigPath=$candidate;$moved=$true;break} + } + } + if($moved){Save-CenterProfiles $data} $data } function Save-CenterProfiles($Data) { diff --git a/Test-DevDriveBackup.ps1 b/Test-DevDriveBackup.ps1 index e74056e..63d318e 100644 --- a/Test-DevDriveBackup.ps1 +++ b/Test-DevDriveBackup.ps1 @@ -43,6 +43,14 @@ try{ $s=Add-CenterSource -Data $data -ProfileId $p.Id -Path $source -Label $label -DefaultMode Both Save-CenterProfiles $data $created+=Join-Path ($DestinationDrive+':\') $s.MirrorFolder + $real=$s.ConfigPath;$s.ConfigPath=Join-Path (Join-Path $dataRoot 'OldToolName') (Split-Path $real -Leaf);Save-CenterProfiles $data + Assert ((Get-Content -LiteralPath (Get-CenterPaths).Profiles -Raw) -match 'OldToolName') 'a stale settings path is on disk before the repair' + $data=Read-CenterProfiles;$p=Get-CenterProfile $data $p.Id;$s=@($p.Sources)[0] + Assert ($s.ConfigPath -eq $real -and (Get-Content -LiteralPath (Get-CenterPaths).Profiles -Raw) -notmatch 'OldToolName') 'a settings path left behind by a moved or renamed tool folder is repaired and saved' + $s.MirrorFolder='Other-Mirror';$s.ConfigPath=Join-Path (Join-Path $dataRoot 'OldToolName') (Split-Path $real -Leaf);Save-CenterProfiles $data + $data=Read-CenterProfiles;$p=Get-CenterProfile $data $p.Id;$s=@($p.Sources)[0] + Assert ($s.ConfigPath -ne $real) 'a file of the same name that describes another source is not taken' + $s.MirrorFolder=Split-Path $created[-1] -Leaf;$s.ConfigPath=$real;Save-CenterProfiles $data $job=Read-CenterJob $s.ConfigPath $created+=Join-Path ($DestinationDrive+':\') $job.RepositoryFolder Assert (@($job.Choices).Count -eq 2 -and @($job.Choices|Where-Object {$_.Mode -ne 'Both'}).Count -eq 0) 'new source lists its top-level folders with the chosen mode'