Odin/scripts/deploy-yggdrasil-repixelizer.ps1
Meta Vens 2265184da3 Ship all three Python packages to Repixelizer
CultLib split packages/cultcache-py into cultcache-py, cultnet-py and
cultmesh-py. Module names did not change, so archiving only the first
still succeeds and produces a tarball missing two thirds of the library --
a failure that surfaces later as an ImportError with nothing pointing back
to this line.

The two sibling deploy scripts ship cultnet-ts and cultcache-ts and are
unaffected by a Python split.

Not exercised: repixelizer.service is inactive, so this is a trap defused
rather than a break repaired.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 21:41:42 +02:00

161 lines
6.9 KiB
PowerShell

param(
[string] $SshTarget = "yggdrasil",
[string] $RepoRoot = (Join-Path (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) "repixelizer"),
[string] $CultLibRoot = (Join-Path (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) "CultLib"),
[string] $AppUser = "repixelizer",
[string] $RemoteAppHome = "/srv/repixelizer",
[string] $RemoteTarballName = "repixelizer-source.tar",
[string] $RemoteCultLibTarballName = "repixelizer-cultlib.tar",
[string] $DeployScript = (Join-Path (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) "gamecult-ops\scripts\deploy-repixelizer-gui.sh"),
[string] $CheckScript = (Join-Path (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) "gamecult-ops\scripts\check-repixelizer-gui.sh"),
[string] $UpstreamRemote = "origin",
[string] $UpstreamBranch = "main",
[string] $CultLibUpstreamRemote = "origin",
[string] $CultLibUpstreamBranch = "main"
)
$ErrorActionPreference = "Stop"
if ($env:IDUNN_ACTUATOR -ne "1" -or $env:IDUNN_COMMAND_AUTHORITY -ne "idunn-daemon") {
throw "This deployment script is an Idunn actuator. Agents must configure Idunn release targets and let Idunn run deployment; do not invoke deploy scripts manually."
}
foreach ($path in @($RepoRoot, $CultLibRoot, $DeployScript, $CheckScript)) {
if (-not (Test-Path -LiteralPath $path)) {
throw "Required path not found: $path"
}
}
$sourceRef = "$UpstreamRemote/$UpstreamBranch"
$cultLibSourceRef = "$CultLibUpstreamRemote/$CultLibUpstreamBranch"
git -C $RepoRoot fetch --prune $UpstreamRemote $UpstreamBranch
if ($LASTEXITCODE -ne 0) { throw "git fetch failed for $RepoRoot $sourceRef" }
git -C $CultLibRoot fetch --prune $CultLibUpstreamRemote $CultLibUpstreamBranch
if ($LASTEXITCODE -ne 0) { throw "git fetch failed for $CultLibRoot $cultLibSourceRef" }
$repixelizerCommit = (git -C $RepoRoot rev-parse $sourceRef).Trim()
if ([string]::IsNullOrWhiteSpace($repixelizerCommit)) {
throw "Could not determine Repixelizer git revision."
}
$cultLibCommit = (git -C $CultLibRoot rev-parse $cultLibSourceRef).Trim()
if ([string]::IsNullOrWhiteSpace($cultLibCommit)) {
throw "Could not determine CultLib git revision."
}
$scratch = Join-Path (Join-Path (Split-Path -Parent $PSScriptRoot) "scratch\idunn-deploy") "yggdrasil-repixelizer"
$sourceTarPath = Join-Path $scratch $RemoteTarballName
$cultLibTarPath = Join-Path $scratch $RemoteCultLibTarballName
$manifestPath = Join-Path $scratch "deployment-manifest.txt"
New-Item -ItemType Directory -Force -Path $scratch | Out-Null
git -C $RepoRoot archive --format=tar --output=$sourceTarPath $sourceRef
if ($LASTEXITCODE -ne 0) {
throw "git archive failed for $RepoRoot $sourceRef"
}
if (Test-Path -LiteralPath $cultLibTarPath) {
Remove-Item -LiteralPath $cultLibTarPath -Force
}
# All three Python packages. CultLib split packages/cultcache-py into
# cultcache-py / cultnet-py / cultmesh-py; module names did not change, so
# archiving only the first still succeeds and ships an incomplete library
# that fails later as an ImportError, far from this line.
git -C $CultLibRoot archive --format=tar --output=$cultLibTarPath $cultLibSourceRef packages/cultcache-py packages/cultnet-py packages/cultmesh-py
if ($LASTEXITCODE -ne 0) {
throw "CultLib tar build failed for $CultLibRoot $cultLibSourceRef"
}
$sourceHash = (Get-FileHash $sourceTarPath -Algorithm SHA256).Hash.ToLowerInvariant()
$cultLibHash = (Get-FileHash $cultLibTarPath -Algorithm SHA256).Hash.ToLowerInvariant()
$deployedAt = [DateTimeOffset]::UtcNow.ToString("O")
@(
"schema=gamecult.idunn.deployment_manifest.v1"
"appId=yggdrasil-repixelizer"
"repoRoot=$RepoRoot"
"upstreamRemote=$UpstreamRemote"
"upstreamBranch=$UpstreamBranch"
"sourceRef=$sourceRef"
"gitCommit=$repixelizerCommit"
"artifact=$RemoteTarballName"
"sha256=$sourceHash"
"cultLibRepoRoot=$CultLibRoot"
"cultLibUpstreamRemote=$CultLibUpstreamRemote"
"cultLibUpstreamBranch=$CultLibUpstreamBranch"
"cultLibSourceRef=$cultLibSourceRef"
"cultLibGitCommit=$cultLibCommit"
"cultLibArtifact=$RemoteCultLibTarballName"
"cultLibSha256=$cultLibHash"
"deployedAtUtc=$deployedAt"
) | Set-Content -Encoding ASCII -LiteralPath $manifestPath
$remoteDeploy = "/home/gamecultadmin/$([IO.Path]::GetFileName($DeployScript))"
$remoteCheck = "/home/gamecultadmin/$([IO.Path]::GetFileName($CheckScript))"
$remoteSourceTarball = "/home/gamecultadmin/$RemoteTarballName"
$remoteCultLibTarball = "/home/gamecultadmin/$RemoteCultLibTarballName"
$remoteManifest = "/home/gamecultadmin/yggdrasil-repixelizer-deployment-manifest.txt"
$remoteRunner = "/home/gamecultadmin/run-yggdrasil-repixelizer-deploy.sh"
$targetSourceTarball = "$RemoteAppHome/$RemoteTarballName"
$targetCultLibTarball = "$RemoteAppHome/$RemoteCultLibTarballName"
$targetManifest = "$RemoteAppHome/deployment-manifest.txt"
$sftpBatchPath = Join-Path $scratch "yggdrasil-repixelizer-upload.sftp"
$runnerPath = Join-Path $scratch "run-yggdrasil-repixelizer-deploy.sh"
$runnerScript = @"
#!/usr/bin/env bash
set -euo pipefail
sudo -n install -d -o '$AppUser' -g '$AppUser' '$RemoteAppHome'
sudo -n install -o '$AppUser' -g '$AppUser' -m 600 '$remoteSourceTarball' '$targetSourceTarball'
sudo -n install -o '$AppUser' -g '$AppUser' -m 600 '$remoteCultLibTarball' '$targetCultLibTarball'
sudo -n install -m 644 '$remoteManifest' '$targetManifest'
chmod +x '$remoteDeploy' '$remoteCheck'
sudo -n env GAMECULT_IDUNN_REMOTE_ACTUATOR=1 IDUNN_COMMAND_AUTHORITY=idunn-daemon bash '$remoteDeploy'
check_ok=0
for i in `$(seq 1 30); do
if sudo -n bash '$remoteCheck'; then
check_ok=1
break
fi
sleep 2
done
if [ "`$check_ok" -ne 1 ]; then
sudo -n bash '$remoteCheck'
exit 1
fi
"@.Replace("`r`n", "`n")
[System.IO.File]::WriteAllText($runnerPath, $runnerScript, [System.Text.Encoding]::ASCII)
@(
"put ""$sourceTarPath"" ""$remoteSourceTarball"""
"put ""$cultLibTarPath"" ""$remoteCultLibTarball"""
"put ""$DeployScript"" ""$remoteDeploy"""
"put ""$CheckScript"" ""$remoteCheck"""
"put ""$manifestPath"" ""$remoteManifest"""
"put ""$runnerPath"" ""$remoteRunner"""
) | Set-Content -Encoding ASCII -LiteralPath $sftpBatchPath
try {
& sftp.exe -b $sftpBatchPath $SshTarget
if ($LASTEXITCODE -ne 0) {
throw "sftp upload failed for yggdrasil-repixelizer"
}
& ssh.exe -o BatchMode=yes -o ConnectTimeout=10 $SshTarget "chmod +x '$remoteRunner' && bash '$remoteRunner'"
$remoteExit = $LASTEXITCODE
& ssh.exe -o BatchMode=yes -o ConnectTimeout=10 $SshTarget "rm -f '$remoteRunner'"
if ($remoteExit -ne 0) {
throw "remote deploy/check failed for yggdrasil-repixelizer"
}
}
finally {
Remove-Item -LiteralPath $runnerPath -Force -ErrorAction SilentlyContinue
Remove-Item -LiteralPath $sftpBatchPath -Force -ErrorAction SilentlyContinue
}
Write-Host "yggdrasil-repixelizer deployed with CultLib snapshot."
Write-Host " repixelizerCommit=$repixelizerCommit"
Write-Host " cultLibCommit=$cultLibCommit"
Write-Host " artifactSha256=$sourceHash"
Write-Host " cultLibSha256=$cultLibHash"