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>
137 lines
5.3 KiB
PowerShell
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"
|