2026-06-09 16:33:28 +01:00
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 ,
2026-06-16 23:44:37 +01:00
[ string ] $SshTarget = " yggdrasil " ,
[ string ] $UpstreamRemote = " origin " ,
[ string ] $UpstreamBranch = " main " ,
[ string ] $StateMigrationScript = " "
2026-06-09 16:33:28 +01:00
)
$ErrorActionPreference = " Stop "
2026-06-17 00:16:07 +01:00
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. "
}
2026-06-09 16:33:28 +01:00
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 "
}
2026-06-16 23:44:37 +01:00
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 "
}
2026-06-09 16:33:28 +01:00
2026-06-16 23:44:37 +01:00
$sourceRef = " $UpstreamRemote / $UpstreamBranch "
$commit = ( git -C $RepoRoot rev-parse $sourceRef ) . Trim ( )
2026-06-09 16:33:28 +01:00
if ( [ string ] :: IsNullOrWhiteSpace ( $commit ) ) {
2026-06-16 23:44:37 +01:00
throw " Could not determine git revision for $RepoRoot $sourceRef "
2026-06-09 16:33:28 +01:00
}
$scratch = Join-Path " E:\Projects\Odin\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
2026-06-16 23:44:37 +01:00
git -C $RepoRoot archive - -format = tar - -output = $tarPath $sourceRef
2026-06-09 16:33:28 +01:00
if ( $LASTEXITCODE -ne 0 ) {
2026-06-16 23:44:37 +01:00
throw " git archive failed for $RepoRoot $sourceRef "
2026-06-09 16:33:28 +01:00
}
$hash = ( Get-FileHash $tarPath -Algorithm SHA256 ) . Hash . ToLowerInvariant ( )
$deployedAt = [ DateTimeOffset ] :: UtcNow . ToString ( " O " )
@ (
" schema=gamecult.idunn.deployment_manifest.v1 "
" appId= $AppId "
" repoRoot= $RepoRoot "
2026-06-16 23:44:37 +01:00
" upstreamRemote= $UpstreamRemote "
" upstreamBranch= $UpstreamBranch "
" sourceRef= $sourceRef "
2026-06-09 16:33:28 +01:00
" 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 ) ) "
2026-06-16 23:44:37 +01:00
$remoteMigration = if ( [ string ] :: IsNullOrWhiteSpace ( $StateMigrationScript ) ) { " " } else { " /home/gamecultadmin/ $( [ IO.Path ] :: GetFileName ( $StateMigrationScript ) ) " }
2026-06-09 16:33:28 +01:00
$remoteTarball = " /home/gamecultadmin/ $RemoteTarballName "
$remoteManifest = " /home/gamecultadmin/ $AppId -deployment-manifest.txt "
2026-06-16 17:35:18 +01:00
$remoteRunner = " /home/gamecultadmin/run- $AppId -deploy.sh "
2026-06-09 16:33:28 +01:00
$targetTarball = " $RemoteAppHome / $RemoteTarballName "
$targetManifest = " $RemoteAppHome /deployment-manifest.txt "
$sshArgs = @ ( " -o " , " BatchMode=yes " , " -o " , " ConnectTimeout=10 " , $SshTarget )
$scpArgs = @ ( " -o " , " BatchMode=yes " , " -o " , " ConnectTimeout=10 " )
2026-06-16 17:35:18 +01:00
$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'
2026-06-16 23:44:37 +01:00
if [ -n '$remoteMigration' ] ; then
chmod + x '$remoteMigration'
sudo -n bash '$remoteMigration'
fi
2026-06-16 17:35:18 +01:00
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 )
2026-06-09 16:33:28 +01:00
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 " }
2026-06-16 23:44:37 +01:00
if ( -not [ string ] :: IsNullOrWhiteSpace ( $StateMigrationScript ) ) {
scp . exe @scpArgs $StateMigrationScript " ${SshTarget} : $remoteMigration "
if ( $LASTEXITCODE -ne 0 ) { throw " scp state migration script failed for $AppId " }
}
2026-06-09 16:33:28 +01:00
scp . exe @scpArgs $manifestPath " ${SshTarget} : $remoteManifest "
if ( $LASTEXITCODE -ne 0 ) { throw " scp manifest failed for $AppId " }
2026-06-16 17:35:18 +01:00
scp . exe @scpArgs $runnerPath " ${SshTarget} : $remoteRunner "
if ( $LASTEXITCODE -ne 0 ) { throw " scp runner script failed for $AppId " }
2026-06-09 16:33:28 +01:00
2026-06-16 17:35:18 +01:00
ssh . exe @sshArgs " chmod +x ' $remoteRunner ' && bash ' $remoteRunner ' "
$remoteExit = $LASTEXITCODE
ssh . exe @sshArgs " rm -f ' $remoteRunner ' "
if ( $remoteExit -ne 0 ) {
2026-06-09 16:33:28 +01:00
throw " remote deploy/check failed for $AppId "
}
2026-06-16 17:35:18 +01:00
Remove-Item -LiteralPath $runnerPath -Force -ErrorAction SilentlyContinue
2026-06-16 23:44:37 +01:00
Write-Host " $AppId deployed to Yggdrasil from $sourceRef at $commit . "
2026-06-09 16:33:28 +01:00
Write-Host " artifactSha256= $hash "