diff --git a/scripts/dev-db.ps1 b/scripts/dev-db.ps1 new file mode 100644 index 0000000..390403b --- /dev/null +++ b/scripts/dev-db.ps1 @@ -0,0 +1,112 @@ +#!/usr/bin/env pwsh +<# +.SYNOPSIS + Manage the local Postgres container used by the YesChef backend in dev. + +.DESCRIPTION + Spins up a postgres:17 container named "yeschef-pg" on localhost:5432 with + the database/user/password the API's appsettings.json expects. Persists + data in a Docker volume so restarts retain state until reset is run. + +.PARAMETER Command + start - Create + start the container (no-op if already running). + stop - Stop the container, leaving the volume intact. + reset - Stop and remove the container AND its data volume. Next start is + a fresh DB; the API will run all migrations from scratch. + status - Print the container state and connection string. + logs - Tail the container logs. + +.EXAMPLE + ./scripts/dev-db.ps1 start + ./scripts/dev-db.ps1 reset +#> +param( + [Parameter(Position = 0)] + [ValidateSet('start', 'stop', 'reset', 'status', 'logs')] + [string]$Command = 'status' +) + +$ErrorActionPreference = 'Stop' + +$Name = 'yeschef-pg' +$Volume = 'yeschef-pgdata-dev' +$Image = 'postgres:17' +$ConnectionString = 'Host=localhost;Database=yeschef;Username=yeschef;Password=yeschef' + +function Get-ContainerState { + $state = docker inspect --format '{{.State.Status}}' $Name 2>$null + if ($LASTEXITCODE -ne 0) { return 'absent' } + return $state +} + +function Start-DevDb { + $state = Get-ContainerState + if ($state -eq 'running') { + Write-Host "Container '$Name' already running." -ForegroundColor Green + return + } + if ($state -ne 'absent') { + Write-Host "Starting existing container '$Name'..." + docker start $Name | Out-Null + } else { + Write-Host "Creating new container '$Name'..." + docker run -d ` + --name $Name ` + -p 5432:5432 ` + -e POSTGRES_DB=yeschef ` + -e POSTGRES_USER=yeschef ` + -e POSTGRES_PASSWORD=yeschef ` + -v "${Volume}:/var/lib/postgresql/data" ` + $Image | Out-Null + } + + # Wait until pg_isready returns healthy so the caller can immediately + # start the API without racing on the listener. + Write-Host "Waiting for Postgres to accept connections..." + for ($i = 0; $i -lt 30; $i++) { + docker exec $Name pg_isready -U yeschef -d yeschef -q 2>$null + if ($LASTEXITCODE -eq 0) { + Write-Host "Ready. Connection string:" -ForegroundColor Green + Write-Host " $ConnectionString" + return + } + Start-Sleep -Seconds 1 + } + Write-Warning "Postgres did not become ready within 30s. Check 'docker logs $Name'." +} + +function Stop-DevDb { + $state = Get-ContainerState + if ($state -eq 'absent') { + Write-Host "Container '$Name' is not present." + return + } + docker stop $Name | Out-Null + Write-Host "Stopped '$Name'." -ForegroundColor Yellow +} + +function Reset-DevDb { + $state = Get-ContainerState + if ($state -ne 'absent') { + docker rm -f $Name | Out-Null + Write-Host "Removed container '$Name'." + } + docker volume rm $Volume 2>$null | Out-Null + Write-Host "Removed volume '$Volume'. Next 'start' will create a fresh database." -ForegroundColor Yellow +} + +function Show-Status { + $state = Get-ContainerState + Write-Host "Container: $Name" + Write-Host " State : $state" + Write-Host " Volume: $Volume" + Write-Host " Conn : $ConnectionString" +} + +switch ($Command) { + 'start' { Start-DevDb } + 'stop' { Stop-DevDb } + 'reset' { Reset-DevDb } + 'status' { Show-Status } + 'logs' { docker logs -f $Name } +}