[CmdletBinding()] param( [string]$HostName = "panopainter-mac", [string]$RemoteDirectory = "~/Dev/panopainter", [string]$RepositoryUrl = "ssh://git@git.omar.synology.me:3022/omar/panopainter.git", [string]$Branch = "codex/modernization-cmake-foundation", [string[]]$Presets = @("macos", "ios-simulator", "ios-device"), [switch]$Quiet, [string]$LogDir = "out/logs/apple-remote-build", [int]$FailureTailLines = 0 ) $ErrorActionPreference = "Stop" function Join-RemoteArgument { param([string[]]$Values) $expanded = @() foreach ($value in $Values) { foreach ($part in ($value -split ",")) { $trimmed = $part.Trim() if ($trimmed.Length -gt 0) { $expanded += $trimmed } } } return ($expanded -join " ") } function ConvertTo-ShellSingleQuoted { param([string]$Value) return "'" + ($Value -replace "'", "'\\''") + "'" } $presetArgument = Join-RemoteArgument -Values $Presets $remoteDirectoryLiteral = ConvertTo-ShellSingleQuoted -Value $RemoteDirectory $repositoryLiteral = ConvertTo-ShellSingleQuoted -Value $RepositoryUrl $branchLiteral = ConvertTo-ShellSingleQuoted -Value $Branch $presetLiteral = ConvertTo-ShellSingleQuoted -Value $presetArgument $quietLiteral = if ($Quiet) { "1" } else { "0" } $remoteScript = @" set -eu export PATH="/opt/homebrew/bin:/usr/local/bin:`$HOME/tools/bin:`$PATH" export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer" remote_dir=$remoteDirectoryLiteral repository_url=$repositoryLiteral branch_name=$branchLiteral presets=$presetLiteral quiet_mode=$quietLiteral case "`$remote_dir" in "~/"*) remote_dir="`$HOME/`$(printf '%s' "`$remote_dir" | sed 's|^~/||')" ;; esac mkdir -p "`$(dirname "`$remote_dir")" if [ ! -d "`$remote_dir/.git" ]; then git clone "`$repository_url" "`$remote_dir" fi cd "`$remote_dir" git fetch origin git checkout "`$branch_name" git pull --ff-only origin "`$branch_name" git submodule update --init --recursive \ libs/tinyxml2 \ libs/glm \ libs/stb/stb \ libs/yoga \ libs/poly2tri \ libs/base64 \ libs/sqlite3 \ libs/nanort \ libs/hash-library \ libs/fmt \ libs/glad \ libs/tinyfiledialogs mkdir -p out/logs log="out/logs/apple-platform-build-`$(date +%Y%m%d-%H%M%S).log" set +e sh ./scripts/automation/platform-build.sh "`$presets" > "`$log" 2>&1 exit_code=`$? set -e printf '{"command":"apple-remote-build","host":"%s","branch":"%s","presets":"%s","log":"%s","exitCode":%s}\n' \ "`$(hostname)" "`$branch_name" "`$presets" "`$log" "`$exit_code" if [ "`$quiet_mode" != "1" ]; then tail -n 80 "`$log" fi exit "`$exit_code" "@ $remoteScript = $remoteScript -replace "`r`n", "`n" $encodedRemoteScript = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($remoteScript)) if (-not $Quiet) { & ssh -o BatchMode=yes $HostName "printf '%s' '$encodedRemoteScript' | base64 -D | sh" exit $LASTEXITCODE } New-Item -ItemType Directory -Force -Path $LogDir | Out-Null $runId = Get-Date -Format "yyyyMMdd-HHmmss" $logPath = Join-Path -Path $LogDir -ChildPath "$runId-apple-remote-build.log" $stderrPath = Join-Path -Path $LogDir -ChildPath "$runId-apple-remote-build.stderr.log" $started = Get-Date $exitCode = 0 try { $process = Start-Process ` -FilePath "ssh" ` -ArgumentList @("-o", "BatchMode=yes", $HostName, "printf '%s' '$encodedRemoteScript' | base64 -D | sh") ` -NoNewWindow ` -Wait ` -PassThru ` -RedirectStandardOutput $logPath ` -RedirectStandardError $stderrPath $exitCode = $process.ExitCode } catch { $_ | Out-File -LiteralPath $LogPath -Append -Encoding utf8 $exitCode = 1 } finally { if (Test-Path -LiteralPath $stderrPath) { Get-Content -LiteralPath $stderrPath | Out-File -LiteralPath $logPath -Append -Encoding utf8 Remove-Item -LiteralPath $stderrPath -Force } } $rawLines = if (Test-Path -LiteralPath $logPath) { @(Get-Content -LiteralPath $logPath) } else { @() } $remoteSummary = $null foreach ($line in $rawLines) { if ($line -match '"command":"apple-remote-build"') { try { $remoteSummary = $line | ConvertFrom-Json } catch { } } } $payload = [ordered]@{ command = "apple-remote-build" exitCode = $exitCode elapsedMs = [int]((Get-Date) - $started).TotalMilliseconds host = $HostName branch = $Branch presets = $Presets log = $logPath } if ($null -ne $remoteSummary) { $payload.remoteHost = $remoteSummary.host $payload.remoteLog = $remoteSummary.log } if ($exitCode -ne 0 -and $FailureTailLines -gt 0 -and $rawLines.Count -gt 0) { $payload.failureTail = @($rawLines | Select-Object -Last $FailureTailLines | ForEach-Object { [string]$_ }) } $payload | ConvertTo-Json -Compress -Depth 6 exit $exitCode