feat(tui): 主入口 Manage-Backup 与配置入口 Edit-Config(第一轮第六块·完成)

Manage-Backup.ps1:不带参数进 TUI 主菜单(备份 / 恢复 / 配置);带 -Action 直接做那个动作,可配 -Quiet 走无头。退出码原样传出(计划任务靠它);只有两种情况由它自己给码:进不了交互界面(2)与菜单里按 Esc(0)。

动作目前仍是**子进程**:一份编排、两个前端是终局目标(ADR-0012),但那要求先把编排从入口脚本下沉进模块。先让 TUI 能用起来,等下沉做完把子进程调用换成进程内直调,界面层不受影响。

-Quiet 与交互两条路的输出处理**故意不同**:-Quiet 时调用方要拿输出(管道 / 日志 / 计划任务),所以捕获并转发;交互时让子进程继承控制台,输出实时、顺序正确、还能接键盘。这套转发收进模块函数 Invoke-BakNRetEntryScript(垫片与主入口共用同一处逻辑)。

Edit-Config.ps1:把已做好的清单编辑器挂上(外科式改写 + 校验通过才落盘 + 时间戳备份);设置与名录两个界面明确报"还没做"并返回非零,而不是假装成功。

判据 7 条。过程中夹具又踩了"& 不传退出码"这个事实(我在垫片那层修过,却在测试夹具这一层又犯)—— 所以断言里改用 -File 调入口;输出检查走**文件重定向**而不是管道捕获(与 E2E 同一个理由:沙箱下给子进程建管道可能失败,写文件不会)。

验收:test.ps1 9/9 全绿(5.1 与 7);真实清单只读冒烟 4/4。
This commit is contained in:
Shuery committed 2026-09-27 20:41:39 +08:00
1 parent 4e3c0461c0
commit c2bae35506
6 files changed
+257

No files matched your search

+78
View File
@@ -0,0 +1,78 @@
<#
.SYNOPSIS
BakNRet 主入口:不带参数时进 TUI 菜单;带 -Action 时按动作执行。
.DESCRIPTION
不带参数 = 交互式界面(TUI 主菜单)。带 `-Action Backup|Restore|Config` = 直接做那个动作,
可以配 `-Quiet` 走无头。
为什么动作仍然是**子进程**(而不是把编排搬进模块):一份编排、两个前端是终局目标(ADR-0012),
但那要求先把编排从入口脚本里下沉进模块 —— 那是另一大块工作。先让 TUI 能用起来,等下沉做完
再把这里的子进程调用换成进程内直调,界面层不受影响。
-Quiet:调用方要拿输出(管道 / 日志 / 计划任务),所以捕获并转发子进程的输出。
不加 -Quiet 时子进程继承控制台:输出实时、顺序正确、还能接键盘。
退出码:动作的退出码**原样传出**(计划任务靠它)。只有两种情况由本脚本自己给码:
既没有控制台又没有 -InputScript(无法进交互界面)→ 2;在菜单里按 Esc 退出 → 0。
绝不挂起:进不了界面就明确失败(ADR-0010)。
.EXAMPLE
.\Manage-Backup.ps1 # 进 TUI 菜单
.EXAMPLE
.\Manage-Backup.ps1 -Action Backup -Quiet -DryRun
#>
[CmdletBinding()]
param(
[ValidateSet('Backup', 'Restore', 'Config')][string]$Action,
[switch]$Quiet,
[string[]]$InputScript,
[Parameter(ValueFromRemainingArguments = $true)]$Rest
)
$ErrorActionPreference = 'Stop'
$savedVerbosePreference = $VerbosePreference
$VerbosePreference = 'SilentlyContinue'
Import-Module (Join-Path $PSScriptRoot 'BakNRet\BakNRet.psd1') -Force
$VerbosePreference = $savedVerbosePreference
# -Verbose / -Debug 是通用参数,**不会**落进 $Rest,所以要显式转发(垫片那一轮踩过这个坑)
$forwardExtra = @($Rest) + @(if ($PSBoundParameters.ContainsKey('Verbose')) { '-Verbose' }) + @(if ($PSBoundParameters.ContainsKey('Debug')) { '-Debug' })
$entryFor = @{
'Backup' = 'Backup-Data.ps1'
'Restore' = 'Restore-Data.ps1'
'Config' = 'Edit-Config.ps1'
}
if ($Action) {
$target = Join-Path $PSScriptRoot $entryFor[$Action]
if ($Quiet) {
$exitCode = Invoke-BakNRetEntryScript -Path $target -ArgumentList $forwardExtra -Capture
}
else {
$exitCode = Invoke-BakNRetEntryScript -Path $target -ArgumentList $forwardExtra
}
exit $exitCode
}
# 不带 -Action:进 TUI 主菜单。进不去就明确失败,绝不挂起。
if ([Console]::IsOutputRedirected -and -not $InputScript) {
Write-Host '没有可用的控制台,也没有给出 -InputScript:无法进入交互界面。' -ForegroundColor Red
Write-Host '要非交互执行,请指定动作,例如:-Action Backup -Quiet' -ForegroundColor Red
exit 2
}
$driver = New-BakNRetInputDriver -Script $InputScript -NonInteractive:$([bool]$InputScript)
$items = @(
[pscustomobject]@{ Key = 'Backup'; Text = '备份  把清单里该备份的条目打包' }
[pscustomobject]@{ Key = 'Restore'; Text = '恢复  从归档恢复(含干跑与校验)' }
[pscustomobject]@{ Key = 'Config'; Text = '配置  清单 / 设置 / 名录' }
)
$picked = Invoke-BakNRetMenu -Title 'BakNRet' -Items $items -Driver $driver -MultiSelect $false -Hint '↑↓ 选择 回车 进入 Esc 退出'
if ($picked.Action -ne 'confirm') { exit 0 }
$target = Join-Path $PSScriptRoot $entryFor[$picked.Chosen.Key]
exit (Invoke-BakNRetEntryScript -Path $target -ArgumentList $forwardExtra)