deploy.ps1
Rama main · réplica de lectura del repositorio original.
# deploy.ps1 — Deploys this repo to a remote Docker host over SSH.
#
# Reads HOST and SUDO_PASS from .env.deploy (gitignored). Example .env.deploy:
# HOST=user@1.2.3.4
# REMOTE_DIR=/home/user/chess
# SUDO_PASS=...
# HOST_PORT=8800
param([switch]$NoBuild, [switch]$Logs)
$ErrorActionPreference = 'Stop'
$envFile = Join-Path $PSScriptRoot '.env.deploy'
if (-not (Test-Path $envFile)) {
throw ".env.deploy not found. Create it with HOST=user@host, REMOTE_DIR=/path, SUDO_PASS=... (optional)"
}
$envVars = @{}
Get-Content $envFile | ForEach-Object {
if ($_ -match '^\s*([A-Z_][A-Z0-9_]*)\s*=\s*(.*?)\s*$') {
$envVars[$matches[1]] = $matches[2]
}
}
$HOST_TARGET = $envVars['HOST']
$REMOTE_DIR = $envVars['REMOTE_DIR']
$SUDO_PASS = $envVars['SUDO_PASS']
$HOST_PORT = if ($envVars['HOST_PORT']) { $envVars['HOST_PORT'] } else { '8800' }
if (-not $HOST_TARGET -or -not $REMOTE_DIR) {
throw "HOST and REMOTE_DIR must be set in .env.deploy"
}
function Invoke-Remote([string]$cmd) {
& ssh $HOST_TARGET $cmd
if ($LASTEXITCODE -ne 0) { throw "Remote command failed: $cmd" }
}
# Run a multi-line bash script on the remote with sudo. Avoids PowerShell quoting
# pitfalls by scp'ing the script first, then executing it.
function Invoke-RemoteSudoScript([string]$script) {
$localScript = Join-Path $env:TEMP "chess-remote-$(Get-Random).sh"
$remoteScript = "/tmp/chess-remote-$(Get-Random).sh"
# Write with LF line endings, no BOM
[System.IO.File]::WriteAllText($localScript, "#!/usr/bin/env bash`nset -e`n" + $script.Replace("`r`n", "`n"), (New-Object System.Text.UTF8Encoding $false))
& scp -q $localScript "${HOST_TARGET}:$remoteScript"
if ($LASTEXITCODE -ne 0) { Remove-Item -Force $localScript; throw "scp script failed" }
if ($SUDO_PASS) {
& ssh $HOST_TARGET "chmod +x $remoteScript && echo '$SUDO_PASS' | sudo -S -p '' bash $remoteScript; rc=`$?; rm -f $remoteScript; exit `$rc"
} else {
& ssh $HOST_TARGET "chmod +x $remoteScript && sudo bash $remoteScript; rc=`$?; rm -f $remoteScript; exit `$rc"
}
$rc = $LASTEXITCODE
Remove-Item -Force $localScript
if ($rc -ne 0) { throw "Remote script failed (exit $rc)" }
}
Write-Host "→ Ensuring remote dir $REMOTE_DIR exists" -ForegroundColor Cyan
Invoke-Remote "mkdir -p $REMOTE_DIR"
Write-Host "→ Syncing source to $HOST_TARGET`:$REMOTE_DIR" -ForegroundColor Cyan
# Tar to a local temp file, scp it, extract on remote. PowerShell pipelines mangle
# binary streams, so we cannot pipe tar | ssh directly.
$localTar = Join-Path $env:TEMP "chess-deploy-$(Get-Random).tar.gz"
$remoteTar = "/tmp/chess-deploy-$(Get-Random).tar.gz"
# Prefer Windows native bsdtar — it understands drive letters cleanly. GNU tar (Git Bash)
# treats "R:" as a remote host. Fall back to whatever's on PATH.
$tarExe = Join-Path $env:WINDIR 'System32\tar.exe'
if (-not (Test-Path $tarExe)) { $tarExe = 'tar' }
$tarArgs = @(
'--exclude=node_modules', '--exclude=.git', '--exclude=dist',
'--exclude=build', '--exclude=data', '--exclude=bin',
'--exclude=.env', '--exclude=.env.deploy',
'-czf', $localTar, '-C', $PSScriptRoot, '.'
)
& $tarExe @tarArgs
if ($LASTEXITCODE -ne 0) { throw "tar failed" }
& scp -q $localTar "${HOST_TARGET}:$remoteTar"
if ($LASTEXITCODE -ne 0) { Remove-Item -Force $localTar; throw "scp failed" }
Invoke-Remote "tar -xzf $remoteTar -C $REMOTE_DIR && rm -f $remoteTar"
Remove-Item -Force $localTar
if (-not $NoBuild) {
Write-Host "→ Building image on remote (this can take a few minutes)" -ForegroundColor Cyan
Invoke-RemoteSudoScript @"
cd $REMOTE_DIR
HOST_PORT=$HOST_PORT docker compose build
"@
}
Write-Host "→ Starting container" -ForegroundColor Cyan
Invoke-RemoteSudoScript @"
cd $REMOTE_DIR
HOST_PORT=$HOST_PORT docker compose up -d
sleep 2
docker compose ps
"@
if ($Logs) {
Invoke-RemoteSudoScript @"
cd $REMOTE_DIR
docker compose logs --tail=50
"@
}
$serverIp = ($HOST_TARGET -split '@')[-1]
Write-Host ""
Write-Host "Deployed. Open http://${serverIp}:$HOST_PORT" -ForegroundColor Green