diff --git a/CHECKSUMS.txt b/CHECKSUMS.txt index 37e9282..fb64473 100644 --- a/CHECKSUMS.txt +++ b/CHECKSUMS.txt @@ -8,7 +8,7 @@ c336a61cb8563736bd6550671d7dc90b14702a43f25a97b1ac15ffd64605fbf6 zbackup_ec2.cm 20e6282901a919aa32ea717c739ba2dc9657426dff860f0e5fce201edefd99da zchecksums.cmd 692b7b0ff9e51e5d9b880358b33c54d2db4f967297a5350e154df1ce6f06ebc6 zchecksums.ps1 b56085112e7c573926a70a58872163b71416d58ce91ea7114b1f5de5fc96c982 zdeploy.cmd -3352214fac08cd83242680c0e7f60025c8f132bafb510384d7c7a0fe9a4c5094 zdeploy.ps1 +c4189cef72368487af4524a00603967d7c8dc03c58ace5d4dd1e264ce1d22bd9 zdeploy.ps1 fa817d3bd7bba98b03b411d3c5a82b210d3cca316ac0c9785afa5b4ac5fe0766 zec2.cmd 5f70432af23df3459a98d9ad6a0da723783f2269dd5f5ab8ecdd6366ebc84942 zec2.ps1 4df94eda29b6f5b2cb6bfa63e0add4d90ea24d936305f36a69988e8dd5d69154 zec2_rotatekeys.cmd diff --git a/zconfig.example.json b/zconfig.example.json index 682dda9..6ce9850 100644 --- a/zconfig.example.json +++ b/zconfig.example.json @@ -79,6 +79,12 @@ "path": "/home/YOUR_SSH_USER/stack/nextapp", "appService": "web" }, + "verify": { + "_comment": "Optional: after deploy, curl this ON the server (localhost:port+path). Add it when compose publishes the app to 127.0.0.1 only, as it usually does behind the edge proxy — probing the public IP on that port can never answer. Redirects are followed, so a Next.js '/' that 307s to '/login' still verifies. Takes precedence over the domain check.", + "port": 3000, + "path": "/", + "expect": "" + }, "deploy": { "zipName": "NextAppDeploy.zip" } }, diff --git a/zdeploy.ps1 b/zdeploy.ps1 index e1a9481..ed135b6 100644 --- a/zdeploy.ps1 +++ b/zdeploy.ps1 @@ -25,12 +25,15 @@ # python kind: app service "app", db service "db" # nextjs kind: app service "web", db service "db" # -# Verification (python kind, when there is no scripts/build_version_tool.py): +# Verification (python kind when there is no scripts/build_version_tool.py, and +# nextjs kind): # Projects with a "verify" block are checked ON the server via # localhost: — the only accurate way for stacks that are not # published through the edge proxy. Projects with only a "domain" fall back # to a Host-header request. Projects with neither are reported as NOT # verified rather than passing on the proxy's default vhost. +# A compose stack usually publishes to 127.0.0.1 only, so probing the public +# IP on the app's port can never answer — give those a "verify" block. # param( [Parameter(Position = 0, ValueFromRemainingArguments = $true)] @@ -260,11 +263,18 @@ function Test-DeployHealth { Write-Host "`n--- [$Key] Health check (on server: localhost:$port$path) ---" -ForegroundColor Cyan $deadline = (Get-Date).AddSeconds($TimeoutSec) while ((Get-Date) -lt $deadline) { - $raw = ssh -o StrictHostKeyChecking=no -i $PEM_KEY $SSH_TARGET "curl -s -m 8 http://localhost:$port$path" + # -L: an app whose "/" redirects (e.g. Next.js "/" -> "/login") answers a + # 307 whose body is a few bytes or empty, which reads as "not ready". + # Follow to the page that actually renders before judging. + $raw = ssh -o StrictHostKeyChecking=no -i $PEM_KEY $SSH_TARGET "curl -sL -m 8 http://localhost:$port$path" $body = ($raw | Out-String).Trim() if ($LASTEXITCODE -eq 0 -and $body) { if (-not $expect -or $body.Contains($expect)) { - Write-Host " PASS - $body" -ForegroundColor Green + # A /health endpoint returns a line of JSON; an app page returns + # kilobytes of HTML. Match on the whole body, but only print + # enough to recognise it — the rest is unreadable in a log. + $shown = if ($body.Length -gt 200) { $body.Substring(0, 200) + "... ($($body.Length) bytes)" } else { $body } + Write-Host " PASS - $shown" -ForegroundColor Green return $true } Write-Host " Responding but '$expect' not found - waiting..." -ForegroundColor DarkYellow @@ -539,16 +549,23 @@ function Invoke-NextDeploy { Invoke-Ec2Step "record deploy timestamp; remove remote zip" "date -u +'%Y-%m-%d %H:%M:%S UTC' | sudo tee $remotePath/.last_deploy_utc > /dev/null && rm -f $RemoteHome/$zipName" Write-Host "`n--- [5] Verifying deployment ---" -ForegroundColor Cyan - if ($Proj.ports -and $Proj.ports.prod) { - $directUrl = "http://${EC2_IP}:$([int]$Proj.ports.prod)/" + # Same precedence as the python handler. Do NOT probe http://:/ + # here: a compose stack behind the edge proxy usually publishes to + # 127.0.0.1 only, so that probe can never answer and the old "is the port + # open in the security group?" warning sent you chasing a firewall rule + # for an app that was already up. + if ($Proj.verify -and $Proj.verify.port) { + Test-DeployHealth -Key $Key -Proj $Proj -TimeoutSec 60 | Out-Null + } elseif ($Proj.domain) { + $headers = @{ 'Host' = $Proj.domain } $deadline = (Get-Date).AddSeconds(60) $verified = $false while ((Get-Date) -lt $deadline) { Start-Sleep -Seconds 4 try { - $resp = Invoke-WebRequest -Uri $directUrl -TimeoutSec 8 -ErrorAction Stop -UseBasicParsing - if ($resp.StatusCode -eq 200) { - Write-Host " PASS - app is responding at $directUrl" -ForegroundColor Green + $resp = Invoke-WebRequest -Uri "http://$EC2_IP/" -Headers $headers -TimeoutSec 8 -ErrorAction Stop -UseBasicParsing + if ($resp.StatusCode -lt 500) { + Write-Host " PASS - app is responding at https://$($Proj.domain)/" -ForegroundColor Green $verified = $true break } @@ -557,8 +574,12 @@ function Invoke-NextDeploy { } } if (-not $verified) { - Write-Host " WARNING: no response at $directUrl within 60s (is the port open in the security group?)." -ForegroundColor Yellow + Write-Host " WARNING: no response for https://$($Proj.domain)/ within 60s." -ForegroundColor Yellow } + } else { + Write-Host " Deploy finished - NOT verified." -ForegroundColor Yellow + Write-Host " No 'domain' and no 'verify' block in zconfig.json for '$Key'," -ForegroundColor Yellow + Write-Host ' Add to the project: "verify": { "port": , "path": "/health" }' -ForegroundColor Gray } if ($preZipBuild) { # Committed stamp, not +1 — see the note in Wait-VerifyStaticBuild.