增加 Windows 脚本交互菜单
This commit is contained in:
@@ -80,6 +80,7 @@ IDs must remain synchronized across the catalog, default navigation, icon map, a
|
|||||||
- Supported deployments are Docker and Node.js 22.12.0+ with `web-tools.ps1` on Windows Server. A Windows single EXE is an optional future deliverable and is not currently built or supported.
|
- Supported deployments are Docker and Node.js 22.12.0+ with `web-tools.ps1` on Windows Server. A Windows single EXE is an optional future deliverable and is not currently built or supported.
|
||||||
- Keep production port changes synchronized across `server/index.mjs`, `web-tools.ps1`, `Dockerfile`, `docker-compose.yml`, `.env.example`, deployment tests, and both READMEs where applicable.
|
- Keep production port changes synchronized across `server/index.mjs`, `web-tools.ps1`, `Dockerfile`, `docker-compose.yml`, `.env.example`, deployment tests, and both READMEs where applicable.
|
||||||
- The Windows update flow requires a clean `main` checkout and uses `git pull --ff-only`. Preserve external `C:\ProgramData\WebTools\data` state.
|
- The Windows update flow requires a clean `main` checkout and uses `git pull --ff-only`. Preserve external `C:\ProgramData\WebTools\data` state.
|
||||||
|
- Running `web-tools.ps1` without an action is an interactive user entry point and must keep the start/update/update-without-start/exit menu. Scheduled tasks and automation must pass an explicit `start` or `update` action so they never wait for input.
|
||||||
- New catalog data is loaded by the Node process at startup. Rebuilding `dist` alone is insufficient after catalog changes; restart the server and refresh already-open browser pages.
|
- New catalog data is loaded by the Node process at startup. Rebuilding `dist` alone is insufficient after catalog changes; restart the server and refresh already-open browser pages.
|
||||||
- If a single EXE is later authorized, use Node.js SEA with embedded read-only web/catalog assets and an external writable data directory. Add per-architecture builds, asset-read abstraction, Windows smoke tests, replacement/update instructions, and optional Authenticode signing. Derive EXE version metadata from `package.json`; do not create another manually maintained version source. Do not commit generated executables.
|
- If a single EXE is later authorized, use Node.js SEA with embedded read-only web/catalog assets and an external writable data directory. Add per-architecture builds, asset-read abstraction, Windows smoke tests, replacement/update instructions, and optional Authenticode signing. Derive EXE version metadata from `package.json`; do not create another manually maintained version source. Do not commit generated executables.
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
- 固件 Hash 在普通 HTTP 局域网访问时增加本地 SHA-256/SHA-512 回退,不再依赖 `SubtleCrypto` 必须可用。
|
- 固件 Hash 在普通 HTTP 局域网访问时增加本地 SHA-256/SHA-512 回退,不再依赖 `SubtleCrypto` 必须可用。
|
||||||
- 生产服务在监听端口前检查 `dist/index.html`,缺少构建产物时立即给出构建命令,不再启动后按请求重复返回 500。
|
- 生产服务在监听端口前检查 `dist/index.html`,缺少构建产物时立即给出构建命令,不再启动后按请求重复返回 500。
|
||||||
- 修复固件 Hash 和安全随机数模式选择框宽度过大,以及 801-1040 像素中间宽度下 CRC 参数区可能横向溢出的问题。
|
- 修复固件 Hash 和安全随机数模式选择框宽度过大,以及 801-1040 像素中间宽度下 CRC 参数区可能横向溢出的问题。
|
||||||
|
- 修复 Windows 脚本无参数运行时直接启动旧 `dist`、容易被误认为已完成更新的问题;现在交互启动会明确选择启动、更新并启动、仅更新或退出。
|
||||||
|
|
||||||
### Documentation
|
### Documentation
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,8 @@ powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\web-tools.ps1 start
|
|||||||
|
|
||||||
The script listens on `0.0.0.0:9000` by default and stores navigation settings in `C:\ProgramData\WebTools\data\navigation.json`. It accepts `-ListenAddress`, `-Port`, and `-DataDir` overrides. It does not create Windows Firewall rules.
|
The script listens on `0.0.0.0:9000` by default and stores navigation settings in `C:\ProgramData\WebTools\data\navigation.json`. It accepts `-ListenAddress`, `-Port`, and `-DataDir` overrides. It does not create Windows Firewall rules.
|
||||||
|
|
||||||
|
Running `web-tools.ps1` without an action opens an interactive menu: start the existing build, update/rebuild/start, update/rebuild without starting, or exit. Explicit `start` and `update` arguments remain available for shortcuts, scheduled tasks, and automation. When launching interactively after a repository update, choose option `2`; option `1` intentionally starts the existing `dist` without rebuilding it.
|
||||||
|
|
||||||
To update a clean checkout on the `main` branch, stop the foreground process with `Ctrl+C`, then run:
|
To update a clean checkout on the `main` branch, stop the foreground process with `Ctrl+C`, then run:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
|
|||||||
@@ -160,6 +160,17 @@ powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\web-tools.ps1 start `
|
|||||||
-DataDir C:\ProgramData\WebTools\data
|
-DataDir C:\ProgramData\WebTools\data
|
||||||
```
|
```
|
||||||
|
|
||||||
|
不带操作参数运行 `web-tools.ps1` 时会显示交互菜单:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[1] Start current build (no update)
|
||||||
|
[2] Update from Git, rebuild, and start
|
||||||
|
[3] Update from Git and rebuild only
|
||||||
|
[0] Exit
|
||||||
|
```
|
||||||
|
|
||||||
|
日常双击或使用“使用 PowerShell 运行”打开脚本后,需要更新代码时选择 `2`;`1` 只启动磁盘上已有的 `dist`,不会拉取代码或重新构建。计划任务、快捷方式和自动化仍应显式传入 `start`、`update` 或 `update -NoStart`,不会进入菜单等待输入。
|
||||||
|
|
||||||
脚本不会自动修改 Windows 防火墙。需要从局域网访问默认端口时,可由管理员仅对专用网络配置入站规则:
|
脚本不会自动修改 Windows 防火墙。需要从局域网访问默认端口时,可由管理员仅对专用网络配置入站规则:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
|
|||||||
@@ -50,6 +50,17 @@ describe('deployment configuration', () => {
|
|||||||
expect(script).toContain("@('ci', '--include=dev')")
|
expect(script).toContain("@('ci', '--include=dev')")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('offers an interactive Windows action menu without changing explicit automation actions', async () => {
|
||||||
|
const script = await readProjectFile('web-tools.ps1')
|
||||||
|
|
||||||
|
expect(script).toContain('function Select-InteractiveAction')
|
||||||
|
expect(script).toContain("[2] Update from Git, rebuild, and start")
|
||||||
|
expect(script).toContain("[3] Update from Git and rebuild only")
|
||||||
|
expect(script).toContain("$PSBoundParameters.ContainsKey('Action')")
|
||||||
|
expect(script).toContain("[ValidateSet('start', 'update')]")
|
||||||
|
expect(script).not.toContain("[string]$Action = 'start'")
|
||||||
|
})
|
||||||
|
|
||||||
it('provides standard entry points for a new AI session', async () => {
|
it('provides standard entry points for a new AI session', async () => {
|
||||||
const [entryReadme, agentGuide, completeReadme] = await Promise.all([
|
const [entryReadme, agentGuide, completeReadme] = await Promise.all([
|
||||||
readProjectFile('README.md'),
|
readProjectFile('README.md'),
|
||||||
|
|||||||
+35
-1
@@ -4,7 +4,7 @@
|
|||||||
param(
|
param(
|
||||||
[Parameter(Position = 0)]
|
[Parameter(Position = 0)]
|
||||||
[ValidateSet('start', 'update')]
|
[ValidateSet('start', 'update')]
|
||||||
[string]$Action = 'start',
|
[string]$Action,
|
||||||
|
|
||||||
[ValidateNotNullOrEmpty()]
|
[ValidateNotNullOrEmpty()]
|
||||||
[string]$ListenAddress = '0.0.0.0',
|
[string]$ListenAddress = '0.0.0.0',
|
||||||
@@ -62,6 +62,26 @@ function Invoke-NativeCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Select-InteractiveAction {
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host 'Web Tools'
|
||||||
|
Write-Host ' [1] Start current build (no update)'
|
||||||
|
Write-Host ' [2] Update from Git, rebuild, and start'
|
||||||
|
Write-Host ' [3] Update from Git and rebuild only'
|
||||||
|
Write-Host ' [0] Exit'
|
||||||
|
|
||||||
|
while ($true) {
|
||||||
|
$selection = (Read-Host 'Select an action (0-3)').Trim()
|
||||||
|
switch ($selection) {
|
||||||
|
'1' { return 'start' }
|
||||||
|
'2' { return 'update' }
|
||||||
|
'3' { return 'update-no-start' }
|
||||||
|
'0' { return 'exit' }
|
||||||
|
default { Write-Warning 'Invalid selection. Enter 0, 1, 2, or 3.' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function Assert-ProductionBuild {
|
function Assert-ProductionBuild {
|
||||||
if (-not (Test-Path -LiteralPath $serverEntry -PathType Leaf)) {
|
if (-not (Test-Path -LiteralPath $serverEntry -PathType Leaf)) {
|
||||||
throw "Server entry was not found: $serverEntry"
|
throw "Server entry was not found: $serverEntry"
|
||||||
@@ -166,6 +186,20 @@ function Start-WebTools {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (-not $PSBoundParameters.ContainsKey('Action')) {
|
||||||
|
$interactiveAction = Select-InteractiveAction
|
||||||
|
if ($interactiveAction -eq 'exit') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ($interactiveAction -eq 'update-no-start') {
|
||||||
|
$Action = 'update'
|
||||||
|
$NoStart = $true
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$Action = $interactiveAction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($Action -eq 'start' -and $NoStart) {
|
if ($Action -eq 'start' -and $NoStart) {
|
||||||
throw "-NoStart can only be used with the 'update' action."
|
throw "-NoStart can only be used with the 'update' action."
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user