mirror of
https://github.com/bunny-lab-io/Borealis.git
synced 2025-10-26 17:41:58 -06:00
737 lines
26 KiB
PowerShell
737 lines
26 KiB
PowerShell
[CmdletBinding()]
|
|
param()
|
|
|
|
$scriptDir = Split-Path $MyInvocation.MyCommand.Path -Parent
|
|
$symbols = @{
|
|
Success = [char]0x2705
|
|
Running = [char]0x23F3
|
|
Fail = [char]0x274C
|
|
Info = [char]0x2139
|
|
}
|
|
|
|
$repositoryUrl = 'https://github.com/bunny-lab-io/Borealis.git'
|
|
|
|
function Write-ProgressStep {
|
|
param (
|
|
[string]$Message,
|
|
[string]$Status = $symbols["Info"]
|
|
)
|
|
Write-Host "`r$Status $Message... " -NoNewline
|
|
}
|
|
|
|
function Run-Step {
|
|
param (
|
|
[string] $Message,
|
|
[scriptblock]$Script
|
|
)
|
|
Write-ProgressStep -Message $Message -Status "$($symbols.Running)"
|
|
try {
|
|
& $Script
|
|
if ($LASTEXITCODE -eq 0 -or $?) {
|
|
Write-Host "`r$($symbols.Success) $Message "
|
|
} else {
|
|
throw "Non-zero exit code"
|
|
}
|
|
} catch {
|
|
Write-Host "`r$($symbols.Fail) $Message - Failed: $_ " -ForegroundColor Red
|
|
throw
|
|
}
|
|
}
|
|
|
|
function Get-GitExecutablePath {
|
|
param(
|
|
[string]$ProjectRoot
|
|
)
|
|
|
|
$candidates = @()
|
|
if ($ProjectRoot) {
|
|
$candidates += (Join-Path $ProjectRoot 'Dependencies\git\cmd\git.exe')
|
|
$candidates += (Join-Path $ProjectRoot 'Dependencies\git\bin\git.exe')
|
|
}
|
|
|
|
foreach ($candidate in ($candidates | Select-Object -Unique)) {
|
|
try {
|
|
if (Test-Path $candidate -PathType Leaf) { return $candidate }
|
|
} catch {}
|
|
}
|
|
|
|
return ''
|
|
}
|
|
|
|
function Invoke-GitCommand {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$GitExe,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$WorkingDirectory,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string[]]$Arguments
|
|
)
|
|
|
|
if ([string]::IsNullOrWhiteSpace($GitExe) -or -not (Test-Path $GitExe -PathType Leaf)) {
|
|
throw "Git executable not found at '$GitExe'"
|
|
}
|
|
|
|
if (-not (Test-Path $WorkingDirectory -PathType Container)) {
|
|
throw "Working directory '$WorkingDirectory' does not exist."
|
|
}
|
|
|
|
$fullArgs = @('-C', $WorkingDirectory) + $Arguments
|
|
$output = & $GitExe @fullArgs 2>&1
|
|
$exitCode = $LASTEXITCODE
|
|
if ($exitCode -ne 0) {
|
|
$joined = ($Arguments -join ' ')
|
|
$message = "git $joined failed with exit code $exitCode."
|
|
if ($output) {
|
|
$message = "$message Output: $output"
|
|
}
|
|
throw $message
|
|
}
|
|
|
|
return $output
|
|
}
|
|
|
|
function Stop-AgentScheduledTasks {
|
|
param(
|
|
[string[]]$TaskNames
|
|
)
|
|
|
|
$stopped = @()
|
|
foreach ($name in $TaskNames) {
|
|
$taskExists = $false
|
|
try {
|
|
$null = Get-ScheduledTask -TaskName $name -ErrorAction Stop
|
|
$taskExists = $true
|
|
} catch {
|
|
try {
|
|
schtasks.exe /Query /TN "$name" 2>$null | Out-Null
|
|
if ($LASTEXITCODE -eq 0) { $taskExists = $true }
|
|
} catch {}
|
|
}
|
|
|
|
if (-not $taskExists) { continue }
|
|
|
|
Write-Host "Stopping scheduled task: $name" -ForegroundColor Yellow
|
|
$stopped += $name
|
|
try { Stop-ScheduledTask -TaskName $name -ErrorAction SilentlyContinue } catch {}
|
|
try { schtasks.exe /End /TN "$name" /F 2>$null | Out-Null } catch {}
|
|
try {
|
|
for ($i = 0; $i -lt 20; $i++) {
|
|
$info = Get-ScheduledTaskInfo -TaskName $name -ErrorAction Stop
|
|
if ($info.State -ne 'Running' -and $info.State -ne 'Queued') { break }
|
|
Start-Sleep -Milliseconds 500
|
|
}
|
|
} catch {}
|
|
}
|
|
|
|
return ,$stopped
|
|
}
|
|
|
|
function Start-AgentScheduledTasks {
|
|
param(
|
|
[string[]]$TaskNames
|
|
)
|
|
|
|
foreach ($name in $TaskNames) {
|
|
Write-Host "Restarting scheduled task: $name" -ForegroundColor Green
|
|
try {
|
|
Start-ScheduledTask -TaskName $name -ErrorAction Stop | Out-Null
|
|
continue
|
|
} catch {}
|
|
|
|
try { schtasks.exe /Run /TN "$name" 2>$null | Out-Null } catch {}
|
|
}
|
|
}
|
|
|
|
function Get-BorealisServerUrl {
|
|
param(
|
|
[string]$AgentRoot
|
|
)
|
|
|
|
$serverBaseUrl = $env:BOREALIS_SERVER_URL
|
|
if (-not $serverBaseUrl) {
|
|
try {
|
|
if (-not $AgentRoot) { $AgentRoot = $scriptDir }
|
|
$settingsDir = Join-Path $AgentRoot 'Settings'
|
|
$serverUrlFile = Join-Path $settingsDir 'server_url.txt'
|
|
if (Test-Path $serverUrlFile -PathType Leaf) {
|
|
$content = Get-Content -Path $serverUrlFile -Raw -ErrorAction Stop
|
|
if ($content) { $serverBaseUrl = $content.Trim() }
|
|
}
|
|
} catch {}
|
|
}
|
|
|
|
if (-not $serverBaseUrl) { $serverBaseUrl = 'http://localhost:5000' }
|
|
return $serverBaseUrl.Trim()
|
|
}
|
|
|
|
function Get-AgentServiceId {
|
|
param(
|
|
[string]$AgentRoot
|
|
)
|
|
|
|
if (-not $AgentRoot) { $AgentRoot = $scriptDir }
|
|
$settingsDir = Join-Path $AgentRoot 'Settings'
|
|
$candidates = @(
|
|
(Join-Path $settingsDir 'agent_settings_svc.json')
|
|
(Join-Path $settingsDir 'agent_settings_user.json')
|
|
(Join-Path $settingsDir 'agent_settings.json')
|
|
)
|
|
|
|
foreach ($path in $candidates) {
|
|
try {
|
|
if (Test-Path $path -PathType Leaf) {
|
|
$raw = Get-Content -Path $path -Raw -ErrorAction Stop
|
|
if (-not $raw) { continue }
|
|
$cfg = $raw | ConvertFrom-Json -ErrorAction Stop
|
|
$value = ($cfg.agent_id)
|
|
if ($value) { return ($value.ToString()).Trim() }
|
|
}
|
|
} catch {}
|
|
}
|
|
|
|
return ''
|
|
}
|
|
|
|
function Get-AgentGuid {
|
|
param(
|
|
[string]$AgentRoot
|
|
)
|
|
|
|
$candidates = @()
|
|
if (-not $AgentRoot) { $AgentRoot = $scriptDir }
|
|
if ($AgentRoot) { $candidates += (Join-Path $AgentRoot 'agent_GUID') }
|
|
$defaultPath = Join-Path $scriptDir 'Agent\Borealis\agent_GUID'
|
|
if ($defaultPath -and ($candidates -notcontains $defaultPath)) { $candidates += $defaultPath }
|
|
|
|
foreach ($path in ($candidates | Select-Object -Unique)) {
|
|
try {
|
|
if (Test-Path $path -PathType Leaf) {
|
|
$value = (Get-Content -Path $path -Raw -ErrorAction Stop)
|
|
if ($value) { return $value.Trim() }
|
|
}
|
|
} catch {}
|
|
}
|
|
|
|
return ''
|
|
}
|
|
|
|
function Get-RepositoryCommitHash {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$ProjectRoot,
|
|
|
|
[string]$AgentRoot,
|
|
|
|
[string]$GitExe
|
|
)
|
|
|
|
$candidates = @()
|
|
if ($ProjectRoot -and ($candidates -notcontains $ProjectRoot)) { $candidates += $ProjectRoot }
|
|
if ($AgentRoot -and ($candidates -notcontains $AgentRoot)) { $candidates += $AgentRoot }
|
|
if ($ProjectRoot) {
|
|
$agentRootCandidate = Join-Path $ProjectRoot 'Agent\Borealis'
|
|
if ((Test-Path $agentRootCandidate -PathType Container) -and ($candidates -notcontains $agentRootCandidate)) {
|
|
$candidates += $agentRootCandidate
|
|
}
|
|
}
|
|
|
|
if ($GitExe -and (Test-Path $GitExe -PathType Leaf)) {
|
|
foreach ($root in $candidates) {
|
|
try {
|
|
if (-not (Test-Path (Join-Path $root '.git') -PathType Container)) { continue }
|
|
$revParse = Invoke-GitCommand -GitExe $GitExe -WorkingDirectory $root -Arguments @('rev-parse','HEAD')
|
|
if ($revParse) {
|
|
$candidate = ($revParse | Select-Object -Last 1)
|
|
if ($candidate) { return ($candidate.Trim()) }
|
|
}
|
|
} catch {}
|
|
}
|
|
}
|
|
|
|
foreach ($root in $candidates) {
|
|
try {
|
|
$gitDir = Join-Path $root '.git'
|
|
$fetchHead = Join-Path $gitDir 'FETCH_HEAD'
|
|
if (-not (Test-Path $fetchHead -PathType Leaf)) { continue }
|
|
foreach ($line in Get-Content -Path $fetchHead -ErrorAction Stop) {
|
|
$trim = ($line).Trim()
|
|
if (-not $trim -or $trim.StartsWith('#')) { continue }
|
|
$split = $trim.Split(@("`t", ' '), [StringSplitOptions]::RemoveEmptyEntries)
|
|
if ($split.Count -gt 0) {
|
|
$candidate = $split[0].Trim()
|
|
if ($candidate) { return $candidate }
|
|
}
|
|
}
|
|
} catch {}
|
|
}
|
|
|
|
foreach ($root in $candidates) {
|
|
try {
|
|
$gitDir = Join-Path $root '.git'
|
|
$headPath = Join-Path $gitDir 'HEAD'
|
|
if (-not (Test-Path $headPath -PathType Leaf)) { continue }
|
|
$head = (Get-Content -Path $headPath -Raw -ErrorAction Stop).Trim()
|
|
if (-not $head) { continue }
|
|
|
|
if ($head -match '^ref:\s*(.+)$') {
|
|
$ref = $Matches[1].Trim()
|
|
if ($ref) {
|
|
$refPath = $gitDir
|
|
foreach ($part in ($ref -split '/')) {
|
|
if ($part) { $refPath = Join-Path $refPath $part }
|
|
}
|
|
if (Test-Path $refPath -PathType Leaf) {
|
|
$commit = (Get-Content -Path $refPath -Raw -ErrorAction Stop).Trim()
|
|
if ($commit) { return $commit }
|
|
}
|
|
$packedRefs = Join-Path $gitDir 'packed-refs'
|
|
if (Test-Path $packedRefs -PathType Leaf) {
|
|
foreach ($line in Get-Content -Path $packedRefs -ErrorAction Stop) {
|
|
$trim = ($line).Trim()
|
|
if (-not $trim -or $trim.StartsWith('#') -or $trim.StartsWith('^')) { continue }
|
|
$parts = $trim.Split(' ', 2)
|
|
if ($parts.Count -ge 2 -and $parts[1].Trim() -eq $ref) {
|
|
$candidate = $parts[0].Trim()
|
|
if ($candidate) { return $candidate }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
$detached = $head.Split([Environment]::NewLine, [StringSplitOptions]::RemoveEmptyEntries)
|
|
if ($detached.Length -gt 0) {
|
|
$candidate = $detached[0].Trim()
|
|
if ($candidate) { return $candidate }
|
|
}
|
|
}
|
|
} catch {}
|
|
}
|
|
|
|
if ($AgentRoot) {
|
|
$stored = Get-StoredAgentHash -AgentRoot $AgentRoot
|
|
if ($stored) { return $stored }
|
|
}
|
|
|
|
return ''
|
|
}
|
|
|
|
function Get-StoredAgentHash {
|
|
param(
|
|
[string]$AgentRoot
|
|
)
|
|
|
|
if (-not $AgentRoot) { return '' }
|
|
|
|
try {
|
|
$settingsDir = Join-Path $AgentRoot 'Settings'
|
|
$hashFile = Join-Path $settingsDir 'agent_hash.txt'
|
|
if (Test-Path $hashFile -PathType Leaf) {
|
|
$value = (Get-Content -Path $hashFile -Raw -ErrorAction Stop).Trim()
|
|
return $value
|
|
}
|
|
} catch {}
|
|
|
|
return ''
|
|
}
|
|
|
|
function Set-StoredAgentHash {
|
|
param(
|
|
[string]$AgentRoot,
|
|
[string]$AgentHash
|
|
)
|
|
|
|
if ([string]::IsNullOrWhiteSpace($AgentRoot) -or [string]::IsNullOrWhiteSpace($AgentHash)) { return }
|
|
|
|
try {
|
|
$settingsDir = Join-Path $AgentRoot 'Settings'
|
|
if (-not (Test-Path $settingsDir -PathType Container)) {
|
|
New-Item -ItemType Directory -Force -Path $settingsDir | Out-Null
|
|
}
|
|
$hashFile = Join-Path $settingsDir 'agent_hash.txt'
|
|
Set-Content -Path $hashFile -Value $AgentHash.Trim() -Encoding UTF8
|
|
} catch {}
|
|
}
|
|
|
|
function Set-GitFetchHeadHash {
|
|
param(
|
|
[string]$ProjectRoot,
|
|
[string]$CommitHash,
|
|
[string]$BranchName = 'main'
|
|
)
|
|
|
|
if ([string]::IsNullOrWhiteSpace($ProjectRoot) -or [string]::IsNullOrWhiteSpace($CommitHash)) { return }
|
|
|
|
try {
|
|
$gitDir = Join-Path $ProjectRoot '.git'
|
|
if (-not (Test-Path $gitDir -PathType Container)) {
|
|
New-Item -ItemType Directory -Force -Path $gitDir | Out-Null
|
|
}
|
|
$fetchHead = Join-Path $gitDir 'FETCH_HEAD'
|
|
$branchSegment = if ([string]::IsNullOrWhiteSpace($BranchName)) { '' } else { "`tbranch '$BranchName'" }
|
|
$content = "{0}{1}" -f ($CommitHash.Trim()), $branchSegment
|
|
Set-Content -Path $fetchHead -Value $content -Encoding UTF8
|
|
} catch {}
|
|
}
|
|
|
|
function Get-ServerCurrentRepoHash {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$ServerBaseUrl
|
|
)
|
|
|
|
if ([string]::IsNullOrWhiteSpace($ServerBaseUrl)) { return $null }
|
|
|
|
$base = $ServerBaseUrl.TrimEnd('/')
|
|
$uri = "$base/api/repo/current_hash"
|
|
$headers = @{ 'User-Agent' = 'borealis-agent-updater' }
|
|
|
|
try {
|
|
$resp = Invoke-WebRequest -Uri $uri -Method Get -Headers $headers -UseBasicParsing -ErrorAction Stop
|
|
$json = $resp.Content | ConvertFrom-Json
|
|
return $json
|
|
} catch {
|
|
return $null
|
|
}
|
|
}
|
|
|
|
function Submit-AgentHash {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$ServerBaseUrl,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$AgentId,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$AgentHash,
|
|
|
|
[string]$AgentGuid
|
|
)
|
|
|
|
if ([string]::IsNullOrWhiteSpace($ServerBaseUrl) -or [string]::IsNullOrWhiteSpace($AgentHash)) {
|
|
return
|
|
}
|
|
|
|
$base = $ServerBaseUrl.TrimEnd('/')
|
|
$uri = "$base/api/agent/hash"
|
|
$payloadBody = @{ agent_hash = $AgentHash }
|
|
if (-not [string]::IsNullOrWhiteSpace($AgentId)) { $payloadBody.agent_id = $AgentId }
|
|
if (-not [string]::IsNullOrWhiteSpace($AgentGuid)) { $payloadBody.agent_guid = $AgentGuid }
|
|
$payload = $payloadBody | ConvertTo-Json -Depth 3
|
|
$headers = @{ 'User-Agent' = 'borealis-agent-updater' }
|
|
|
|
$resp = Invoke-WebRequest -Uri $uri -Method Post -Headers $headers -Body $payload -ContentType 'application/json' -UseBasicParsing -ErrorAction Stop
|
|
try {
|
|
$json = $resp.Content | ConvertFrom-Json
|
|
return $json
|
|
} catch {
|
|
return $null
|
|
}
|
|
}
|
|
|
|
function Sync-AgentHashRecord {
|
|
param(
|
|
[string]$ProjectRoot,
|
|
[string]$AgentRoot,
|
|
[string]$AgentHash,
|
|
[string]$ServerBaseUrl,
|
|
[string]$AgentId,
|
|
[string]$AgentGuid,
|
|
[string]$BranchName = 'main'
|
|
)
|
|
|
|
if ([string]::IsNullOrWhiteSpace($AgentHash)) { return }
|
|
|
|
if ($ProjectRoot) {
|
|
Set-GitFetchHeadHash -ProjectRoot $ProjectRoot -CommitHash $AgentHash -BranchName $BranchName
|
|
}
|
|
if ($AgentRoot) {
|
|
Set-StoredAgentHash -AgentRoot $AgentRoot -AgentHash $AgentHash
|
|
}
|
|
|
|
if ([string]::IsNullOrWhiteSpace($ServerBaseUrl)) { return }
|
|
|
|
Write-Host ("Submitting agent hash to server: {0}" -f $AgentHash)
|
|
|
|
if ([string]::IsNullOrWhiteSpace($AgentId) -and [string]::IsNullOrWhiteSpace($AgentGuid)) {
|
|
Write-Host "Agent identifier unavailable; skipping agent hash submission." -ForegroundColor DarkYellow
|
|
return
|
|
}
|
|
|
|
try {
|
|
$submitResult = Submit-AgentHash -ServerBaseUrl $ServerBaseUrl -AgentId $AgentId -AgentHash $AgentHash -AgentGuid $AgentGuid
|
|
if ($submitResult -and ($submitResult.status -eq 'ok')) {
|
|
Write-Host "Server agent_hash database record updated successfully."
|
|
} elseif ($submitResult -and ($submitResult.status -eq 'ignored')) {
|
|
Write-Host "Server ignored agent_hash update (agent not registered)." -ForegroundColor DarkYellow
|
|
} elseif ($submitResult) {
|
|
Write-Host "Server agent_hash update response unrecognized." -ForegroundColor DarkYellow
|
|
}
|
|
} catch {
|
|
Write-Verbose ("Failed to submit agent hash: {0}" -f $_.Exception.Message)
|
|
}
|
|
}
|
|
|
|
function Invoke-BorealisUpdate {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$GitExe,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$RepositoryUrl,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$TargetHash,
|
|
|
|
[string]$BranchName = 'main',
|
|
|
|
[switch]$Silent
|
|
)
|
|
|
|
if ([string]::IsNullOrWhiteSpace($TargetHash)) {
|
|
throw 'Target commit hash is required for Borealis update.'
|
|
}
|
|
|
|
$preservePath = Join-Path $scriptDir "Data\Server\Python_API_Endpoints\Tesseract-OCR"
|
|
$preserveBackupPath = Join-Path $scriptDir "Update_Staging\Tesseract-OCR"
|
|
|
|
Run-Step "Updating: Move Tesseract-OCR Folder Somewhere Safe to Restore Later" {
|
|
if (Test-Path $preservePath) {
|
|
$stagingPath = Join-Path $scriptDir "Update_Staging"
|
|
if (-not (Test-Path $stagingPath)) { New-Item -ItemType Directory -Force -Path $stagingPath | Out-Null }
|
|
Move-Item -Path $preservePath -Destination $preserveBackupPath -Force
|
|
}
|
|
}
|
|
|
|
Run-Step "Updating: Clean Up Folders to Prepare for Update" {
|
|
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue `
|
|
(Join-Path $scriptDir "Data"), `
|
|
(Join-Path $scriptDir "Server\web-interface\src"), `
|
|
(Join-Path $scriptDir "Server\web-interface\build"), `
|
|
(Join-Path $scriptDir "Server\web-interface\public"), `
|
|
(Join-Path $scriptDir "Server\Borealis"), `
|
|
(Join-Path $scriptDir '.git')
|
|
}
|
|
|
|
$stagingPath = Join-Path $scriptDir "Update_Staging"
|
|
$cloneDir = Join-Path $stagingPath 'repo'
|
|
|
|
Run-Step "Updating: Create Update Staging Folder" {
|
|
if (-not (Test-Path $stagingPath)) { New-Item -ItemType Directory -Force -Path $stagingPath | Out-Null }
|
|
if (Test-Path $cloneDir) {
|
|
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue $cloneDir
|
|
}
|
|
}
|
|
|
|
Run-Step "Updating: Clone Repository Source" {
|
|
$cloneArgs = @('clone','--no-tags','--depth','1')
|
|
if (-not [string]::IsNullOrWhiteSpace($BranchName)) {
|
|
$cloneArgs += @('--branch', $BranchName)
|
|
}
|
|
$cloneArgs += @($RepositoryUrl, $cloneDir)
|
|
Invoke-GitCommand -GitExe $GitExe -WorkingDirectory $stagingPath -Arguments $cloneArgs | Out-Null
|
|
}
|
|
|
|
Run-Step "Updating: Checkout Target Revision" {
|
|
$normalizedHash = $TargetHash.Trim()
|
|
$haveHash = $false
|
|
try {
|
|
Invoke-GitCommand -GitExe $GitExe -WorkingDirectory $cloneDir -Arguments @('rev-parse', $normalizedHash) | Out-Null
|
|
$haveHash = $true
|
|
} catch {
|
|
$haveHash = $false
|
|
}
|
|
|
|
if (-not $haveHash) {
|
|
Invoke-GitCommand -GitExe $GitExe -WorkingDirectory $cloneDir -Arguments @('fetch','origin',$normalizedHash) | Out-Null
|
|
}
|
|
|
|
if ([string]::IsNullOrWhiteSpace($BranchName)) {
|
|
Invoke-GitCommand -GitExe $GitExe -WorkingDirectory $cloneDir -Arguments @('checkout', $normalizedHash) | Out-Null
|
|
} else {
|
|
Invoke-GitCommand -GitExe $GitExe -WorkingDirectory $cloneDir -Arguments @('checkout','-B',$BranchName,$normalizedHash) | Out-Null
|
|
}
|
|
}
|
|
|
|
Run-Step "Updating: Copy Update Files into Production Borealis Root Folder" {
|
|
Get-ChildItem -Path $cloneDir -Force | ForEach-Object {
|
|
$destination = Join-Path $scriptDir $_.Name
|
|
if ($_.PSIsContainer) {
|
|
Copy-Item -Path $_.FullName -Destination $destination -Recurse -Force
|
|
} else {
|
|
Copy-Item -Path $_.FullName -Destination $scriptDir -Force
|
|
}
|
|
}
|
|
}
|
|
|
|
Run-Step "Updating: Restore Tesseract-OCR Folder" {
|
|
$restorePath = Join-Path $scriptDir "Data\Server\Python_API_Endpoints"
|
|
if (Test-Path $preserveBackupPath) {
|
|
if (-not (Test-Path $restorePath)) { New-Item -ItemType Directory -Force -Path $restorePath | Out-Null }
|
|
Move-Item -Path $preserveBackupPath -Destination $restorePath -Force
|
|
}
|
|
}
|
|
|
|
Run-Step "Updating: Clean Up Update Staging Folder" {
|
|
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue $stagingPath
|
|
}
|
|
|
|
if (-not $Silent) {
|
|
Write-Host "Unattended Borealis update completed." -ForegroundColor Green
|
|
}
|
|
}
|
|
|
|
function Invoke-BorealisAgentUpdate {
|
|
Write-Host "==============================================="
|
|
Write-Host " Borealis - Automation Platform Updater Script "
|
|
Write-Host "==============================================="
|
|
|
|
$agentRootCandidate = Join-Path $scriptDir 'Agent\Borealis'
|
|
$agentRoot = $scriptDir
|
|
if (Test-Path $agentRootCandidate -PathType Container) {
|
|
try {
|
|
$agentRoot = (Resolve-Path -Path $agentRootCandidate -ErrorAction Stop).Path
|
|
} catch {
|
|
$agentRoot = $agentRootCandidate
|
|
}
|
|
}
|
|
|
|
$agentGuid = Get-AgentGuid -AgentRoot $agentRoot
|
|
if ($agentGuid) {
|
|
Write-Host ("Agent GUID: {0}" -f $agentGuid)
|
|
} else {
|
|
Write-Host "Warning: No agent GUID detected - Please deploy the agent, associating it with a Borealis server then try running the updater script again." -ForegroundColor Yellow
|
|
Write-Host "⚠️ Borealis update aborted."
|
|
return
|
|
}
|
|
|
|
$gitExe = Get-GitExecutablePath -ProjectRoot $scriptDir
|
|
$currentHash = Get-RepositoryCommitHash -ProjectRoot $scriptDir -AgentRoot $agentRoot -GitExe $gitExe
|
|
$serverBaseUrl = Get-BorealisServerUrl -AgentRoot $agentRoot
|
|
$agentId = Get-AgentServiceId -AgentRoot $agentRoot
|
|
|
|
$serverRepoInfo = Get-ServerCurrentRepoHash -ServerBaseUrl $serverBaseUrl
|
|
$serverHash = ''
|
|
$serverBranch = 'main'
|
|
if ($serverRepoInfo) {
|
|
try { $serverHash = (($serverRepoInfo.sha) -as [string]).Trim() } catch { $serverHash = '' }
|
|
try {
|
|
$branchCandidate = (($serverRepoInfo.branch) -as [string]).Trim()
|
|
if ($branchCandidate) { $serverBranch = $branchCandidate }
|
|
} catch { $serverBranch = 'main' }
|
|
}
|
|
|
|
$updateMode = $env:update_mode
|
|
if ($updateMode) { $updateMode = $updateMode.ToLowerInvariant() } else { $updateMode = 'update' }
|
|
$forceUpdate = $updateMode -eq 'force_update'
|
|
|
|
if ($currentHash) {
|
|
Write-Host ("Local Agent Hash: {0}" -f $currentHash)
|
|
} else {
|
|
Write-Host "Local Agent Hash: unavailable"
|
|
}
|
|
|
|
if ($serverHash) {
|
|
Write-Host ("Borealis Server Hash: {0}" -f $serverHash)
|
|
} else {
|
|
Write-Host "Borealis Server Hash: unavailable"
|
|
}
|
|
|
|
$normalizedLocalHash = if ($currentHash) { $currentHash.Trim().ToLowerInvariant() } else { '' }
|
|
$normalizedServerHash = if ($serverHash) { $serverHash.Trim().ToLowerInvariant() } else { '' }
|
|
$hashesMatch = ($normalizedLocalHash -and $normalizedServerHash -and ($normalizedLocalHash -eq $normalizedServerHash))
|
|
$needsUpdate = $forceUpdate -or (-not $hashesMatch)
|
|
|
|
if ($forceUpdate) {
|
|
Write-Host "Force update requested; skipping hash comparison." -ForegroundColor Yellow
|
|
} elseif (-not $serverHash) {
|
|
Write-Host "Borealis server hash unavailable; cannot continue." -ForegroundColor Yellow
|
|
Write-Host "⚠️ Borealis update aborted."
|
|
return
|
|
} elseif (-not $needsUpdate) {
|
|
Write-Host "Local agent files already match the server repository hash." -ForegroundColor Green
|
|
Sync-AgentHashRecord -ProjectRoot $scriptDir -AgentRoot $agentRoot -AgentHash $serverHash -ServerBaseUrl $serverBaseUrl -AgentId $agentId -AgentGuid $agentGuid -BranchName $serverBranch
|
|
Write-Host "✅ Borealis - Automation Platform Already Up-to-Date"
|
|
return
|
|
} else {
|
|
Write-Host "Repository hash mismatch detected; update required."
|
|
}
|
|
|
|
if (-not ($gitExe) -or -not (Test-Path $gitExe -PathType Leaf)) {
|
|
Write-Host "Bundled Git dependency not found. Run '.\\Borealis.ps1 -Agent -AgentAction repair' to bootstrap dependencies and try again." -ForegroundColor Yellow
|
|
Write-Host "⚠️ Borealis update aborted."
|
|
return
|
|
}
|
|
|
|
$mutex = $null
|
|
$gotMutex = $false
|
|
$managedTasks = @()
|
|
try {
|
|
$mutex = New-Object System.Threading.Mutex($false, 'Global\BorealisUpdate')
|
|
$gotMutex = $mutex.WaitOne(0)
|
|
if (-not $gotMutex) {
|
|
Write-Verbose 'Another update is already running (mutex held). Exiting quietly.'
|
|
Write-Host "⚠️ Borealis update already in progress on this device."
|
|
return
|
|
}
|
|
|
|
$staging = Join-Path $scriptDir 'Update_Staging'
|
|
|
|
$managedTasks = Stop-AgentScheduledTasks -TaskNames @('Borealis Agent','Borealis Agent (UserHelper)')
|
|
|
|
$updateSucceeded = $false
|
|
try {
|
|
Invoke-BorealisUpdate -GitExe $gitExe -RepositoryUrl $repositoryUrl -TargetHash $serverHash -BranchName $serverBranch -Silent
|
|
$updateSucceeded = $true
|
|
} finally {
|
|
if ($managedTasks.Count -gt 0) {
|
|
Start-AgentScheduledTasks -TaskNames $managedTasks
|
|
}
|
|
}
|
|
|
|
if (-not $updateSucceeded) {
|
|
throw 'Borealis update failed.'
|
|
}
|
|
|
|
$postUpdateInfo = Get-ServerCurrentRepoHash -ServerBaseUrl $serverBaseUrl
|
|
if ($postUpdateInfo) {
|
|
try {
|
|
$refreshedSha = (($postUpdateInfo.sha) -as [string]).Trim()
|
|
if ($refreshedSha) { $serverHash = $refreshedSha }
|
|
} catch {}
|
|
try {
|
|
$branchCandidate = (($postUpdateInfo.branch) -as [string]).Trim()
|
|
if ($branchCandidate) { $serverBranch = $branchCandidate }
|
|
} catch {}
|
|
}
|
|
|
|
$newHash = Get-RepositoryCommitHash -ProjectRoot $scriptDir -AgentRoot $agentRoot -GitExe $gitExe
|
|
|
|
$normalizedNewHash = if ($newHash) { $newHash.Trim().ToLowerInvariant() } else { '' }
|
|
$normalizedServerHash = if ($serverHash) { $serverHash.Trim().ToLowerInvariant() } else { '' }
|
|
|
|
if ($normalizedServerHash -and (-not $normalizedNewHash -or $normalizedNewHash -ne $normalizedServerHash)) {
|
|
$newHash = $serverHash
|
|
$normalizedNewHash = $normalizedServerHash
|
|
} elseif (-not $newHash -and $serverHash) {
|
|
$newHash = $serverHash
|
|
}
|
|
|
|
if ($newHash) {
|
|
Sync-AgentHashRecord -ProjectRoot $scriptDir -AgentRoot $agentRoot -AgentHash $newHash -ServerBaseUrl $serverBaseUrl -AgentId $agentId -AgentGuid $agentGuid -BranchName $serverBranch
|
|
} else {
|
|
Write-Host "Unable to determine repository hash for submission; server hash not updated." -ForegroundColor DarkYellow
|
|
}
|
|
|
|
Write-Host "✅ Borealis - Automation Platform Successfully Updated"
|
|
} finally {
|
|
if ($mutex -and $gotMutex) { $mutex.ReleaseMutex() | Out-Null }
|
|
if ($mutex) { $mutex.Dispose() }
|
|
}
|
|
}
|
|
|
|
Invoke-BorealisAgentUpdate
|