|
| 1 | +# PR #351 Windows Validation |
| 2 | + |
| 3 | +This is the complete Windows handoff for |
| 4 | +[PR #351](https://github.com/Fei-Away/Codex-Dream-Skin/pull/351). A Windows |
| 5 | +agent should pull that PR and validate the exact PR head. Do not test the |
| 6 | +published v1.5.12 installer: it predates every change in this PR. |
| 7 | + |
| 8 | +The PR combines three related candidate areas that need one real-Windows pass: |
| 9 | + |
| 10 | +1. Windows client localization and language persistence (`System`, `English`, |
| 11 | + and `中文`), including tray actions, dialogs, notifications, installer |
| 12 | + payload repair, update checks, theme import/apply, and restore paths. |
| 13 | +2. Shared renderer/runtime changes used by both macOS and Windows. |
| 14 | +3. Issue #337: PowerShell 5.1 must preserve a bundled Node executable path |
| 15 | + when Inno Setup extracts it below a non-ASCII temporary directory. The old |
| 16 | + failure text was `Node.js executable path could not be validated`. |
| 17 | + |
| 18 | +This document authorizes validation only. The Windows agent must not merge the |
| 19 | +PR, bump a version, create a tag or Release, publish an installer, upload its |
| 20 | +local build, edit WindowsApps ACLs, modify `app.asar`, disable Defender, or use |
| 21 | +`ExecutionPolicy Bypass`. |
| 22 | + |
| 23 | +## 1. Check Out The Exact PR Head |
| 24 | + |
| 25 | +Use a fresh clone or a clean worktree. These commands deliberately detach at |
| 26 | +the current PR head so the tested commit cannot move locally during the run: |
| 27 | + |
| 28 | +```powershell |
| 29 | +git fetch origin pull/351/head |
| 30 | +git switch --detach FETCH_HEAD |
| 31 | +
|
| 32 | +$candidateSha = (git rev-parse HEAD).Trim() |
| 33 | +$onlineSha = (gh pr view 351 --repo Fei-Away/Codex-Dream-Skin ` |
| 34 | + --json headRefOid --jq .headRefOid).Trim() |
| 35 | +if ($candidateSha -cne $onlineSha) { |
| 36 | + throw "PR head changed during checkout: local=$candidateSha online=$onlineSha" |
| 37 | +} |
| 38 | +if (git status --porcelain) { |
| 39 | + throw 'The validation worktree is dirty.' |
| 40 | +} |
| 41 | +"CANDIDATE_SHA=$candidateSha" |
| 42 | +``` |
| 43 | + |
| 44 | +Record the printed full SHA. Before sending the final report, run the online |
| 45 | +SHA comparison again. If the PR moved, stop and rerun against its new head. |
| 46 | + |
| 47 | +## 2. Record The Host |
| 48 | + |
| 49 | +Use a real Windows 10/11 x64 host with the official Microsoft Store Codex |
| 50 | +package installed for the current user. Record: |
| 51 | + |
| 52 | +```powershell |
| 53 | +$PSVersionTable | Format-List PSVersion, PSEdition, OS, OSVersion |
| 54 | +[Environment]::OSVersion.Version |
| 55 | +node --version |
| 56 | +git --version |
| 57 | +gh --version |
| 58 | +Get-AppxPackage OpenAI.Codex | Select-Object Name, Version, Architecture |
| 59 | +``` |
| 60 | + |
| 61 | +Windows PowerShell 5.1 is mandatory. PowerShell 7 is an additional gate when |
| 62 | +available; it cannot replace the 5.1 result. |
| 63 | + |
| 64 | +## 3. Run Automated Gates |
| 65 | + |
| 66 | +Run from the repository root. Every command must exit `0`: |
| 67 | + |
| 68 | +```powershell |
| 69 | +node .\tools\sync-runtime-assets.mjs --check |
| 70 | +
|
| 71 | +$portableTests = @( |
| 72 | + Get-ChildItem .\macos\tests\*.test.mjs, |
| 73 | + .\windows\tests\*.test.mjs, |
| 74 | + .\tools\*.test.mjs |
| 75 | +) | ForEach-Object FullName |
| 76 | +node --test @portableTests |
| 77 | +
|
| 78 | +powershell.exe -NoLogo -NoProfile -ExecutionPolicy RemoteSigned ` |
| 79 | + -File .\windows\tests\run-tests.ps1 |
| 80 | +powershell.exe -NoLogo -NoProfile -ExecutionPolicy RemoteSigned ` |
| 81 | + -File .\windows\tests\installer-static.tests.ps1 |
| 82 | +``` |
| 83 | + |
| 84 | +When `pwsh.exe` is installed, rerun both PowerShell test entry points with it: |
| 85 | + |
| 86 | +```powershell |
| 87 | +pwsh.exe -NoLogo -NoProfile -ExecutionPolicy RemoteSigned ` |
| 88 | + -File .\windows\tests\run-tests.ps1 |
| 89 | +pwsh.exe -NoLogo -NoProfile -ExecutionPolicy RemoteSigned ` |
| 90 | + -File .\windows\tests\installer-static.tests.ps1 |
| 91 | +``` |
| 92 | + |
| 93 | +Preserve full output for any failure. Do not weaken or skip a failing test. |
| 94 | + |
| 95 | +## 4. Build The Candidate Setup |
| 96 | + |
| 97 | +Use Inno Setup 6.7.1. Confirm `ISCC.exe` exists, then build from this exact |
| 98 | +detached PR tree: |
| 99 | + |
| 100 | +```powershell |
| 101 | +$iscc = Join-Path ${env:ProgramFiles(x86)} 'Inno Setup 6\ISCC.exe' |
| 102 | +if (-not (Test-Path -LiteralPath $iscc -PathType Leaf)) { |
| 103 | + $iscc = Join-Path $env:ProgramFiles 'Inno Setup 6\ISCC.exe' |
| 104 | +} |
| 105 | +if (-not (Test-Path -LiteralPath $iscc -PathType Leaf)) { |
| 106 | + throw 'Install official Inno Setup 6.7.1 before continuing.' |
| 107 | +} |
| 108 | +
|
| 109 | +$candidateOutput = Join-Path $env:LOCALAPPDATA 'DreamSkin-PR351-Build' |
| 110 | +powershell.exe -NoLogo -NoProfile -ExecutionPolicy RemoteSigned ` |
| 111 | + -File .\windows\installer\build-release.ps1 ` |
| 112 | + -OutputDirectory $candidateOutput -IsccPath $iscc |
| 113 | +
|
| 114 | +$candidateSetup = Join-Path $candidateOutput 'CodexDreamSkin-Setup-v1.5.12.exe' |
| 115 | +if (-not (Test-Path -LiteralPath $candidateSetup -PathType Leaf) -or |
| 116 | + (Get-Item -LiteralPath $candidateSetup).Length -le 0) { |
| 117 | + throw 'The candidate Setup.exe was not built.' |
| 118 | +} |
| 119 | +Get-FileHash -LiteralPath $candidateSetup -Algorithm SHA256 |
| 120 | +``` |
| 121 | + |
| 122 | +The `v1.5.12` filename identifies the unchanged candidate base only. This |
| 123 | +local file is not the public v1.5.12 asset and must not be distributed. |
| 124 | + |
| 125 | +## 5. Exercise Issue #337 With A Real CJK Temp Path |
| 126 | + |
| 127 | +Exit Codex and the Dream Skin tray. Start the candidate Setup from a process |
| 128 | +whose real `TEMP` and `TMP` point to a CJK path. Keep the Inno log for evidence: |
| 129 | + |
| 130 | +```powershell |
| 131 | +$cjkTemp = Join-Path $env:LOCALAPPDATA 'DreamSkin-验证-临时目录' |
| 132 | +$setupLog = Join-Path $env:LOCALAPPDATA 'DreamSkin-PR351-Setup.log' |
| 133 | +New-Item -ItemType Directory -Path $cjkTemp -Force | Out-Null |
| 134 | +$oldTemp = $env:TEMP |
| 135 | +$oldTmp = $env:TMP |
| 136 | +try { |
| 137 | + $env:TEMP = $cjkTemp |
| 138 | + $env:TMP = $cjkTemp |
| 139 | + $process = Start-Process -FilePath $candidateSetup ` |
| 140 | + -ArgumentList "/LOG=$setupLog" -Wait -PassThru |
| 141 | + if ($process.ExitCode -ne 0) { |
| 142 | + throw "Candidate Setup exited with $($process.ExitCode)." |
| 143 | + } |
| 144 | +} finally { |
| 145 | + $env:TEMP = $oldTemp |
| 146 | + $env:TMP = $oldTmp |
| 147 | +} |
| 148 | +``` |
| 149 | + |
| 150 | +In the GUI, complete a normal current-user install. Pass only when all of these |
| 151 | +are true: |
| 152 | + |
| 153 | +- Setup reaches completion without |
| 154 | + `Node.js executable path could not be validated`. |
| 155 | +- The sanitized Inno log proves its temporary extraction path was below the |
| 156 | + exact CJK directory above. If it used another directory, this test did not |
| 157 | + exercise #337 and must be repeated. |
| 158 | +- No administrator elevation, ACL change, security bypass, or untrusted Node |
| 159 | + fallback was needed. |
| 160 | +- The installed engine contains the exact PR sources below: |
| 161 | + |
| 162 | +```powershell |
| 163 | +$sourceRoot = (Resolve-Path .\windows).Path |
| 164 | +$engineRoot = Join-Path $env:LOCALAPPDATA 'CodexDreamSkin\engine' |
| 165 | +foreach ($relative in @( |
| 166 | + 'VERSION', |
| 167 | + 'assets\renderer-inject.js', |
| 168 | + 'scripts\common-windows.ps1', |
| 169 | + 'scripts\localization-windows.ps1', |
| 170 | + 'scripts\injector.mjs', |
| 171 | + 'scripts\theme-windows.ps1', |
| 172 | + 'scripts\tray-dream-skin.ps1' |
| 173 | +)) { |
| 174 | + $source = Join-Path $sourceRoot $relative |
| 175 | + $installed = Join-Path $engineRoot $relative |
| 176 | + if (-not (Test-Path -LiteralPath $installed -PathType Leaf)) { |
| 177 | + throw "Installed engine is missing: $relative" |
| 178 | + } |
| 179 | + if ((Get-FileHash -LiteralPath $source -Algorithm SHA256).Hash -cne |
| 180 | + (Get-FileHash -LiteralPath $installed -Algorithm SHA256).Hash) { |
| 181 | + throw "Installed engine differs from PR head: $relative" |
| 182 | + } |
| 183 | +} |
| 184 | +'ENGINE_HASH_BINDING=PASS' |
| 185 | +``` |
| 186 | + |
| 187 | +## 6. Validate The Windows Language Workflows |
| 188 | + |
| 189 | +Use the tray UI for the primary test. Do not substitute an environment |
| 190 | +variable for the menu selection. |
| 191 | + |
| 192 | +1. Select `Language / 语言` -> `English`. Close and reopen the tray. Confirm the |
| 193 | + checked selection persists and the status, apply/reapply, pause/resume, |
| 194 | + background, import, saved themes, links, update, restore, and exit labels |
| 195 | + are English. |
| 196 | +2. Select `Language / 语言` -> `中文`. Reopen the tray and repeat the same check |
| 197 | + in Chinese. |
| 198 | +3. Select `System / 系统`. Reopen the tray and confirm it follows Windows UI |
| 199 | + culture. The preference override must be removed when System is selected. |
| 200 | +4. In both English and Chinese, exercise apply/reapply, pause/resume, background |
| 201 | + picker cancellation, invalid-image failure, theme ZIP import, duplicate or |
| 202 | + update result, save/switch theme, update check, and restore cancellation. |
| 203 | +5. Confirm cancellation is never reported as success and a failure does not |
| 204 | + leave a false active state. Machine-readable status/state JSON must remain |
| 205 | + unchanged; only human-facing copy is localized. |
| 206 | +6. Apply one test theme with an explicit white accent and one with an explicit |
| 207 | + black accent. On both light and dark Codex appearances, accent-filled |
| 208 | + controls must remain readable: black text on white and white text on black. |
| 209 | + Reapply an adaptive/default accent afterward and confirm no stale explicit |
| 210 | + foreground remains. |
| 211 | + |
| 212 | +Also test one normal apply and one restore against the real Store Codex app. |
| 213 | +Run the installed verifier on Home and one normal task route: |
| 214 | + |
| 215 | +```powershell |
| 216 | +powershell.exe -NoLogo -NoProfile -ExecutionPolicy RemoteSigned ` |
| 217 | + -File "$engineRoot\scripts\verify-dream-skin.ps1" ` |
| 218 | + -ScreenshotPath "$env:TEMP\dreamskin-pr351-real-codex.png" |
| 219 | +``` |
| 220 | + |
| 221 | +Pass only with `scope.level=L1`, an empty `missingL1`, an interactive real |
| 222 | +`app://` Codex renderer, no horizontal overflow, and no console/runtime error |
| 223 | +introduced by this PR. The screenshot must be from the real installed Codex |
| 224 | +app, not a fixture. |
| 225 | + |
| 226 | +## 7. Return One Result |
| 227 | + |
| 228 | +Return exactly one consolidated report to the PR owner: |
| 229 | + |
| 230 | +```text |
| 231 | +PR: #351 |
| 232 | +CANDIDATE_SHA: <40-character SHA> |
| 233 | +HOST: <Windows edition/build; x64> |
| 234 | +CODEX: <Store package version> |
| 235 | +POWERSHELL_5_1: PASS|FAIL <version> |
| 236 | +POWERSHELL_7: PASS|FAIL|NOT_INSTALLED <version> |
| 237 | +PORTABLE_TESTS: PASS|FAIL <count> |
| 238 | +INSTALLER_STATIC: PASS|FAIL |
| 239 | +SETUP_BUILD: PASS|FAIL <SHA-256> |
| 240 | +CJK_TEMP_SETUP_337: PASS|FAIL |
| 241 | +ENGINE_HASH_BINDING: PASS|FAIL |
| 242 | +LANGUAGE_SYSTEM: PASS|FAIL |
| 243 | +LANGUAGE_ENGLISH: PASS|FAIL |
| 244 | +LANGUAGE_CHINESE: PASS|FAIL |
| 245 | +REAL_CODEX_L1: PASS|FAIL <scope.level; missingL1> |
| 246 | +SCREENSHOT: <local path> |
| 247 | +SANITIZED_FAILURES: <none or exact excerpts without private paths/tokens> |
| 248 | +FINAL: PASS|FAIL |
| 249 | +``` |
| 250 | + |
| 251 | +Any mandatory failure makes `FINAL: FAIL`. Include sanitized excerpts from the |
| 252 | +Setup log and `%LOCALAPPDATA%\CodexDreamSkin\logs` for failures, but redact user |
| 253 | +names and private paths. Do not post secrets or upload the locally built Setup. |
0 commit comments