refactor: 公共面补 BakNRet 前缀,产品名大小写全仓统一
16 个没有前缀的公共函数补上 BakNRet(Write-Log → Write-BakNRetLog、Resolve-BackupEntry → Resolve-BakNRetBackupEntry、Find-ChildDirectoryByName → Find-BakNRetChildDirectoryByName 等), 另外把全仓的 Baknret 统一成 BakNRet(47 个文件、940 处、65 个定义文件重命名)。 这不是审美问题:静态分析直接拓出一条实据 —— Write-Log 与本机某个已装模块导出的命令 **重名**(PSAvoidOverwritingBuiltInCmdlets),而重名的后果是导入两个模块时有一方的命令被 静默遮蔽。补前缀正是这条规则的解法,改名后它归零。 为什么敢做这个规模:PowerShell 的函数名解析大小写不敏感,所以 Baknret → BakNRet 在功能 上是零风险;真正要验证的是 16 个补前缀的调用点,而 276 个断言几乎覆盖了每个函数。另外 "名字与文件名一致"这条不变式有断言盯着(加载器点源的文件集合 vs 磁盘)。 踩到并记下的坑:Windows 文件系统大小写不敏感,所以**只改大小写**的重命名会被 Move-Item 当成同一个文件而静默跳过 —— 同一批里同时改了名字的那 16 个文件却成功了,于是"看起来能跑"。 最后用"先移到临时名、再移到目标名"的两步走解决,判断与替换全部改用显式大小写敏感的形式 (-creplace / -cmatch)。 顺带把名录指纹缓存从 MD5 换成 SHA256(PSAvoidUsingBrokenHashAlgorithms):它只是缓存键, 没有兼容负担。 验收:test.ps1 9/9 全绿(7 与 5.1)、100 个文件两版解析零错、276 个断言全过、 构建工具仍能合回单文件(3300 行)。
This commit is contained in:
1 parent
187d2759fd
commit
42f02d0eca
84 files changed
+1043
-1043
No files matched your search
@@ -51,14 +51,14 @@ if (-not $BackupListPath) { $BackupListPath = Join-Path $projectRoot 'BackupList
|
||||
Import-Module (Join-Path $projectRoot 'BakNRet\BakNRet.psd1') -Force
|
||||
|
||||
if (-not $ConfigPath) { $ConfigPath = Join-Path $projectRoot 'BackupConfig.psd1' }
|
||||
$config = Get-BaknretConfig -Path $ConfigPath
|
||||
$config = Get-BakNRetConfig -Path $ConfigPath
|
||||
|
||||
if (-not $BackupDir) {
|
||||
$BackupDir = $config.BackupDir
|
||||
if (-not [System.IO.Path]::IsPathRooted($BackupDir)) { $BackupDir = Join-Path $projectRoot $BackupDir }
|
||||
}
|
||||
|
||||
$catalogPath = Resolve-CatalogPath -Configured $config.SoftwareCatalog -Root $projectRoot
|
||||
$catalogPath = Resolve-BakNRetCatalogPath -Configured $config.SoftwareCatalog -Root $projectRoot
|
||||
$manifestPath = Join-Path $BackupDir 'manifest.json'
|
||||
$supportedFormats = @('.7z', '.rar', '.zip', '.tar')
|
||||
|
||||
@@ -85,7 +85,7 @@ function Find-ArchiveByBaseName {
|
||||
# 阶段一:规划
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
$manifestOld = Read-BaknretManifest -Path $manifestPath
|
||||
$manifestOld = Read-BakNRetManifest -Path $manifestPath
|
||||
|
||||
# manifest 里的历史归档名按 source / resolvedSource 建索引:
|
||||
# 条目写法改过(软件名 -> 手写路径、改名、合并)之后,键对不上了,
|
||||
@@ -121,7 +121,7 @@ foreach ($line in (Get-Content -Encoding UTF8 -LiteralPath $BackupListPath)) {
|
||||
$item = ConvertFrom-BackupListLine -Line $line
|
||||
if (-not $item) { continue }
|
||||
|
||||
$resolved = Resolve-BackupEntry -Entry $item -CatalogPath $catalogPath -MaxDepth $config.CatalogMaxDepth
|
||||
$resolved = Resolve-BakNRetBackupEntry -Entry $item -CatalogPath $catalogPath -MaxDepth $config.CatalogMaxDepth
|
||||
$newName = $resolved.BaseName
|
||||
if (-not $newName) { continue }
|
||||
|
||||
@@ -143,11 +143,11 @@ foreach ($line in (Get-Content -Encoding UTF8 -LiteralPath $BackupListPath)) {
|
||||
$pathSource = if ($slots.Count -eq 1) { $slots[0].Declared } else { $null }
|
||||
}
|
||||
if ($pathSource) {
|
||||
$derived = Get-BackupBaseName -RawPath $pathSource
|
||||
$derived = Get-BakNRetBackupBaseName -RawPath $pathSource
|
||||
if ($derived) { $candidates += $derived }
|
||||
}
|
||||
if ($resolved.IsName) {
|
||||
$candidates += (Format-CatalogName -Name $item.Path)
|
||||
$candidates += (Format-BakNRetCatalogName -Name $item.Path)
|
||||
}
|
||||
if ($manifestOld.items.Contains($newName)) {
|
||||
$recorded = $manifestOld.items[$newName]
|
||||
@@ -316,7 +316,7 @@ $manifestNew = [pscustomobject]@{
|
||||
compressor = $manifestOld.compressor
|
||||
items = $rebuilt
|
||||
}
|
||||
Write-BaknretManifest -Path $manifestPath -Manifest $manifestNew | Out-Null
|
||||
Write-BakNRetManifest -Path $manifestPath -Manifest $manifestNew | Out-Null
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 阶段四:报告
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
# tools\lab —— BakNRet 的隔离测试环境(Hyper-V 真机级 VM)
|
||||
# tools\lab —— BakNRet 的隔离测试环境(Hyper-V 真机级 VM)
|
||||
|
||||
在**宿主机之外的 Windows 虚拟机**里跑 BakNRet 的备份 / 恢复 / 测试。宿主机仓库、`Backups\`、
|
||||
`logs\` 在本环境里只被读取,从不写入;VM 内也没有挂载宿主机的任何目录(一切交互走
|
||||
@@ -138,14 +138,14 @@ gsudo pwsh -NoProfile -File .\tools\lab\Lab.ps1 reset # 秒回 clean-baseli
|
||||
|
||||
### 在 VM 里直接跑 `tests\Run-Pester.ps1` 会红 8 项(都是中文断言)
|
||||
|
||||
`tests\BakNRet*.Tests.ps1` 里的 `Invoke-BaknretScript` 这样抓子进程输出:
|
||||
`tests\BakNRet*.Tests.ps1` 里的 `Invoke-BakNRetScript` 这样抓子进程输出:
|
||||
|
||||
```
|
||||
cmd /c pwsh -File Backup.ps1 ... > out.txt 2>&1
|
||||
Get-Content -LiteralPath out.txt -Encoding UTF8
|
||||
```
|
||||
|
||||
而 `Backup.ps1` / `Restore.ps1` 的 `Write-Log` 走 `Write-Host`,写进 `out.txt` 的**字节编码取自
|
||||
而 `Backup.ps1` / `Restore.ps1` 的 `Write-BakNRetLog` 走 `Write-Host`,写进 `out.txt` 的**字节编码取自
|
||||
`[Console]::OutputEncoding`**:
|
||||
|
||||
| 环境 | `[Console]::OutputEncoding` | 结果 |
|
||||
|
||||
@@ -101,7 +101,7 @@ function Get-SecurityFingerprint {
|
||||
return ('O={0} G={1} P={2} [{3}]' -f $acl.GetOwner($sid).Value, $acl.GetGroup($sid).Value, $acl.AreAccessRulesProtected, ($aces -join ' '))
|
||||
}
|
||||
|
||||
function Invoke-BaknretChild {
|
||||
function Invoke-BakNRetChild {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
用独立进程跑 Backup.ps1 / Restore.ps1(两个脚本结尾都会 exit)。
|
||||
@@ -190,7 +190,7 @@ function Remove-TreeHard {
|
||||
Where-Object { $_.Attributes -band [IO.FileAttributes]::ReparsePoint })
|
||||
foreach ($link in $links) {
|
||||
$null = Invoke-NativeTolerant -FilePath 'cmd.exe' -ArgumentList @('/c', ('rmdir "{0}"' -f $link.FullName))
|
||||
Remove-BaknretJunction -Path $link.FullName
|
||||
Remove-BakNRetJunction -Path $link.FullName
|
||||
}
|
||||
|
||||
$null = Invoke-NativeTolerant -FilePath 'cmd.exe' -ArgumentList @('/c', ('rmdir /s /q "{0}"' -f $Path))
|
||||
@@ -217,7 +217,7 @@ else {
|
||||
$BackupDir = Join-Path $WorkRoot 'backups'
|
||||
New-Item -ItemType Directory -Path $BackupDir -Force | Out-Null
|
||||
|
||||
$privileges = Enable-BaknretPrivilege -Name @('SeRestorePrivilege', 'SeBackupPrivilege')
|
||||
$privileges = Enable-BakNRetPrivilege -Name @('SeRestorePrivilege', 'SeBackupPrivilege')
|
||||
if ($privileges.Missing.Count -gt 0) {
|
||||
Write-Host ('[acl] 警告:{0} 不在令牌里 —— 属主无法恢复,B 段会失败(应以管理员/SYSTEM 运行)' -f ($privileges.Missing -join '、')) -ForegroundColor Yellow
|
||||
}
|
||||
@@ -398,7 +398,7 @@ $configText = @"
|
||||
|
||||
Write-Host ''
|
||||
Write-Host '[备份] Backup.ps1' -ForegroundColor Yellow
|
||||
$backup = Invoke-BaknretChild -Script (Join-Path $RepoPath 'Backup.ps1') -Parameters @{
|
||||
$backup = Invoke-BakNRetChild -Script (Join-Path $RepoPath 'Backup.ps1') -Parameters @{
|
||||
BackupListPath = $listPath
|
||||
ConfigPath = $configPath
|
||||
BackupDir = $BackupDir
|
||||
@@ -420,7 +420,7 @@ Test-Scenario '源已删除(模拟真的丢了)' ($leftovers.Count -eq 0) ($
|
||||
|
||||
Write-Host ''
|
||||
Write-Host '[恢复] Restore.ps1' -ForegroundColor Yellow
|
||||
$restore = Invoke-BaknretChild -Script (Join-Path $RepoPath 'Restore.ps1') -Parameters @{
|
||||
$restore = Invoke-BakNRetChild -Script (Join-Path $RepoPath 'Restore.ps1') -Parameters @{
|
||||
BackupListPath = $listPath
|
||||
ConfigPath = $configPath
|
||||
BackupDir = $BackupDir
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
在 VM 内以 UTF-8 控制台编码运行一个测试套件(不改仓库里的任何测试代码)。
|
||||
|
||||
.DESCRIPTION
|
||||
为什么需要它 —— tests\BakNRet*.Tests.ps1 的 Invoke-BaknretScript 是这么抓子进程输出的:
|
||||
为什么需要它 —— tests\BakNRet*.Tests.ps1 的 Invoke-BakNRetScript 是这么抓子进程输出的:
|
||||
|
||||
cmd /c pwsh -File Backup.ps1 ... > out.txt 2>&1
|
||||
Get-Content -LiteralPath out.txt -Encoding UTF8
|
||||
|
||||
而 Backup.ps1 / Restore.ps1 的 Write-Log 走 Write-Host,写进 out.txt 的字节用的是
|
||||
而 Backup.ps1 / Restore.ps1 的 Write-BakNRetLog 走 Write-Host,写进 out.txt 的字节用的是
|
||||
`[Console]::OutputEncoding`:
|
||||
|
||||
* 宿主机上它是 utf-8 -> 文件是 UTF-8 -> 按 UTF-8 读回,中文正确,套件全绿;
|
||||
|
||||
Reference in new issue
Block a user