Files
nodeagent/.github/workflows/windows-e2e.yml
T
jkooe d065242413 docs/ci: README 数字校正 + checkout 升 v5
- README:统计口径已过期(43 能力 · **32** MCP 工具 · **114** 单测 · **26** E2E),
  校正为实测值:43 能力 · 38 MCP 工具 · 215 单测 · 30 E2E · 11 Rust 单测。
  另补「桌面控制台」小节(架构图、页面表、构建命令、平台门控说明)与构建步骤说明
  —— 新组件入库后不同步 README,读者会以为仓库里没有它。
- CI:actions/checkout@v4 → v5,消除 runner 上的 Node.js 20 弃用告警。
  仅升被标记的这一个;setup-node / pnpm-action 未被标记,不做未验证的连带升级。
2026-10-07 10:18:52 +08:00

184 lines
8.1 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: Windows E2E
on:
push:
branches: [main]
paths:
- 'apps/**'
- 'packages/**'
- 'tests/**'
- 'scripts/**'
- 'package.json'
- 'pnpm-lock.yaml'
- '.github/workflows/windows-e2e.yml'
pull_request:
branches: [main]
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
windows-e2e:
name: Windows 真机验证
runs-on: windows-latest
timeout-minutes: 30
steps:
- name: 检出代码
uses: actions/checkout@v5
- name: 安装 pnpm
uses: pnpm/action-setup@v4
with:
version: 12
- name: 安装 Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- name: 安装依赖
run: pnpm install --frozen-lockfile
- name: 构建
run: pnpm -r build
- name: 类型检查
run: pnpm typecheck
- name: 单元测试 — 协议纯函数 / 清单守护 / 脚本生成
# 来自 ci/workflow-unit-tests 分支(4d73d6e)的实质贡献;该分支基于老 main,
# 直接 merge 会回退上万行,故只取其提交内容手工并入。
run: node --test "tests/unit/**/*.test.mjs"
- name: E2E — 协议与跨平台能力
run: node tests/e2e/run.mjs
- name: E2E — Windows 专属能力(服务 / 软件 / winget)
run: node tests/e2e/windows.mjs
# ⚠️ 这一步刻意用 **powershell(Windows PowerShell 5.1)** 而非 pwsh。
# 5.1 读无 BOM 的 .ps1 会按 ANSI/GBK 解析,中文注释会破坏字符串边界 →
# 整份脚本 ParseError(真机 2026-10-04 踩过:install.ps1 报「","后面缺少表达式」)。
# 之前的冒烟全用 pwsh(7+ 认无 BOM UTF-8),所以 CI 绿、真机炸。
# 这里只做「解析 + 首行 help」检查:5.1 能否读懂,就是真机的核心约束。
# ⚠️ 必须用 shell: powershell(runner 上的 powershell 即 Windows PowerShell 5.1)。
# 不要用 shell: cmd 再套 powershell -Command "..." —— 多行带引号经 cmd 转义会全乱
# (实测:Missing closing '}' / '$errs' is not recognized,是步骤自身坏,不是脚本坏)。
- name: 冒烟 — 脚本可被 Windows PowerShell 5.1 解析(BOM 守卫)
shell: powershell
run: |
$ErrorActionPreference = 'Stop'
foreach ($f in @('scripts\install.ps1', 'scripts\uninstall.ps1', 'scripts\rescue-network.ps1')) {
$errs = $null
[void][System.Management.Automation.Language.Parser]::ParseFile((Resolve-Path $f).Path, [ref]$null, [ref]$errs)
if ($errs -and $errs.Count -gt 0) { throw ('5.1 parse FAILED: ' + $f + ' -> ' + $errs[0].Message) }
Write-Host ('OK 5.1 parse: ' + $f)
}
- name: 冒烟 — install.ps1 安装链路
shell: powershell
run: |
$ErrorActionPreference = "Stop"
./scripts/install.ps1 -Port 18769 -NodeId ci_win
$task = Get-ScheduledTask -TaskName "nodeagent" -ErrorAction Stop
Write-Host "Scheduled task state: $($task.State)"
$cfg = Get-Content "$env:USERPROFILE\.nodeagent\agent.json" -Raw | ConvertFrom-Json
if ($cfg.port -ne 18769) { throw "port mismatch: $($cfg.port)" }
if (-not $cfg.key) { throw "PSK missing in agent.json" }
if (-not $cfg.tls) { throw "TLS should be on by default" }
$rule = Get-NetFirewallRule -DisplayName "nodeagent (TCP 18769)" -ErrorAction Stop
if (-not $rule) { throw "Firewall rule" }
Write-Host "OK scheduled task / config / firewallall in place"
# 一键入口冒烟:真跑 install.cmd / control.cmd。
# 目的不是「再装一遍」,而是证明双击路径可用 —— 这些脚本是用户唯一的
# 上手入口,语法或编码一旦坏了,只有真机 cmd.exe 才测得出来。
# 端口用 18770 避开前一步的 18770 之外的安装,便于分辨是哪一步装的。
- name: 冒烟 — install.cmd 双击链路(含自提权与 PSK 读取)
shell: cmd
run: call scripts\install.cmd 18770
env:
CI: "true"
- name: 冒烟 — control.cmd 状态查询
shell: cmd
run: scripts\control.cmd 18770 < nul
env:
CI: "true"
- name: 冒烟 — 产物包结构与编码
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
pnpm pack:win
$zip = "release/nodeagent-win-x64.zip"
if (-not (Test-Path $zip)) { throw "zip not produced: $zip" }
Add-Type -AssemblyName System.IO.Compression.FileSystem
$z = [IO.Compression.ZipFile]::OpenRead((Resolve-Path $zip))
try {
$names = $z.Entries.Name
# 必须随包分发
foreach ($need in @('install.cmd','control.cmd','install.ps1','control.ps1','README.txt','agent.mjs','node.exe')) {
if ($names -notcontains $need) { throw "missing in package: $need" }
}
# 必须**不**在包内:PSK.txt 是现生成给本机的,打进公开资产等于泄露密钥
# (eaca9e0 起已从 zip 剔除;此处反向断言守住该性质)
foreach ($forbidden in @('PSK.txt','*.key','*.pem','certs')) {
if ($names -contains $forbidden) { throw "package must NOT contain: $forbidden" }
}
if ($names -match 'PSK\.txt') { throw "package leaks PSK.txt" }
foreach ($e in $z.Entries) {
$s = $e.Open(); $buf = New-Object byte[] 3
[void]$s.Read($buf, 0, 3); $s.Close()
$hasBom = ($buf[0] -eq 0xEF -and $buf[1] -eq 0xBB -and $buf[2] -eq 0xBF)
if ($e.Name -like '*.ps1' -and -not $hasBom) { throw "$($e.Name) lost its BOM in package" }
if ($e.Name -like '*.cmd' -and $hasBom) { throw "$($e.Name) must not have a BOM" }
}
Write-Host "OK - package complete; .ps1 has BOM / .cmd has none; no key shipped"
} finally { $z.Dispose() }
- name: 冒烟 — 被控端监听
shell: pwsh
run: |
Start-Process -FilePath "node" -ArgumentList "apps/agent/dist/index.js" -NoNewWindow
Start-Sleep -Seconds 4
$ok = (Test-NetConnection 127.0.0.1 -Port 18769 -WarningAction SilentlyContinue).TcpTestSucceeded
if (-not $ok) { throw "agent 未监听 18769" }
Write-Host "agent 已在 18769 监听"
- name: 冒烟 — CLI 连入(wss + 自签证书 + HMAC 鉴权)
shell: pwsh
run: |
$cfg = Get-Content "$env:USERPROFILE\.nodeagent\agent.json" -Raw | ConvertFrom-Json
node apps/cli/dist/index.js connect 127.0.0.1 --port 18769 --key $cfg.key --insecure
Write-Host "--- status ---"
node apps/cli/dist/index.js status
Write-Host "--- services ---"
node apps/cli/dist/index.js services --limit 5
- name: 冒烟 — MCP 工具列表
shell: pwsh
run: |
$cfg = Get-Content "$env:USERPROFILE\.nodeagent\agent.json" -Raw | ConvertFrom-Json
@{ client_id="ci"; host="127.0.0.1"; port=18769; tls=$true; insecure=$true; key=$cfg.key } |
ConvertTo-Json | Set-Content "$env:USERPROFILE\.nodeagent\config.json" -Encoding UTF8
$req = @(
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"ci","version":"1.0"}}}',
'{"jsonrpc":"2.0","method":"notifications/initialized"}',
'{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
) -join "`n"
($req + "`n") | node apps/mcp/dist/index.js 2>$null | Select-String "na_status"
- name: 清理
if: always()
# 清理失败不掩盖真实结论(前置挂了会连带这里报错)
continue-on-error: true
shell: powershell
run: |
Get-Process node -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
./scripts/uninstall.ps1 -Port 18769 -Purge