Release: weekly deploy
This commit is contained in:
@@ -36,7 +36,7 @@ For a typical weekly release, use the orchestrator instead of running each step
|
|||||||
- `smb.user` / `smb.password` — optional; maps `\\host\share` before image sync
|
- `smb.user` / `smb.password` — optional; maps `\\host\share` before image sync
|
||||||
- `backupFile` — optional fixed path; otherwise uses the newest `gallery_dev_data_*.txt` after backup
|
- `backupFile` — optional fixed path; otherwise uses the newest `gallery_dev_data_*.txt` after backup
|
||||||
|
|
||||||
3. Prerequisites still apply: Docker Desktop running, `docker login gitea.mysuperlab.netcraze.pro`, [`infra/docker/.env.prod`](../infra/docker/.env.prod) present.
|
3. Prerequisites still apply: **`npm run dev:web` running** (Vite `:5173` + API `:3451`), Docker Desktop running, `docker login gitea.mysuperlab.netcraze.pro`, [`infra/docker/.env.prod`](../infra/docker/.env.prod) present.
|
||||||
|
|
||||||
### Run
|
### Run
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ npm run devtoprod:release -- -DryRun
|
|||||||
|
|
||||||
| Config step | Maps to runbook |
|
| Config step | Maps to runbook |
|
||||||
|-------------|-----------------|
|
|-------------|-----------------|
|
||||||
| `validateBuild` | Step 0 — `prod:build` + dev `/api/bounds` check |
|
| `validateBuild` | Step 0 — `prod:build` + dev `/api/bounds` check (API `:3451` and/or Vite `:5173` proxy) |
|
||||||
| `gitCommitPush` | Step 1 — `git add`, commit, push |
|
| `gitCommitPush` | Step 1 — `git add`, commit, push |
|
||||||
| `thumbnails` | Step 2 — `devtoprod:thumbnails` |
|
| `thumbnails` | Step 2 — `devtoprod:thumbnails` |
|
||||||
| `backupDev` | Step 3 — `dev:db:backup` |
|
| `backupDev` | Step 3 — `dev:db:backup` |
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
"password": ""
|
"password": ""
|
||||||
},
|
},
|
||||||
"verify": {
|
"verify": {
|
||||||
|
"devApiUrl": "http://127.0.0.1:3451/api/bounds",
|
||||||
"devLanUrl": "http://192.168.10.70:5173/api/bounds",
|
"devLanUrl": "http://192.168.10.70:5173/api/bounds",
|
||||||
"devUrl": "https://devgallery.mysuperlab.netcraze.pro/api/bounds",
|
"devUrl": "https://devgallery.mysuperlab.netcraze.pro/api/bounds",
|
||||||
"lanUrl": "http://192.168.10.122:5173/api/bounds",
|
"lanUrl": "http://192.168.10.122:5173/api/bounds",
|
||||||
|
|||||||
@@ -203,29 +203,57 @@ function Get-LatestDevBackup {
|
|||||||
return $latest.FullName
|
return $latest.FullName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Get-DevApiPort {
|
||||||
|
$envFile = Join-Path $RepoRoot '.env'
|
||||||
|
$port = '3451'
|
||||||
|
if (Test-Path $envFile) {
|
||||||
|
foreach ($line in Get-Content -LiteralPath $envFile) {
|
||||||
|
if ($line -match '^\s*PORT\s*=\s*(\d+)\s*$') {
|
||||||
|
$port = $Matches[1]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $port
|
||||||
|
}
|
||||||
|
|
||||||
function Test-BoundsJson {
|
function Test-BoundsJson {
|
||||||
param(
|
param(
|
||||||
[string]$Url,
|
[string]$Url,
|
||||||
[switch]$Insecure,
|
[switch]$Insecure,
|
||||||
[int]$MaxTimeSeconds = 10,
|
[int]$MaxTimeSeconds = 10,
|
||||||
[int]$Retries = 3
|
[int]$Retries = 3,
|
||||||
|
[ref]$Detail
|
||||||
)
|
)
|
||||||
|
|
||||||
|
$lastDetail = 'no response'
|
||||||
for ($attempt = 1; $attempt -le $Retries; $attempt++) {
|
for ($attempt = 1; $attempt -le $Retries; $attempt++) {
|
||||||
$curlArgs = @('-s', '-f', '--max-time', $MaxTimeSeconds)
|
$curlArgs = @('-s', '-w', "`nHTTP_CODE:%{http_code}", '--max-time', $MaxTimeSeconds)
|
||||||
if ($Insecure) { $curlArgs += '-k' }
|
if ($Insecure) { $curlArgs += '-k' }
|
||||||
$curlArgs += $Url
|
$curlArgs += $Url
|
||||||
|
|
||||||
$body = & curl.exe @curlArgs 2>$null
|
$raw = (& curl.exe @curlArgs 2>&1 | Out-String).TrimEnd()
|
||||||
if ($LASTEXITCODE -eq 0 -and $body) {
|
$httpCode = $null
|
||||||
|
$body = $raw
|
||||||
|
if ($raw -match '(?s)^(.*)HTTP_CODE:(\d+)\s*$') {
|
||||||
|
$body = $Matches[1].TrimEnd()
|
||||||
|
$httpCode = $Matches[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($httpCode -eq '200' -and $body) {
|
||||||
try {
|
try {
|
||||||
$json = $body | ConvertFrom-Json
|
$json = $body | ConvertFrom-Json
|
||||||
if ($null -ne $json.min_year -and $null -ne $json.max_year) {
|
if ($null -ne $json.min_year -and $null -ne $json.max_year) {
|
||||||
return $true
|
return $true
|
||||||
}
|
}
|
||||||
|
$lastDetail = 'HTTP 200 but JSON missing min_year/max_year'
|
||||||
} catch {
|
} catch {
|
||||||
# Continue retries below.
|
$lastDetail = 'HTTP 200 but response is not valid JSON'
|
||||||
}
|
}
|
||||||
|
} elseif ($httpCode) {
|
||||||
|
$lastDetail = "HTTP $httpCode"
|
||||||
|
} else {
|
||||||
|
$lastDetail = "curl exit $LASTEXITCODE"
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($attempt -lt $Retries) {
|
if ($attempt -lt $Retries) {
|
||||||
@@ -233,6 +261,7 @@ function Test-BoundsJson {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($Detail) { $Detail.Value = $lastDetail }
|
||||||
return $false
|
return $false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,9 +311,13 @@ if (Get-StepEnabled $steps 'validateBuild') {
|
|||||||
if ($code -ne 0) { return $code }
|
if ($code -ne 0) { return $code }
|
||||||
|
|
||||||
# Dev-only endpoints. Do not use verify.lanUrl here (that is prod on TrueNAS).
|
# Dev-only endpoints. Do not use verify.lanUrl here (that is prod on TrueNAS).
|
||||||
|
$devApiPort = Get-DevApiPort
|
||||||
$devCandidates = @(
|
$devCandidates = @(
|
||||||
|
"http://127.0.0.1:$devApiPort/api/bounds"
|
||||||
|
'http://localhost:5173/api/bounds'
|
||||||
'http://192.168.10.70:5173/api/bounds'
|
'http://192.168.10.70:5173/api/bounds'
|
||||||
)
|
)
|
||||||
|
if ($cfg.verify.devApiUrl) { $devCandidates += $cfg.verify.devApiUrl }
|
||||||
if ($cfg.verify.devLanUrl) { $devCandidates += $cfg.verify.devLanUrl }
|
if ($cfg.verify.devLanUrl) { $devCandidates += $cfg.verify.devLanUrl }
|
||||||
if ($cfg.verify.devUrl) { $devCandidates += $cfg.verify.devUrl }
|
if ($cfg.verify.devUrl) { $devCandidates += $cfg.verify.devUrl }
|
||||||
$devCandidates = $devCandidates | Select-Object -Unique
|
$devCandidates = $devCandidates | Select-Object -Unique
|
||||||
@@ -293,16 +326,19 @@ if (Get-StepEnabled $steps 'validateBuild') {
|
|||||||
foreach ($u in $devCandidates) {
|
foreach ($u in $devCandidates) {
|
||||||
Write-Host "Checking dev /api/bounds: $u"
|
Write-Host "Checking dev /api/bounds: $u"
|
||||||
$useInsecure = $u.StartsWith('https://', [System.StringComparison]::OrdinalIgnoreCase)
|
$useInsecure = $u.StartsWith('https://', [System.StringComparison]::OrdinalIgnoreCase)
|
||||||
if (Test-BoundsJson -Url $u -Insecure:$useInsecure -MaxTimeSeconds 10 -Retries 3) {
|
$detail = ''
|
||||||
|
if (Test-BoundsJson -Url $u -Insecure:$useInsecure -MaxTimeSeconds 10 -Retries 3 -Detail ([ref]$detail)) {
|
||||||
Write-Host "Dev API OK: $u"
|
Write-Host "Dev API OK: $u"
|
||||||
$devOk = $true
|
$devOk = $true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
Write-Host "Dev API check failed for: $u" -ForegroundColor DarkYellow
|
Write-Host "Dev API check failed for: $u ($detail)" -ForegroundColor DarkYellow
|
||||||
}
|
}
|
||||||
|
|
||||||
if (-not $devOk) {
|
if (-not $devOk) {
|
||||||
Write-Host "Dev API check failed on all URLs." -ForegroundColor Red
|
Write-Host "Dev API check failed on all URLs." -ForegroundColor Red
|
||||||
|
Write-Host "Start the full dev stack before release: npm run dev:web" -ForegroundColor Yellow
|
||||||
|
Write-Host "Vite on :5173 alone is not enough - the API must listen on :$devApiPort (PORT in .env)." -ForegroundColor Yellow
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
Reference in New Issue
Block a user