Initial commit: Harat's Booking monorepo with deploy setup
This commit is contained in:
@@ -0,0 +1,261 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Harat's Booking Deploy — builds and sends release to production server.
|
||||
|
||||
.DESCRIPTION
|
||||
1. Bumps version.json with current build timestamp
|
||||
2. Builds client (unless -SkipBuild)
|
||||
3. Creates a tar.gz archive (no node_modules, no .env)
|
||||
4. Uploads to server via SCP
|
||||
5. Runs deploy-remote.sh on the server via SSH
|
||||
|
||||
.PARAMETER Server
|
||||
Production server IP/hostname.
|
||||
|
||||
.PARAMETER User
|
||||
SSH username on the server.
|
||||
|
||||
.PARAMETER Port
|
||||
SSH port (default 2182).
|
||||
|
||||
.PARAMETER SkipBuild
|
||||
Skip client build (use existing dist/).
|
||||
|
||||
.EXAMPLE
|
||||
.\deploy\deploy.ps1 # full deploy
|
||||
.\deploy\deploy.ps1 -SkipBuild # skip client build
|
||||
#>
|
||||
|
||||
param(
|
||||
[string]$Server = "178.169.87.152",
|
||||
[string]$User = "eugenius",
|
||||
[int]$Port = 2182,
|
||||
[switch]$SkipBuild
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$ProjectRoot = Split-Path -Parent $PSScriptRoot # harats/
|
||||
$DeployDir = "/apps/harats"
|
||||
|
||||
# -- Error reporting -------------------------------------------------------
|
||||
function Write-DeployError {
|
||||
param(
|
||||
[string]$Step,
|
||||
[string]$Message,
|
||||
[string]$Detail = "",
|
||||
[string]$Hint = ""
|
||||
)
|
||||
Write-Host ""
|
||||
Write-Host " +============================================+" -ForegroundColor Red
|
||||
Write-Host " | DEPLOY FAILED |" -ForegroundColor Red
|
||||
Write-Host " +============================================+" -ForegroundColor Red
|
||||
Write-Host ""
|
||||
Write-Host " Step: " -NoNewline -ForegroundColor DarkGray
|
||||
Write-Host "$Step" -ForegroundColor White
|
||||
Write-Host " Error: " -NoNewline -ForegroundColor DarkGray
|
||||
Write-Host "$Message" -ForegroundColor Yellow
|
||||
if ($Detail) {
|
||||
Write-Host " Detail: " -NoNewline -ForegroundColor DarkGray
|
||||
Write-Host "$Detail" -ForegroundColor Gray
|
||||
}
|
||||
if ($Hint) {
|
||||
Write-Host ""
|
||||
Write-Host " Hint: " -NoNewline -ForegroundColor DarkGray
|
||||
Write-Host "$Hint" -ForegroundColor Cyan
|
||||
}
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
# -- Main deploy pipeline --------------------------------------------------
|
||||
$currentStep = "Init"
|
||||
try {
|
||||
|
||||
# -- 1. Read version --
|
||||
$currentStep = "[1/5] Version read"
|
||||
$versionFile = Join-Path $ProjectRoot "version.json"
|
||||
if (-not (Test-Path $versionFile)) {
|
||||
throw "version.json not found at: $versionFile"
|
||||
}
|
||||
$versionData = Get-Content $versionFile -Raw | ConvertFrom-Json
|
||||
$version = $versionData.version
|
||||
if (-not $version) { throw "version.json is missing the 'version' field" }
|
||||
$build = Get-Date -Format "yyyyMMdd-HHmm"
|
||||
$tag = "${version}+${build}"
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "=======================================" -ForegroundColor Cyan
|
||||
Write-Host " Harat's Deploy - v${tag}" -ForegroundColor Cyan
|
||||
Write-Host " Target: ${User}@${Server}:${Port}" -ForegroundColor Cyan
|
||||
Write-Host "=======================================" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
# -- 2. Update version.json --
|
||||
$currentStep = "[1/5] Version update"
|
||||
$versionData.build = $build
|
||||
$versionData.buildDate = (Get-Date -Format "o")
|
||||
$jsonOut = $versionData | ConvertTo-Json -Depth 3
|
||||
[System.IO.File]::WriteAllText($versionFile, $jsonOut, [System.Text.UTF8Encoding]::new($false))
|
||||
Write-Host "[1/5] Version: $tag" -ForegroundColor Green
|
||||
|
||||
# -- 3. Build client --
|
||||
$currentStep = "[2/5] Client build"
|
||||
if (-not $SkipBuild) {
|
||||
Write-Host "[2/5] Building client..." -ForegroundColor Yellow
|
||||
|
||||
# Build shared first
|
||||
$sharedDir = Join-Path $ProjectRoot "packages\shared"
|
||||
if (Test-Path (Join-Path $sharedDir "package.json")) {
|
||||
Push-Location $sharedDir
|
||||
try {
|
||||
npm run build 2>$null
|
||||
} finally {
|
||||
Pop-Location
|
||||
}
|
||||
}
|
||||
|
||||
$clientDir = Join-Path $ProjectRoot "client"
|
||||
if (-not (Test-Path (Join-Path $clientDir "package.json"))) {
|
||||
throw "client/package.json not found -- is the workspace intact?"
|
||||
}
|
||||
|
||||
Push-Location $clientDir
|
||||
try {
|
||||
npm run build
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Vite build exited with code $LASTEXITCODE -- check the compiler output above"
|
||||
}
|
||||
} finally {
|
||||
Pop-Location
|
||||
}
|
||||
|
||||
# Build server
|
||||
Write-Host " Building server..." -ForegroundColor Yellow
|
||||
$serverDir = Join-Path $ProjectRoot "server"
|
||||
Push-Location $serverDir
|
||||
try {
|
||||
npm run build
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Server tsc build exited with code $LASTEXITCODE"
|
||||
}
|
||||
} finally {
|
||||
Pop-Location
|
||||
}
|
||||
|
||||
Write-Host " Build OK" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[2/5] Skipping build" -ForegroundColor DarkGray
|
||||
}
|
||||
|
||||
# -- 4. Create archive --
|
||||
$currentStep = "[3/5] Archive creation"
|
||||
Write-Host "[3/5] Creating archive..." -ForegroundColor Yellow
|
||||
$archiveName = "harats-${tag}.tar.gz"
|
||||
$archivePath = Join-Path $ProjectRoot $archiveName
|
||||
|
||||
# Verify git is available (needed for GNU tar)
|
||||
$gitCmd = Get-Command git -ErrorAction SilentlyContinue
|
||||
if (-not $gitCmd) {
|
||||
throw "git not found in PATH -- GNU tar from Git for Windows is required to create archives"
|
||||
}
|
||||
|
||||
$gitRoot = $gitCmd.Source | Split-Path | Split-Path
|
||||
$gnuTar = Join-Path $gitRoot "usr\bin\tar.exe"
|
||||
if (-not (Test-Path $gnuTar)) {
|
||||
throw "GNU tar not found at: $gnuTar -- expected Git for Windows installation"
|
||||
}
|
||||
|
||||
Push-Location $ProjectRoot
|
||||
try {
|
||||
$oldPath = $env:PATH
|
||||
$env:PATH = "$gitRoot\usr\bin;$env:PATH"
|
||||
|
||||
& $gnuTar -czf $archiveName `
|
||||
--exclude="node_modules" `
|
||||
--exclude="*.tar.gz" `
|
||||
--exclude=".git" `
|
||||
--exclude="envs/prod/.env" `
|
||||
server/dist/ `
|
||||
server/prisma/ `
|
||||
server/package.json `
|
||||
server/package-lock.json `
|
||||
client/dist/ `
|
||||
packages/shared/ `
|
||||
Dockerfile `
|
||||
.dockerignore `
|
||||
docker-compose.prod.yml `
|
||||
ecosystem.config.cjs `
|
||||
version.json `
|
||||
envs/ `
|
||||
deploy/
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "tar exited with code $LASTEXITCODE -- check file list for missing paths"
|
||||
}
|
||||
|
||||
$env:PATH = $oldPath
|
||||
} finally {
|
||||
Pop-Location
|
||||
}
|
||||
|
||||
if (-not (Test-Path $archivePath)) {
|
||||
throw "Archive was not created at: $archivePath"
|
||||
}
|
||||
|
||||
$sizeMB = [math]::Round((Get-Item $archivePath).Length / 1MB, 1)
|
||||
if ($sizeMB -lt 0.1) {
|
||||
Write-Host " Warning: archive is suspiciously small (${sizeMB} MB)" -ForegroundColor DarkYellow
|
||||
}
|
||||
Write-Host " Archive: $archiveName - ${sizeMB} MB" -ForegroundColor Green
|
||||
|
||||
# -- 5. Upload --
|
||||
$currentStep = "[4/5] SCP upload"
|
||||
Write-Host "[4/5] Uploading to ${Server}..." -ForegroundColor Yellow
|
||||
scp -P $Port $archivePath "${User}@${Server}:${DeployDir}/releases/"
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
$hint = switch ($LASTEXITCODE) {
|
||||
255 { "Connection refused or auth failed. Check SSH key, port ($Port), and server availability." }
|
||||
1 { "SCP error -- target directory ${DeployDir}/releases/ may not exist on the server." }
|
||||
default { "SCP exited with code $LASTEXITCODE." }
|
||||
}
|
||||
throw $hint
|
||||
}
|
||||
Write-Host " Upload OK" -ForegroundColor Green
|
||||
|
||||
# -- 6. Remote deploy --
|
||||
$currentStep = "[5/5] Remote deploy (SSH)"
|
||||
Write-Host "[5/5] Deploying on server..." -ForegroundColor Yellow
|
||||
ssh -p $Port "${User}@${Server}" "cd ${DeployDir}; cp deploy/deploy-remote.sh /tmp/_harats-deploy.sh; bash /tmp/_harats-deploy.sh '${archiveName}' '${tag}'"
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
$hint = switch ($LASTEXITCODE) {
|
||||
255 { "SSH connection failed. Is the server reachable at ${Server}:${Port}?" }
|
||||
126 { "deploy-remote.sh exists but is not executable." }
|
||||
127 { "deploy-remote.sh not found on server at ${DeployDir}/deploy/deploy-remote.sh" }
|
||||
default { "Remote script exited with code $LASTEXITCODE -- check server logs" }
|
||||
}
|
||||
throw $hint
|
||||
}
|
||||
|
||||
# -- Cleanup local archive --
|
||||
$currentStep = "Cleanup"
|
||||
Remove-Item $archivePath -Force -ErrorAction SilentlyContinue
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "=======================================" -ForegroundColor Green
|
||||
Write-Host " Deployed Harat's v${tag}" -ForegroundColor Green
|
||||
Write-Host "=======================================" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
|
||||
} catch {
|
||||
Write-DeployError `
|
||||
-Step $currentStep `
|
||||
-Message $_.Exception.Message `
|
||||
-Detail ($_.ScriptStackTrace -split "`n" | Select-Object -First 3 | Out-String).Trim() `
|
||||
-Hint "Fix the issue above and re-run: .\deploy\deploy.ps1$(if ($SkipBuild) { ' -SkipBuild' })"
|
||||
|
||||
# Cleanup partial archive if it exists
|
||||
if ($archivePath -and (Test-Path $archivePath -ErrorAction SilentlyContinue)) {
|
||||
Remove-Item $archivePath -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
exit 1
|
||||
}
|
||||
Reference in New Issue
Block a user