fix(ps): zbackup reports the real pg_dump failure, not "No DATABASE_URL"

Mirror of the bash fix. Invoke-LocalPgDump now owns all its messaging
(caller just captures success) and distinguishes the cases:

- no .env / no DATABASE_URL -> calm "No local DATABASE_URL".
- database not reachable (connection refused / could not connect / DNS /
  timeout) -> calm "Local database not running at host:port" - a stopped
  dev DB is a normal state.
- any other failure (version mismatch, auth, missing db) -> the loud,
  full pg_dump error plus the host:port/db it tried, instead of a bare
  "pg_dump failed (exit N)" followed by a misleading "No DATABASE_URL".

Captures pg_dump stderr (was 2>$null); drops ErrorActionPreference to
Continue locally so PS 5.1 doesn't turn that stderr into a terminating
NativeCommandError under the script's 'Stop' setting.
This commit is contained in:
KellyMichels 2026-07-23 00:18:38 -05:00
parent 84523c8e4d
commit a7b8712063

View File

@ -67,13 +67,20 @@ function Ensure-BackupDir {
# Dump the project's Postgres database if its .env declares a DATABASE_URL. # Dump the project's Postgres database if its .env declares a DATABASE_URL.
# Checks <root>\.env, then <root>\backend\.env (frontend/backend split projects). # Checks <root>\.env, then <root>\backend\.env (frontend/backend split projects).
# Prints its own status line for every outcome; returns $true (dumped) or $false.
# A not-running database (connection refused) is a calm, expected skip; a real
# failure (version mismatch, auth, missing db) prints the loud pg_dump error.
function Invoke-LocalPgDump { function Invoke-LocalPgDump {
param([string]$Root, [string]$OutPath) param([string]$Root, [string]$OutPath)
$envFile = Join-Path $Root ".env" $envFile = Join-Path $Root ".env"
if (-not (Test-Path -LiteralPath $envFile)) { $envFile = Join-Path $Root "backend\.env" } if (-not (Test-Path -LiteralPath $envFile)) { $envFile = Join-Path $Root "backend\.env" }
if (-not (Test-Path -LiteralPath $envFile)) { return $false } if (-not (Test-Path -LiteralPath $envFile)) {
Write-Host " No local DATABASE_URL - source-only backup." -ForegroundColor DarkGray; return $false
}
$dbLine = @(Get-Content -LiteralPath $envFile | Where-Object { $_ -match "^DATABASE_URL=" } | Select-Object -First 1) $dbLine = @(Get-Content -LiteralPath $envFile | Where-Object { $_ -match "^DATABASE_URL=" } | Select-Object -First 1)
if ($dbLine.Count -eq 0) { return $false } if ($dbLine.Count -eq 0) {
Write-Host " No local DATABASE_URL - source-only backup." -ForegroundColor DarkGray; return $false
}
# Strip surrounding quotes (Prisma-style .env values are double-quoted), # Strip surrounding quotes (Prisma-style .env values are double-quoted),
# accept postgres:// and postgresql+driver:// schemes, and treat the # accept postgres:// and postgresql+driver:// schemes, and treat the
@ -104,16 +111,31 @@ function Invoke-LocalPgDump {
foreach ($candidate in $pgBinPaths) { foreach ($candidate in $pgBinPaths) {
if (Test-Path $candidate) { $pgDump = $candidate; break } if (Test-Path $candidate) { $pgDump = $candidate; break }
} }
& $pgDump -h $pgHost -p $pgPort -U $pgUser -d $pgDb -F p -f $OutPath 2>$null # Capture stderr so a failure is diagnosable (version mismatch, unreachable
# host, auth) instead of a bare "pg_dump failed". PS 5.1 turns native stderr
# into a terminating NativeCommandError under 'Stop', so drop to Continue
# locally (function-scoped, auto-reverts) while running pg_dump.
$ErrorActionPreference = 'Continue'
$errOut = & $pgDump -h $pgHost -p $pgPort -U $pgUser -d $pgDb -F p -f $OutPath 2>&1
$ok = ($LASTEXITCODE -eq 0) -and (Test-Path -LiteralPath $OutPath) $ok = ($LASTEXITCODE -eq 0) -and (Test-Path -LiteralPath $OutPath)
Remove-Item Env:\PGPASSWORD -ErrorAction SilentlyContinue Remove-Item Env:\PGPASSWORD -ErrorAction SilentlyContinue
if ($ok) { if ($ok) {
$size = [math]::Round((Get-Item $OutPath).Length / 1KB, 1) $size = [math]::Round((Get-Item $OutPath).Length / 1KB, 1)
Write-Host " PostgreSQL dump: ${size} KB" -ForegroundColor Green Write-Host " PostgreSQL dump: ${size} KB" -ForegroundColor Green
} else { return $true
Write-Host " pg_dump failed (exit $LASTEXITCODE)" -ForegroundColor Red
} }
return $ok $errText = ($errOut | Out-String).Trim()
# Not reachable (usually just not running locally) is expected - stay calm.
if ($errText -match '(?i)connection refused|could not connect|no route to host|could not translate host|timeout expired') {
Write-Host " Local database not running at ${pgHost}:${pgPort} - source-only backup." -ForegroundColor DarkGray
return $false
}
# A real failure (version mismatch, auth, missing db) - show the reason.
Write-Host " pg_dump failed (${pgHost}:${pgPort}/${pgDb} as ${pgUser}):" -ForegroundColor Red
foreach ($line in ($errText -split '\r?\n' | Where-Object { $_.Trim() -ne '' })) {
Write-Host " $line" -ForegroundColor Red
}
return $false
} }
function Invoke-ProjectBackup { function Invoke-ProjectBackup {
@ -141,8 +163,6 @@ function Invoke-ProjectBackup {
$dbDumpPath = Join-Path $dumpDir "database_pg.sql" $dbDumpPath = Join-Path $dumpDir "database_pg.sql"
if (Invoke-LocalPgDump -Root $root -OutPath $dbDumpPath) { if (Invoke-LocalPgDump -Root $root -OutPath $dbDumpPath) {
$extraFiles += $dbDumpPath $extraFiles += $dbDumpPath
} else {
Write-Host " No local DATABASE_URL - source-only backup." -ForegroundColor DarkGray
} }
Write-Host " [2/3] Archiving source..." -ForegroundColor Yellow Write-Host " [2/3] Archiving source..." -ForegroundColor Yellow