75 lines
4.1 KiB
PowerShell
75 lines
4.1 KiB
PowerShell
param([string]$ProjectRoot = (Split-Path $PSScriptRoot -Parent))
|
|
$ErrorActionPreference = 'Stop'
|
|
Set-Location $ProjectRoot
|
|
. (Join-Path $ProjectRoot 'scripts/Initialize-SecurityEnv.ps1')
|
|
$content = [System.IO.File]::ReadAllText((Join-Path $ProjectRoot '.env'))
|
|
if ((Get-EnvValue $content 'INFRASTRUCTURE_AUTO_UPDATE_ENABLED') -eq 'false') { return }
|
|
$state = Join-Path $ProjectRoot 'data/infrastructure-update'
|
|
[System.IO.Directory]::CreateDirectory($state) | Out-Null
|
|
try { $lock = [System.IO.File]::Open((Join-Path $state 'task.lock'), 'OpenOrCreate', 'ReadWrite', 'None') }
|
|
catch [System.IO.IOException] { return }
|
|
$log = Join-Path $state 'last-run.log'
|
|
$previousConfig = $env:DOCKER_CONFIG
|
|
try {
|
|
[System.IO.File]::WriteAllText($log, "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') 检查 Watchtower 和 updater 更新`n")
|
|
$configFile = Get-EnvValue $content 'DOCKER_CONFIG_FILE'
|
|
if (-not [string]::IsNullOrWhiteSpace($configFile) -and (Test-Path $configFile -PathType Leaf)) {
|
|
$env:DOCKER_CONFIG = Split-Path $configFile -Parent
|
|
}
|
|
& docker compose version *> $null
|
|
$v2 = $LASTEXITCODE -eq 0
|
|
function Invoke-InfrastructureCompose {
|
|
param([string[]]$ComposeArguments)
|
|
if ($v2) { $output = & docker compose @ComposeArguments 2>&1 }
|
|
else { $output = & docker-compose @ComposeArguments 2>&1 }
|
|
$code = $LASTEXITCODE
|
|
$output | Out-File $log -Append -Encoding utf8
|
|
if ($code -ne 0) { throw '组件更新命令未成功,现有容器状态需查看日志' }
|
|
}
|
|
if ($v2) { $available = @(& docker compose config --services) }
|
|
else { $available = @(& docker-compose config --services) }
|
|
if ($LASTEXITCODE -ne 0 -or $available -notcontains 'watchtower') { throw '无法读取 Watchtower 部署配置' }
|
|
$failed = @()
|
|
foreach ($service in @('watchtower', 'easyai-updater')) {
|
|
# Do not enable updater in profile-disabled legacy deployments.
|
|
if ($available -notcontains $service) {
|
|
"$service 未启用,跳过维护" | Out-File $log -Append -Encoding utf8
|
|
continue
|
|
}
|
|
try {
|
|
"检查 $service" | Out-File $log -Append -Encoding utf8
|
|
Invoke-InfrastructureCompose -ComposeArguments @('pull', $service)
|
|
Invoke-InfrastructureCompose -ComposeArguments @('up', '-d', '--no-deps', $service)
|
|
$healthy = $false
|
|
for ($attempt=0; $attempt -lt 12; $attempt++) {
|
|
if ($v2) { $containerId = & docker compose ps -q $service }
|
|
else { $containerId = & docker-compose ps -q $service }
|
|
if ($LASTEXITCODE -ne 0) { throw "无法读取 $service 状态" }
|
|
if ($containerId) {
|
|
$status = & docker inspect -f '{{.State.Running}} {{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}' $containerId
|
|
if ($LASTEXITCODE -ne 0) { throw "无法读取 $service 状态" }
|
|
if ($status -eq 'true healthy') { $healthy = $true; break }
|
|
if ($status -eq 'true none') {
|
|
if ($service -eq 'watchtower') {
|
|
& docker exec $containerId /watchtower --health-check *> $null
|
|
} else {
|
|
& docker exec $containerId node -e "require('http').get('http://127.0.0.1:3015/health',r=>{r.resume();process.exit(r.statusCode===200?0:1)}).on('error',()=>process.exit(1))" *> $null
|
|
}
|
|
if ($LASTEXITCODE -eq 0) { $healthy = $true; break }
|
|
}
|
|
}
|
|
Start-Sleep -Seconds 5
|
|
}
|
|
if (-not $healthy) { throw "$service 暂未恢复可用" }
|
|
"$service 已恢复可用" | Out-File $log -Append -Encoding utf8
|
|
} catch {
|
|
"$service 本次维护未成功,下次定时任务将继续检查:$($_.Exception.Message)" | Out-File $log -Append -Encoding utf8
|
|
$failed += $service
|
|
}
|
|
}
|
|
if ($failed.Count -gt 0) { throw "更新组件维护未全部成功:$($failed -join ', ')" }
|
|
} finally {
|
|
$env:DOCKER_CONFIG = $previousConfig
|
|
$lock.Dispose()
|
|
}
|