Release ForgeFlow 0.6.0

This commit is contained in:
NuklearRabbit
2026-07-25 05:59:07 +02:00
parent cf1f67a823
commit 9d3933c878
48 changed files with 2208 additions and 466 deletions
+116 -13
View File
@@ -4,11 +4,15 @@ param(
[Parameter(Mandatory=$true)][string]$ExpectedVersion,
[Parameter(Mandatory=$true)][string]$ExpectedSha256,
[Parameter(Mandatory=$true)][int]$ParentPid,
[Parameter(Mandatory=$true)][string]$LogPath
[Parameter(Mandatory=$true)][string]$LogPath,
[Parameter(Mandatory=$true)][string]$StatusPath,
[Parameter(Mandatory=$true)][string]$UpdateId
)
$ErrorActionPreference = "Stop"
$ProgressPreference = "SilentlyContinue"
$working = $null
$backup = $null
function Write-UpdateLog {
param([string]$Message)
@@ -17,6 +21,33 @@ function Write-UpdateLog {
Add-Content -Path $LogPath -Value $line -Encoding UTF8
}
function Write-UpdateState {
param(
[Parameter(Mandatory=$true)][string]$State,
[string]$Message = "",
[hashtable]$Extra = @{}
)
$payload = [ordered]@{
schemaVersion = 1
updateId = $UpdateId
state = $State
expectedVersion = $ExpectedVersion
sourcePath = $SourcePath
logPath = $LogPath
statusPath = $StatusPath
message = $Message
updatedAt = (Get-Date).ToUniversalTime().ToString("o")
}
foreach ($key in $Extra.Keys) { $payload[$key] = $Extra[$key] }
$directory = Split-Path -Parent $StatusPath
New-Item -ItemType Directory -Force -Path $directory | Out-Null
$temporary = "$StatusPath.$PID.tmp"
$json = $payload | ConvertTo-Json -Depth 8
$utf8NoBom = New-Object System.Text.UTF8Encoding($false)
[System.IO.File]::WriteAllText($temporary, $json, $utf8NoBom)
Move-Item -LiteralPath $temporary -Destination $StatusPath -Force
}
function Invoke-Robocopy {
param([string]$From, [string]$To)
New-Item -ItemType Directory -Force -Path $To | Out-Null
@@ -24,8 +55,37 @@ function Invoke-Robocopy {
if ($LASTEXITCODE -gt 7) { throw "robocopy failed with exit code $LASTEXITCODE" }
}
function Install-ForgeFlowDependencies {
param([string]$WorkingDirectory)
Push-Location $WorkingDirectory
try {
if (Test-Path -LiteralPath (Join-Path $WorkingDirectory "package-lock.json")) {
Write-UpdateLog "Installing dependencies from package-lock.json with npm ci."
& cmd.exe /d /s /c "npm ci --no-audit --no-fund" *>> $LogPath
if ($LASTEXITCODE -ne 0) { throw "npm ci failed with exit code $LASTEXITCODE." }
} else {
Write-UpdateLog "No package-lock.json was supplied; installing pinned direct dependencies with npm install."
& cmd.exe /d /s /c "npm install --no-audit --no-fund" *>> $LogPath
if ($LASTEXITCODE -ne 0) { throw "npm install failed with exit code $LASTEXITCODE." }
}
} finally { Pop-Location }
}
function Start-ForgeFlow {
param([string]$WorkingDirectory)
$electron = Join-Path $WorkingDirectory "node_modules\electron\dist\electron.exe"
if (-not (Test-Path -LiteralPath $electron)) { throw "electron.exe was not found after dependency installation." }
$process = Start-Process -FilePath $electron -WorkingDirectory $WorkingDirectory -ArgumentList @(".") -PassThru
Start-Sleep -Milliseconds 1200
if (-not $process -or $process.HasExited) { throw "ForgeFlow restart process exited before the application window could start." }
return $process
}
try {
Write-UpdateLog "ForgeFlow source update started for version $ExpectedVersion."
Write-UpdateLog "ForgeFlow source update helper started for version $ExpectedVersion."
Write-UpdateState -State "started" -Message "The external update helper started successfully." -Extra @{ helperPid = $PID; startedAt = (Get-Date).ToUniversalTime().ToString("o") }
Write-UpdateState -State "waiting-for-exit" -Message "Waiting for the running ForgeFlow process to exit."
$deadline = (Get-Date).AddMinutes(2)
while (Get-Process -Id $ParentPid -ErrorAction SilentlyContinue) {
if ((Get-Date) -gt $deadline) { throw "ForgeFlow did not exit before the update timeout." }
@@ -39,10 +99,13 @@ try {
$extract = Join-Path $working "extract"
$backup = Join-Path $working "backup"
New-Item -ItemType Directory -Force -Path $extract | Out-Null
Write-UpdateLog "Creating source backup."
Write-UpdateState -State "backing-up" -Message "Creating a restorable backup of the current source."
Invoke-Robocopy -From $SourcePath -To $backup
Write-UpdateLog "Extracting update archive."
Write-UpdateState -State "extracting" -Message "Extracting the verified update archive."
Expand-Archive -LiteralPath $ArchivePath -DestinationPath $extract -Force
$manifest = Get-ChildItem -Path $extract -Filter package.json -File -Recurse |
Where-Object {
@@ -56,37 +119,77 @@ try {
if (-not $manifest) { throw "The update does not contain ForgeFlow version $ExpectedVersion." }
$incoming = Split-Path -Parent $manifest.FullName
Write-UpdateLog "Applying verified source files."
Write-UpdateState -State "applying" -Message "Replacing the local source with ForgeFlow $ExpectedVersion."
Invoke-Robocopy -From $incoming -To $SourcePath
Write-UpdateState -State "validating" -Message "Installing dependencies and running the complete quality gate."
Install-ForgeFlowDependencies -WorkingDirectory $SourcePath
Push-Location $SourcePath
try {
Write-UpdateLog "Installing exact dependencies."
& cmd.exe /d /s /c "npm install --no-audit --no-fund" *>> $LogPath
if ($LASTEXITCODE -ne 0) { throw "npm install failed with exit code $LASTEXITCODE." }
Write-UpdateLog "Running ForgeFlow quality gate."
& cmd.exe /d /s /c "npm run check" *>> $LogPath
if ($LASTEXITCODE -ne 0) { throw "npm run check failed with exit code $LASTEXITCODE." }
} finally { Pop-Location }
Write-UpdateLog "Update validated successfully. Restarting ForgeFlow."
Start-Process -FilePath "cmd.exe" -WorkingDirectory $SourcePath -ArgumentList "/d", "/s", "/c", "npm start"
Remove-Item -LiteralPath $working -Recurse -Force -ErrorAction SilentlyContinue
$completedAt = (Get-Date).ToUniversalTime().ToString("o")
Write-UpdateState -State "success" -Message "ForgeFlow $ExpectedVersion was installed successfully." -Extra @{
installedVersion = $ExpectedVersion
completedAt = $completedAt
restartLaunched = $true
restartPid = $null
restartError = $null
}
try {
$restart = Start-ForgeFlow -WorkingDirectory $SourcePath
Write-UpdateLog "Update validated successfully. ForgeFlow was restarted directly with Electron PID $($restart.Id)."
} catch {
$restartError = $_.Exception.Message
Write-UpdateLog "Update validated successfully, but automatic restart failed: $restartError"
Write-UpdateState -State "success" -Message "ForgeFlow $ExpectedVersion was installed successfully, but must be started manually." -Extra @{
installedVersion = $ExpectedVersion
completedAt = $completedAt
restartLaunched = $false
restartPid = $null
restartError = $restartError
}
}
if ($working) { Remove-Item -LiteralPath $working -Recurse -Force -ErrorAction SilentlyContinue }
exit 0
}
catch {
Write-UpdateLog ("Update failed: " + $_.Exception.Message)
$failureMessage = $_.Exception.Message
Write-UpdateLog ("Update failed: " + $failureMessage)
Write-UpdateState -State "failed" -Message $failureMessage -Extra @{ failedAt = (Get-Date).ToUniversalTime().ToString("o") }
try {
if ($backup -and (Test-Path $backup)) {
Write-UpdateLog "Restoring previous source version."
Invoke-Robocopy -From $backup -To $SourcePath
Push-Location $SourcePath
Install-ForgeFlowDependencies -WorkingDirectory $SourcePath
$rollbackCompletedAt = (Get-Date).ToUniversalTime().ToString("o")
Write-UpdateState -State "rolled-back" -Message $failureMessage -Extra @{
completedAt = $rollbackCompletedAt
restartLaunched = $true
restartPid = $null
restartError = $null
}
try {
& cmd.exe /d /s /c "npm install --no-audit --no-fund" *>> $LogPath
} finally { Pop-Location }
Start-Process -FilePath "cmd.exe" -WorkingDirectory $SourcePath -ArgumentList "/d", "/s", "/c", "npm start"
$rollbackRestart = Start-ForgeFlow -WorkingDirectory $SourcePath
Write-UpdateLog "Rollback restored and ForgeFlow restarted directly with Electron PID $($rollbackRestart.Id)."
} catch {
$rollbackRestartError = $_.Exception.Message
Write-UpdateLog ("Rollback restart failed: " + $rollbackRestartError)
Write-UpdateState -State "rolled-back" -Message $failureMessage -Extra @{
completedAt = $rollbackCompletedAt
restartLaunched = $false
restartPid = $null
restartError = $rollbackRestartError
}
}
}
} catch {
Write-UpdateLog ("Rollback failed: " + $_.Exception.Message)
Write-UpdateState -State "failed" -Message ("$failureMessage Rollback also failed: " + $_.Exception.Message) -Extra @{ completedAt = (Get-Date).ToUniversalTime().ToString("o") }
}
exit 1
}