refactor: 条目记录的工厂搬进模块(manifest 的 schema 有了唯一落点)
New-ItemRecord 是纯工厂:我先用正则扫过它读了哪些外部状态 —— 一处都没有,输入全在参数里,只有一个 Get-Date。所以这是"有状态核心"里唯一可以零判断搬走的一块。 搬走并改名 New-BakNRetItemRecord,同时补上字段说明:这个有序哈希就是 manifest 每个条目的 schema。它原先藏在 Backup.ps1 里,于是"manifest 条目长什么样"这件事只有一个隐式落点;恢复端与工具脚本读的也是同一份 schema,改字段时漏掉某一侧的风险就出在这里。 新增一条断言把这个 schema 钉住:26 个字段的名字与**顺序**逐项比对(顺序是 manifest diff 可读性的前提),另外断言 attemptedAt 每次现取 —— 后者是"把它误写成模块加载时的常量"这种错唯一能被发现的地方。 验收:test.ps1 9/9 全绿(7 与 5.1)、104 个文件两版解析零错、真实清单只读冒烟 4/4。Backup.ps1 从 920 行降到 889 行。
This commit is contained in:
1 parent
67ee2af006
commit
319ec1da78
5 files changed
+72
-34
No files matched your search
+3
-34
@@ -182,37 +182,6 @@ if ($freeSpaceGB -ge 0) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function New-ItemRecord {
|
|
||||||
param([string]$BaseName, [string]$Source, [string]$ResolvedSource, [string]$Phase)
|
|
||||||
return [ordered]@{
|
|
||||||
baseName = $BaseName
|
|
||||||
source = $Source
|
|
||||||
resolvedSource = $ResolvedSource
|
|
||||||
roots = @()
|
|
||||||
layouts = @()
|
|
||||||
catalog = $null
|
|
||||||
archive = $null
|
|
||||||
action = $null
|
|
||||||
reason = $null
|
|
||||||
phase = $Phase
|
|
||||||
attemptedAt = (Get-Date).ToString('o')
|
|
||||||
finishedAt = $null
|
|
||||||
durationSec = $null
|
|
||||||
exitCode = $null
|
|
||||||
verified = $false
|
|
||||||
warnings = $false
|
|
||||||
attemptWarnings = $false
|
|
||||||
encrypted = $false
|
|
||||||
security = $null
|
|
||||||
sourceFiles = $null
|
|
||||||
sourceBytes = $null
|
|
||||||
archiveBytes = $null
|
|
||||||
sha256 = $null
|
|
||||||
lastSuccessAt = $null
|
|
||||||
successCount = 0
|
|
||||||
failCount = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Save-ItemRecord {
|
function Save-ItemRecord {
|
||||||
param($Record, [string]$Action, [string]$Reason, [bool]$ArchiveWarnings = $false)
|
param($Record, [string]$Action, [string]$Reason, [bool]$ArchiveWarnings = $false)
|
||||||
@@ -523,7 +492,7 @@ foreach ($line in $lines) {
|
|||||||
$resolved = Resolve-BakNRetBackupEntry -Entry $item -CatalogPath $catalogPath
|
$resolved = Resolve-BakNRetBackupEntry -Entry $item -CatalogPath $catalogPath
|
||||||
|
|
||||||
if (-not $resolved.BaseName) {
|
if (-not $resolved.BaseName) {
|
||||||
$record = New-ItemRecord -BaseName ('raw:' + $displayPath) -Source $displayPath -ResolvedSource $displayPath -Phase 'parse'
|
$record = New-BakNRetItemRecord -BaseName ('raw:' + $displayPath) -Source $displayPath -ResolvedSource $displayPath -Phase 'parse'
|
||||||
Save-ItemRecord -Record $record -Action 'failed' -Reason '无法生成归档名' | Out-Null
|
Save-ItemRecord -Record $record -Action 'failed' -Reason '无法生成归档名' | Out-Null
|
||||||
$failed++; $failures += $displayPath
|
$failed++; $failures += $displayPath
|
||||||
continue
|
continue
|
||||||
@@ -543,7 +512,7 @@ foreach ($line in $lines) {
|
|||||||
if ($seenBaseNames.ContainsKey($baseName)) {
|
if ($seenBaseNames.ContainsKey($baseName)) {
|
||||||
$reason = "归档名 '$baseName' 与清单中的 '$($seenBaseNames[$baseName])' 重复(由 '$displayPath' 生成),两者会互相覆盖"
|
$reason = "归档名 '$baseName' 与清单中的 '$($seenBaseNames[$baseName])' 重复(由 '$displayPath' 生成),两者会互相覆盖"
|
||||||
Write-BakNRetLog "失败: $displayPath,$reason" -Level ERROR
|
Write-BakNRetLog "失败: $displayPath,$reason" -Level ERROR
|
||||||
$record = New-ItemRecord -BaseName $baseName -Source $displayPath -ResolvedSource $sourcePath -Phase 'backup'
|
$record = New-BakNRetItemRecord -BaseName $baseName -Source $displayPath -ResolvedSource $sourcePath -Phase 'backup'
|
||||||
Save-ItemRecord -Record $record -Action 'failed' -Reason $reason | Out-Null
|
Save-ItemRecord -Record $record -Action 'failed' -Reason $reason | Out-Null
|
||||||
$failed++; $failures += $displayPath
|
$failed++; $failures += $displayPath
|
||||||
continue
|
continue
|
||||||
@@ -555,7 +524,7 @@ foreach ($line in $lines) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
$record = New-ItemRecord -BaseName $baseName -Source $displayPath -ResolvedSource $sourcePath -Phase 'backup'
|
$record = New-BakNRetItemRecord -BaseName $baseName -Source $displayPath -ResolvedSource $sourcePath -Phase 'backup'
|
||||||
$record.archive = $baseName + $tool.Extension
|
$record.archive = $baseName + $tool.Extension
|
||||||
if ($resolved.CatalogEntry) { $record.catalog = $resolved.CatalogEntry.Path }
|
if ($resolved.CatalogEntry) { $record.catalog = $resolved.CatalogEntry.Path }
|
||||||
$finalPath = Join-Path $BackupDir $record.archive
|
$finalPath = Join-Path $BackupDir $record.archive
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
CompatiblePSEditions = @('Desktop', 'Core')
|
CompatiblePSEditions = @('Desktop', 'Core')
|
||||||
|
|
||||||
FunctionsToExport = @(
|
FunctionsToExport = @(
|
||||||
|
'New-BakNRetItemRecord',
|
||||||
'Find-BakNRet7zExecutable',
|
'Find-BakNRet7zExecutable',
|
||||||
'Resolve-BakNRetRootedPath',
|
'Resolve-BakNRetRootedPath',
|
||||||
'Test-BakNRetItemSelected',
|
'Test-BakNRetItemSelected',
|
||||||
|
|||||||
@@ -138,7 +138,10 @@
|
|||||||
|
|
||||||
. (Join-Path $PSScriptRoot 'Public\Find-BakNRet7zExecutable.ps1')
|
. (Join-Path $PSScriptRoot 'Public\Find-BakNRet7zExecutable.ps1')
|
||||||
|
|
||||||
|
. (Join-Path $PSScriptRoot 'Public\New-BakNRetItemRecord.ps1')
|
||||||
|
|
||||||
Export-ModuleMember -Function @(
|
Export-ModuleMember -Function @(
|
||||||
|
'New-BakNRetItemRecord',
|
||||||
'Find-BakNRet7zExecutable',
|
'Find-BakNRet7zExecutable',
|
||||||
'Resolve-BakNRetRootedPath',
|
'Resolve-BakNRetRootedPath',
|
||||||
'Test-BakNRetItemSelected',
|
'Test-BakNRetItemSelected',
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
function New-BakNRetItemRecord {
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
造一条 manifest 条目的初始记录(未完成状态)。
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
这个有序哈希就是 manifest 里每个条目的 schema:谁(baseName / source / resolvedSource)、
|
||||||
|
这次做了什么(phase / action / reason / exitCode)、结果如何(verified / warnings / sha256 /
|
||||||
|
archiveBytes)、以及累计计数(successCount / failCount / lastSuccessAt)。
|
||||||
|
|
||||||
|
它是纯工厂:不读任何外部状态,所有输入都是参数,时间戳自己取。原先它躺在 Backup.ps1 里,
|
||||||
|
搬进模块之后这个"manifest 条目长什么样"的事实就有了唯一的落点 —— 恢复端与工具脚本读的
|
||||||
|
也是同一份 schema,改字段时不会再漏掉某一侧。
|
||||||
|
#>
|
||||||
|
param([string]$BaseName, [string]$Source, [string]$ResolvedSource, [string]$Phase)
|
||||||
|
|
||||||
|
return [ordered]@{
|
||||||
|
baseName = $BaseName
|
||||||
|
source = $Source
|
||||||
|
resolvedSource = $ResolvedSource
|
||||||
|
roots = @()
|
||||||
|
layouts = @()
|
||||||
|
catalog = $null
|
||||||
|
archive = $null
|
||||||
|
action = $null
|
||||||
|
reason = $null
|
||||||
|
phase = $Phase
|
||||||
|
attemptedAt = (Get-Date).ToString('o')
|
||||||
|
finishedAt = $null
|
||||||
|
durationSec = $null
|
||||||
|
exitCode = $null
|
||||||
|
verified = $false
|
||||||
|
warnings = $false
|
||||||
|
attemptWarnings = $false
|
||||||
|
encrypted = $false
|
||||||
|
security = $null
|
||||||
|
sourceFiles = $null
|
||||||
|
sourceBytes = $null
|
||||||
|
archiveBytes = $null
|
||||||
|
sha256 = $null
|
||||||
|
lastSuccessAt = $null
|
||||||
|
successCount = 0
|
||||||
|
failCount = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1364,6 +1364,26 @@ Test-Case '模块可以合回单文件:加载器的顺序声明完整、导出
|
|||||||
Assert-Equal 0 $noFile.Count ("导出名单里有名字没有对应文件:" + ($noFile -join '、'))
|
Assert-Equal 0 $noFile.Count ("导出名单里有名字没有对应文件:" + ($noFile -join '、'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Test-Case '条目记录的字段名与顺序是 manifest 的 schema(顺序变了 diff 就没法读)' {
|
||||||
|
# manifest.json 是给人看的:字段按固定顺序排列,两次运行之间只有值会变,
|
||||||
|
# 于是 git diff / 文本对比才读得下去。顺序被无意改掉时,这个断言会拦下来。
|
||||||
|
$expected = @(
|
||||||
|
'baseName', 'source', 'resolvedSource', 'roots', 'layouts', 'catalog', 'archive',
|
||||||
|
'action', 'reason', 'phase', 'attemptedAt', 'finishedAt', 'durationSec', 'exitCode',
|
||||||
|
'verified', 'warnings', 'attemptWarnings', 'encrypted', 'security', 'sourceFiles',
|
||||||
|
'sourceBytes', 'archiveBytes', 'sha256', 'lastSuccessAt', 'successCount', 'failCount'
|
||||||
|
)
|
||||||
|
$record = New-BakNRetItemRecord -BaseName 'x' -Source 's' -ResolvedSource 'rs' -Phase 'backup'
|
||||||
|
Assert-Equal $expected.Count @($record.Keys).Count '字段数不对'
|
||||||
|
for ($i = 0; $i -lt $expected.Count; $i++) {
|
||||||
|
Assert-Equal $expected[$i] @($record.Keys)[$i] "第 $($i + 1) 个字段应当是 $($expected[$i])"
|
||||||
|
}
|
||||||
|
Assert-True (@($record.attemptedAt) -match '^\d{4}-\d{2}-\d{2}T') 'attemptedAt 应当是 ISO 时间'
|
||||||
|
# 时间戳每次现取,不能被写成模块加载时的常量
|
||||||
|
$again = New-BakNRetItemRecord -BaseName 'y' -Source 's' -ResolvedSource 'rs' -Phase 'backup'
|
||||||
|
Assert-True ($record.attemptedAt -ne $again.attemptedAt) '两条记录的 attemptedAt 不该相同'
|
||||||
|
}
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
Remove-Item -LiteralPath $sandbox -Recurse -Force -ErrorAction SilentlyContinue
|
Remove-Item -LiteralPath $sandbox -Recurse -Force -ErrorAction SilentlyContinue
|
||||||
Remove-Item -LiteralPath $itemSandbox -Recurse -Force -ErrorAction SilentlyContinue
|
Remove-Item -LiteralPath $itemSandbox -Recurse -Force -ErrorAction SilentlyContinue
|
||||||
|
|||||||
Reference in new issue
Block a user