P0 正确性 - 退出码:改用 .NET Process 直接启动、让子进程继承控制台,不再用 Start-Process -PassThru (在 7.7.0-preview.4 上 ExitCode 恒为 $null,会把成功的压缩判成失败); 7z / RAR / tar 三条解压分支统一走同一个取退出码的封装。 - BackupList 解析:先按第一个 :: 切段再处理引号(整行被一对引号包住的写法不再把排除表 吞进路径);排除表同时接受 , 与 ;(旧实现只认 ;,导致排除从未生效);支持 :- / :+ / @flag。 - 补回 .ssh 与孤儿归档:.ssh 进清单;孤儿归档在备份端也做审计并点名; 带 -Only / -Skip 时不再把未选中的归档误报成孤儿。 - Resolve-BackupEntry 里 $rootName 在赋值前被引用(会读到外层作用域残留值),已提前赋值。 P1 归档可靠性 - 每个条目写进 manifest.json:源、归档、时间、退出码、校验结果、失败原因, 并区分 warnings(在位归档)与 attemptWarnings(本次尝试)。 - 归档后做 7z t 内容校验,先写 .tmp、校验通过再原子替换(File.Move overwrite)。 - manifest.roots 记录归档内**真实**的顶层条目名(原先记的是软件名,Edge 实际是 "User Data")。 P2 可用性 - Restore 支持 -WhatIf / -DryRun / -VerifyOnly / -Only / -Skip; 这三种"只看不写"的模式一个字节都不写(原先会写回 manifest.json)。 - Edge 等高缓存条目加排除规则并实测:1781 MB / 27961 项 -> 72 MB / 2294 项; 书签、密码、Cookies、偏好、历史、IndexedDB、Local Storage 全部保留。 普通模式是相对归档根目录锚定的,嵌套的那些(如 OneAuth\WebView2 里的 Crashpad) 改用 ! 组件形式才会命中。 - 日志落盘 logs/<backup|restore>-<时间戳>.log;退出码按失败数返回。 - tools/Register-BackupTask.ps1 注册每日计划任务;tools/Rename-Archives.ps1 迁移旧归档名。 - root= 标记此前静默失效,现在明确告警(该功能尚未实现)。 P3 测试与验证 - tests/BakNRet.Tests.ps1:Pester 5 套件 62 项(含用子进程跑 Backup.ps1 / Restore.ps1 的端到端与针对上述缺陷的回归)。 - tests/Run-Pester.ps1 + tools/Install-TestDependencies.ps1:把 Pester 装到仓库内 .tools/, 不动机器上的全局模块(系统自带的 3.4.0 缺 Should -Be)。 - tests/Restore-Drill.ps1:真实归档恢复演练,明确区分"源在备份后变过"与"归档/解压有问题"。 - tests/Run-Tests.ps1(49 项,零依赖)与 tests/Run-E2E.ps1(23 项)继续可用;三套共 134 项全通过。 真实机器验证 - 生产归档 22/22 通过 7z t;-VerifyOnly 不再改动 manifest.json(SHA256 前后一致)。 - 真实恢复演练 12/12 通过,27,670 个文件与活源逐字节一致。 - 修复了生产 scoop-persist.7z:原先只有 90 字节(空归档)而源有 1.3 GB, 重打包后 233 MB,恢复演练 26981/26981 全部一致。
91 lines
3.7 KiB
PowerShell
91 lines
3.7 KiB
PowerShell
<#
|
||
软件名录:维护"软件名 -> 目录"的映射。
|
||
|
||
有这个文件之后,BackupList.txt 里可以直接写软件名:
|
||
|
||
FooClolor
|
||
Kazumi :: !*Cache
|
||
Edge :: !*Cache,component_crx_cache
|
||
.ssh @encrypt
|
||
|
||
归档包的名字也就是软件名(`FooClolor.7z`),不再是
|
||
`FooClolor_from_C_+Programs.7z` 这种由路径拼出来的名字。
|
||
|
||
写法:
|
||
|
||
<软件名> = '<目录>'
|
||
|
||
软件名的限制:
|
||
* 必须是合法的文件名(不能含 \ / : * ? " < > |),因为它就是归档名;
|
||
* 不能含 `\` 或 `/` 或 `%`,否则会被当作字面路径而不是软件名;
|
||
* **含 `-` 或 `.` 的名字必须写成带引号的键**,否则 PowerShell 会把
|
||
`a-b` 解析成减法表达式并报 "Missing '=' operator":
|
||
'scoop-config' = '...' # 正确
|
||
scoop-config = '...' # 报错
|
||
* 建议用英文/数字,但中文也可以。
|
||
|
||
目录可以写环境变量,例如 '%UserProfile%\.ssh'。
|
||
|
||
两个便利特性:
|
||
|
||
1. 目录不存在时会按前缀补全:写 'D:\Programs\legendary',实际目录是
|
||
'D:\Programs\legendary_2.0.4',会自动匹配(只认 `<名>_*` 与 `<名>-*`,
|
||
不会把 Legendary 误配成 LegendarySomething)。
|
||
2. **一个软件包含多个目录**时,用 Dirs 数组列出,全部打进同一个归档:
|
||
|
||
scoop = @{ Dirs = @(
|
||
'%UserProfile%\scoop\persist'
|
||
'C:\Programs\ScoopApps\persist'
|
||
'%UserProfile%\.config\scoop'
|
||
) }
|
||
|
||
归档里每个目录仍是自己的名字与层级,恢复时各自还原回原位。
|
||
同名目录出现在多个位置的旧写法 Variants 等价于 Dirs,继续可用。
|
||
|
||
分文件维护:用 Includes 引入其它名录文件(路径相对本文件):
|
||
|
||
@{
|
||
Includes = @('SoftwareCatalog.games.psd1')
|
||
...
|
||
}
|
||
#>
|
||
@{
|
||
# ---- 用户配置 / 开发环境 ----
|
||
# 含 `-` 或 `.` 的键必须加引号,否则会被当成减法表达式(见文件开头说明)
|
||
legendary = '%UserProfile%\.config\legendary'
|
||
opencode = '%UserProfile%\.config\opencode'
|
||
'scoop-config' = '%UserProfile%\.config\scoop'
|
||
'scoop-persist' = '%UserProfile%\scoop\persist'
|
||
'.ssh' = '%UserProfile%\.ssh'
|
||
CodeSpace = 'D:\UserData\Documents\CodeSpace'
|
||
PowerShell = '%UserProfile%\Documents\PowerShell'
|
||
WindowsPowerShell = '%UserProfile%\Documents\WindowsPowerShell'
|
||
|
||
# ---- 应用数据 ----
|
||
AutoDarkMode = '%AppData%\AutoDarkMode'
|
||
Kazumi = '%AppData%\com.example\Kazumi'
|
||
piliplus = '%AppData%\com.example\piliplus'
|
||
fnm = '%AppData%\fnm'
|
||
'twinkle-tray' = '%AppData%\twinkle-tray'
|
||
|
||
# ---- 浏览器与终端 ----
|
||
Edge = '%LocalAppData%\Microsoft\Edge\User Data'
|
||
WindowsTerminal = '%LocalAppData%\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json'
|
||
|
||
# ---- 系统 ----
|
||
Startup = '%ProgramData%\Microsoft\Windows\Start Menu\Programs\Startup'
|
||
|
||
# ---- C:\Programs ----
|
||
BaiduNetdisk = 'C:\Programs\BaiduNetdisk'
|
||
FooClolor = 'C:\Programs\FooClolor'
|
||
March7thAssistant = 'C:\Programs\March7thAssistant'
|
||
MiFlash = 'C:\Programs\MiFlash'
|
||
MiFlash_Unlock = 'C:\Programs\MiFlash_Unlock'
|
||
QuarkCloudDrive = 'C:\Programs\QuarkCloudDrive'
|
||
translucenttb = 'C:\Programs\ScoopApps\apps\translucenttb\current\settings.json'
|
||
'ScoopApps-persist' = 'C:\Programs\ScoopApps\persist'
|
||
|
||
# ---- 其它盘 ----
|
||
Aria = 'D:\UserData\Documents\Aria'
|
||
}
|