MotorSignal-App/scripts/deploy-website.ps1

138 lines
5.0 KiB
PowerShell

param(
[string]$HostIp = $env:MOTORSIGNAL_PROD_HOST,
[string]$User = $(if ($env:MOTORSIGNAL_PROD_USER) { $env:MOTORSIGNAL_PROD_USER } else { "motorsignal-admin" }),
[string]$Key = $env:MOTORSIGNAL_PROD_SSH_KEY,
[switch]$BuildRemote
)
$ErrorActionPreference = "Stop"
if (-not $HostIp) {
Write-Error "HostIp parameter or MOTORSIGNAL_PROD_HOST environment variable is required."
exit 1
}
$SshKeyArgs = @()
if ($Key) {
if (-not (Test-Path $Key)) {
Write-Error "Specified SSH key file not found: $Key"
exit 1
}
$SshKeyArgs = @("-i", $Key)
}
$RepositoryRoot = (Resolve-Path "$PSScriptRoot\..").Path
$GitSha = (git -C $RepositoryRoot rev-parse HEAD).Trim()
if ($GitSha -notmatch '^[0-9a-f]{40}$') {
Write-Error "Unable to resolve a valid 40-character Git revision."
exit 1
}
# Refuse dirty website inputs before assigning the HEAD revision
$DirtyWebsite = "$((git -C $RepositoryRoot status --porcelain -- website))".Trim()
if ($DirtyWebsite) {
Write-Error "Cannot deploy website: working tree contains uncommitted changes under website/:`n$DirtyWebsite"
exit 1
}
$Timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
Write-Host ">>> Deploying website revision $GitSha" -ForegroundColor Cyan
if ($BuildRemote) {
# Ultra-lean mode: Upload only source files (~100 KB total)
$TarName = "website-src-$Timestamp.tar.gz"
$TempTar = "$PSScriptRoot\$TarName"
Write-Host ">>> 1. Packaging source code (~100 KB)..." -ForegroundColor Cyan
tar -czf "$TempTar" -C "$RepositoryRoot\website" app components lib public package.json tsconfig.json next.config.ts
Write-Host ">>> 2. Uploading source code to server..." -ForegroundColor Cyan
scp @SshKeyArgs "$TempTar" "${User}@${HostIp}:/tmp/$TarName"
Remove-Item "$TempTar" -Force
Write-Host ">>> 3. Building and restarting on server..." -ForegroundColor Cyan
$RemoteCmd = @"
bash -c '
set -euo pipefail
TAR=/tmp/$TarName
TARGET=/opt/motorsignal/current/website
echo "Updating source files in `$TARGET..."
sudo tar -xzf "`$TAR" -C "`$TARGET"
rm -f "`$TAR"
cd "`$TARGET"
echo "Running Next.js production build on server..."
MOTORSIGNAL_GIT_SHA="$GitSha" npm run build
echo "Restarting motorsignal-website service..."
sudo systemctl restart motorsignal-website
echo "Done!"
'
"@
ssh -t @SshKeyArgs "${User}@${HostIp}" $RemoteCmd
} else {
# Fast local build: Strip Next.js cache & dev artifacts (~2 MB total instead of 500 MB)
Write-Host ">>> 1. Building Next.js application locally..." -ForegroundColor Cyan
Set-Location -Path "$PSScriptRoot\..\website"
$PreviousDeploySha = $env:MOTORSIGNAL_GIT_SHA
try {
$env:MOTORSIGNAL_GIT_SHA = $GitSha
npm run build
if (Test-Path "$PSScriptRoot\..\website\.next\standalone\server.js") {
Copy-Item "$PSScriptRoot\..\website\.next\standalone\server.js" "$PSScriptRoot\..\website\server.js" -Force
}
} finally {
if ($null -eq $PreviousDeploySha) {
Remove-Item Env:MOTORSIGNAL_GIT_SHA -ErrorAction SilentlyContinue
} else {
$env:MOTORSIGNAL_GIT_SHA = $PreviousDeploySha
}
Set-Location -Path $RepositoryRoot
}
$TarName = "website-update-$Timestamp.tar.gz"
$TempTar = "$PSScriptRoot\$TarName"
$TarArgs = @("-czf", "$TempTar", "--exclude=cache", "--exclude=dev", "--exclude=standalone", "-C", "$RepositoryRoot\website", ".next/server", ".next/static", ".next/types", ".next/BUILD_ID", ".next/*.json", "public", "package.json", "app", "components", "lib")
if (Test-Path "$RepositoryRoot\website\server.js") {
$TarArgs += "server.js"
}
tar @TarArgs
Remove-Item "$RepositoryRoot\website\server.js" -Force -ErrorAction SilentlyContinue
$SizeMb = [math]::Round(((Get-Item "$TempTar").Length / 1MB), 2)
Write-Host ">>> 3. Uploading $SizeMb MB to server..." -ForegroundColor Cyan
scp @SshKeyArgs "$TempTar" "${User}@${HostIp}:/tmp/$TarName"
Remove-Item "$TempTar" -Force
Write-Host ">>> 4. Activating update on server..." -ForegroundColor Cyan
$BashScript = @"
set -euo pipefail
TAR=/tmp/$TarName
TARGET=/opt/motorsignal/current/website
echo "Extracting runtime to `$TARGET..."
tar -xzf "`$TAR" --no-same-owner --no-same-permissions --no-overwrite-dir -C "`$TARGET"
rm -f "`$TAR"
echo "Restarting motorsignal-website service..."
sudo systemctl restart motorsignal-website
echo "Service restarted."
"@.Replace("`r", "")
ssh @SshKeyArgs "${User}@${HostIp}" "bash -c '$BashScript'"
}
Write-Host ">>> 5. Checking deployed revision..." -ForegroundColor Green
Start-Sleep -Seconds 2
$Version = Invoke-RestMethod -Uri "https://motorsignal.app/api/version" -Method Get
if ($Version.status -ne "ok" -or $Version.service -ne "motorsignal-website") {
Write-Error "Website version endpoint returned an unhealthy response."
exit 1
}
if ($Version.revision -ne $GitSha) {
Write-Error "Deployment revision mismatch. Expected $GitSha but production reports $($Version.revision)."
exit 1
}
Write-Host ">>> Deployed successfully! Verified revision: $($Version.shortRevision)" -ForegroundColor Green