vignette/scripts/boot-public-runtime.ps1
Yun Chan 778e8526d4 세션 평가·라이브코치·교수자 분석 라운드 마감 + 문서 정리 + 코드품질 리팩터
- 누적 작업트리 커밋: 회기 평가 복구·durable 저장, 라이브 코치 이력/근거, 교수자 학생분석, 음성 비언어 메타, PII 마스킹, 운영 티켓/헬스 등
- 문서: 완료 기록 docs/archive/ 냉동 보관, docs/ 단일 인덱스(docs/README.md)+통합 TODO(docs/TODO.md)로 정리
- 리팩터(행위 보존): Stage enum SSOT(taxonomy 소유·state_machine re-export), store recent/masked_turns 중복 제거, speaker_ko_label 단일 헬퍼, _list_sessions N+1 제거(state/turns 배치 + 턴평가 하이드레이션 배치)
- 검증: 백엔드 pytest 352 passed, _list_sessions E2E chromium-single-run 2 passed
2026-07-02 02:50:36 +09:00

136 lines
5.1 KiB
PowerShell

# boot-public-runtime.ps1
# 목적: PC 재부팅/재로그온 후 퍼블릭 런타임을 자동으로 복구한다.
# 순서: Docker Desktop 데몬 대기 -> postgres(vignette-dev-db) 기동 ->
# API 가 이미 healthy 면 스킵, 아니면 start-public-runtime.ps1(-SkipWebRestart) 로
# 엔진 게이트웨이(9099) + API(8001) + cloudflared 터널을 올린다.
# 멱등: 어느 단계든 이미 살아있으면 건드리지 않는다. 수동으로 여러 번 실행해도 안전.
#
# 등록(로그온 시 자동 실행, 숨김 창):
# powershell -NoProfile -ExecutionPolicy Bypass -File scripts\register-boot-task.ps1
# 또는 수동:
# powershell -NoProfile -ExecutionPolicy Bypass -File scripts\boot-public-runtime.ps1
param(
[string]$Workspace = "D:\workspace\vignette",
[string]$DockerDesktop = "C:\Program Files\Docker\Docker\Docker Desktop.exe",
[int]$DaemonTimeoutSec = 360,
[int]$DbTimeoutSec = 90,
[int]$DbPort = 55432,
[int]$ApiPort = 8001,
[string]$BootLog = "D:\workspace\vignette\boot-public-runtime.log"
)
$ErrorActionPreference = "Continue" # 부트 스크립트는 끝까지 로깅하고 종료한다.
function Write-BootLog([string]$Message) {
$line = "[{0}] {1}" -f (Get-Date -Format "yyyy-MM-dd HH:mm:ss"), $Message
try {
Add-Content -LiteralPath $BootLog -Value $line -Encoding UTF8
} catch {
# 로그 파일이 잠겨도 부팅은 계속한다.
}
}
function Test-DockerDaemon([int]$TimeoutSec = 10) {
try {
$ver = & docker.exe info --format '{{.ServerVersion}}' 2>$null
if ($LASTEXITCODE -eq 0 -and $ver) { return $true }
} catch {}
return $false
}
function Test-Tcp([string]$Host_, [int]$Port) {
try {
$t = (Test-NetConnection -ComputerName $Host_ -Port $Port -WarningAction SilentlyContinue)
return [bool]$t.TcpTestSucceeded
} catch { return $false }
}
function Test-ApiHealthy {
# HttpWebRequest + Proxy=$null: WININET/시스템 프록시에 영향받지 않는 가장 직결적인 검사.
# 비대화형 스케줄러 컨텍스트에서도 127.0.0.1 로 직접 연결한다. 3회 재시도.
for ($i = 1; $i -le 3; $i++) {
try {
$req = [System.Net.HttpWebRequest]::Create("http://127.0.0.1:$ApiPort/health")
$req.Timeout = 5000
$req.ReadWriteTimeout = 5000
$req.Proxy = $null
$resp = $req.GetResponse()
$reader = New-Object System.IO.StreamReader($resp.GetResponseStream())
$body = $reader.ReadToEnd()
$reader.Close(); $resp.Close()
$h = $body | ConvertFrom-Json
if ($h.environment -eq "prod" -and $h.db -eq $true -and $h.engine -eq $true) { return $true }
Write-BootLog (" health probe attempt {0}: not-healthy body={1}" -f $i, $body)
return $false
} catch {
Write-BootLog (" health probe attempt {0} failed: {1}" -f $i, $_.Exception.Message)
Start-Sleep -Seconds 2
}
}
return $false
}
Write-BootLog "================ boot start ================"
# 1) Docker 데몬(내려가 있으면 Docker Desktop 기동 후 대기)
if (-not (Test-DockerDaemon)) {
Write-BootLog "docker daemon down; launching Docker Desktop"
if (Test-Path -LiteralPath $DockerDesktop) {
Start-Process -FilePath $DockerDesktop | Out-Null
} else {
Write-BootLog "ERROR: Docker Desktop.exe not found at $DockerDesktop"
exit 1
}
$deadline = (Get-Date).AddSeconds($DaemonTimeoutSec)
while ((Get-Date) -lt $deadline) {
Start-Sleep -Seconds 5
if (Test-DockerDaemon) { break }
}
}
if (-not (Test-DockerDaemon)) {
Write-BootLog "ERROR: docker daemon did not come up within ${DaemonTimeoutSec}s"
exit 1
}
Write-BootLog "docker daemon up"
# 2) postgres 컨테이너(restart 정책 백업 + 명시 기동) 후 포트 대기
$null = & docker.exe update --restart unless-stopped vignette-dev-db 2>$null
$null = & docker.exe start vignette-dev-db 2>$null
$dbDeadline = (Get-Date).AddSeconds($DbTimeoutSec)
while ((Get-Date) -lt $dbDeadline) {
if (Test-Tcp -Host_ "127.0.0.1" -Port $DbPort) { break }
Start-Sleep -Seconds 3
}
if (-not (Test-Tcp -Host_ "127.0.0.1" -Port $DbPort)) {
Write-BootLog "ERROR: postgres not listening on 127.0.0.1:$DbPort"
exit 1
}
Write-BootLog "postgres 127.0.0.1:$DbPort up"
# 3) 엔진/API/cloudflared — 이미 healthy 면 스킵(불필요한 재시작/다운타임 방지)
if (Test-ApiHealthy) {
Write-BootLog "API already healthy on $ApiPort; skipping engine/api/cloudflared restart"
} else {
$pub = Join-Path $Workspace "scripts\start-public-runtime.ps1"
if (-not (Test-Path -LiteralPath $pub)) {
Write-BootLog "ERROR: start-public-runtime.ps1 not found at $pub"
exit 1
}
Write-BootLog "running start-public-runtime.ps1 -SkipWebRestart"
$out = & powershell.exe -NoProfile -ExecutionPolicy Bypass -File $pub -SkipWebRestart 2>&1
$out | ForEach-Object { Write-BootLog (" pub> " + $_) }
if ($LASTEXITCODE -ne 0) {
Write-BootLog "ERROR: start-public-runtime.ps1 exit $LASTEXITCODE"
exit 1
}
}
# 4) 최종 확인
if (Test-ApiHealthy) {
Write-BootLog "boot OK: API healthy on $ApiPort"
exit 0
} else {
Write-BootLog "WARN: boot finished but API health check failed — see apps/api/api.public.err.log"
exit 2
}