Odin/scripts/deploy-yggdrasil-source-app.ps1
Meta Vens 515a864ef0 Rebase Idunn actuator scripts off the projects root
The previous commit fixed the target catalog and the operator alarm, but the
actuators the catalog invokes carried the same E: paths. Idunn would have
resolved every target and then failed on the first restart or deploy it ran,
for exactly the reason the alarm failed.

Rebased 41 references across 27 scripts:

- .cmd bodies invoke their sibling .ps1 through %~dp0 instead of an absolute
  drive path, and restart-weksa.cmd reaches its sibling repo the same way.
- .ps1 parameter defaults and assignments derive from $PSScriptRoot: one level
  up for paths inside Odin, two for sibling repos under the projects root.

Left deliberately alone: $RemoteViliRoot and $StateRoot in restart-vili.ps1.
Both are consumed inside the EncodedCommand block that restart-vili ssh's to
raven, so they describe raven's filesystem, not starfire's. Whether raven also
moved off E: is an operator question, not something to infer from here.

Verified: all 46 scripts under scripts/ parse clean, and the derivation
resolves to F:\Projects\Odin and F:\Projects on this host.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 01:05:40 +02:00

137 lines
5.3 KiB
PowerShell

param(
[Parameter(Mandatory = $true)] [string] $AppId,
[Parameter(Mandatory = $true)] [string] $RepoRoot,
[Parameter(Mandatory = $true)] [string] $AppUser,
[Parameter(Mandatory = $true)] [string] $RemoteAppHome,
[Parameter(Mandatory = $true)] [string] $RemoteTarballName,
[Parameter(Mandatory = $true)] [string] $DeployScript,
[Parameter(Mandatory = $true)] [string] $CheckScript,
[string] $SshTarget = "yggdrasil",
[string] $UpstreamRemote = "origin",
[string] $UpstreamBranch = "main",
[string] $StateMigrationScript = ""
)
$ErrorActionPreference = "Stop"
if ($env:IDUNN_ACTUATOR -ne "1") {
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."
}
if (-not (Test-Path -LiteralPath $RepoRoot)) {
throw "Repo root not found: $RepoRoot"
}
if (-not (Test-Path -LiteralPath $DeployScript)) {
throw "Deploy script not found: $DeployScript"
}
if (-not (Test-Path -LiteralPath $CheckScript)) {
throw "Check script not found: $CheckScript"
}
if (-not [string]::IsNullOrWhiteSpace($StateMigrationScript) -and -not (Test-Path -LiteralPath $StateMigrationScript)) {
throw "State migration script not found: $StateMigrationScript"
}
git -C $RepoRoot fetch --prune $UpstreamRemote $UpstreamBranch
if ($LASTEXITCODE -ne 0) {
throw "git fetch failed for $RepoRoot $UpstreamRemote/$UpstreamBranch"
}
$sourceRef = "$UpstreamRemote/$UpstreamBranch"
$commit = (git -C $RepoRoot rev-parse $sourceRef).Trim()
if ([string]::IsNullOrWhiteSpace($commit)) {
throw "Could not determine git revision for $RepoRoot $sourceRef"
}
$scratch = Join-Path (Join-Path (Split-Path -Parent $PSScriptRoot) "scratch\idunn-deploy") $AppId
$tarPath = Join-Path $scratch $RemoteTarballName
$manifestPath = Join-Path $scratch "deployment-manifest.txt"
New-Item -ItemType Directory -Force -Path $scratch | Out-Null
git -C $RepoRoot archive --format=tar --output=$tarPath $sourceRef
if ($LASTEXITCODE -ne 0) {
throw "git archive failed for $RepoRoot $sourceRef"
}
$hash = (Get-FileHash $tarPath -Algorithm SHA256).Hash.ToLowerInvariant()
$deployedAt = [DateTimeOffset]::UtcNow.ToString("O")
@(
"schema=gamecult.idunn.deployment_manifest.v1"
"appId=$AppId"
"repoRoot=$RepoRoot"
"upstreamRemote=$UpstreamRemote"
"upstreamBranch=$UpstreamBranch"
"sourceRef=$sourceRef"
"gitCommit=$commit"
"artifact=$RemoteTarballName"
"sha256=$hash"
"deployedAtUtc=$deployedAt"
) | Set-Content -Encoding ASCII -LiteralPath $manifestPath
$remoteDeploy = "/home/gamecultadmin/$([IO.Path]::GetFileName($DeployScript))"
$remoteCheck = "/home/gamecultadmin/$([IO.Path]::GetFileName($CheckScript))"
$remoteMigration = if ([string]::IsNullOrWhiteSpace($StateMigrationScript)) { "" } else { "/home/gamecultadmin/$([IO.Path]::GetFileName($StateMigrationScript))" }
$remoteTarball = "/home/gamecultadmin/$RemoteTarballName"
$remoteManifest = "/home/gamecultadmin/$AppId-deployment-manifest.txt"
$remoteRunner = "/home/gamecultadmin/run-$AppId-deploy.sh"
$targetTarball = "$RemoteAppHome/$RemoteTarballName"
$targetManifest = "$RemoteAppHome/deployment-manifest.txt"
$sshArgs = @("-o", "BatchMode=yes", "-o", "ConnectTimeout=10", $SshTarget)
$scpArgs = @("-o", "BatchMode=yes", "-o", "ConnectTimeout=10")
$runnerPath = Join-Path $scratch "run-$AppId-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 '$remoteTarball' '$targetTarball'
sudo -n install -m 644 '$remoteManifest' '$targetManifest'
chmod +x '$remoteDeploy' '$remoteCheck'
if [ -n '$remoteMigration' ]; then
chmod +x '$remoteMigration'
sudo -n bash '$remoteMigration'
fi
sudo -n 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)
scp.exe @scpArgs $tarPath "${SshTarget}:$remoteTarball"
if ($LASTEXITCODE -ne 0) { throw "scp source tarball failed for $AppId" }
scp.exe @scpArgs $DeployScript "${SshTarget}:$remoteDeploy"
if ($LASTEXITCODE -ne 0) { throw "scp deploy script failed for $AppId" }
scp.exe @scpArgs $CheckScript "${SshTarget}:$remoteCheck"
if ($LASTEXITCODE -ne 0) { throw "scp check script failed for $AppId" }
if (-not [string]::IsNullOrWhiteSpace($StateMigrationScript)) {
scp.exe @scpArgs $StateMigrationScript "${SshTarget}:$remoteMigration"
if ($LASTEXITCODE -ne 0) { throw "scp state migration script failed for $AppId" }
}
scp.exe @scpArgs $manifestPath "${SshTarget}:$remoteManifest"
if ($LASTEXITCODE -ne 0) { throw "scp manifest failed for $AppId" }
scp.exe @scpArgs $runnerPath "${SshTarget}:$remoteRunner"
if ($LASTEXITCODE -ne 0) { throw "scp runner script failed for $AppId" }
ssh.exe @sshArgs "chmod +x '$remoteRunner' && bash '$remoteRunner'"
$remoteExit = $LASTEXITCODE
ssh.exe @sshArgs "rm -f '$remoteRunner'"
if ($remoteExit -ne 0) {
throw "remote deploy/check failed for $AppId"
}
Remove-Item -LiteralPath $runnerPath -Force -ErrorAction SilentlyContinue
Write-Host "$AppId deployed to Yggdrasil from $sourceRef at $commit."
Write-Host " artifactSha256=$hash"