[CmdletBinding()] param( [Parameter(Mandatory=$true)][string]$CoreScript, [Parameter(Mandatory=$true)][string]$StatusFile, [Parameter(Mandatory=$true)][string]$EventFile, [Parameter(Mandatory=$true)][string]$InputFile, [Parameter(Mandatory=$true)][string]$CancelFile, [Parameter(Mandatory=$true)][string]$SessionFile, [Parameter(Mandatory=$true)][string]$LogFile, [switch]$SkipProductionDeploy, [switch]$SelfTest, [string]$SelfTestFailAt = '' ) Set-StrictMode -Version 2.0 $ErrorActionPreference = 'Stop' $TargetVersion = '1.43.0' $UpdaterVersion = '3.4.0' function Get-DefaultSteps { return @( [ordered]@{id='package';title='Paket prüfen';state='pending'}, [ordered]@{id='tools';title='System prüfen';state='pending'}, [ordered]@{id='github';title='GitHub verbinden';state='pending'}, [ordered]@{id='production';title='Zielzustand erkennen';state='pending'}, [ordered]@{id='workspace';title='Payload sicher vorbereiten';state='pending'}, [ordered]@{id='tests';title='Vollständig testen';state='pending'}, [ordered]@{id='pullrequest';title='Pull Request abgleichen';state='pending'}, [ordered]@{id='merge';title='Finalen Head prüfen und mergen';state='pending'}, [ordered]@{id='deploy';title='Coolify deployen';state='pending'}, [ordered]@{id='verify';title='Produktion bestätigen';state='pending'} ) } function Append-HostLog([string]$Level,[string]$Message) { try { $parent = Split-Path -Parent $LogFile if ($parent) { New-Item -ItemType Directory -Path $parent -Force | Out-Null } $line = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss.fff') [$Level] $Message$([Environment]::NewLine)" [IO.File]::AppendAllText($LogFile,$line,(New-Object Text.UTF8Encoding($false))) } catch {} } function Write-HostStatus([string]$State,[string]$Phase,[string]$Detail,[string]$ErrorText='',[string]$Remediation='') { try { $percent = 1 if ($State -in @('failed','cancelled','success')) { $percent = 100 } $payload = [ordered]@{ version=$TargetVersion updaterVersion=$UpdaterVersion percent=$percent phase=$Phase detail=$Detail state=$State error=$ErrorText remediation=$Remediation updatedAt=(Get-Date).ToString('o') logFile=$LogFile sessionFile=$SessionFile steps=(Get-DefaultSteps) request=$null } | ConvertTo-Json -Depth 8 $parent = Split-Path -Parent $StatusFile if ($parent) { New-Item -ItemType Directory -Path $parent -Force | Out-Null } $tmp = "$StatusFile.$PID.host.tmp" [IO.File]::WriteAllText($tmp,$payload,(New-Object Text.UTF8Encoding($false))) Move-Item -LiteralPath $tmp -Destination $StatusFile -Force } catch { Append-HostLog 'HOST-ERROR' "Status konnte nicht geschrieben werden: $($_.Exception.ToString())" } } try { foreach($path in @($StatusFile,$EventFile,$InputFile,$CancelFile,$SessionFile,$LogFile)) { $parent = Split-Path -Parent $path if ($parent) { New-Item -ItemType Directory -Path $parent -Force | Out-Null } } if (-not (Test-Path -LiteralPath $CoreScript -PathType Leaf)) { throw "Worker-Core fehlt: $CoreScript" } Append-HostLog 'HOST' "Worker-Host $UpdaterVersion gestartet. Core: $CoreScript" Write-HostStatus 'running' 'PowerShell-Parserprüfung' 'Der Worker-Kern wird vor der Ausführung mit dem echten PowerShell-Parser geprüft.' $parserTokens = $null $parserErrors = $null [void][System.Management.Automation.Language.Parser]::ParseFile($CoreScript,[ref]$parserTokens,[ref]$parserErrors) if ($parserErrors -and $parserErrors.Count -gt 0) { $details = @($parserErrors | ForEach-Object { "{0}:{1}:{2} {3}" -f $_.Extent.File,$_.Extent.StartLineNumber,$_.Extent.StartColumnNumber,$_.Message }) -join [Environment]::NewLine throw "PowerShell-Parserprüfung des Worker-Kerns fehlgeschlagen.$([Environment]::NewLine)$details" } Append-HostLog 'HOST' 'PowerShell-Parserprüfung des Worker-Kerns erfolgreich.' Write-HostStatus 'running' 'Worker wird gestartet' 'Der geprüfte Worker-Kern wird geladen.' $invokeParams = @{ StatusFile=$StatusFile EventFile=$EventFile InputFile=$InputFile CancelFile=$CancelFile SessionFile=$SessionFile LogFile=$LogFile } if ($SkipProductionDeploy) { $invokeParams.SkipProductionDeploy = $true } if ($SelfTest) { $invokeParams.SelfTest = $true } if ($SelfTestFailAt) { $invokeParams.SelfTestFailAt = $SelfTestFailAt } & $CoreScript @invokeParams $terminal = $false $stateName = '' if (Test-Path -LiteralPath $StatusFile) { try { $final = Get-Content -LiteralPath $StatusFile -Raw -Encoding UTF8 | ConvertFrom-Json $stateName = [string]$final.state $terminal = $stateName -in @('success','failed','cancelled') } catch { Append-HostLog 'HOST-ERROR' "Abschlussstatus ist nicht lesbar: $($_.Exception.Message)" } } if (-not $terminal) { $message = 'Der Update-Kern wurde beendet, ohne einen terminalen Status zu schreiben.' Append-HostLog 'HOST-ERROR' $message Write-HostStatus 'failed' 'Update-Kern unvollständig beendet' $message $message 'Das Protokoll enthält jetzt die vollständige Start- und Worker-Diagnose. Es wurden keine stillen Fehler verworfen.' } else { Append-HostLog 'HOST' "Worker-Core regulär beendet. Status: $stateName" } exit 0 } catch { $full = $_.Exception.ToString() $message = $_.Exception.Message Append-HostLog 'HOST-FATAL' $full Write-HostStatus 'failed' 'Worker konnte nicht initialisiert werden' $message $full 'Der Fehler entstand vor oder beim Laden des Worker-Kerns. Protokoll öffnen zeigt die vollständige PowerShell-Ausnahme einschließlich Datei und Zeile.' try { [Console]::Error.WriteLine($full) } catch {} Start-Sleep -Milliseconds 350 exit 1 }