返回 DeepSeek-Reasonix
test-scoop-desktop-launch.ps1
根目录 / scripts / test-scoop-desktop-launch.ps1
1 #Requires -Version 5.1
2
3 [CmdletBinding()]
4 param()
5
6 Set-StrictMode -Version Latest
7 $ErrorActionPreference = "Stop"
8
9 $scoopVersion = "v0.5.3"
10 $appName = "reasonix-desktop"
11 $appVersion = "0.0.0-ci"
12 $activeVersion = "v$appVersion"
13 $shortcutName = "Reasonix Desktop"
14 $repoRoot = Split-Path -Parent $PSScriptRoot
15 $workRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("reasonix-scoop-launch-" + [guid]::NewGuid().ToString("N"))
16 $fixtureRoot = Join-Path $workRoot "fixture"
17 $packageRoot = Join-Path $fixtureRoot "package"
18 $scoopRoot = Join-Path $workRoot "scoop-root"
19 $scoopHome = Join-Path $scoopRoot "apps\scoop\current"
20 $manifestPath = Join-Path $fixtureRoot "$appName.json"
21 $archivePath = Join-Path $fixtureRoot "reasonix-windows-portable.zip"
22 $markerPath = Join-Path $workRoot "desktop-launched.json"
23 $shortcutPath = Join-Path ([Environment]::GetFolderPath("StartMenu")) "Programs\Scoop Apps\$shortcutName.lnk"
24 $server = $null
25
26 function Invoke-Native {
27 param(
28 [Parameter(Mandatory = $true)]
29 [string]$FilePath,
30
31 [Parameter(ValueFromRemainingArguments = $true)]
32 [string[]]$ArgumentList
33 )
34
35 & $FilePath @ArgumentList
36 if ($LASTEXITCODE -ne 0) {
37 throw "Command failed with exit code $LASTEXITCODE`: $FilePath $($ArgumentList -join ' ')"
38 }
39 }
40
41 function Write-Utf8NoBom {
42 param(
43 [Parameter(Mandatory = $true)]
44 [string]$Path,
45
46 [Parameter(Mandatory = $true)]
47 [string]$Content
48 )
49
50 $encoding = New-Object System.Text.UTF8Encoding($false)
51 [System.IO.File]::WriteAllText($Path, $Content, $encoding)
52 }
53
54 try {
55 if ($env:OS -ne "Windows_NT") {
56 throw "This integration test must run on Windows."
57 }
58 if (Test-Path -LiteralPath $shortcutPath) {
59 throw "Refusing to overwrite an existing shortcut: $shortcutPath"
60 }
61
62 New-Item -ItemType Directory -Force -Path (Join-Path $packageRoot "versions\$activeVersion") | Out-Null
63 New-Item -ItemType Directory -Force -Path (Split-Path -Parent $scoopHome) | Out-Null
64
65 $launcherPath = Join-Path $packageRoot "reasonix-launcher.exe"
66 Push-Location $repoRoot
67 try {
68 Invoke-Native -FilePath "go" -ArgumentList @(
69 "build", "-trimpath", "-ldflags", "-s -w -H windowsgui -X main.version=$appVersion",
70 "-o", $launcherPath, "./cmd/reasonix-launcher"
71 )
72 } finally {
73 Pop-Location
74 }
75
76 $fixtureSource = Join-Path $fixtureRoot "desktop-fixture.go"
77 $desktopPath = Join-Path $packageRoot "versions\$activeVersion\reasonix-desktop.exe"
78 Write-Utf8NoBom -Path $fixtureSource -Content @'
79 package main
80
81 import (
82 "encoding/json"
83 "os"
84 )
85
86 func main() {
87 marker := os.Getenv("REASONIX_SCOOP_MARKER")
88 executable, _ := os.Executable()
89 workingDirectory, _ := os.Getwd()
90 payload, _ := json.Marshal(map[string]any{
91 "args": os.Args[1:],
92 "executable": executable,
93 "workingDir": workingDirectory,
94 })
95 if marker == "" || os.WriteFile(marker, payload, 0o600) != nil {
96 os.Exit(1)
97 }
98 }
99 '@
100 Invoke-Native -FilePath "go" -ArgumentList @(
101 "build", "-trimpath", "-ldflags", "-s -w -H windowsgui", "-o", $desktopPath, $fixtureSource
102 )
103
104 Write-Utf8NoBom -Path (Join-Path $packageRoot "current.json") -Content @"
105 {
106 "schemaVersion": 1,
107 "activeVersion": "$activeVersion",
108 "activeDir": "versions/$activeVersion"
109 }
110 "@
111
112 Compress-Archive -Path (Join-Path $packageRoot "*") -DestinationPath $archivePath -CompressionLevel Optimal
113 $archiveHash = (Get-FileHash -Algorithm SHA256 -LiteralPath $archivePath).Hash.ToLowerInvariant()
114
115 $listener = New-Object System.Net.Sockets.TcpListener([System.Net.IPAddress]::Loopback, 0)
116 $listener.Start()
117 $port = ([System.Net.IPEndPoint]$listener.LocalEndpoint).Port
118 $listener.Stop()
119
120 $python = Get-Command python -ErrorAction Stop
121 $serverOut = Join-Path $workRoot "http-server.stdout.log"
122 $serverErr = Join-Path $workRoot "http-server.stderr.log"
123 $server = Start-Process -FilePath $python.Source -ArgumentList @(
124 "-m", "http.server", $port, "--bind", "127.0.0.1", "--directory", $fixtureRoot
125 ) -RedirectStandardOutput $serverOut -RedirectStandardError $serverErr -PassThru
126
127 $archiveUrl = "http://127.0.0.1:$port/$(Split-Path -Leaf $archivePath)"
128 $ready = $false
129 for ($attempt = 0; $attempt -lt 40; $attempt++) {
130 try {
131 $response = Invoke-WebRequest -UseBasicParsing -Uri $archiveUrl -Method Head -TimeoutSec 2
132 if ($response.StatusCode -eq 200) {
133 $ready = $true
134 break
135 }
136 } catch {
137 Start-Sleep -Milliseconds 250
138 }
139 }
140 if (!$ready) {
141 throw "Fixture HTTP server did not become ready."
142 }
143
144 Write-Utf8NoBom -Path $manifestPath -Content @"
145 {
146 "version": "$appVersion",
147 "description": "Reasonix Scoop integration fixture",
148 "homepage": "https://github.com/esengine/DeepSeek-Reasonix",
149 "license": "Apache-2.0",
150 "url": "$archiveUrl",
151 "hash": "$archiveHash",
152 "shortcuts": [
153 ["reasonix-launcher.exe", "$shortcutName", "launch --detach"]
154 ]
155 }
156 "@
157
158 Invoke-Native -FilePath "git" -ArgumentList @(
159 "clone", "--branch", $scoopVersion, "--depth", "1", "https://github.com/ScoopInstaller/Scoop.git", $scoopHome
160 )
161 $env:SCOOP = $scoopRoot
162 $env:SCOOP_CACHE = Join-Path $workRoot "scoop-cache"
163 $env:REASONIX_SCOOP_MARKER = $markerPath
164 $scoopCommand = Join-Path $scoopHome "bin\scoop.ps1"
165 Invoke-Native -FilePath "powershell.exe" -ArgumentList @(
166 "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", $scoopCommand,
167 "install", "--no-update-scoop", $manifestPath
168 )
169
170 $currentPath = Join-Path $scoopRoot "apps\$appName\current"
171 $currentItem = Get-Item -LiteralPath $currentPath -Force
172 if (($currentItem.Attributes -band [System.IO.FileAttributes]::ReparsePoint) -eq 0) {
173 throw "Scoop current path is not a directory junction: $currentPath"
174 }
175
176 if (!(Test-Path -LiteralPath $shortcutPath -PathType Leaf)) {
177 throw "Scoop did not create the expected shortcut: $shortcutPath"
178 }
179 $shell = New-Object -ComObject WScript.Shell
180 $shortcut = $shell.CreateShortcut($shortcutPath)
181 $expectedTarget = Join-Path $currentPath "reasonix-launcher.exe"
182 if (![string]::Equals($shortcut.TargetPath, $expectedTarget, [System.StringComparison]::OrdinalIgnoreCase)) {
183 throw "Unexpected shortcut target. Expected '$expectedTarget', got '$($shortcut.TargetPath)'."
184 }
185 if ($shortcut.Arguments -ne "launch --detach") {
186 throw "Unexpected shortcut arguments: '$($shortcut.Arguments)'."
187 }
188
189 Start-Process -FilePath $shortcutPath
190 $deadline = [DateTime]::UtcNow.AddSeconds(30)
191 while (!(Test-Path -LiteralPath $markerPath) -and [DateTime]::UtcNow -lt $deadline) {
192 Start-Sleep -Milliseconds 250
193 }
194 if (!(Test-Path -LiteralPath $markerPath)) {
195 throw "The desktop fixture was not launched through the Scoop shortcut."
196 }
197
198 $launch = Get-Content -LiteralPath $markerPath -Raw | ConvertFrom-Json
199 $versionPath = Join-Path $scoopRoot "apps\$appName\$appVersion"
200 $expectedDesktop = Join-Path $versionPath "versions\$activeVersion\reasonix-desktop.exe"
201 if (![string]::Equals($launch.executable, $expectedDesktop, [System.StringComparison]::OrdinalIgnoreCase)) {
202 throw "Unexpected desktop executable. Expected '$expectedDesktop', got '$($launch.executable)'."
203 }
204 if (![string]::Equals($launch.workingDir, $versionPath, [System.StringComparison]::OrdinalIgnoreCase)) {
205 throw "Unexpected launcher working directory. Expected '$versionPath', got '$($launch.workingDir)'."
206 }
207 if (@($launch.args).Count -ne 0) {
208 throw "Legacy shortcut arguments reached the desktop fixture: $($launch.args -join ' ')"
209 }
210
211 Write-Host "Scoop install, current junction, shortcut, and desktop launch verified."
212 } finally {
213 if ($server -and !$server.HasExited) {
214 Stop-Process -Id $server.Id -Force -ErrorAction SilentlyContinue
215 }
216 if (Test-Path -LiteralPath $shortcutPath) {
217 Remove-Item -LiteralPath $shortcutPath -Force -ErrorAction SilentlyContinue
218 }
219 if (Test-Path -LiteralPath $workRoot) {
220 Remove-Item -LiteralPath $workRoot -Recurse -Force -ErrorAction SilentlyContinue
221 }
222 }
223
223 lines Plain Text