8.5 KiB
8.5 KiB
spec:增强健壮性 + 重构代码 + 重组结构 + 微软规范落地
Status: accepted Date: 2026-09-26
本文件是这次改造的唯一验收锚点。每一项改动都要能被这里的某一条门槛判成通过或失败;判不了的就不要做。
1. 四件事
- 增强健壮性 —— 修掉实测确认的缺陷,并把「支持 5.1」从纸面承诺变成可验证的事实。
- 重构代码 —— 消除重复实现、死代码与闭包依赖,让两个入口只做编排。
- 重组结构 —— 让文件系统与职责对齐,并落一份可读的模块导出面。
- 按微软规范格式化代码与文档 —— 落到可重复执行的工具上,而不是一次性手工活。
2. 验收标准
| # | 门槛 | 改造前 | 目标 |
|---|---|---|---|
| 1 | Pester 套件 | 175 通过 / 0 失败 | 全绿(覆盖不得减少) |
| 2 | 零依赖套件(瘦身后为冒烟) | 101 通过 / 0 失败 | 全绿 |
| 3 | 全仓 Parser::ParseFile(5.1 与 7) |
5.1 上 6/6 文件失败 | 两版都零错误 |
| 4 | tests/Run-E2E.ps1(36 项) |
只在 7.x 上跑过 | 7 与 5.1 都全绿 |
| 5 | PSScriptAnalyzer 默认规则集 | 未安装 | -Severity Warning,Error 下 0 条 |
| 6 | PSScriptAnalyzer 格式化规则集 | 无 | 0 条 |
| 7 | 只读冒烟 | 未系统跑过 | Backup.ps1 -DryRun 与 Restore.ps1 -VerifyOnly 在真实清单上跑通,一个字节都不写 |
| 8 | markdownlint(可选档,不进必绿门槛) | 无 | 配置就位,0 error |
| 9 | 术语表与决策记录 | 无 | CONTEXT.md + docs/adr/ 6 条 |
第 3、4 条是新增的:改造前这两条根本不存在,而 README 却承诺了 5.1。
3. 已确认的决策
3.1 过程
- D1 基线与分支:先提交当前工作区为基线,再从基线开
refactor/ms-conventions。 - D2 过程产物:本文件即 spec;不为四条工作流各开 ticket。
- D3 行为边界:重构类改动保持行为等价;健壮性类改动可改变边界行为,但对外契约只增不改(参数名、退出码语义、配置字段、清单语法、归档布局一律向后兼容)。
3.2 结构
- D4 库代码形态:
BakNRet/目录承载模块 ——BakNRet.psd1+BakNRet.psm1(薄加载器,按显式顺序点源)+Public/+Private/,一函数一文件,文件名 = 函数名。 - D5 入口与配置原地不动:
Backup.ps1、Restore.ps1、BackupList.txt、SoftwareCatalog.psd1、BackupConfig.psd1全部留在仓库根。 - D6
src/不用。实测证据:纯 PowerShell 且无构建步骤的仓库里,用src/的为 0。 - D7 公共面:
.psd1写显式FunctionsToExport白名单(官方要求,且PSUseToExportFieldsInManifest恒开不可关);私有件靠Private/+ 不导出双保险。 - D8 测试拿私有函数:用 Pester 的
InModuleScope(已实测在 5.1 与 7 上都可用)。不让测试点源Private/*.ps1—— 那会把模块级状态建在测试脚本自己的作用域里。 - D9 入口沉到只做编排:
Backup.ps1/Restore.ps1里的逻辑(压缩→校验→原子替换、manifest 条目读写、解压三分支、孤档审计、7z 校验、工具定位)全部下沉进模块,分两个提交:先纯搬家,再参数化。 - D10 闭包依赖参数化:
Save-ItemRecord不再直接改脚本级$manifest;Invoke-BackupItem不再读脚本级$tool/$password;Test-ItemSelected不再读$Only/$Skip。 - D11 模块级状态收敛:5 个
$script:变量集中到Private/State.ps1,访问器只覆盖"写",并补一个测试用的状态重置函数。
3.3 命名
- D12 公共面函数补
BakNRet前缀;私有面保留短名。动词已 63/63 全部是批准动词,动词不动。 - D13
Set-Alias过渡垫片不留。 - D14 产品名统一写作
BakNRet(见CONTEXT.md)。
3.4 声明与编码
- D15 可拼接性不变量(三条,写成测试):每个私有文件不写自己的
param()/using/Set-StrictMode/Export-ModuleMember;私有文件不引用$PSScriptRoot或相对路径;加载顺序只在加载器里出现一次。 - D16
tools/Build-BakNRetModule.ps1:按加载器声明的同序拼接各文件,产出dist/BakNRet.psm1(gitignore);不引入 ModuleBuilder/Sampler。 - D17 源文件一律 UTF-8 with BOM;
.editorconfig用charset = utf-8-bom把这条锁住。 - D18
.gitattributes用* text=auto eol=lf加二进制规则(照 PSScriptAnalyzer 的写法)。 - D19 版本声明:
#Requires -Version 5.1;绝不写#Requires -PSEdition。.psd1写CompatiblePSEditions = @('Desktop','Core')。 - D20
Set-StrictMode -Version 3.0(不用Latest,双版本要的是确定性),先在库模块内开,跑绿后再考虑入口。
3.5 静态分析与文档
- D21 PSScriptAnalyzer 装进
.tools/(不动机器上的全局模块);PSScriptAnalyzerSettings.psd1放仓库根;tools/Invoke-Analyzer.ps1是唯一入口;独立门禁脚本,不塞进 Pester 用例。 - D22 全局
ExcludeRules排除PSAvoidUsingWriteHost(彩色控制台输出是本工具的刻意设计)。 - D23
PSUseSingularNouns用NounAllowList放行SecurityRecords/MapKeys/ArchiveTopLevelNames。 - D24
Backup.ps1补SupportsShouldProcess(新增-WhatIf/-Confirm,属只增不改)。 - D25 启用
PSAvoidLongLines,上限 120,全仓折断。 - D26 注释帮助:公共面补齐
.SYNOPSIS/.DESCRIPTION/.PARAMETER/.OUTPUTS/.EXAMPLE;私有面只要求前两个。 - D27 文档拆分:README 瘦身成「是什么 + 快速开始 + 命令速查 + 指向
docs/」;11 篇主题进docs/;「相对旧版修了什么」抽成CHANGELOG.md;known-limits留在 README。 - D28 6 条 ADR,不拆更细。
- D29 markdownlint 用
default: true,行长放宽到 240(照 PowerShell 仓库的实践),只作可选检查。 - D30 测试入口:根级
test.ps1作为唯一入口(-Suite与-PSVersion参数);tests/保留 Pester 的*.Tests.ps1命名,端到端降级为tests/E2E.ps1与tests/Drill.ps1。
3.6 明确不做
- 不搬三个配置文件(D5)。
- 不把库发布到 PSGallery,不引入 ModuleBuilder / Sampler(D16)。
- 不留旧函数名的
Set-Alias垫片(D13)。 - 不为 manifest 写一次性迁移脚本 —— 它是运行产物,下次运行自然老化。
- 不补一批特征化测试再动手 —— 现有 276 个断言足够,新增断言按「修一个补一个」加。
- 不自动搬
baknret.key。搬密钥是使用者的动作,本改造只负责停止往仓库里放默认路径。
4. 执行顺序
每一步独立提交,每一步都跑一遍全套验证。
chore基线提交(含.gitignore加*.key)fix源文件改存 UTF-8 with BOM + 立起 5.1 验收门槛fix暂存目录泄漏 —— 全仓唯一会把 junction 留在真实数据上的缺陷docsCONTEXT.md术语表refactorCommon.psm1→BakNRet/{Public,Private},纯搬家 + 拼接校验refactor入口逻辑下沉 + 闭包依赖参数化refactor改名 + 模块清单显式导出面fix其余 P0(口令进日志 / manifest 原子写 / 空间守卫 / 并发保护)style格式化规则集 + 机械重排,再逐条修静态分析告警docsREADME 拆分 +CHANGELOG.md+ 6 条 ADR +test.ps1/Invoke-Analyzer.ps1+ 门禁
顺序的三条依据:先让 5.1 能解析(否则后续所有验证都少一半);先修唯一会伤数据的那一项;把可证明无损的结构搬家放在行为修复之前(搬家用拼接校验证明零变化,之后每个修复都是小文件里的几行 diff,各自独立可回滚)。
5. 风险与回滚
| 风险 | 控制 |
|---|---|
| BOM 改造后 5.1 暴露新问题 | 单独一个提交,改完立刻跑 5.1 验收;有问题就在这一个提交上回退 |
| 结构搬家引入行为变化 | 用「同序拼接 == 模块内容」逐文件校验;先纯搬家、函数体一行不改 |
| 改名牵动 276 个断言 | 纯改名单独提交,跑绿后再做其它;不留别名垫片,避免两个名字并存 |
| 机械重排污染后续 review | 排在行为修复之后,单独提交,用 git diff -w 复核 |
| 真实归档数据受影响 | 全程不碰 Backups/;只读冒烟只用 -DryRun / -VerifyOnly |