Commit Graph
6 Commits
Author SHA1 Message Date
Shuery 187d2759fd 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 层。这条要改行为、且影响真实名录的解析
    结果,留给你拍板,不在本提交里动手。
2026-09-27 09:46:08 +08:00
Shuery 2ad7987ffe refactor: Common.psm1 拆成 BakNRet/{Public,Private},一函数一文件 + 薄加载器
3152 行、66 个函数的单文件模块拆成:
  BakNRet\BakNRet.psd1   模块清单:FunctionsToExport 是显式白名单(62 个名字)
  BakNRet\BakNRet.psm1   加载器:点源顺序的唯一一处声明
  BakNRet\Public\*.ps1   62 个对外函数,一函数一文件,文件名 = 函数名
  BakNRet\Private\*.ps1  4 个内部函数 + State.ps1(模块级状态集中一处)

为什么是一函数一文件:这是社区里脚本模块的主流形态(调研实测:winutil 79 个、
Terminal-Icons 24 个、ModuleBuilder 23 个,全部如此)。收益是改动落在小文件里、diff 按职责
可读、模块级状态有唯一去处。

为什么这不算"打散":模块内 dot-source 的文件共享同一个模块作用域(实测确认),所以
"按顺序点源 67 个文件"与"点源一个大文件"在语义上等价;顺序只在加载器里出现一次,
tools\Build-BakNRetModule.ps1 从那里读出顺序就能拼回单文件 —— 本次产物 dist\BakNRet.psm1
3240 行、两个版本都解析零错。

新增一条断言把这条承诺钉住:加载器点源的文件集合必须与磁盘一致、导出名单必须与
Public\ 一一对应。漏一个文件或漏一个名字就是静默少一个函数 —— 而那种错在运行时只表现为
"找不到命令"。

引用更新:11 个文件里的 Common.psm1 改成 BakNRet\BakNRet.psd1(走清单导入,
FunctionsToExport 才真的说了算);Common.psm1 直接删除,不留转发垫片。

验收:test.ps1 9/9 全绿(7 与 5.1),98 个文件两版解析零错,276 个断言原样通过 ——
这次搬家没有改变任何可观察行为。
2026-09-27 09:34:10 +08:00
Shuery 10f97246c8 fix: lab 的 ACL 场景在 5.1 上会被原生命令的 stderr 中断
tools\lab\payload\run-acl-scenario.ps1 是 $ErrorActionPreference = Stop,而它要大量调用
rmdir / takeown / icacls / scoop / code —— 其中 rmdir 与 takeown 在"对象已经处理过"或
"连接点已经悬空"时就会往 stderr 写字。Windows PowerShell 5.1 会把那升级成终止性的
NativeCommandError(7 改了这条规矩),于是清理函数在设计要处理的**恰恰那个场景**里直接崩掉。

修法:新增 Invoke-NativeTolerant,在进入原生命令时把 EAP 放到 Continue、退出时还原,
把 stdout 与 stderr 合并返回;12 处调用全部收进它。判断"删掉了没有"本来就该用 Test-Path,
不需要让 stderr 变成异常。这与 tests\BakNRet.Security.Tests.ps1 里对 takeown / icacls
用的是同一招(那里已被 5.1 的失败实测逼出来过)。

范围说明:只改了 run-acl-scenario.ps1。provision.ps1 的同类写法**不需要**改 ——
它是 $ErrorActionPreference = Continue,那两处本来就不会踩。

验证边界(如实说明):这个文件要 Hyper-V + 一台 VM 才跑得到,本会话无法执行它。
所以这一步的证据是:两个版本上解析零错(Parse 层覆盖)、12 处替换逐条断言命中、以及
diff 逐行复核。它是"降低风险",不是"已验证修复"。

验收:test.ps1 9/9 全绿(7 与 5.1);Run-RealSmoke 4/4 全绿。
2026-09-27 09:22:45 +08:00
Shuery e10503be76 fix: 让 5.1 真正可用(显式编码 + 原生 stderr 处理 + .psd1 夹具带 BOM)
上一提交让 5.1 能解析源码,但 Unit 与 Smoke 在 5.1 上仍然是红的。根因是三类彼此
无关的 5.1/7 行为差,全部实测确认:

1) 不写 -Encoding 时,5.1 的 Get-Content / Set-Content 默认是 ANSI,7 是 UTF-8。
   症状是 UTF-8 字节被按 GBK 解出「璇存槑」这类乱码。62 处补上显式 -Encoding UTF8。
   用 AST 而不是正则定位,避免把注释里的散文也改掉。

2) .psd1 夹具用无 BOM 写,而引擎的 .psd1 读取器(Import-PowerShellDataFile)只能靠
   BOM 判断编码、没有参数可传,于是 5.1 按 ANSI 解。40 处夹具改为带 BOM 写 —— 这正是
   .editorconfig 里 [*.psd1] charset = utf-8-bom 本来就要求的,是夹具违反了自己的约定。
   .cmd 批次文件刻意保持无 BOM:cmd.exe 会被 BOM 弄坏。

3) 5.1 在 $ErrorActionPreference = Stop 下会把原生命令写到 stderr 的内容升级成终止性
   NativeCommandError,7 改了这条。takeown/icacls 的 ACL 复位调用、以及 test.ps1 自己
   调子进程的地方,都需要在 Continue 下跑。

验收:test.ps1 9/9 全绿(Encode + Parse + Unit + Smoke + E2E,在 7 与 5.1 上各跑一遍)。

已知未处理(留待后续提交):tools/lab/** 里还有若干「原生命令 + 2>&1 + Stop」的同类
写法(takeown / icacls / scoop / code / Get-WimInfo)。它们要 Hyper-V 实验机才跑得到,
不在验收门槛内。
2026-09-26 22:16:04 +08:00
Shuery 2d26f78d15 fix: 源文件改存 UTF-8 with BOM,让 Windows PowerShell 5.1 真正可用
改造前:全仓 6/6 个源文件在 5.1 上解析失败(README 却承诺支持 5.1)。原因是文件是无 BOM 的
UTF-8,而 5.1 没有 BOM 就按 ANSI 代码页解码源码,中文变乱码、全角问号吃掉引号,整块语法塌掉。
现在 28/28 个文件在 5.1 与 7 上都解析零错误,E2E 36 项在 5.1 上全绿。

顺带修掉一个被 5.1 掩盖的缺陷:带 [CmdletBinding()] 的脚本在 5.1 上,param() 默认值里
拿不到 $PSScriptRoot(实测为空串,7 上正常)。于是 Backup.ps1 / Restore.ps1 在 5.1 上不传
路径参数就报错退出 —— 而计划任务恰恰不传。E2E 之所以看不见,是因为它总是显式传路径。
8 处默认值全部移到 param() 之后的解析段,沿用本仓库对 -BackupDir 一直在用的写法。

新增三条可重放的约定,让编码不再是一次性动作:
  .gitattributes 接管行尾(本机 core.autocrlf=true,会把工作区改成 CRLF 制造伪 diff)
  .editorconfig 用 charset = utf-8-bom 锁住 BOM
  tools\Set-SourceEncoding.ps1 是规范化脚本,tools\Invoke-* 之外的任何改动之后都能重放
  test.ps1 是唯一验收入口:Encode + Parse + Unit + Smoke + E2E,在 7 与 5.1 上各跑一遍

test.ps1 的 Encode 层直接检查"必须有 BOM"这条规则。加它的原因很实际:实测本仓库用的
编辑工具在保存时会悄悄去掉 BOM,而丢了 BOM 的文件只在 5.1 上出错、在 7 上完全正常,
没有这条检查就会一直漏过去。

已知未修(下一步处理):5.1 上 Unit 有 6 项、Smoke 有 1 项失败,全部源于测试夹具写
临时文件时没指定编码(5.1 的 Set-Content 默认 ANSI),与产品代码无关。
2026-09-26 22:05:42 +08:00
Shuery 2937eb6652 chore: 记录改造前基线
改造开始前的完整状态,作为可回退的基点。此提交之后:Pester 175 项、零依赖套件 101 项全绿;PowerShell 5.1 尚不可用(源文件无 BOM)。

包含此前未提交的在制品:安全描述符套件、Hyper-V 实验环境(tools/lab)、agent 约定(AGENTS.md 与 docs/agents)。

.gitignore 增加 *.key / *.pfx:BackupConfig.psd1 的 PasswordFile 此前默认指向仓库内的 baknret.key,一次 git add -A 就会把口令提交进版本库。默认值在后续提交中改为空。
2026-09-26 21:46:55 +08:00