#Requires -Version 5.1 # EasyAI Windows 更新脚本 # 拉取仓库最新代码,更新镜像并重启服务 # 用法: .\update.ps1 # 设置控制台编码为 UTF-8 [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 $OutputEncoding = [System.Text.Encoding]::UTF8 $ErrorActionPreference = "Stop" $script:LogDir = $PSScriptRoot if (-not $script:LogDir) { $script:LogDir = $env:TEMP } $script:LogFile = Join-Path $script:LogDir "update.ps1.log" function Write-Log { param([string]$Msg) try { $line = "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] $Msg" Add-Content -Path $script:LogFile -Value $line -Encoding UTF8 -ErrorAction SilentlyContinue } catch { } } trap { $errMsg = $_.Exception.Message try { Add-Content -Path $script:LogFile -Value "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] 错误: $errMsg" -Encoding UTF8 -ErrorAction SilentlyContinue } catch { } Write-Host "" Write-Host "发生错误: $errMsg" -ForegroundColor Red if ($env:CI -ne "true") { Read-Host "按 Enter 键退出" } exit 1 } function Wait-ForExit { if ($env:CI -eq "true") { return } Write-Host "" Read-Host "按 Enter 键退出" } function Write-Step { param($Msg) Write-Host $Msg } function Write-Ok { param($Msg) Write-Host " ✓ $Msg" -ForegroundColor Green } function Write-Err { param($Msg) Write-Host "❌ $Msg" -ForegroundColor Red; throw $Msg } function Write-Warn { param($Msg) Write-Host "⚠️ $Msg" -ForegroundColor Yellow } # -h/--help if ($args -contains "-h" -or $args -contains "--help") { Write-Host "用法: .\update.ps1" Write-Host "" Write-Host "脚本将提示选择更新方式,默认拉取仓库并更新镜像。" exit 0 } # 进入项目目录 $scriptDir = $PSScriptRoot if (-not $scriptDir) { Write-Err "无法获取脚本所在目录" } $composePath = Join-Path $scriptDir "docker-compose.yml" if (-not (Test-Path $composePath)) { Write-Err "未找到 docker-compose.yml,请在 easyai 目录下运行 update.ps1" } Set-Location $scriptDir Write-Step "📁 项目目录: $scriptDir" # ==================== 命令行内选择 ==================== Write-Host "" Write-Host "请选择更新方式:" Write-Host " [1] 更新并拉取仓库(git pull)+ 更新镜像并重启(默认)" Write-Host " [2] 仅更新镜像并重启(跳过 git pull)" $choice = Read-Host "请选择 [1/2,回车默认 1]" if ([string]::IsNullOrWhiteSpace($choice)) { $choice = "1" } $skipRepoUpdate = $false switch ($choice) { "2" { $skipRepoUpdate = $true } "1" { } default { Write-Warn "无效选择,将使用默认:更新并拉取仓库" $skipRepoUpdate = $false } } # ==================== 拉取仓库 ==================== if (-not $skipRepoUpdate) { Write-Host "" Write-Host "===========================" Write-Host "📥 拉取仓库最新代码" Write-Host "===========================" if (-not (Test-Path ".git")) { Write-Err "当前目录不是 Git 仓库,请使用 git clone 克隆项目后使用 update.ps1" } Write-Step "📥 正在执行 git pull..." try { $output = git pull 2>&1 if ($LASTEXITCODE -ne 0) { throw "git pull 返回 $LASTEXITCODE" } Write-Ok "仓库已更新到最新版本" } catch { Write-Err "git pull 失败,请检查网络或远程仓库配置" } # 确保环境配置文件存在 Write-Host "" Write-Step "📝 检查环境配置文件..." if (-not (Test-Path ".env") -and (Test-Path ".env.sample")) { Copy-Item ".env.sample" ".env" Write-Ok ".env" } if (-not (Test-Path ".env.tools") -and (Test-Path ".env.tools.sample")) { Copy-Item ".env.tools.sample" ".env.tools" Write-Ok ".env.tools" } if (-not (Test-Path ".env.ASG") -and (Test-Path ".env.ASG.sample")) { Copy-Item ".env.ASG.sample" ".env.ASG" Write-Ok ".env.ASG" } if (-not (Test-Path ".env.AMS") -and (Test-Path ".env.AMS.sample")) { Copy-Item ".env.AMS.sample" ".env.AMS" Write-Ok ".env.AMS" } Write-Host "" } else { Write-Host "" Write-Step "⏭️ 跳过仓库更新,仅更新镜像并重启" Write-Host "" } # ==================== Docker 检查 ==================== function Test-DockerInstalled { $docker = Get-Command docker -ErrorAction SilentlyContinue if (-not $docker) { return $false } try { $null = & docker --version 2>&1; return $true } catch { return $false } } function Test-DockerRunning { param([int]$TimeoutSeconds = 10) try { $psi = New-Object System.Diagnostics.ProcessStartInfo $psi.FileName = "docker"; $psi.Arguments = "info" $psi.RedirectStandardOutput = $true; $psi.RedirectStandardError = $true $psi.UseShellExecute = $false; $psi.CreateNoWindow = $true $p = [System.Diagnostics.Process]::Start($psi) $exited = $p.WaitForExit($TimeoutSeconds * 1000) if (-not $exited) { try { $p.Kill() } catch { }; return $false } return ($p.ExitCode -eq 0) } catch { return $false } } function Ensure-DockerRunning { if (-not (Test-DockerInstalled)) { return $false } if (Test-DockerRunning) { Write-Ok "Docker 引擎已运行" $v = docker --version 2>$null; if ($v) { Write-Host " $v" } return $true } Write-Warn "Docker 未运行,请手动启动 Docker Desktop 后重新执行" return $false } Write-Host "===========================" Write-Host "🚀 检查 Docker" Write-Host "===========================" Write-Host "" if (-not (Test-DockerInstalled)) { Write-Err "未检测到 Docker,请先运行 start.ps1 完成首次部署" } if (-not (Ensure-DockerRunning)) { Write-Host "" Write-Host "请启动 Docker Desktop,确认其完全就绪后再重新运行 update.ps1" Wait-ForExit exit 1 } # 检测 docker compose $hasComposeV2 = $false try { $null = docker compose version 2>&1; $hasComposeV2 = $true } catch { } # ==================== 更新并启动 ==================== Write-Host "" Write-Step "🚀 重新启动 EasyAI..." if ($hasComposeV2) { docker compose pull if ($LASTEXITCODE -ne 0) { Write-Err "docker compose pull 失败" } docker compose up -d if ($LASTEXITCODE -ne 0) { Write-Err "docker compose up 失败" } } else { docker-compose pull if ($LASTEXITCODE -ne 0) { Write-Err "docker-compose pull 失败" } docker-compose up -d if ($LASTEXITCODE -ne 0) { Write-Err "docker-compose up 失败" } } Write-Host "" Write-Host "================================" Write-Host " 更新完成" Write-Host "================================" Write-Host "🎉 EasyAI 应用更新成功" Write-Host "访问地址: http://127.0.0.1:3010" Write-Host "" Write-Log "=== 更新完成 ===" Wait-ForExit