mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-15 12:53:31 +08:00
130 lines
4.9 KiB
PowerShell
130 lines
4.9 KiB
PowerShell
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory = $true)][string]$ArtifactsDir,
|
|
[ValidateSet('x64')][string]$Arch = 'x64'
|
|
)
|
|
|
|
Set-StrictMode -Version Latest
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
if ($env:CI -ne 'true') {
|
|
throw 'This installer smoke mutates Windows installer registry state and may run only on an ephemeral CI runner.'
|
|
}
|
|
|
|
$resolvedArtifactsDir = (Resolve-Path -LiteralPath $ArtifactsDir).Path
|
|
$installers = @(Get-ChildItem -LiteralPath $resolvedArtifactsDir -File |
|
|
Where-Object { $_.Name -like "Claude-Code-Haha-*-win-$Arch.exe" })
|
|
if ($installers.Count -ne 1) {
|
|
throw "Expected exactly one Windows $Arch installer in $resolvedArtifactsDir, found $($installers.Count)."
|
|
}
|
|
$installer = $installers[0].FullName
|
|
|
|
$testRoot = Join-Path ([IO.Path]::GetTempPath()) "cc-haha-installer-smoke-$([Guid]::NewGuid().ToString('N'))"
|
|
$installDir = Join-Path $testRoot 'Claude Code Haha'
|
|
$appData = Join-Path $testRoot 'AppData\Roaming'
|
|
$localAppData = Join-Path $testRoot 'AppData\Local'
|
|
$userProfile = Join-Path $testRoot 'UserProfile'
|
|
$appExe = Join-Path $installDir 'Claude Code Haha.exe'
|
|
$uninstaller = Join-Path $installDir 'Uninstall Claude Code Haha.exe'
|
|
$recoveryHelper = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot '..\build\recover-legacy-install-data.ps1')).Path
|
|
|
|
$savedEnvironment = @{}
|
|
foreach ($name in @('APPDATA', 'LOCALAPPDATA', 'USERPROFILE', 'CLAUDE_CONFIG_DIR', 'CC_HAHA_APP_PORTABLE_DIR')) {
|
|
$savedEnvironment[$name] = [Environment]::GetEnvironmentVariable($name, 'Process')
|
|
}
|
|
|
|
function Invoke-CheckedProcess {
|
|
param(
|
|
[Parameter(Mandatory = $true)][string]$FilePath,
|
|
[Parameter(Mandatory = $true)][string]$Stage,
|
|
[Parameter(Mandatory = $true)][string[]]$Arguments,
|
|
[int]$TimeoutSeconds = 180
|
|
)
|
|
|
|
[Console]::Out.WriteLine("$Stage starting...")
|
|
$process = Start-Process -FilePath $FilePath -ArgumentList $Arguments -PassThru
|
|
try {
|
|
if (-not $process.WaitForExit($TimeoutSeconds * 1000)) {
|
|
Stop-Process -Id $process.Id -Force -ErrorAction SilentlyContinue
|
|
throw "$Stage timed out after $TimeoutSeconds seconds."
|
|
}
|
|
if ($process.ExitCode -ne 0) {
|
|
throw "$Stage failed with process exit code $($process.ExitCode)."
|
|
}
|
|
[Console]::Out.WriteLine("$Stage completed successfully.")
|
|
} finally {
|
|
$process.Dispose()
|
|
}
|
|
}
|
|
|
|
function Invoke-LegacyRecoveryDiagnostic {
|
|
$windowsPowerShell = Join-Path $env:SystemRoot 'System32\WindowsPowerShell\v1.0\powershell.exe'
|
|
$arguments = @(
|
|
'-NoLogo',
|
|
'-NoProfile',
|
|
'-NonInteractive',
|
|
'-ExecutionPolicy',
|
|
'Bypass',
|
|
'-File',
|
|
$recoveryHelper,
|
|
'-PerUserInstallDir',
|
|
$installDir,
|
|
'-CandidateInstallDir',
|
|
$installDir,
|
|
'-UserDataDir',
|
|
(Join-Path $appData 'Claude Code Haha'),
|
|
'-RecoveryRoot',
|
|
(Join-Path $userProfile 'Claude Code Haha Data\Recovered'),
|
|
'-ProcessName',
|
|
'Claude Code Haha.exe',
|
|
'-InstallerIdentitySafety',
|
|
'trusted-user'
|
|
)
|
|
|
|
[Console]::Out.WriteLine('Direct legacy recovery diagnostic starting...')
|
|
$output = @(& $windowsPowerShell @arguments 2>&1)
|
|
$exitCode = $LASTEXITCODE
|
|
foreach ($line in $output) {
|
|
[Console]::Out.WriteLine([string]$line)
|
|
}
|
|
if ($exitCode -ne 0) {
|
|
throw "Direct legacy recovery diagnostic failed with exit code $exitCode."
|
|
}
|
|
[Console]::Out.WriteLine('Direct legacy recovery diagnostic completed successfully.')
|
|
}
|
|
|
|
try {
|
|
New-Item -ItemType Directory -Path $appData, $localAppData, $userProfile -Force | Out-Null
|
|
$env:APPDATA = $appData
|
|
$env:LOCALAPPDATA = $localAppData
|
|
$env:USERPROFILE = $userProfile
|
|
Remove-Item Env:CLAUDE_CONFIG_DIR -ErrorAction SilentlyContinue
|
|
Remove-Item Env:CC_HAHA_APP_PORTABLE_DIR -ErrorAction SilentlyContinue
|
|
|
|
Invoke-CheckedProcess -FilePath $installer -Stage 'Fresh install' -Arguments @('/S', '/currentuser', "/D=$installDir")
|
|
if (-not (Test-Path -LiteralPath $appExe -PathType Leaf)) {
|
|
throw "Fresh install did not create the application executable: $appExe"
|
|
}
|
|
|
|
Invoke-LegacyRecoveryDiagnostic
|
|
Invoke-CheckedProcess -FilePath $installer -Stage 'Default-mode reinstall' -Arguments @('--updated', '/S', '/currentuser', "/D=$installDir")
|
|
if (-not (Test-Path -LiteralPath $appExe -PathType Leaf)) {
|
|
throw "Reinstall removed the application executable: $appExe"
|
|
}
|
|
|
|
[Console]::Out.WriteLine('Windows installer fresh-install and default-mode reinstall smoke passed.')
|
|
} finally {
|
|
if (Test-Path -LiteralPath $uninstaller -PathType Leaf) {
|
|
Invoke-CheckedProcess -FilePath $uninstaller -Stage 'Cleanup uninstall' -Arguments @('/S', '/KEEP_APP_DATA', '/currentuser') -TimeoutSeconds 120
|
|
}
|
|
foreach ($name in $savedEnvironment.Keys) {
|
|
$value = $savedEnvironment[$name]
|
|
if ($null -eq $value) {
|
|
[Environment]::SetEnvironmentVariable($name, $null, 'Process')
|
|
} else {
|
|
[Environment]::SetEnvironmentVariable($name, [string]$value, 'Process')
|
|
}
|
|
}
|
|
Remove-Item -LiteralPath $testRoot -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|