177 lines
5.1 KiB
PowerShell
177 lines
5.1 KiB
PowerShell
param(
|
|
[switch]$Build,
|
|
[int]$BackupRetention = 0,
|
|
[string]$SshUser = "y2keui",
|
|
[string]$SshHost = "comtropy.synology.me",
|
|
[int]$SshPort = 50022,
|
|
[string]$SshKey = "$env:USERPROFILE\.ssh\nas_msix_ed25519",
|
|
[string]$RemoteRoot = "/volume1/web",
|
|
[string]$SiteName = "heros_web"
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
Set-StrictMode -Version Latest
|
|
|
|
function Assert-Command {
|
|
param([string]$Name)
|
|
if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) {
|
|
throw "Required command not found: $Name"
|
|
}
|
|
}
|
|
|
|
function Quote-Sh {
|
|
param([string]$Value)
|
|
return "'" + ($Value -replace "'", "'\''") + "'"
|
|
}
|
|
|
|
Assert-Command "ssh"
|
|
Assert-Command "scp"
|
|
Assert-Command "git"
|
|
|
|
if ($Build) {
|
|
Assert-Command "pnpm"
|
|
pnpm build
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "pnpm build failed with exit code $LASTEXITCODE"
|
|
}
|
|
}
|
|
|
|
$dist = Join-Path (Get-Location) "dist"
|
|
$index = Join-Path $dist "index.html"
|
|
$assets = Join-Path $dist "assets"
|
|
|
|
if (-not (Test-Path $index -PathType Leaf)) {
|
|
throw "Missing dist/index.html. Run pnpm build first or pass -Build."
|
|
}
|
|
|
|
if (-not (Test-Path $assets -PathType Container)) {
|
|
throw "Missing dist/assets. Run pnpm build first or pass -Build."
|
|
}
|
|
|
|
if ($BackupRetention -lt 0) {
|
|
throw "BackupRetention must be 0 or greater."
|
|
}
|
|
|
|
$timestamp = Get-Date -Format "yyyyMMddHHmmss"
|
|
$fullCommit = (git rev-parse HEAD).Trim()
|
|
$commit = (git rev-parse --short=8 HEAD).Trim()
|
|
$qaVersion = if ($env:PUBLIC_QA_VERSION) { $env:PUBLIC_QA_VERSION } else { "deploy-$timestamp" }
|
|
$releaseManifestPath = Join-Path $dist "release-manifest.json"
|
|
$writeReleaseManifest = $true
|
|
if (Test-Path $releaseManifestPath -PathType Leaf) {
|
|
try {
|
|
$existingReleaseManifest = Get-Content -LiteralPath $releaseManifestPath -Raw | ConvertFrom-Json
|
|
if ($existingReleaseManifest.app -ne "heros_web") {
|
|
throw "Existing release manifest has unexpected app '$($existingReleaseManifest.app)'."
|
|
}
|
|
if ($existingReleaseManifest.commit -eq $fullCommit) {
|
|
$writeReleaseManifest = $false
|
|
Write-Host "Using existing $releaseManifestPath for $commit"
|
|
} else {
|
|
Write-Host "Replacing stale $releaseManifestPath commit $($existingReleaseManifest.commit) with $commit"
|
|
}
|
|
} catch {
|
|
throw "Invalid existing release manifest at $releaseManifestPath. $_"
|
|
}
|
|
}
|
|
if ($writeReleaseManifest) {
|
|
@{
|
|
app = "heros_web"
|
|
commit = $fullCommit
|
|
shortCommit = $commit
|
|
qaVersion = $qaVersion
|
|
generatedAt = (Get-Date).ToUniversalTime().ToString("o")
|
|
} | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath $releaseManifestPath -Encoding UTF8
|
|
Write-Host "Wrote $releaseManifestPath for $commit ($qaVersion)"
|
|
}
|
|
|
|
$userHost = "$SshUser@$SshHost"
|
|
$remoteTemp = "$RemoteRoot/.$($SiteName)_deploy_$timestamp"
|
|
$remotePrevious = "$RemoteRoot/.$($SiteName)_previous_$timestamp"
|
|
$remoteBackup = "$RemoteRoot/$SiteName.backup-$timestamp-$commit"
|
|
|
|
$quotedRemoteRoot = Quote-Sh $RemoteRoot
|
|
$quotedSiteName = Quote-Sh $SiteName
|
|
$quotedTemp = Quote-Sh $remoteTemp
|
|
$quotedPrevious = Quote-Sh $remotePrevious
|
|
$quotedBackup = Quote-Sh $remoteBackup
|
|
$retention = $BackupRetention
|
|
|
|
$prepareRemote = @"
|
|
set -e
|
|
remote_root=$quotedRemoteRoot
|
|
temp=$quotedTemp
|
|
site_name=$quotedSiteName
|
|
test -d "`$remote_root"
|
|
rm -rf -- "`$temp"
|
|
mkdir -p -- "`$temp"
|
|
printf 'Prepared %s/%s via %s\n' "`$remote_root" "`$site_name" "`$temp"
|
|
"@
|
|
|
|
ssh -o BatchMode=yes -o ConnectTimeout=8 -i $SshKey -p $SshPort $userHost $prepareRemote
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Failed to prepare remote deploy directory."
|
|
}
|
|
|
|
scp -O -i $SshKey -P $SshPort -r "$dist\*" "$userHost`:$remoteTemp/"
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Failed to upload dist to remote deploy directory."
|
|
}
|
|
|
|
$installRemote = @"
|
|
set -e
|
|
remote_root=$quotedRemoteRoot
|
|
site_name=$quotedSiteName
|
|
temp=$quotedTemp
|
|
previous=$quotedPrevious
|
|
backup=$quotedBackup
|
|
retention=$retention
|
|
site="`$remote_root/`$site_name"
|
|
|
|
test -f "`$temp/index.html"
|
|
test -d "`$temp/assets"
|
|
rm -rf -- "`$previous"
|
|
|
|
if [ -d "`$site" ]; then
|
|
mv -- "`$site" "`$previous"
|
|
fi
|
|
|
|
if ! mv -- "`$temp" "`$site"; then
|
|
if [ -d "`$previous" ]; then
|
|
mv -- "`$previous" "`$site"
|
|
fi
|
|
exit 1
|
|
fi
|
|
|
|
chmod -R a+rX "`$site"
|
|
|
|
if [ -d "`$previous" ]; then
|
|
if [ "`$retention" -gt 0 ]; then
|
|
mv -- "`$previous" "`$backup"
|
|
else
|
|
rm -rf -- "`$previous"
|
|
fi
|
|
fi
|
|
|
|
find "`$remote_root" -maxdepth 1 -type d \( -name ".`${site_name}_deploy_*" -o -name "`${site_name}_deploy_*" -o -name ".`${site_name}_previous_*" \) -exec rm -rf -- {} +
|
|
|
|
if [ "`$retention" -eq 0 ]; then
|
|
find "`$remote_root" -maxdepth 1 -type d -name "`${site_name}.backup-*" -exec rm -rf -- {} +
|
|
else
|
|
keep_count=`$retention
|
|
find "`$remote_root" -maxdepth 1 -type d -name "`${site_name}.backup-*" -printf '%f\n' | sort -r | tail -n +`$((keep_count + 1)) | while IFS= read -r old_backup; do
|
|
[ -n "`$old_backup" ] && rm -rf -- "`$remote_root/`$old_backup"
|
|
done
|
|
fi
|
|
|
|
printf 'Deployed %s at %s\n' "`$site_name" "`$site"
|
|
printf 'Backup retention: %s\n' "`$retention"
|
|
printf 'Remaining site directories:\n'
|
|
find "`$remote_root" -maxdepth 1 -type d -name "*`${site_name}*" -printf '%f\n' | sort
|
|
"@
|
|
|
|
ssh -o BatchMode=yes -o ConnectTimeout=8 -i $SshKey -p $SshPort $userHost $installRemote
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Failed to install remote deploy directory."
|
|
}
|