4b6817381b
Benchmark image — build + push to ECR (any adapter) / build + push (push) Waiting to run
CI / quality (ubuntu-latest) (push) Waiting to run
CI / test (tools-runtime) (push) Waiting to run
CI / test (e2e-general) (push) Waiting to run
CI / test (cli-runtime) (push) Waiting to run
CI / test (e2e-provider-and-openclaw) (push) Waiting to run
CI / test (integrations-and-misc) (push) Waiting to run
CI / coverage-report (push) Blocked by required conditions
CI / test-kubernetes (push) Waiting to run
CI / should-run-thorough (push) Waiting to run
CI / test-thorough (cloudwatch-demo) (push) Blocked by required conditions
CI / test-thorough (flink-ecs) (push) Blocked by required conditions
CI / test-thorough (upstream-lambda) (push) Blocked by required conditions
CI / test-thorough (prefect-ecs-fargate) (push) Blocked by required conditions
CodeQL / Analyze (python) (push) Waiting to run
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Blocked by required conditions
Release / publish-release (push) Blocked by required conditions
Release / publish-main-release (push) Blocked by required conditions
Release / prepare (push) Waiting to run
Release / verify (push) Blocked by required conditions
Release / build-python-dist (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Blocked by required conditions
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Waiting to run
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Waiting to run
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Waiting to run
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
1159 lines
34 KiB
PowerShell
1159 lines
34 KiB
PowerShell
param(
|
|
[ValidateSet("release", "main")]
|
|
[string]$Channel = $(if ($env:OPENSRE_INSTALL_CHANNEL) { $env:OPENSRE_INSTALL_CHANNEL } else { "main" }),
|
|
[switch]$SkipMain
|
|
)
|
|
|
|
Set-StrictMode -Version Latest
|
|
$ErrorActionPreference = "Stop"
|
|
$script:OpenSreProgressStep = 0
|
|
$script:OpenSreChannelExplicit = $PSBoundParameters.ContainsKey("Channel") -or [bool]$env:OPENSRE_INSTALL_CHANNEL
|
|
|
|
function Test-OpenSreVerboseInstall {
|
|
$value = [string]$env:OPENSRE_INSTALL_VERBOSE
|
|
return ($value -eq "1" -or $value -eq "true" -or $value -eq "TRUE" -or $value -eq "yes" -or $value -eq "YES")
|
|
}
|
|
|
|
function Test-OpenSreInteractiveHost {
|
|
try {
|
|
if ([System.Console]::IsOutputRedirected) {
|
|
return $false
|
|
}
|
|
}
|
|
catch {
|
|
if ($null -eq $Host -or $null -eq $Host.UI) {
|
|
return $false
|
|
}
|
|
}
|
|
|
|
try {
|
|
if ($null -eq $Host -or $null -eq $Host.UI -or $null -eq $Host.UI.RawUI) {
|
|
return $false
|
|
}
|
|
|
|
$null = $Host.UI.RawUI.WindowSize
|
|
}
|
|
catch {
|
|
return $false
|
|
}
|
|
|
|
return $true
|
|
}
|
|
|
|
function Get-OpenSreConsoleWidth {
|
|
[int]$width = 0
|
|
|
|
try {
|
|
if ($null -ne $Host -and $null -ne $Host.UI -and $null -ne $Host.UI.RawUI) {
|
|
$hostWidth = [int]$Host.UI.RawUI.WindowSize.Width
|
|
if ($hostWidth -gt 0) {
|
|
$width = $hostWidth
|
|
}
|
|
}
|
|
}
|
|
catch {
|
|
$width = 0
|
|
}
|
|
|
|
if ($width -le 0) {
|
|
try {
|
|
$consoleWidth = [int][System.Console]::WindowWidth
|
|
if ($consoleWidth -gt 0) {
|
|
$width = $consoleWidth
|
|
}
|
|
}
|
|
catch {
|
|
$width = 0
|
|
}
|
|
}
|
|
|
|
if ($width -lt 20) {
|
|
$width = 80
|
|
}
|
|
|
|
return $width
|
|
}
|
|
|
|
function Limit-OpenSreText {
|
|
param(
|
|
[AllowEmptyString()]
|
|
[string]$Text,
|
|
[int]$MaxWidth
|
|
)
|
|
|
|
$value = [string]$Text
|
|
$value = $value.Replace("`r", " ").Replace("`n", " ")
|
|
|
|
if ($MaxWidth -le 0) {
|
|
return ""
|
|
}
|
|
|
|
if ($value.Length -le $MaxWidth) {
|
|
return $value
|
|
}
|
|
|
|
if ($MaxWidth -le 3) {
|
|
return $value.Substring(0, $MaxWidth)
|
|
}
|
|
|
|
return ($value.Substring(0, $MaxWidth - 3) + "...")
|
|
}
|
|
|
|
function Get-OpenSreFriendlyProgressLabel {
|
|
param(
|
|
[AllowEmptyString()]
|
|
[string]$Label
|
|
)
|
|
|
|
if ($Label -like "*Fetching latest main build metadata*" -or
|
|
$Label -like "*Fetching latest release version*" -or
|
|
$Label -like "*Fetching release metadata*") {
|
|
return "fetching metadata"
|
|
}
|
|
|
|
if ($Label -like "*Preparing opensre*") {
|
|
return "resolving build"
|
|
}
|
|
|
|
if ($Label -like "*Downloading release archive*" -or
|
|
$Label -like "*.zip" -or
|
|
$Label -like "*.tar.gz") {
|
|
return "downloading archive"
|
|
}
|
|
|
|
if ($Label -like "*Downloading and verifying checksum*" -or
|
|
$Label -like "*Verifying release archive*" -or
|
|
$Label -like "*.sha256") {
|
|
return "verifying checksum"
|
|
}
|
|
|
|
if ($Label -like "*Extracting and verifying binary*") {
|
|
return "verifying binary"
|
|
}
|
|
|
|
if ($Label -like "*Installing*binary*" -or
|
|
$Label -like "*Installing*opensre*") {
|
|
return "installing binary"
|
|
}
|
|
|
|
return ([System.Text.RegularExpressions.Regex]::Replace([string]$Label, '^\[[0-9]+/[0-9]+\]\s*', ""))
|
|
}
|
|
|
|
function Get-OpenSreProgressFrame {
|
|
param(
|
|
[int]$Step
|
|
)
|
|
|
|
$frames = @("-", "\", "|", "/")
|
|
return $frames[$Step % $frames.Count]
|
|
}
|
|
|
|
function New-OpenSreProgressBar {
|
|
param(
|
|
[int]$Step,
|
|
[int]$Width
|
|
)
|
|
|
|
if ($Width -lt 1) {
|
|
return ""
|
|
}
|
|
|
|
[int]$trail = 8
|
|
[int]$head = $Step % ($Width + $trail)
|
|
$builder = New-Object System.Text.StringBuilder
|
|
|
|
for ($i = 0; $i -lt $Width; $i += 1) {
|
|
$age = $head - $i
|
|
if ($age -ge 0 -and $age -lt $trail) {
|
|
if ($age -eq 0 -or $age -eq 1) {
|
|
[void]$builder.Append("#")
|
|
}
|
|
elseif ($age -eq 2 -or $age -eq 3) {
|
|
[void]$builder.Append("=")
|
|
}
|
|
elseif ($age -eq 4 -or $age -eq 5) {
|
|
[void]$builder.Append("+")
|
|
}
|
|
else {
|
|
[void]$builder.Append("-")
|
|
}
|
|
}
|
|
else {
|
|
[void]$builder.Append(".")
|
|
}
|
|
}
|
|
|
|
return $builder.ToString()
|
|
}
|
|
|
|
function Write-OpenSreLine {
|
|
param(
|
|
[AllowEmptyString()]
|
|
[string]$Message,
|
|
[string]$Color = ""
|
|
)
|
|
|
|
if ((Test-OpenSreInteractiveHost) -and $Color) {
|
|
Write-Host $Message -ForegroundColor $Color
|
|
return
|
|
}
|
|
|
|
Write-Host $Message
|
|
}
|
|
|
|
function Write-OpenSreDetail {
|
|
param(
|
|
[AllowEmptyString()]
|
|
[string]$Message
|
|
)
|
|
|
|
if (-not $Message) {
|
|
return
|
|
}
|
|
|
|
Write-OpenSreLine -Message " $Message" -Color "DarkGray"
|
|
}
|
|
|
|
function Write-OpenSreHeader {
|
|
param(
|
|
[string]$Channel = "",
|
|
[string]$RequestedVersion = "",
|
|
[string]$InstallDir = "",
|
|
[string]$Repo = ""
|
|
)
|
|
|
|
Write-OpenSreLine -Message "OpenSRE installer" -Color "Cyan"
|
|
Write-OpenSreLine -Message "Installing the OpenSRE CLI for Windows." -Color "DarkGray"
|
|
|
|
if (Test-OpenSreVerboseInstall) {
|
|
Write-OpenSreDetail -Message "Verbose logging enabled by OPENSRE_INSTALL_VERBOSE=1."
|
|
if ($Repo) {
|
|
Write-OpenSreDetail -Message "Repository: $Repo"
|
|
}
|
|
if ($Channel) {
|
|
Write-OpenSreDetail -Message "Channel: $Channel"
|
|
}
|
|
if ($RequestedVersion) {
|
|
Write-OpenSreDetail -Message "Requested version: $RequestedVersion"
|
|
}
|
|
if ($InstallDir) {
|
|
Write-OpenSreDetail -Message "Install directory: $InstallDir"
|
|
}
|
|
}
|
|
}
|
|
|
|
function Write-OpenSreProgressLine {
|
|
param(
|
|
[string]$Label,
|
|
[Int64]$DownloadedBytes,
|
|
[Int64]$TotalBytes = -1
|
|
)
|
|
|
|
if (-not (Test-OpenSreInteractiveHost) -or (Test-OpenSreVerboseInstall)) {
|
|
return
|
|
}
|
|
|
|
$width = Get-OpenSreConsoleWidth
|
|
[int]$clearWidth = $width - 1
|
|
if ($clearWidth -lt 1) {
|
|
$clearWidth = 1
|
|
}
|
|
|
|
$title = "Installing OpenSRE"
|
|
if ($width -lt 56) {
|
|
$title = "OpenSRE"
|
|
}
|
|
|
|
$percentText = ""
|
|
if ($TotalBytes -gt 0) {
|
|
$percent = [Math]::Min(100, [Math]::Floor(($DownloadedBytes * 100) / $TotalBytes))
|
|
$percentText = " $percent%"
|
|
}
|
|
|
|
[int]$reserve = 2 + 1 + 1 + 1 + $title.Length + 1 + $percentText.Length
|
|
[int]$available = $clearWidth - $reserve
|
|
[int]$barWidth = 8
|
|
if ($available -lt 12) {
|
|
$barWidth = 4
|
|
}
|
|
else {
|
|
$barWidth = [Math]::Floor($available / 2)
|
|
if ($barWidth -gt 28) {
|
|
$barWidth = 28
|
|
}
|
|
if ($barWidth -lt 8) {
|
|
$barWidth = 8
|
|
}
|
|
}
|
|
|
|
[int]$labelWidth = $clearWidth - $reserve - $barWidth
|
|
if ($labelWidth -lt 8 -and $barWidth -gt 4) {
|
|
$barWidth = $clearWidth - $reserve - 8
|
|
if ($barWidth -lt 4) {
|
|
$barWidth = 4
|
|
}
|
|
$labelWidth = $clearWidth - $reserve - $barWidth
|
|
}
|
|
if ($labelWidth -lt 0) {
|
|
$labelWidth = 0
|
|
}
|
|
|
|
$script:OpenSreProgressStep += 1
|
|
$frame = Get-OpenSreProgressFrame -Step $script:OpenSreProgressStep
|
|
$bar = New-OpenSreProgressBar -Step $script:OpenSreProgressStep -Width $barWidth
|
|
$status = Limit-OpenSreText -Text (Get-OpenSreFriendlyProgressLabel -Label $Label) -MaxWidth $labelWidth
|
|
$content = " $frame $bar $title $status$percentText"
|
|
if ($content.Length -gt $clearWidth) {
|
|
$content = $content.Substring(0, $clearWidth)
|
|
}
|
|
|
|
[System.Console]::Write("`r{0}`r{1}" -f (" " * $clearWidth), $content)
|
|
}
|
|
|
|
function Clear-OpenSreProgressLine {
|
|
if (-not (Test-OpenSreInteractiveHost) -or (Test-OpenSreVerboseInstall)) {
|
|
return
|
|
}
|
|
|
|
$width = Get-OpenSreConsoleWidth
|
|
[int]$clearWidth = $width - 1
|
|
if ($clearWidth -lt 1) {
|
|
$clearWidth = 1
|
|
}
|
|
|
|
[System.Console]::Write("`r{0}`r" -f (" " * $clearWidth))
|
|
}
|
|
|
|
function Invoke-OpenSreStep {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Name,
|
|
[scriptblock]$Operation,
|
|
[string]$Detail = ""
|
|
)
|
|
|
|
Write-OpenSreLine -Message $Name -Color "Cyan"
|
|
Write-OpenSreDetail -Message $Detail
|
|
|
|
if ($Operation) {
|
|
try {
|
|
$result = & $Operation
|
|
Write-OpenSreLine -Message " OK $Name" -Color "Green"
|
|
return $result
|
|
}
|
|
catch {
|
|
Write-OpenSreLine -Message " FAILED $Name" -Color "Red"
|
|
throw
|
|
}
|
|
}
|
|
}
|
|
|
|
function Invoke-OpenSreStreamDownload {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Uri,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$OutFile,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Label
|
|
)
|
|
|
|
$request = [System.Net.HttpWebRequest]::Create($Uri)
|
|
$headers = Get-OpenSreRequestHeaders
|
|
foreach ($key in $headers.Keys) {
|
|
if ($key -eq "User-Agent") {
|
|
$request.UserAgent = [string]$headers[$key]
|
|
}
|
|
elseif ($key -eq "Accept") {
|
|
$request.Accept = [string]$headers[$key]
|
|
}
|
|
else {
|
|
$request.Headers[$key] = [string]$headers[$key]
|
|
}
|
|
}
|
|
|
|
$response = $request.GetResponse()
|
|
try {
|
|
$totalBytes = [Int64]$response.ContentLength
|
|
$inputStream = $response.GetResponseStream()
|
|
$outputStream = [System.IO.File]::Open($OutFile, [System.IO.FileMode]::Create, [System.IO.FileAccess]::Write)
|
|
try {
|
|
$buffer = New-Object byte[] 65536
|
|
[Int64]$downloadedBytes = 0
|
|
|
|
while ($true) {
|
|
$read = $inputStream.Read($buffer, 0, $buffer.Length)
|
|
if ($read -le 0) {
|
|
break
|
|
}
|
|
|
|
$outputStream.Write($buffer, 0, $read)
|
|
$downloadedBytes += $read
|
|
Write-OpenSreProgressLine -Label $Label -DownloadedBytes $downloadedBytes -TotalBytes $totalBytes
|
|
}
|
|
}
|
|
finally {
|
|
if ($outputStream) {
|
|
$outputStream.Dispose()
|
|
}
|
|
if ($inputStream) {
|
|
$inputStream.Dispose()
|
|
}
|
|
Clear-OpenSreProgressLine
|
|
}
|
|
}
|
|
finally {
|
|
if ($response) {
|
|
$response.Dispose()
|
|
}
|
|
}
|
|
}
|
|
|
|
function Get-OpenSreDefaultInstallDir {
|
|
$userHome = if ($HOME) { $HOME } else { [System.Environment]::GetFolderPath("UserProfile") }
|
|
return Join-Path $userHome ".local\bin"
|
|
}
|
|
|
|
function Get-OpenSreRequestHeaders {
|
|
return @{
|
|
"Accept" = "application/vnd.github+json"
|
|
"User-Agent" = "opensre-install-script"
|
|
}
|
|
}
|
|
|
|
function Invoke-OpenSreWithRetry {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[scriptblock]$Operation,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Description,
|
|
[int]$MaxAttempts = 3
|
|
)
|
|
|
|
$attempt = 1
|
|
|
|
while ($true) {
|
|
try {
|
|
return & $Operation
|
|
}
|
|
catch {
|
|
$statusCode = Get-OpenSreHttpStatusCodeFromError -ErrorRecord $_
|
|
if ($null -ne $statusCode -and $statusCode -ge 400 -and $statusCode -lt 500) {
|
|
throw "Failed to $Description. $($_.Exception.Message)"
|
|
}
|
|
|
|
if ($attempt -ge $MaxAttempts) {
|
|
throw "Failed to $Description after $attempt attempts. $($_.Exception.Message)"
|
|
}
|
|
|
|
Write-Warning "Attempt $attempt to $Description failed: $($_.Exception.Message). Retrying..."
|
|
Start-Sleep -Seconds $attempt
|
|
$attempt += 1
|
|
}
|
|
}
|
|
}
|
|
|
|
function Get-OpenSreHttpStatusCodeFromError {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[System.Management.Automation.ErrorRecord]$ErrorRecord
|
|
)
|
|
|
|
$exception = $ErrorRecord.Exception
|
|
|
|
while ($null -ne $exception) {
|
|
if ($exception.PSObject.Properties["Response"] -and $null -ne $exception.Response) {
|
|
$response = $exception.Response
|
|
if ($response.PSObject.Properties["StatusCode"] -and $null -ne $response.StatusCode) {
|
|
try {
|
|
return [int]$response.StatusCode
|
|
}
|
|
catch {
|
|
return $null
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($exception.PSObject.Properties["StatusCode"] -and $null -ne $exception.StatusCode) {
|
|
try {
|
|
return [int]$exception.StatusCode
|
|
}
|
|
catch {
|
|
return $null
|
|
}
|
|
}
|
|
|
|
$exception = $exception.InnerException
|
|
}
|
|
|
|
return $null
|
|
}
|
|
|
|
function Enable-OpenSreTls {
|
|
try {
|
|
$protocol = [System.Net.ServicePointManager]::SecurityProtocol
|
|
$availableProtocols = [System.Enum]::GetNames([System.Net.SecurityProtocolType])
|
|
|
|
if ($availableProtocols -contains "Tls12") {
|
|
$protocol = $protocol -bor [System.Net.SecurityProtocolType]::Tls12
|
|
}
|
|
|
|
if ($availableProtocols -contains "Tls13") {
|
|
$protocol = $protocol -bor [System.Net.SecurityProtocolType]::Tls13
|
|
}
|
|
|
|
[System.Net.ServicePointManager]::SecurityProtocol = $protocol
|
|
}
|
|
catch {
|
|
# Best-effort compatibility tweak for older Windows PowerShell runtimes.
|
|
}
|
|
}
|
|
|
|
function Invoke-OpenSreRestMethod {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Uri
|
|
)
|
|
|
|
$params = @{
|
|
Uri = $Uri
|
|
Headers = Get-OpenSreRequestHeaders
|
|
}
|
|
|
|
$command = Get-Command Invoke-RestMethod -ErrorAction Stop
|
|
if ($command.Parameters.ContainsKey("UseBasicParsing")) {
|
|
$params.UseBasicParsing = $true
|
|
}
|
|
|
|
if (Test-OpenSreVerboseInstall) {
|
|
Write-OpenSreDetail -Message "GET $Uri"
|
|
}
|
|
|
|
return Invoke-OpenSreWithRetry -Description "fetch release metadata from GitHub" -Operation {
|
|
Invoke-RestMethod @params
|
|
}
|
|
}
|
|
|
|
function Invoke-OpenSreDownloadFileWithProgress {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Uri,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$OutFile,
|
|
[string]$Label = ""
|
|
)
|
|
|
|
if (-not $Label) {
|
|
$Label = [System.IO.Path]::GetFileName($OutFile)
|
|
}
|
|
|
|
if (-not $Label) {
|
|
$Label = "file"
|
|
}
|
|
|
|
$params = @{
|
|
Uri = $Uri
|
|
Headers = Get-OpenSreRequestHeaders
|
|
OutFile = $OutFile
|
|
}
|
|
|
|
$command = Get-Command Invoke-WebRequest -ErrorAction Stop
|
|
if ($command.Parameters.ContainsKey("UseBasicParsing")) {
|
|
$params.UseBasicParsing = $true
|
|
}
|
|
|
|
if (Test-OpenSreVerboseInstall) {
|
|
Write-OpenSreDetail -Message "Download URL: $Uri"
|
|
Write-OpenSreDetail -Message "Destination: $OutFile"
|
|
}
|
|
else {
|
|
Write-OpenSreDetail -Message $Label
|
|
}
|
|
|
|
Invoke-OpenSreWithRetry -Description "download '$Uri'" -Operation {
|
|
if ((Test-OpenSreInteractiveHost) -and -not (Test-OpenSreVerboseInstall)) {
|
|
Invoke-OpenSreStreamDownload -Uri $Uri -OutFile $OutFile -Label $Label
|
|
}
|
|
else {
|
|
$previousProgressPreference = $ProgressPreference
|
|
try {
|
|
$ProgressPreference = "SilentlyContinue"
|
|
Invoke-WebRequest @params | Out-Null
|
|
}
|
|
finally {
|
|
$ProgressPreference = $previousProgressPreference
|
|
}
|
|
}
|
|
} | Out-Null
|
|
}
|
|
|
|
function Invoke-OpenSreWebRequest {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Uri,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$OutFile
|
|
)
|
|
|
|
Invoke-OpenSreDownloadFileWithProgress -Uri $Uri -OutFile $OutFile
|
|
}
|
|
|
|
function Get-OpenSreRuntimeArchitecture {
|
|
try {
|
|
$runtimeInformation = [System.Runtime.InteropServices.RuntimeInformation]
|
|
return [string]$runtimeInformation::OSArchitecture
|
|
}
|
|
catch {
|
|
return ""
|
|
}
|
|
}
|
|
|
|
function Resolve-OpenSreWindowsArchitecture {
|
|
param(
|
|
[string]$RuntimeArchitecture = (Get-OpenSreRuntimeArchitecture),
|
|
[string]$ProcessorArchitectureW6432 = $env:PROCESSOR_ARCHITEW6432,
|
|
[string]$ProcessorArchitecture = $env:PROCESSOR_ARCHITECTURE,
|
|
[bool]$Is64BitOperatingSystem = [System.Environment]::Is64BitOperatingSystem
|
|
)
|
|
|
|
$candidates = @(
|
|
$RuntimeArchitecture,
|
|
$ProcessorArchitectureW6432,
|
|
$ProcessorArchitecture
|
|
) | Where-Object { $_ -and $_.Trim() }
|
|
|
|
foreach ($candidate in $candidates) {
|
|
$normalized = $candidate.Trim().ToUpperInvariant()
|
|
|
|
switch ($normalized) {
|
|
{ $_ -in @("X64", "AMD64", "X86_64") } { return "x64" }
|
|
{ $_ -in @("ARM64", "AARCH64") } { return "arm64" }
|
|
{ $_ -in @("X86", "I386", "I686") } {
|
|
throw "Unsupported Windows architecture: $candidate. OpenSRE releases are available only for x64 and arm64."
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($Is64BitOperatingSystem) {
|
|
return "x64"
|
|
}
|
|
|
|
throw "Unsupported Windows architecture. Could not detect a supported architecture from RuntimeInformation, PROCESSOR_ARCHITEW6432, or PROCESSOR_ARCHITECTURE."
|
|
}
|
|
|
|
function Get-OpenSreArchiveName {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Version,
|
|
[Parameter(Mandatory = $true)]
|
|
[ValidateSet("release", "main")]
|
|
[string]$Channel,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$TargetArch
|
|
)
|
|
|
|
$archiveVersion = if ($Channel -eq "main") { "main" } else { $Version }
|
|
return "opensre_${archiveVersion}_windows-$TargetArch.zip"
|
|
}
|
|
|
|
function Get-OpenSreReleaseMetadata {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Repo,
|
|
[ValidateSet("release", "main")]
|
|
[string]$Channel = "release",
|
|
[string]$RequestedVersion = $env:OPENSRE_VERSION
|
|
)
|
|
|
|
$normalizedVersion = ""
|
|
if ($RequestedVersion) {
|
|
$normalizedVersion = $RequestedVersion.Trim().TrimStart("v")
|
|
}
|
|
|
|
if ($Channel -eq "main" -and $normalizedVersion) {
|
|
throw "OPENSRE_VERSION cannot be combined with the main install channel."
|
|
}
|
|
|
|
$mainReleaseTag = if ($env:OPENSRE_MAIN_RELEASE_TAG) { $env:OPENSRE_MAIN_RELEASE_TAG } else { "main-build" }
|
|
|
|
$releaseUri = if ($Channel -eq "main") {
|
|
"https://api.github.com/repos/$Repo/releases/tags/$mainReleaseTag"
|
|
}
|
|
elseif ($normalizedVersion) {
|
|
"https://api.github.com/repos/$Repo/releases/tags/v$normalizedVersion"
|
|
}
|
|
else {
|
|
"https://api.github.com/repos/$Repo/releases/latest"
|
|
}
|
|
|
|
try {
|
|
$release = Invoke-OpenSreRestMethod -Uri $releaseUri
|
|
}
|
|
catch {
|
|
if ($Channel -eq "main") {
|
|
throw "Failed to fetch main build metadata from GitHub for '$Repo'. $($_.Exception.Message)"
|
|
}
|
|
|
|
if ($normalizedVersion) {
|
|
throw "Failed to fetch release metadata for version '$normalizedVersion' from GitHub repo '$Repo'. $($_.Exception.Message)"
|
|
}
|
|
|
|
throw "Failed to fetch latest release metadata from GitHub for '$Repo'. $($_.Exception.Message)"
|
|
}
|
|
|
|
$version = if ($Channel -eq "main") { "main" } else { [string]$release.tag_name }
|
|
if ($Channel -ne "main" -and $version) {
|
|
$version = $version.Trim().TrimStart("v")
|
|
}
|
|
|
|
if (-not $version) {
|
|
if ($Channel -eq "main") {
|
|
throw "Failed to determine the main build tag."
|
|
}
|
|
|
|
throw "Failed to determine the latest release version."
|
|
}
|
|
|
|
return [pscustomobject]@{
|
|
Release = $release
|
|
Version = $version
|
|
}
|
|
}
|
|
|
|
function Get-OpenSreReleaseAsset {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
$Release,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$AssetName
|
|
)
|
|
|
|
foreach ($asset in @($Release.assets)) {
|
|
if ([string]$asset.name -eq $AssetName) {
|
|
return $asset
|
|
}
|
|
}
|
|
|
|
return $null
|
|
}
|
|
|
|
function Resolve-OpenSreArchiveDownload {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
$Release,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Version,
|
|
[Parameter(Mandatory = $true)]
|
|
[ValidateSet("release", "main")]
|
|
[string]$Channel,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$TargetArch
|
|
)
|
|
|
|
$resolvedArch = $TargetArch
|
|
$archiveName = Get-OpenSreArchiveName -Version $Version -Channel $Channel -TargetArch $resolvedArch
|
|
$archiveAsset = Get-OpenSreReleaseAsset -Release $Release -AssetName $archiveName
|
|
|
|
if (-not $archiveAsset -and $TargetArch -eq "arm64") {
|
|
$fallbackArchiveName = Get-OpenSreArchiveName -Version $Version -Channel $Channel -TargetArch "x64"
|
|
$fallbackAsset = Get-OpenSreReleaseAsset -Release $Release -AssetName $fallbackArchiveName
|
|
|
|
if ($fallbackAsset) {
|
|
$resolvedArch = "x64"
|
|
$archiveName = $fallbackArchiveName
|
|
$archiveAsset = $fallbackAsset
|
|
if ($Channel -eq "main") {
|
|
Write-Warning "Windows ARM64 artifact is not published for the main build; falling back to the x64 build."
|
|
}
|
|
else {
|
|
Write-Warning "Windows ARM64 artifact is not published for v$Version; falling back to the x64 build."
|
|
}
|
|
}
|
|
}
|
|
|
|
if (-not $archiveAsset) {
|
|
$availableAssets = @($Release.assets | ForEach-Object { [string]$_.name } | Where-Object { $_ }) -join ", "
|
|
if ($availableAssets) {
|
|
if ($Channel -eq "main") {
|
|
throw "Main build release does not include asset '$archiveName'. Available assets: $availableAssets"
|
|
}
|
|
|
|
throw "Release v$Version does not include asset '$archiveName'. Available assets: $availableAssets"
|
|
}
|
|
|
|
if ($Channel -eq "main") {
|
|
throw "Main build release does not include asset '$archiveName'."
|
|
}
|
|
|
|
throw "Release v$Version does not include asset '$archiveName'."
|
|
}
|
|
|
|
$checksumAsset = Get-OpenSreReleaseAsset -Release $Release -AssetName "$archiveName.sha256"
|
|
|
|
return [pscustomobject]@{
|
|
ArchiveName = $archiveName
|
|
ArchiveUrl = [string]$archiveAsset.browser_download_url
|
|
ChecksumName = if ($checksumAsset) { [string]$checksumAsset.name } else { "" }
|
|
ChecksumUrl = if ($checksumAsset) { [string]$checksumAsset.browser_download_url } else { "" }
|
|
ResolvedArch = $resolvedArch
|
|
}
|
|
}
|
|
|
|
function Get-OpenSreExpectedSha256 {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$ChecksumPath,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$ArchiveName
|
|
)
|
|
|
|
foreach ($line in Get-Content -LiteralPath $ChecksumPath) {
|
|
if (-not $line.Trim()) {
|
|
continue
|
|
}
|
|
|
|
$match = [System.Text.RegularExpressions.Regex]::Match(
|
|
$line,
|
|
'^(?<hash>[A-Fa-f0-9]{64})\s+\*?(?<name>.+)$'
|
|
)
|
|
|
|
if (-not $match.Success) {
|
|
continue
|
|
}
|
|
|
|
$name = [System.IO.Path]::GetFileName($match.Groups["name"].Value.Trim())
|
|
if ($name -eq $ArchiveName) {
|
|
return $match.Groups["hash"].Value.ToLowerInvariant()
|
|
}
|
|
}
|
|
|
|
throw "Checksum file '$ChecksumPath' does not contain a SHA256 entry for '$ArchiveName'."
|
|
}
|
|
|
|
function Normalize-OpenSrePath {
|
|
param(
|
|
[string]$PathValue
|
|
)
|
|
|
|
if (-not $PathValue) {
|
|
return ""
|
|
}
|
|
|
|
$trimmedPath = $PathValue.Trim().TrimEnd("\", "/")
|
|
if (-not $trimmedPath) {
|
|
return ""
|
|
}
|
|
|
|
try {
|
|
return [System.IO.Path]::GetFullPath($trimmedPath).TrimEnd("\", "/")
|
|
}
|
|
catch {
|
|
return $trimmedPath
|
|
}
|
|
}
|
|
|
|
function Test-OpenSreDirectoryOnPath {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Directory,
|
|
[string]$PathValue = $env:PATH
|
|
)
|
|
|
|
if (-not $PathValue) {
|
|
return $false
|
|
}
|
|
|
|
$normalizedDirectory = Normalize-OpenSrePath -PathValue $Directory
|
|
|
|
foreach ($entry in $PathValue -split ";") {
|
|
if (-not $entry) {
|
|
continue
|
|
}
|
|
|
|
if ([string]::Equals(
|
|
$normalizedDirectory,
|
|
(Normalize-OpenSrePath -PathValue $entry),
|
|
[System.StringComparison]::OrdinalIgnoreCase
|
|
)) {
|
|
return $true
|
|
}
|
|
}
|
|
|
|
return $false
|
|
}
|
|
|
|
function Get-OpenSreBinaryPathFromArchive {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$ExtractionRoot,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$BinaryName
|
|
)
|
|
|
|
$directBinaryPath = Join-Path $ExtractionRoot $BinaryName
|
|
if (Test-Path -LiteralPath $directBinaryPath -PathType Leaf) {
|
|
return $directBinaryPath
|
|
}
|
|
|
|
$binaryCandidates = @(Get-ChildItem -Path $ExtractionRoot -Recurse -File -Filter $BinaryName)
|
|
|
|
if ($binaryCandidates.Count -eq 1) {
|
|
return $binaryCandidates[0].FullName
|
|
}
|
|
|
|
if ($binaryCandidates.Count -gt 1) {
|
|
$locations = $binaryCandidates | ForEach-Object { $_.FullName }
|
|
throw "Found multiple '$BinaryName' files after extraction: $($locations -join ', ')"
|
|
}
|
|
|
|
throw "Archive did not contain '$BinaryName'."
|
|
}
|
|
|
|
function Get-OpenSreBinaryVersionInfo {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$BinaryPath
|
|
)
|
|
|
|
try {
|
|
$versionOutput = & $BinaryPath --version 2>&1
|
|
}
|
|
catch {
|
|
throw "Failed to execute '$BinaryPath --version'. $($_.Exception.Message)"
|
|
}
|
|
|
|
$versionText = ($versionOutput | Out-String).Trim()
|
|
$detectedVersion = ""
|
|
$match = [System.Text.RegularExpressions.Regex]::Match($versionText, '\d{4}\.\d{1,2}\.\d{1,2}')
|
|
if ($match.Success) {
|
|
$detectedVersion = $match.Value
|
|
}
|
|
|
|
return [pscustomobject]@{
|
|
Text = $versionText
|
|
Version = $detectedVersion
|
|
}
|
|
}
|
|
|
|
function Test-OpenSreAutoLaunchEnabled {
|
|
$value = [string]$env:OPENSRE_AUTO_LAUNCH
|
|
return -not ($value -eq "0" -or $value -eq "false" -or $value -eq "FALSE" -or $value -eq "no" -or $value -eq "NO" -or $value -eq "off" -or $value -eq "OFF")
|
|
}
|
|
|
|
function Start-OpenSreOnboardingAfterInstall {
|
|
param(
|
|
[string]$BinaryPath,
|
|
[string]$DisplayName
|
|
)
|
|
|
|
if (-not (Test-OpenSreAutoLaunchEnabled) -or -not (Test-OpenSreInteractiveHost)) {
|
|
return
|
|
}
|
|
|
|
# If stdin is redirected (e.g. the installer is piped), the full-screen
|
|
# onboarding prompt cannot take control of the terminal and exits with a
|
|
# terminal I/O error mid-render (issue #3273). Skip the auto-launch; the
|
|
# "Next steps" output already tells the user to run onboarding themselves.
|
|
try {
|
|
if ([System.Console]::IsInputRedirected) {
|
|
return
|
|
}
|
|
}
|
|
catch {
|
|
return
|
|
}
|
|
|
|
if (-not (Test-Path -LiteralPath $BinaryPath -PathType Leaf)) {
|
|
Write-Warning "Could not auto-launch onboarding; $BinaryPath was not found."
|
|
return
|
|
}
|
|
|
|
Write-Host "Launching $DisplayName onboard..."
|
|
& $BinaryPath onboard
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Write-Warning "Onboarding exited before completion. Run '$DisplayName onboard' to retry."
|
|
}
|
|
}
|
|
|
|
function Install-OpenSre {
|
|
$repo = if ($env:OPENSRE_INSTALL_REPO) { $env:OPENSRE_INSTALL_REPO } else { "Tracer-Cloud/opensre" }
|
|
$installDir = if ($env:OPENSRE_INSTALL_DIR) { $env:OPENSRE_INSTALL_DIR } else { Get-OpenSreDefaultInstallDir }
|
|
$binaryName = "opensre.exe"
|
|
$requestedVersion = if ($env:OPENSRE_VERSION) { $env:OPENSRE_VERSION.Trim().TrimStart("v") } else { "" }
|
|
$resolvedChannel = if ($Channel) { $Channel.Trim().ToLowerInvariant() } else { "release" }
|
|
$channelExplicit = [bool]$script:OpenSreChannelExplicit
|
|
|
|
if ($requestedVersion -and $resolvedChannel -eq "main" -and -not $channelExplicit) {
|
|
$resolvedChannel = "release"
|
|
}
|
|
|
|
Write-OpenSreHeader -Channel $resolvedChannel -RequestedVersion $requestedVersion -InstallDir $installDir -Repo $repo
|
|
Enable-OpenSreTls
|
|
|
|
$targetArch = Resolve-OpenSreWindowsArchitecture
|
|
$metadataStepName = ""
|
|
if ($resolvedChannel -eq "main") {
|
|
$metadataStepName = "[1/6] Fetching latest main build metadata"
|
|
}
|
|
elseif ($requestedVersion) {
|
|
$metadataStepName = "[1/6] Fetching release metadata for v$requestedVersion"
|
|
}
|
|
else {
|
|
$metadataStepName = "[1/6] Fetching latest release version"
|
|
}
|
|
$releaseMetadata = Invoke-OpenSreStep -Name $metadataStepName -Operation {
|
|
Get-OpenSreReleaseMetadata -Repo $repo -Channel $resolvedChannel -RequestedVersion $requestedVersion
|
|
}
|
|
$version = [string]$releaseMetadata.Version
|
|
|
|
$assetStepName = if ($resolvedChannel -eq "main") {
|
|
"[2/6] Preparing opensre main build (windows/$targetArch)"
|
|
}
|
|
else {
|
|
"[2/6] Preparing opensre v$version (windows/$targetArch)"
|
|
}
|
|
$downloadPlan = Invoke-OpenSreStep -Name $assetStepName -Operation {
|
|
Resolve-OpenSreArchiveDownload -Release $releaseMetadata.Release -Version $version -Channel $resolvedChannel -TargetArch $targetArch
|
|
}
|
|
$archive = [string]$downloadPlan.ArchiveName
|
|
$downloadUrl = [string]$downloadPlan.ArchiveUrl
|
|
$checksumUrl = [string]$downloadPlan.ChecksumUrl
|
|
$resolvedArch = [string]$downloadPlan.ResolvedArch
|
|
$tmpDir = Join-Path ([System.IO.Path]::GetTempPath()) ("opensre-install-" + [System.Guid]::NewGuid().ToString("N"))
|
|
|
|
New-Item -ItemType Directory -Path $tmpDir | Out-Null
|
|
|
|
try {
|
|
$archivePath = Join-Path $tmpDir $archive
|
|
$checksumPath = "$archivePath.sha256"
|
|
|
|
if ($resolvedArch -ne $targetArch) {
|
|
Write-OpenSreDetail -Message "Using release asset built for windows/$resolvedArch."
|
|
}
|
|
|
|
Invoke-OpenSreStep -Name "[3/6] Downloading release archive" -Operation {
|
|
Invoke-OpenSreDownloadFileWithProgress -Uri $downloadUrl -OutFile $archivePath -Label $archive
|
|
}
|
|
|
|
if ($checksumUrl) {
|
|
$checksumName = [string]$downloadPlan.ChecksumName
|
|
Invoke-OpenSreStep -Name "[4/6] Downloading and verifying checksum" -Operation {
|
|
Invoke-OpenSreDownloadFileWithProgress -Uri $checksumUrl -OutFile $checksumPath -Label $checksumName
|
|
|
|
$expectedHash = Get-OpenSreExpectedSha256 -ChecksumPath $checksumPath -ArchiveName $archive
|
|
$actualHash = (Get-FileHash -LiteralPath $archivePath -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
|
|
if ($actualHash -ne $expectedHash) {
|
|
throw "Checksum verification failed for '$archive'. Expected '$expectedHash' but got '$actualHash'."
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if ($resolvedChannel -eq "main") {
|
|
Write-Warning "Main build release is missing checksum asset '$archive.sha256'."
|
|
}
|
|
else {
|
|
Write-Warning "Release v$version is missing checksum asset '$archive.sha256'."
|
|
}
|
|
}
|
|
|
|
$verifiedBinary = Invoke-OpenSreStep -Name "[5/6] Extracting and verifying binary" -Operation {
|
|
Expand-Archive -LiteralPath $archivePath -DestinationPath $tmpDir -Force
|
|
|
|
$binaryPath = Get-OpenSreBinaryPathFromArchive -ExtractionRoot $tmpDir -BinaryName $binaryName
|
|
$binaryVersionInfo = Get-OpenSreBinaryVersionInfo -BinaryPath $binaryPath
|
|
$binaryVersionText = [string]$binaryVersionInfo.Text
|
|
$binaryVersion = [string]$binaryVersionInfo.Version
|
|
$installVersion = $version
|
|
|
|
if ($resolvedChannel -ne "main" -and $binaryVersionText -notmatch [Regex]::Escape($version)) {
|
|
if ($requestedVersion) {
|
|
throw "Downloaded binary version mismatch. Expected '$version' but got '$binaryVersionText'."
|
|
}
|
|
|
|
if (-not $binaryVersion) {
|
|
throw "Downloaded binary version mismatch. Expected '$version' but got '$binaryVersionText'."
|
|
}
|
|
|
|
Write-Warning "Latest release metadata reports v$version, but the downloaded binary reports v$binaryVersion. Installing the verified binary anyway."
|
|
$installVersion = $binaryVersion
|
|
}
|
|
|
|
return [pscustomobject]@{
|
|
Path = $binaryPath
|
|
VersionText = $binaryVersionText
|
|
Version = $binaryVersion
|
|
InstallVersion = $installVersion
|
|
}
|
|
}
|
|
|
|
$binaryPath = [string]$verifiedBinary.Path
|
|
$binaryVersionText = [string]$verifiedBinary.VersionText
|
|
$binaryVersion = [string]$verifiedBinary.Version
|
|
$version = [string]$verifiedBinary.InstallVersion
|
|
|
|
Invoke-OpenSreStep -Name "[6/6] Installing binary" -Detail (Join-Path $installDir $binaryName) -Operation {
|
|
New-Item -ItemType Directory -Force -Path $installDir | Out-Null
|
|
Copy-Item -LiteralPath $binaryPath -Destination (Join-Path $installDir $binaryName) -Force
|
|
}
|
|
}
|
|
finally {
|
|
Remove-Item -LiteralPath $tmpDir -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|
|
|
|
$installedBinaryPath = Join-Path $installDir $binaryName
|
|
if ($resolvedChannel -eq "main") {
|
|
if ($binaryVersion) {
|
|
Write-Host "Installed opensre main build ($binaryVersion) to $installedBinaryPath"
|
|
}
|
|
else {
|
|
Write-Host "Installed opensre main build to $installedBinaryPath"
|
|
}
|
|
}
|
|
else {
|
|
Write-Host "Installed opensre $version to $installedBinaryPath"
|
|
}
|
|
|
|
if (-not (Test-OpenSreDirectoryOnPath -Directory $installDir)) {
|
|
Write-Warning "Add $installDir to your PATH to run opensre from any terminal."
|
|
}
|
|
|
|
$exe = $binaryName.TrimEnd(".exe")
|
|
$sep = "────────────────────────────────────────────"
|
|
|
|
Write-Host ""
|
|
Write-Host $sep
|
|
if ($resolvedChannel -eq "main") {
|
|
if ($binaryVersion) {
|
|
Write-Host " opensre main build ($binaryVersion) installed successfully"
|
|
}
|
|
else {
|
|
Write-Host " opensre main build installed successfully"
|
|
}
|
|
}
|
|
else {
|
|
Write-Host " opensre v$version installed successfully"
|
|
}
|
|
Write-Host $sep
|
|
Write-Host ""
|
|
Write-Host "Next steps:"
|
|
Write-Host " 1. Run $exe onboard"
|
|
Write-Host " Set up your LLM provider and any observability integrations."
|
|
Write-Host ""
|
|
Write-Host " 2. Run $exe (no subcommand)"
|
|
Write-Host " From a normal interactive terminal this starts the interactive shell; type a"
|
|
Write-Host " prompt or incident description to investigate."
|
|
Write-Host ""
|
|
Write-Host " 3. Optional — one-shot RCA from a file:"
|
|
Write-Host " $exe investigate -i path/to/alert.json"
|
|
Write-Host ""
|
|
Write-Host "Docs: https://www.opensre.com/docs"
|
|
Write-Host ""
|
|
|
|
Start-OpenSreOnboardingAfterInstall -BinaryPath $installedBinaryPath -DisplayName $exe
|
|
}
|
|
|
|
if (-not $SkipMain) {
|
|
Install-OpenSre
|
|
}
|