mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-05 01:23:24 -08:00
COCKATRICE_WIN_INSTALLER now defaults to InnoSetup. NSIS stays selectable for one release as the legacy fallback, and the two can still be built from one tree and compared on a real upgrade. The switches question from the previous commit is settled from Inno Setup's source: the setup command line is an if/else-if chain over the switches it knows, with no fallback that rejects the rest, so /R, /PID= and /D= are ignored rather than refused. The client therefore sends one command line that drives either installer: - /R and /PID= for NSIS, which also needs /D= last and unquoted; - /VERYSILENT and the CloseApplications set for Setup, which releases the mapped runtime DLLs through Restart Manager instead of waiting for a pid; - /UPDATER as the client's own mark, read only by the Inno Setup script. An older client that passes only the NSIS switches still installs; it walks the wizard once instead of running silently. The Sentry offer that AbortInstall carried is ported: a silent run the updater launched, which began replacing files and never reached the end, offers to send its log through the same helper. The helper is embedded with a [Files] dontcopy entry and extracted at send time; a repeated [Files] section merges with the generated one instead of being a compile error. Setup prefixes every log line with a timestamp, so the helper's ABORT scan now allows that prefix. CI installs Inno Setup next to NSIS, dumps ISCCOutput.log and the generated script when the installer fails to compile, and --package NSIS|InnoSetup selects the installer through the CMake option instead of adding a second CPack generator to the run.
124 lines
5.9 KiB
PowerShell
124 lines
5.9 KiB
PowerShell
# Sends the installer log, from either installer, to Sentry as an envelope carrying one
|
|
# error event plus the log as an attachment.
|
|
#
|
|
# Generated at configure time from this template by CMake configure_file: the
|
|
# CPACK_SENTRY_* placeholders are baked in then, so the real values only ever
|
|
# exist in the build directory and the packaged installer, never in the repo.
|
|
# Runs on stock Windows PowerShell 5.1 with no extra modules.
|
|
param(
|
|
[Parameter(Mandatory = $true)][string]$LogPath
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
$EnvelopeUrl = '@CPACK_SENTRY_ENVELOPE_URL@'
|
|
$SentryKey = '@CPACK_SENTRY_KEY@'
|
|
$Release = '@CPACK_SENTRY_RELEASE@'
|
|
$Dsn = '@CPACK_SENTRY_DSN_FULL@'
|
|
|
|
try {
|
|
# The envelope endpoint is TLS-only; stock powershell.exe on older systems
|
|
# still negotiates TLS 1.0 otherwise, so opt into 1.2 explicitly.
|
|
try {
|
|
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
|
|
} catch {
|
|
}
|
|
|
|
# --- Log payload, tail-capped at 128 KiB ---------------------------------
|
|
$MaxLogBytes = 128 * 1024
|
|
$TruncNotice = '[log truncated to last 128 KB]'
|
|
if (Test-Path -LiteralPath $LogPath) {
|
|
$raw = [System.IO.File]::ReadAllBytes($LogPath)
|
|
} else {
|
|
$raw = [System.Text.Encoding]::UTF8.GetBytes('(installer log not found: ' + $LogPath + ')')
|
|
}
|
|
if ($raw.Length -gt $MaxLogBytes) {
|
|
$tail = New-Object byte[] $MaxLogBytes
|
|
[System.Buffer]::BlockCopy($raw, $raw.Length - $MaxLogBytes, $tail, 0, $MaxLogBytes)
|
|
$prefix = [System.Text.Encoding]::UTF8.GetBytes($TruncNotice + "`r`n")
|
|
$logBytes = New-Object byte[] ($prefix.Length + $tail.Length)
|
|
[System.Buffer]::BlockCopy($prefix, 0, $logBytes, 0, $prefix.Length)
|
|
[System.Buffer]::BlockCopy($tail, 0, $logBytes, $prefix.Length, $tail.Length)
|
|
} else {
|
|
$logBytes = $raw
|
|
}
|
|
|
|
# --- Abort reason, read from the log itself --------------------------------
|
|
# Each installer records the reason before offering to send, so it travels inside the
|
|
# file and nothing reason-shaped crosses the command line (no quoting hazards).
|
|
# NSIS writes the line as "ABORT: ..." through CockLog. Inno Setup prefixes every line
|
|
# of its log with a timestamp and three spaces, so that prefix is optional here and
|
|
# removed together with the marker. Select-String yields the matching line alone, so
|
|
# the reason is already one physical line for the message; anything else stays in the
|
|
# attached log. Non-ASCII paths may garble here; the attachment is byte-exact
|
|
# regardless.
|
|
$stamp = '\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}\s+'
|
|
$Reason = 'unknown (no ABORT line in log)'
|
|
if (Test-Path -LiteralPath $LogPath) {
|
|
$abortLine = Select-String -LiteralPath $LogPath -Pattern ('^(' + $stamp + ')?ABORT: ') | Select-Object -Last 1
|
|
if ($abortLine) {
|
|
$Reason = ($abortLine.Line -replace ('^(' + $stamp + ')?ABORT:\s*'), '')
|
|
}
|
|
}
|
|
|
|
# --- Event ---------------------------------------------------------------
|
|
$eventId = [guid]::NewGuid().ToString('N')
|
|
$now = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')
|
|
# $Event is an automatic variable (Microsoft.PowerShell.Utility\Register-ObjectEvent),
|
|
# so the local is named $sentryEvent to avoid shadowing it.
|
|
$sentryEvent = [ordered]@{
|
|
event_id = $eventId
|
|
timestamp = $now
|
|
platform = 'other'
|
|
level = 'error'
|
|
logger = 'cockatrice-installer'
|
|
release = $Release
|
|
environment = 'installer'
|
|
sdk = @{ name = 'cockatrice.installer'; version = $Release }
|
|
message = @{ formatted = 'Installer aborted: ' + $Reason }
|
|
extra = @{
|
|
reason = $Reason
|
|
os = [System.Environment]::OSVersion.VersionString
|
|
}
|
|
}
|
|
$eventJson = $sentryEvent | ConvertTo-Json -Compress -Depth 6
|
|
$eventBytes = [System.Text.Encoding]::UTF8.GetBytes($eventJson)
|
|
|
|
# --- Envelope, assembled as bytes (the log is opaque binary) -------------
|
|
$sentAt = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')
|
|
$headerLine = '{"event_id":"' + $eventId + '","sent_at":"' + $sentAt + '","dsn":"' + $Dsn + '"}'
|
|
$eventHeader = '{"type":"event","length":' + $eventBytes.Length + '}'
|
|
$attachHeader = '{"type":"attachment","filename":"Cockatrice-installer.log","content_type":"text/plain","length":' + $logBytes.Length + '}'
|
|
$enc = [System.Text.Encoding]::UTF8
|
|
$lf = [byte[]]@(10)
|
|
$body = New-Object System.IO.MemoryStream
|
|
foreach ($chunk in @($enc.GetBytes($headerLine), $lf, $enc.GetBytes($eventHeader), $lf, $eventBytes, $lf, $enc.GetBytes($attachHeader), $lf, $logBytes)) {
|
|
$body.Write($chunk, 0, $chunk.Length)
|
|
}
|
|
$bodyBytes = $body.ToArray()
|
|
$body.Close()
|
|
|
|
# --- Send ----------------------------------------------------------------
|
|
# The key travels in the auth header only; stdout below never contains it.
|
|
$auth = 'Sentry sentry_version=7, sentry_client=cockatrice.installer/' + $Release + ', sentry_key=' + $SentryKey
|
|
Invoke-RestMethod -Uri $EnvelopeUrl -Method Post -TimeoutSec 45 -ContentType 'application/x-sentry-envelope' -Headers @{ 'X-Sentry-Auth' = $auth } -Body $bodyBytes | Out-Null
|
|
Write-Output ('SENT ' + $eventId)
|
|
exit 0
|
|
} catch {
|
|
# Scrub the key/DSN out of the failure text in case a transport error echoes them.
|
|
$msg = $_.Exception.Message
|
|
if ($msg -and $SentryKey) {
|
|
$msg = $msg.Replace($SentryKey, '<redacted>')
|
|
}
|
|
if ($msg -and $Dsn) {
|
|
$msg = $msg.Replace($Dsn, '<redacted>')
|
|
}
|
|
# Flatten to one line: this text is echoed into the installer log, which is
|
|
# itself uploaded as an attachment, so a multi-line transport error must not
|
|
# be able to forge extra lines in the artifact we send.
|
|
if ($msg) {
|
|
$msg = ($msg -replace '\s+', ' ').Trim()
|
|
}
|
|
Write-Output ('FAILED: ' + $msg)
|
|
exit 1
|
|
}
|