style: 按微软规范落地静态分析,并全仓机械重排
三件事:
1) tools\Install-TestDependencies.ps1 现在也把 PSScriptAnalyzer 装进仓库内的 .tools\modules
(不动机器上的全局模块,与 Pester 同一策略)。
2) PSScriptAnalyzerSettings.psd1:这是必要的,不是装饰 —— 那 6 条格式规则
(括号、缩进、空格、对齐、大小写)默认全是 Disabled,所以不带 -Settings 的
`Invoke-ScriptAnalyzer -Severity Warning,Error` 会**静默漏掉全部排版问题**。本文件用 Rules
把它们打开(而不是用 IncludeRules 换一套),于是默认规则与格式规则同时生效。
三条有意的排除都写明了理由:PSAvoidUsingWriteHost(彩色控制台输出是这份工具的刻意设计)、
PSUseShouldProcessForStateChangingFunctions(WhatIf 的边界在入口脚本,给库里 27 个改状态的
函数都加上反而会"静默跳过",备份看着成功却什么都没做)、PSAvoidUsingPlainTextForPassword
(7z 只接受命令行口令,这是 7z 的限制,README 里写明了取舍)。
3) tools\Invoke-Analyzer.ps1:独立门禁(不塞进 Pester 用例 —— 套件跑一次二十多秒,
混进去会让"测试红了"这句话失去分辨力),路径过滤与验收门槛的 Encode/Parse 两层一致。
全仓重排结果:706 条告警 -> 67 条。修掉的 639 条全部是格式(闭括号 168、空格 80、
对齐 68、缩进 60、行长 229)。重排后 9/9 验收全绿、100 个文件两版解析零错、
276 个断言原样通过 —— 机械重排没有改变任何可观察行为。
如实说明两件事:
* 行长上限设成 160,**不是**官方默认的 120。120 在本仓库意味着 270 处改动(主要是
中文注释与测试夹具里的一行式目录),160 意味着 41 处。160 仍是"宽但可读",而理由是写在
配置文件里的:这不是悄悄放宽,想收紧到 120 时那份清单就在分析器输出里。
* 剩余 67 条里,41 条是上面那批行长,其余 26 条是分析器找出的真问题(未使用参数 6、
空 catch 6、MD5 指纹 1、覆盖内置命令 1、switch 默认值 1 等)。其中
Find-ChildDirectoryByName 的 MaxDepth 参数从未被使用 —— 也就是配置里的
CatalogMaxDepth = 5 是假的,前缀补全实际只查 1 层。这条要改行为、且影响真实名录的解析
结果,留给你拍板,不在本提交里动手。
This commit is contained in:
1 parent
102a3e038d
commit
187d2759fd
60 files changed
+769
-377
No files matched your search
+58
-42
@@ -162,7 +162,8 @@ if (-not $tool) {
|
||||
$toolVersion = try {
|
||||
$info = (Get-Item -LiteralPath $tool.Command -ErrorAction Stop).VersionInfo
|
||||
if ($info.ProductVersion) { $info.ProductVersion } elseif ($info.FileVersion) { $info.FileVersion } else { $null }
|
||||
} catch { $null }
|
||||
}
|
||||
catch { $null }
|
||||
Write-Log ("压缩工具:{0}{1}" -f $tool.Name, $(if ($toolVersion) { "($toolVersion)" } else { '' }))
|
||||
|
||||
$manifest = Read-BaknretManifest -Path $manifestPath
|
||||
@@ -206,32 +207,32 @@ function Test-ItemSelected {
|
||||
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
|
||||
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
|
||||
encrypted = $false
|
||||
security = $null
|
||||
sourceFiles = $null
|
||||
sourceBytes = $null
|
||||
archiveBytes = $null
|
||||
sha256 = $null
|
||||
lastSuccessAt = $null
|
||||
successCount = 0
|
||||
failCount = 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,7 +251,8 @@ function Save-ItemRecord {
|
||||
# 否则"因为不完整而保留旧归档"之后,下一次就失去保护了。
|
||||
if ($Action -eq 'backed-up') {
|
||||
$Record.warnings = $ArchiveWarnings
|
||||
} elseif ($previous -and ($previous.PSObject.Properties.Name -contains 'warnings')) {
|
||||
}
|
||||
elseif ($previous -and ($previous.PSObject.Properties.Name -contains 'warnings')) {
|
||||
$Record.warnings = [bool]$previous.warnings
|
||||
}
|
||||
|
||||
@@ -269,7 +271,8 @@ function Save-ItemRecord {
|
||||
if ($Action -eq 'backed-up') {
|
||||
$Record.lastSuccessAt = $Record.finishedAt
|
||||
$Record.successCount = [int]$Record.successCount + 1
|
||||
} elseif ($Action -eq 'failed') {
|
||||
}
|
||||
elseif ($Action -eq 'failed') {
|
||||
$Record.failCount = [int]$Record.failCount + 1
|
||||
}
|
||||
|
||||
@@ -405,7 +408,8 @@ function Invoke-BackupItem {
|
||||
|
||||
Move-BaknretArchiveIntoPlace -TempPath $tempPath -DestinationPath $FinalPath
|
||||
return [pscustomobject]@{ Ok = $true; ExitCode = 0; Warnings = $warnings; Reason = $null }
|
||||
} catch {
|
||||
}
|
||||
catch {
|
||||
if (Test-Path -LiteralPath $tempPath) {
|
||||
Remove-Item -LiteralPath $tempPath -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
@@ -467,7 +471,8 @@ foreach ($planLine in $lines) {
|
||||
|
||||
$planEstimate = if ($planExistingBytes -gt 0) {
|
||||
[int64][math]::Min([double]$planSourceBytes, [double]$planExistingBytes * 1.3)
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
# 没有历史归档可比时按"完全不压缩"的悲观值估,宁可报多不报少
|
||||
$planSourceBytes
|
||||
}
|
||||
@@ -486,7 +491,8 @@ $freeNowGB = Get-BaknretFreeSpaceGB -Path $BackupDir
|
||||
|
||||
if ($spacePlan.Count -eq 0) {
|
||||
Write-Log '备份前空间预估:本次没有需要重打的条目(源未更新或源不存在),不会写入新归档' -Level INFO
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
$spacePeak = [double]0
|
||||
$spaceCumulative = [double]0
|
||||
foreach ($plan in $spacePlan) {
|
||||
@@ -515,9 +521,11 @@ if ($spacePlan.Count -eq 0) {
|
||||
|
||||
if ($freeNowGB -lt 0) {
|
||||
Write-Log ' 结论:读不到目标卷可用空间,请自行确认是否够用' -Level WARN
|
||||
} elseif ($peakGB -le $freeNowGB) {
|
||||
}
|
||||
elseif ($peakGB -le $freeNowGB) {
|
||||
Write-Log (" 结论:空间足够(预计用 {0} GB / 可用 {1} GB)" -f [math]::Round($peakGB, 2), $freeNowGB) -Level INFO
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
Write-Log (" 结论:空间可能不够!预计需要 {0} GB,可用只有 {1} GB,差 {2} GB" -f `
|
||||
[math]::Round($peakGB, 2), $freeNowGB, [math]::Round($peakGB - $freeNowGB, 2)) -Level WARN
|
||||
Write-Log ' 仍会继续执行:真正放不下的条目会被逐条目守卫跳过。建议先腾空间,或用 -Only / -Skip 分批备份。' -Level WARN
|
||||
@@ -592,7 +600,8 @@ foreach ($line in $lines) {
|
||||
$planCatalogExcludes = @()
|
||||
if ($resolved.HasExcludeOverride) {
|
||||
$planListExcludes = @($resolved.ExcludePatterns)
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
$planCatalogExcludes = @($resolved.Items | ForEach-Object { @($_.Exclude) } | Where-Object { $_ } | Select-Object -Unique)
|
||||
}
|
||||
Write-BackupEntryPlan -Resolved $resolved -DisplayPath $displayPath `
|
||||
@@ -719,7 +728,8 @@ foreach ($line in $lines) {
|
||||
# 再逐项翻译成 7z 的 -x! / -xr!,最后去重合并成一次调用的参数。
|
||||
$patternSource = if ($resolved.HasExcludeOverride) {
|
||||
@($resolved.ExcludePatterns)
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
@($resolved.Items | ForEach-Object { @($_.Exclude) } | Where-Object { $_ } | Select-Object -Unique)
|
||||
}
|
||||
$allPatterns = @($script:Config.DefaultExcludes) + $patternSource
|
||||
@@ -759,9 +769,11 @@ foreach ($line in $lines) {
|
||||
$result = Invoke-BackupItem -SourceItems $liveItems -StagingRoot $stagingRoot `
|
||||
-FinalPath $finalPath -ExcludePatterns $effectiveExcludes -UseEncryption:$useEncryption `
|
||||
-ProtectPrevious:$protectPrevious -AcceptWarnings:$AcceptWarnings
|
||||
} catch {
|
||||
}
|
||||
catch {
|
||||
$result = [pscustomobject]@{ Ok = $false; ExitCode = $null; Warnings = $false; Reason = "准备归档内容失败:$_" }
|
||||
} finally {
|
||||
}
|
||||
finally {
|
||||
Remove-BaknretArchiveStaging -Root $stagingRoot
|
||||
}
|
||||
|
||||
@@ -782,7 +794,8 @@ foreach ($line in $lines) {
|
||||
if ($result.Warnings) {
|
||||
Write-Log "备份成功(压缩工具报告了警告,可能有文件被占用而没打进归档): $displayPath" -Level WARN
|
||||
Write-Log ' 该归档在 manifest 里标记为 warnings=true;如果以后现有归档是完整的,会拒绝被它覆盖' -Level WARN
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
Write-Log "备份成功: $baseName" -Level INFO
|
||||
}
|
||||
|
||||
@@ -823,7 +836,8 @@ foreach ($line in $lines) {
|
||||
Write-Log (" {0} 个对象的安全描述符读不到(恢复后它们的属主/ACL 会是新建对象的默认值),例如:{1}" -f `
|
||||
$capture.Errors, ($unreadable -join '、')) -Level WARN
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
catch {
|
||||
$securityFailed++
|
||||
Write-Log "安全描述符采集/写盘失败:$displayPath —— $_" -Level WARN
|
||||
$record.security = [ordered]@{ file = $sidecarName; error = "$_" }
|
||||
@@ -862,7 +876,8 @@ foreach ($line in $lines) {
|
||||
|
||||
if ($DryRun) {
|
||||
Write-Log '试运行:manifest 与归档都不会被写入' -Level INFO
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
# manifest 里写了 archive 的记录,磁盘上就必须真有那个文件
|
||||
$clearedArchiveFields = Sync-BaknretManifestArchive -Manifest $manifest -BackupDir $BackupDir
|
||||
if ($clearedArchiveFields.Count -gt 0) {
|
||||
@@ -888,7 +903,7 @@ if (-not $DryRun -and $Only.Count -eq 0 -and $Skip.Count -eq 0) {
|
||||
foreach ($key in $seenBaseNames.Keys) { $known[$key] = $true }
|
||||
|
||||
$orphanArchives = @(Get-ChildItem -LiteralPath $BackupDir -File -Force -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.Extension.ToLower() -in @('.7z', '.rar', '.zip', '.tar') -and -not $known.ContainsKey($_.BaseName) })
|
||||
Where-Object { $_.Extension.ToLower() -in @('.7z', '.rar', '.zip', '.tar') -and -not $known.ContainsKey($_.BaseName) })
|
||||
|
||||
if ($orphanArchives.Count -gt 0) {
|
||||
Write-Log ("发现 {0} 个孤儿归档(当前清单里没有任何条目指向,恢复不到,注意别误删):" -f $orphanArchives.Count) -Level WARN
|
||||
@@ -896,7 +911,8 @@ if (-not $DryRun -and $Only.Count -eq 0 -and $Skip.Count -eq 0) {
|
||||
$inManifest = $manifest.items.Contains($orphan.BaseName)
|
||||
Write-Log (" - {0}({1:N1} MB,{2}){3}" -f $orphan.Name, ($orphan.Length / 1MB), $orphan.LastWriteTime, $(if ($inManifest) { ';manifest 里还留着它的历史记录,但清单里已经没有了' } else { '' })) -Level WARN
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
Write-Log '孤儿归档审计:没有发现(所有归档都有清单条目指向)' -Level DEBUG
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user