fix(packaging): 修复 Windows 安装与固件打包并完善客户端文案
构建客户端 / 检查(类型 + 单元测试) (push) Canceled after 0s
构建客户端 / Linux x64(deb + AppImage) (push) Canceled after 0s
构建客户端 / Windows x64(NSIS 安装包) (push) Canceled after 0s
构建客户端 / macOS 通用(.app + .dmg) (push) Canceled after 0s

- fix(installer): 修复低完整性产物无法写入临时目录的问题,并在 CI 打包前后检查完整性标签
- fix(firmware): 从本次 Cargo ELF 打包并同步配套镜像,修正短目录说明与 LLVM 搜索顺序
- fix(installer): 修复中文完成页和页眉遮挡,侧栏使用等比缩放的原始 Logo
- style(client): 应用图标增加圆角,统一“裁判系统客户端”命名
- fix(config): 安装提示改用可选本地热点配置,排除 Wi-Fi 凭据
- docs: 润色项目说明并统一裁判系统术语
- test(packaging): 补充 Windows 完整性和固件打包回归,覆盖外部输出目录、旧镜像与重复生成

验证:Windows NSIS 构建及原生页面预览;通用与本地安装配置编译;固件 release 构建、重复打包和客户端离线校验;6 项固件打包回归与 Windows 权限回归;git diff --check。未执行实机烧录。
This commit is contained in:
2026-10-21 09:25:17 +08:00
parent 6a0eb14b28
commit 054379dc64
52 changed files with 1070 additions and 280 deletions
+158
View File
@@ -0,0 +1,158 @@
# Integration regression: actual Windows labels, child process tokens, and an
# NSIS bootstrap that writes to TEMP and exits without installing anything.
[CmdletBinding()]
param(
[string]$NsisCompiler = (Join-Path $env:LOCALAPPDATA 'tauri/NSIS/makensis.exe')
)
$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest
$prepare = Join-Path $PSScriptRoot 'prepare-windows-bundle.ps1'
& $prepare -CheckProcessOnly
if (-not (Test-Path -LiteralPath $NsisCompiler)) { throw "NSIS compiler not found: $NsisCompiler" }
function Assert([bool]$Condition, [string]$Message) {
if (-not $Condition) { throw $Message }
}
$testId = [Guid]::NewGuid().ToString('N')
$tempRoot = [IO.Path]::GetFullPath([IO.Path]::GetTempPath())
$fixture = Join-Path $tempRoot "rmic bundle test $testId"
$probeFile = Join-Path $tempRoot "rmic-nsis-temp-$testId.txt"
$installer = Join-Path $fixture 'bundle/nsis/probe-setup.exe'
$main = Join-Path $fixture 'cam-desktop.exe'
try {
New-Item -ItemType Directory -Force -Path (Join-Path $fixture 'bundle/nsis'), (Join-Path $fixture 'nsis/x64') | Out-Null
Copy-Item -LiteralPath (Join-Path $env:WINDIR 'System32/whoami.exe') -Destination $main
$accessBefore = (Get-Acl -LiteralPath $main).Sddl
# A small real NSIS executable exercises its early TEMP validation and
# plugin extraction without touching app files, registry, or shortcuts.
$source = Join-Path $fixture 'probe.nsi'
@"
Unicode true
Name "RMIC temporary directory probe"
OutFile "$($installer.Replace('$', '$$'))"
RequestExecutionLevel user
SilentInstall silent
Section
InitPluginsDir
FileOpen `$0 "`$TEMP\rmic-nsis-temp-$testId.txt" w
IfErrors failed
FileWrite `$0 "NSIS TEMP writable"
FileClose `$0
SetErrorLevel 0
Quit
failed:
SetErrorLevel 3
SectionEnd
"@ | Set-Content -LiteralPath $source -Encoding UTF8
& $NsisCompiler /V2 $source
Assert ($LASTEXITCODE -eq 0) 'Cannot compile the NSIS TEMP probe.'
& icacls.exe $fixture /setintegritylevel '(OI)(CI)L' | Out-Null
Assert ($LASTEXITCODE -eq 0) 'Cannot create a Low integrity fixture.'
& icacls.exe $main /setintegritylevel L | Out-Null
Assert ($LASTEXITCODE -eq 0) 'Cannot create an explicit Low executable label.'
$before = (& $main /groups) -join "`n"
Assert ($LASTEXITCODE -eq 0 -and $before -match 'S-1-16-4096') 'Low executable did not start with a Low token.'
$rejected = $false
try { & $prepare -OutputDir $fixture -CheckOnly } catch {
if ($_.Exception.Message -notmatch 'Low / Untrusted') { throw }
$rejected = $true
}
Assert $rejected 'Read-only artifact verification accepted a Low label.'
Assert ([RmicBundleIntegrity]::ReadLabel($installer) -match ';LW\)') 'Read-only verification changed the installer.'
# A Low process must fail before it tries to modify any output labels.
$lowShell = Join-Path $fixture 'low-shell.exe'
Copy-Item -LiteralPath (Join-Path $env:WINDIR 'System32/WindowsPowerShell/v1.0/powershell.exe') -Destination $lowShell
# Windows PowerShell 5 treats redirected native stderr as ErrorRecord objects.
# This command is expected to fail, so collect its diagnostic before asserting.
try {
$ErrorActionPreference = 'Continue'
$lowOutput = (& $lowShell -NoProfile -ExecutionPolicy Bypass -File $prepare -CheckProcessOnly 2>&1) -join "`n"
} finally {
$ErrorActionPreference = 'Stop'
}
Assert ($LASTEXITCODE -ne 0 -and $lowOutput -match 'requires Medium integrity') 'Low packaging process was not rejected.'
& $prepare -OutputDir $fixture
& $prepare -OutputDir $fixture -CheckOnly
& $prepare -OutputDir $fixture
$after = (& $main /groups) -join "`n"
Assert ($LASTEXITCODE -eq 0 -and $after -match 'S-1-16-(\d+)' -and [int]$Matches[1] -ge 8192) 'Repaired executable still starts below Medium integrity.'
Assert ((Get-Acl -LiteralPath $main).Sddl -eq $accessBefore) 'Repair changed the executable DACL.'
# The repaired shell now starts at Medium even from an elevated test runner.
# Verify that repairing owned Low outputs does not require elevation.
$mediumFixture = Join-Path $fixture 'medium repair'
New-Item -ItemType Directory -Path $mediumFixture | Out-Null
& icacls.exe $mediumFixture /setintegritylevel '(OI)(CI)L' | Out-Null
Assert ($LASTEXITCODE -eq 0) 'Cannot prepare the Medium-process repair fixture.'
$mediumOutput = (& $lowShell -NoProfile -ExecutionPolicy Bypass -File $prepare -OutputDir $mediumFixture 2>&1) -join "`n"
Assert ($LASTEXITCODE -eq 0) "Repair failed at Medium integrity: $mediumOutput"
Assert ([RmicBundleIntegrity]::ReadLabel($mediumFixture) -match ';ME\)') 'Medium process did not repair the output label.'
$probe = Start-Process -FilePath $installer -ArgumentList '/S' -PassThru -WindowStyle Hidden
try {
Assert ($probe.WaitForExit(15000)) 'NSIS TEMP probe timed out.'
Assert ($probe.ExitCode -eq 0) "NSIS TEMP probe failed: $($probe.ExitCode)"
Assert ((Get-Content -LiteralPath $probeFile -Raw) -eq 'NSIS TEMP writable') 'NSIS could not write to the normal TEMP directory.'
} finally {
if (-not $probe.HasExited) { $probe.Kill(); $probe.WaitForExit() }
$probe.Dispose()
}
$emptyOutput = Join-Path $fixture 'empty'
& $prepare -OutputDir $emptyOutput
$rejected = $false
try { & $prepare -OutputDir $emptyOutput -CheckOnly } catch {
if ($_.Exception.Message -notmatch 'No NSIS installer') { throw }
$rejected = $true
}
Assert $rejected 'Artifact verification accepted missing installers.'
# Exercise automatic Cargo output discovery, debug target selection and the
# separate host build-script directory, without invoking a real Rust build.
$savedEnvironment = @{}
foreach ($name in @('CARGO_TARGET_DIR', 'TAURI_ENV_TARGET_TRIPLE', 'TAURI_ENV_DEBUG')) {
$savedEnvironment[$name] = @{
Exists = Test-Path -LiteralPath "Env:$name"
Value = [Environment]::GetEnvironmentVariable($name, 'Process')
}
}
try {
$env:CARGO_TARGET_DIR = Join-Path $fixture 'cargo cache'
$env:TAURI_ENV_TARGET_TRIPLE = 'x86_64-pc-windows-msvc'
$env:TAURI_ENV_DEBUG = 'true'
$hostBuildDir = Join-Path $env:CARGO_TARGET_DIR 'debug/build/probe'
New-Item -ItemType Directory -Force -Path $hostBuildDir | Out-Null
$hostScript = Join-Path $hostBuildDir 'build-script-build.exe'
Copy-Item -LiteralPath $main -Destination $hostScript
& icacls.exe $env:CARGO_TARGET_DIR /setintegritylevel '(OI)(CI)L' | Out-Null
Assert ($LASTEXITCODE -eq 0) 'Cannot create Low host build outputs.'
& $prepare
Assert ([RmicBundleIntegrity]::ReadLabel($hostScript) -match ';ME\)') 'Host build script was not repaired.'
$targetOutput = Join-Path $env:CARGO_TARGET_DIR 'x86_64-pc-windows-msvc/debug/bundle/nsis'
Assert (Test-Path -LiteralPath $targetOutput) 'Cargo target directory or debug profile was ignored.'
} finally {
foreach ($name in $savedEnvironment.Keys) {
if ($savedEnvironment[$name].Exists) {
Set-Item -LiteralPath "Env:$name" -Value $savedEnvironment[$name].Value
} else {
Remove-Item -LiteralPath "Env:$name" -ErrorAction SilentlyContinue
}
}
}
Write-Host 'PASS: Low token reproduction, process guard, read-only rejection, label repair, unchanged DACL, repeat repair, NSIS TEMP bootstrap, missing artifacts, custom Cargo output and host build scripts.'
} finally {
# Only delete this test's uniquely named fixture, after checking its absolute path.
$resolvedFixture = [IO.Path]::GetFullPath($fixture)
if ($resolvedFixture.StartsWith($tempRoot.TrimEnd('\') + '\', [StringComparison]::OrdinalIgnoreCase) -and
(Split-Path -Leaf $resolvedFixture) -eq "rmic bundle test $testId") {
if (Test-Path -LiteralPath $resolvedFixture) { Remove-Item -LiteralPath $resolvedFixture -Recurse -Force }
}
if (Test-Path -LiteralPath $probeFile) { Remove-Item -LiteralPath $probeFile -Force }
}