G0~G8 성과·동맹 측정 OS 작업 일괄 고정
8월 7일까지 워킹트리에만 남아 있던 미커밋 작업을 커밋한다. 여러 사본 폴더(worktree·clone)에 흩어져 있던 중간 스냅샷을 정리하기 전에 원본을 git 이력으로 고정하는 것이 목적이다. - contracts/routes/services: measurement, outcome_trajectory, rupture_repair, deliberate_practice, calibration_transfer, supervision_research, multimodal_alliance, continuous_improvement 계열 신규 모듈과 테스트 - infra/db/init: 07~16 마이그레이션(측정 기반~calibration transfer 실행) - apps/web: 세션 리뷰 카드·관리 화면·E2E 스펙 추가 - docs/ops: G0~G8 라이브 통합·배포·롤백 증거 문서와 evidence JSON/PNG - scripts: smoke·ledger·릴리스 에이전트·NAS 프리뷰 운영 스크립트 engine.public 로그 .bak과 apps/web/test-results 산출물은 커밋에서 제외했다.
This commit is contained in:
parent
93dd8f82d7
commit
16e791e044
390 changed files with 243188 additions and 499 deletions
|
|
@ -36,6 +36,8 @@ $repo = Split-Path -Parent $PSScriptRoot
|
|||
$api = Join-Path $repo 'apps\api'
|
||||
$web = Join-Path $repo 'apps\web'
|
||||
$logs = Join-Path $repo '.devlogs'
|
||||
$DevDbContainerName = 'vignette-dev-db'
|
||||
$DevDbDataVolume = 'vignette-dev-db-pgdata'
|
||||
New-Item -ItemType Directory -Force -Path $logs | Out-Null
|
||||
|
||||
# uvicorn 이 설치된 python 을 해석한다(시스템에 3.11/3.14 등 복수 python 공존 — 'python' 별칭이
|
||||
|
|
@ -137,21 +139,47 @@ function Test-DevDbRoleSafety([string]$ownerUser, [string]$dbName) {
|
|||
$parts = "$row".Split(':')
|
||||
if ($parts.Count -lt 3) { continue }
|
||||
if ($parts[0] -eq 'vignette_app' -and ($parts[1] -eq 't' -or $parts[2] -eq 't')) {
|
||||
Write-Host ' WARN vignette_app role 이 SUPERUSER/BYPASSRLS 상태 - RLS 검증이 무의미함. 컨테이너를 owner/app 분리로 재생성 필요.'
|
||||
Write-Host ' WARN vignette_app role 이 SUPERUSER/BYPASSRLS 상태 - RLS 검증이 무의미함. 데이터 보존형 owner/app migration 필요(dev-up은 DB를 교체하지 않음).'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Inspect-DevDbContainer {
|
||||
$healthMode = & docker inspect -f '{{if .Config.Healthcheck}}health{{else}}none{{end}}' vignette-dev-db 2>$null
|
||||
if ($LASTEXITCODE -eq 0 -and "$healthMode" -eq 'none') {
|
||||
Write-Host ' WARN vignette-dev-db healthcheck 없음 - 기존 컨테이너라면 재생성 시 healthcheck가 붙음.'
|
||||
function Get-DevDbContainerState {
|
||||
$state = & docker inspect -f '{{.State.Status}}' $DevDbContainerName 2>$null
|
||||
if ($LASTEXITCODE -ne 0) { return $null }
|
||||
return "$state".Trim()
|
||||
}
|
||||
|
||||
function Get-DevDbContainerEnvValue([string]$key, [string]$fallback) {
|
||||
$envLines = @(& docker inspect -f '{{range .Config.Env}}{{println .}}{{end}}' $DevDbContainerName 2>$null)
|
||||
if ($LASTEXITCODE -ne 0) { return $fallback }
|
||||
$prefix = "${key}="
|
||||
foreach ($line in $envLines) {
|
||||
$text = "$line"
|
||||
if ($text.StartsWith($prefix, [StringComparison]::Ordinal)) {
|
||||
return $text.Substring($prefix.Length)
|
||||
}
|
||||
}
|
||||
$ownerUser = & docker exec vignette-dev-db printenv POSTGRES_USER 2>$null
|
||||
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($ownerUser)) { $ownerUser = 'vignette_owner' }
|
||||
$dbName = & docker exec vignette-dev-db printenv POSTGRES_DB 2>$null
|
||||
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($dbName)) { $dbName = 'vignette' }
|
||||
& docker exec vignette-dev-db pg_isready -U $ownerUser 1>$null 2>$null
|
||||
return $fallback
|
||||
}
|
||||
|
||||
function Wait-DevDbReady([string]$ownerUser, [int]$attempts = 24) {
|
||||
for ($i = 0; $i -lt $attempts; $i++) {
|
||||
& docker exec $DevDbContainerName pg_isready -U $ownerUser 1>$null 2>$null
|
||||
if ($LASTEXITCODE -eq 0) { return $true }
|
||||
Start-Sleep -Seconds 2
|
||||
}
|
||||
return $false
|
||||
}
|
||||
|
||||
function Inspect-DevDbContainer {
|
||||
$healthMode = & docker inspect -f '{{if .Config.Healthcheck}}health{{else}}none{{end}}' $DevDbContainerName 2>$null
|
||||
if ($LASTEXITCODE -eq 0 -and "$healthMode" -eq 'none') {
|
||||
Write-Host ' WARN vignette-dev-db healthcheck 없음 - 데이터는 유지하고 pg_isready로 점검함(dev-up은 DB를 재생성하지 않음).'
|
||||
}
|
||||
$ownerUser = Get-DevDbContainerEnvValue 'POSTGRES_USER' 'vignette_owner'
|
||||
$dbName = Get-DevDbContainerEnvValue 'POSTGRES_DB' 'vignette'
|
||||
& docker exec $DevDbContainerName pg_isready -U $ownerUser 1>$null 2>$null
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Host ' OK db (vignette-dev-db 준비됨)'
|
||||
} else {
|
||||
|
|
@ -160,6 +188,37 @@ function Inspect-DevDbContainer {
|
|||
Test-DevDbRoleSafety "$ownerUser" "$dbName"
|
||||
}
|
||||
|
||||
function Start-ExistingDevDbContainer([string]$state) {
|
||||
if ($state -notin @('created', 'exited')) {
|
||||
throw "vignette-dev-db 상태 '$state'는 자동 복구 대상이 아님. dev-up은 기존 DB를 제거하거나 교체하지 않는다."
|
||||
}
|
||||
Write-Host (" start db {0} (기존 PGDATA 보존)" -f $DevDbContainerName)
|
||||
& docker start $DevDbContainerName 1>$null
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw '기존 vignette-dev-db 시작 실패. 데이터 교체 없이 중단함. pg_dump와 복구 검증을 갖춘 별도 유지보수 절차가 필요하다.'
|
||||
}
|
||||
$ownerUser = Get-DevDbContainerEnvValue 'POSTGRES_USER' 'vignette_owner'
|
||||
if (-not (Wait-DevDbReady $ownerUser)) {
|
||||
throw '기존 vignette-dev-db가 시작됐지만 pg_isready가 실패함. 컨테이너/볼륨을 교체하지 않고 중단함.'
|
||||
}
|
||||
Inspect-DevDbContainer
|
||||
}
|
||||
|
||||
function Ensure-DevDbDataVolume {
|
||||
$existing = & docker volume inspect -f '{{.Name}}' $DevDbDataVolume 2>$null
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
if ("$existing".Trim() -ne $DevDbDataVolume) {
|
||||
throw "DB named volume 식별 불일치: $existing"
|
||||
}
|
||||
return
|
||||
}
|
||||
$created = & docker volume create $DevDbDataVolume
|
||||
if ($LASTEXITCODE -ne 0 -or "$created".Trim() -ne $DevDbDataVolume) {
|
||||
throw "DB named volume 생성 실패: $DevDbDataVolume"
|
||||
}
|
||||
Write-Host (" create volume {0} (새 DB 전용 named PGDATA)" -f $DevDbDataVolume)
|
||||
}
|
||||
|
||||
function Ensure-DevDb {
|
||||
if (!(Get-Command docker -ErrorAction SilentlyContinue)) {
|
||||
Write-Host ' WARN docker CLI 없음 - DB 없이 degraded(UI 세션 시작 제한).'
|
||||
|
|
@ -175,13 +234,16 @@ function Ensure-DevDb {
|
|||
return
|
||||
}
|
||||
|
||||
$running = docker ps --filter name=vignette-dev-db --format '{{.Names}}' 2>$null
|
||||
if ($running -match 'vignette-dev-db') {
|
||||
Inspect-DevDbContainer
|
||||
$containerState = Get-DevDbContainerState
|
||||
if (-not [string]::IsNullOrWhiteSpace($containerState)) {
|
||||
if ($containerState -eq 'running') {
|
||||
Inspect-DevDbContainer
|
||||
return
|
||||
}
|
||||
Start-ExistingDevDbContainer $containerState
|
||||
return
|
||||
}
|
||||
|
||||
docker rm -f vignette-dev-db 1>$null 2>$null
|
||||
$apiEnv = Join-Path $api '.env'
|
||||
$dbUrl = Read-EnvFileValue $apiEnv 'DATABASE_URL'
|
||||
if ($dbUrl -notmatch 'postgresql://([^:]+):([^@]+)@[^:]+:([0-9]+)/(\S+)') {
|
||||
|
|
@ -200,10 +262,11 @@ function Ensure-DevDb {
|
|||
if ($appUser -eq $ownerUser) {
|
||||
Write-Host ' WARN DATABASE_URL 이 owner role 을 사용 중 - RLS 검증을 위해 앱 전용 role URL 권장.'
|
||||
}
|
||||
Ensure-DevDbDataVolume
|
||||
$initPath = Join-Path $repo 'infra\db\init'
|
||||
$dockerArgs = @(
|
||||
'run', '-d',
|
||||
'--name', 'vignette-dev-db',
|
||||
'--name', $DevDbContainerName,
|
||||
'-p', ('{0}:5432' -f $port),
|
||||
'--health-cmd', ('pg_isready -U {0}' -f $ownerUser),
|
||||
'--health-interval', '10s',
|
||||
|
|
@ -214,22 +277,22 @@ function Ensure-DevDb {
|
|||
'-e', ('POSTGRES_DB={0}' -f $db),
|
||||
'-e', ('APP_DB_USER={0}' -f $appUser),
|
||||
'-e', ('APP_DB_PASSWORD={0}' -f $appPassword),
|
||||
'--mount', ('type=volume,source={0},target=/var/lib/postgresql/data' -f $DevDbDataVolume),
|
||||
'-v', ('{0}:/docker-entrypoint-initdb.d:ro' -f $initPath),
|
||||
'pgvector/pgvector:pg16'
|
||||
)
|
||||
& docker @dockerArgs 1>$null
|
||||
|
||||
for ($i = 0; $i -lt 24; $i++) {
|
||||
Start-Sleep -Seconds 2
|
||||
docker exec vignette-dev-db pg_isready -U $ownerUser 1>$null 2>$null
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Host ' OK db (Postgres 준비됨, init 스키마 적용)'
|
||||
Test-DevDbRoleSafety $ownerUser $db
|
||||
Start-Sleep -Seconds 1
|
||||
return
|
||||
}
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw '새 vignette-dev-db 컨테이너 생성 실패. named PGDATA volume은 보존함.'
|
||||
}
|
||||
Write-Host ' WARN db 준비 타임아웃'
|
||||
|
||||
if (Wait-DevDbReady $ownerUser) {
|
||||
Write-Host ' OK db (Postgres 준비됨, named PGDATA + init 스키마 적용)'
|
||||
Test-DevDbRoleSafety $ownerUser $db
|
||||
Start-Sleep -Seconds 1
|
||||
return
|
||||
}
|
||||
throw '새 vignette-dev-db 준비 타임아웃. 컨테이너와 named PGDATA volume은 조사/복구를 위해 보존함.'
|
||||
}
|
||||
|
||||
Write-Host '[1/4] 기존 스택 정리...'
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue