Files
smartmate/backend/scripts/dev-common.ps1
Losita 7d324b77aa Version: 0.9.79.dev.260506
后端:
1. 本地后端启动体系收口到 `backend/scripts`,移除 `cmd/all` 聚合入口,并将仓库根兼容启动语义收敛为 `StartAPI` 别名;新增 dev-up / dev-down / services-up / services-down / dev-status / dev-logs / service-restart 脚本,统一托管多服务进程、日志、PID 与基础设施启动。
2. 课表服务超时口径统一放宽到 5 分钟,覆盖 gateway / client / rpc server / config example,避免课表导入与图片识别在长耗时场景下被内层提前截断。
3. `today` 课表查询修正为读取真实当前日期,不再使用硬编码测试日期;同时剔除旧缓存与返回结果里的 `empty` 占位事件,后端只返回真实日程,空档改由前端时间轴自行补齐。

前端:
4. 首页路由切回改为复用 `DashboardView` 实例,补 `keep-alive`、`onActivated` 与双帧缩放重算,修复从侧栏返回首页时首帧布局放大与重复加载闪动问题。
5. 首页加载态与今日时间线口径收口:移除额外 800ms `pageLoading` 人为延迟,task / schedule 改为分开驱动;时间线忽略 `empty` 事件,并统一空档文案为“无课”。
6. 收敛助手页与首页若干进场/弹性动画,降低结果卡片、微调弹窗、思考区与面板切换时的抖动感。

仓库:
7. README 补充后端本地快速启动说明,`.gitignore` 忽略 `backend/.dev` 脚本运行态产物。
2026-05-06 12:59:29 +08:00

901 lines
26 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
$BackendRoot = Split-Path -Parent $PSScriptRoot
$RepoRoot = Split-Path -Parent $BackendRoot
$ComposeFile = Join-Path $RepoRoot "docker-compose.yml"
$StateRoot = Join-Path $BackendRoot ".dev"
$PidRoot = Join-Path $StateRoot "pids"
$LogRoot = Join-Path $StateRoot "logs"
$BinRoot = Join-Path $StateRoot "bin"
$DockerConfigRoot = Join-Path $StateRoot "docker-config"
$GoCacheRoot = Join-Path $StateRoot "gocache"
function Initialize-DevState {
foreach ($path in @($StateRoot, $PidRoot, $LogRoot, $BinRoot, $DockerConfigRoot, $GoCacheRoot)) {
if (-not (Test-Path -LiteralPath $path)) {
New-Item -ItemType Directory -Path $path -Force | Out-Null
}
}
# 1. 当前桌面环境里可能同时注入 Path / PATH 两个大小写不同的环境变量。
# 2. Windows PowerShell 的 Env: 提供器与 Start-Process 在这种情况下会报“重复键”错误。
# 3. 这里统一只保留一个进程级 Path避免启动脚本因为环境脏数据直接失败。
$effectivePath = ""
try {
$pathValue = [System.Environment]::GetEnvironmentVariable("Path", "Process")
if ($null -ne $pathValue) {
$effectivePath = [string]$pathValue
}
}
catch {
$effectivePath = ""
}
[System.Environment]::SetEnvironmentVariable("PATH", $null, "Process")
[System.Environment]::SetEnvironmentVariable("Path", $null, "Process")
[System.Environment]::SetEnvironmentVariable("Path", $effectivePath, "Process")
$env:DOCKER_CONFIG = $DockerConfigRoot
$env:GOCACHE = $GoCacheRoot
}
function Assert-ToolExists {
param(
[Parameter(Mandatory = $true)]
[string]$Name
)
if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) {
throw "Command not found: $Name"
}
}
function Invoke-ExternalCommand {
param(
[Parameter(Mandatory = $true)]
[string]$FilePath,
[Parameter(Mandatory = $true)]
[string[]]$Arguments,
[Parameter(Mandatory = $true)]
[string]$WorkingDirectory
)
Push-Location $WorkingDirectory
try {
& $FilePath @Arguments
if ($LASTEXITCODE -ne 0) {
throw "Command failed: $FilePath $($Arguments -join ' ')"
}
}
finally {
Pop-Location
}
}
function Get-InfrastructureDefinitions {
return @(
[pscustomobject]@{
Name = "mysql"
Container = "smartflow-mysql"
TimeoutSec = 120
},
[pscustomobject]@{
Name = "redis"
Container = "smartflow-redis"
TimeoutSec = 90
},
[pscustomobject]@{
Name = "kafka"
Container = "smartflow-kafka"
TimeoutSec = 180
},
[pscustomobject]@{
Name = "etcd"
Container = "smartflow-etcd"
TimeoutSec = 120
},
[pscustomobject]@{
Name = "minio"
Container = "smartflow-minio"
TimeoutSec = 120
},
[pscustomobject]@{
Name = "milvus-standalone"
Container = "smartflow-milvus"
TimeoutSec = 240
}
)
}
function Get-InfrastructureComposeServices {
return @(
"mysql",
"redis",
"kafka",
"etcd",
"minio",
"milvus-standalone",
"kafka-init"
)
}
function Get-BackendServiceDefinitions {
return @(
[pscustomobject]@{
Name = "userauth"
Package = "./cmd/userauth"
BinaryPath = (Join-Path $BinRoot "userauth.exe")
Port = 9081
ProbeType = "tcp"
ProbeTarget = $null
StartTimeoutSec = 60
Dependencies = @()
},
[pscustomobject]@{
Name = "task"
Package = "./cmd/task"
BinaryPath = (Join-Path $BinRoot "task.exe")
Port = 9085
ProbeType = "tcp"
ProbeTarget = $null
StartTimeoutSec = 90
Dependencies = @()
},
[pscustomobject]@{
Name = "schedule"
Package = "./cmd/schedule"
BinaryPath = (Join-Path $BinRoot "schedule.exe")
Port = 9084
ProbeType = "tcp"
ProbeTarget = $null
StartTimeoutSec = 90
Dependencies = @()
},
[pscustomobject]@{
Name = "task-class"
Package = "./cmd/task-class"
BinaryPath = (Join-Path $BinRoot "task-class.exe")
Port = 9086
ProbeType = "tcp"
ProbeTarget = $null
StartTimeoutSec = 90
Dependencies = @()
},
[pscustomobject]@{
Name = "course"
Package = "./cmd/course"
BinaryPath = (Join-Path $BinRoot "course.exe")
Port = 9087
ProbeType = "tcp"
ProbeTarget = $null
StartTimeoutSec = 120
Dependencies = @()
},
[pscustomobject]@{
Name = "tokenstore"
Package = "./cmd/tokenstore"
BinaryPath = (Join-Path $BinRoot "tokenstore.exe")
Port = 9095
ProbeType = "tcp"
ProbeTarget = $null
StartTimeoutSec = 90
Dependencies = @()
},
[pscustomobject]@{
Name = "notification"
Package = "./cmd/notification"
BinaryPath = (Join-Path $BinRoot "notification.exe")
Port = 9082
ProbeType = "tcp"
ProbeTarget = $null
StartTimeoutSec = 90
Dependencies = @()
},
[pscustomobject]@{
Name = "memory"
Package = "./cmd/memory"
BinaryPath = (Join-Path $BinRoot "memory.exe")
Port = 9088
ProbeType = "tcp"
ProbeTarget = $null
StartTimeoutSec = 150
Dependencies = @()
},
[pscustomobject]@{
Name = "taskclassforum"
Package = "./cmd/taskclassforum"
BinaryPath = (Join-Path $BinRoot "taskclassforum.exe")
Port = 9090
ProbeType = "tcp"
ProbeTarget = $null
StartTimeoutSec = 90
Dependencies = @("task-class")
},
[pscustomobject]@{
Name = "active-scheduler"
Package = "./cmd/active-scheduler"
BinaryPath = (Join-Path $BinRoot "active-scheduler.exe")
Port = 9083
ProbeType = "tcp"
ProbeTarget = $null
StartTimeoutSec = 120
Dependencies = @("task", "schedule")
},
[pscustomobject]@{
Name = "agent"
Package = "./cmd/agent"
BinaryPath = (Join-Path $BinRoot "agent.exe")
Port = 9089
ProbeType = "tcp"
ProbeTarget = $null
StartTimeoutSec = 180
Dependencies = @("task", "schedule", "task-class", "memory")
},
[pscustomobject]@{
Name = "api"
Package = "./cmd/api"
BinaryPath = (Join-Path $BinRoot "api.exe")
Port = 8080
ProbeType = "http"
ProbeTarget = "http://127.0.0.1:8080/api/v1/health"
StartTimeoutSec = 90
Dependencies = @(
"userauth",
"task",
"schedule",
"task-class",
"course",
"tokenstore",
"notification",
"memory",
"taskclassforum",
"active-scheduler",
"agent"
)
}
)
}
function Get-BackendServiceDefinition {
param(
[Parameter(Mandatory = $true)]
[string]$Name
)
foreach ($service in (Get-BackendServiceDefinitions)) {
if ($service.Name -eq $Name) {
return $service
}
}
throw "Service definition not found: $Name"
}
function Get-ServicePidFilePath {
param(
[Parameter(Mandatory = $true)]
[pscustomobject]$Service
)
return (Join-Path $PidRoot "$($Service.Name).json")
}
function Get-ServiceStdoutLogPath {
param(
[Parameter(Mandatory = $true)]
[pscustomobject]$Service
)
return (Join-Path $LogRoot "$($Service.Name).log")
}
function Get-ServiceStderrLogPath {
param(
[Parameter(Mandatory = $true)]
[pscustomobject]$Service
)
return (Join-Path $LogRoot "$($Service.Name).err.log")
}
function New-ServiceLogPaths {
param(
[Parameter(Mandatory = $true)]
[pscustomobject]$Service
)
$stamp = Get-Date -Format "yyyyMMdd-HHmmss"
return [pscustomobject]@{
Stdout = (Join-Path $LogRoot "$($Service.Name)-$stamp.log")
Stderr = (Join-Path $LogRoot "$($Service.Name)-$stamp.err.log")
}
}
function Read-ServicePidInfo {
param(
[Parameter(Mandatory = $true)]
[pscustomobject]$Service
)
$path = Get-ServicePidFilePath -Service $Service
if (-not (Test-Path -LiteralPath $path)) {
return $null
}
return (Get-Content -LiteralPath $path -Encoding UTF8 | ConvertFrom-Json)
}
function Get-LatestServiceLogPath {
param(
[Parameter(Mandatory = $true)]
[pscustomobject]$Service,
[Parameter(Mandatory = $true)]
[ValidateSet("stdout", "stderr")]
[string]$Stream
)
$pidInfo = Read-ServicePidInfo -Service $Service
if ($null -ne $pidInfo) {
$managedPath = if ($Stream -eq "stdout") { [string]$pidInfo.StdoutLogPath } else { [string]$pidInfo.StderrLogPath }
if (-not [string]::IsNullOrWhiteSpace($managedPath) -and (Test-Path -LiteralPath $managedPath)) {
return $managedPath
}
}
if ($Stream -eq "stdout") {
$candidates = @(Get-ChildItem -LiteralPath $LogRoot -Filter "$($Service.Name)-*.log" -ErrorAction SilentlyContinue |
Where-Object { $_.Name -notlike "*.err.log" } |
Sort-Object LastWriteTime -Descending)
}
else {
$candidates = @(Get-ChildItem -LiteralPath $LogRoot -Filter "$($Service.Name)-*.err.log" -ErrorAction SilentlyContinue |
Sort-Object LastWriteTime -Descending)
}
if ($null -eq $candidates -or $candidates.Count -eq 0) {
return $null
}
return $candidates[0].FullName
}
function Write-ServicePidInfo {
param(
[Parameter(Mandatory = $true)]
[pscustomobject]$Service,
[Parameter(Mandatory = $true)]
[System.Diagnostics.Process]$Process,
[Parameter(Mandatory = $true)]
[string]$StdoutLogPath,
[Parameter(Mandatory = $true)]
[string]$StderrLogPath
)
$payload = [pscustomobject]@{
Name = $Service.Name
Pid = [int]$Process.Id
Port = [int]$Service.Port
StartedAt = (Get-Date).ToString("o")
BinaryPath = $Service.BinaryPath
StdoutLogPath = $StdoutLogPath
StderrLogPath = $StderrLogPath
}
$path = Get-ServicePidFilePath -Service $Service
$payload | ConvertTo-Json | Set-Content -LiteralPath $path -Encoding UTF8
}
function Remove-ServicePidInfo {
param(
[Parameter(Mandatory = $true)]
[pscustomobject]$Service
)
$path = Get-ServicePidFilePath -Service $Service
if (Test-Path -LiteralPath $path) {
Remove-Item -LiteralPath $path -Force
}
}
function Test-ProcessAlive {
param(
[Parameter(Mandatory = $true)]
[int]$ProcessId
)
return ($null -ne (Get-Process -Id $ProcessId -ErrorAction SilentlyContinue))
}
function Get-ListeningProcessId {
param(
[Parameter(Mandatory = $true)]
[int]$Port
)
try {
$connection = Get-NetTCPConnection -LocalPort $Port -State Listen -ErrorAction Stop |
Sort-Object -Property OwningProcess |
Select-Object -First 1
if ($null -ne $connection) {
return [int]$connection.OwningProcess
}
}
catch {
}
$pattern = "^\s*TCP\s+\S+:$Port\s+\S+\s+LISTENING\s+(\d+)\s*$"
foreach ($line in (netstat -ano -p tcp)) {
if ($line -match $pattern) {
return [int]$matches[1]
}
}
return $null
}
function Test-TcpPort {
param(
[Parameter(Mandatory = $true)]
[int]$Port,
[string]$HostName = "127.0.0.1",
[int]$TimeoutMs = 500
)
$client = New-Object System.Net.Sockets.TcpClient
try {
$asyncResult = $client.BeginConnect($HostName, $Port, $null, $null)
if (-not $asyncResult.AsyncWaitHandle.WaitOne($TimeoutMs, $false)) {
return $false
}
$client.EndConnect($asyncResult)
return $true
}
catch {
return $false
}
finally {
$client.Close()
}
}
function Test-HttpEndpoint {
param(
[Parameter(Mandatory = $true)]
[string]$Url,
[int]$TimeoutSec = 2
)
try {
$response = Invoke-WebRequest -Uri $Url -Method Get -TimeoutSec $TimeoutSec -UseBasicParsing
return ($response.StatusCode -ge 200 -and $response.StatusCode -lt 300)
}
catch {
return $false
}
}
function Test-ServiceHealthy {
param(
[Parameter(Mandatory = $true)]
[pscustomobject]$Service
)
switch ($Service.ProbeType) {
"http" {
return (Test-HttpEndpoint -Url $Service.ProbeTarget)
}
default {
return (Test-TcpPort -Port $Service.Port)
}
}
}
function Wait-ServiceReady {
param(
[Parameter(Mandatory = $true)]
[pscustomobject]$Service,
[int]$TimeoutSec,
[int]$ProcessId
)
$effectiveTimeout = $TimeoutSec
if ($effectiveTimeout -le 0) {
$effectiveTimeout = [int]$Service.StartTimeoutSec
}
$deadline = (Get-Date).AddSeconds($effectiveTimeout)
while ((Get-Date) -lt $deadline) {
if ($ProcessId -gt 0 -and -not (Test-ProcessAlive -ProcessId $ProcessId)) {
return $false
}
if (Test-ServiceHealthy -Service $Service) {
return $true
}
Start-Sleep -Seconds 1
}
return $false
}
function Get-ServiceStatus {
param(
[Parameter(Mandatory = $true)]
[pscustomobject]$Service
)
$pidInfo = Read-ServicePidInfo -Service $Service
$managedPid = $null
$hasStalePid = $false
$stdoutLogPath = Get-ServiceStdoutLogPath -Service $Service
$stderrLogPath = Get-ServiceStderrLogPath -Service $Service
if ($null -ne $pidInfo) {
$managedPid = [int]$pidInfo.Pid
if (-not [string]::IsNullOrWhiteSpace($pidInfo.StdoutLogPath)) {
$stdoutLogPath = [string]$pidInfo.StdoutLogPath
}
if (-not [string]::IsNullOrWhiteSpace($pidInfo.StderrLogPath)) {
$stderrLogPath = [string]$pidInfo.StderrLogPath
}
if (-not (Test-ProcessAlive -ProcessId $managedPid)) {
$hasStalePid = $true
$managedPid = $null
}
}
$healthy = Test-ServiceHealthy -Service $Service
$listenerPid = Get-ListeningProcessId -Port $Service.Port
if ($null -ne $managedPid) {
if ($healthy) {
return [pscustomobject]@{
State = "managed"
Summary = "managed-running"
Pid = $managedPid
Port = $Service.Port
HasStalePid = $false
StdoutLog = $stdoutLogPath
StderrLog = $stderrLogPath
}
}
return [pscustomobject]@{
State = "managed_unhealthy"
Summary = "managed-not-ready"
Pid = $managedPid
Port = $Service.Port
HasStalePid = $false
StdoutLog = $stdoutLogPath
StderrLog = $stderrLogPath
}
}
if ($healthy) {
return [pscustomobject]@{
State = "external"
Summary = "external-running"
Pid = $listenerPid
Port = $Service.Port
HasStalePid = $hasStalePid
StdoutLog = $null
StderrLog = $null
}
}
if ($null -ne $listenerPid) {
return [pscustomobject]@{
State = "conflict"
Summary = "port-conflict"
Pid = $listenerPid
Port = $Service.Port
HasStalePid = $hasStalePid
StdoutLog = $null
StderrLog = $null
}
}
if ($hasStalePid) {
return [pscustomobject]@{
State = "stale"
Summary = "stale-pid-file"
Pid = $null
Port = $Service.Port
HasStalePid = $true
StdoutLog = $stdoutLogPath
StderrLog = $stderrLogPath
}
}
return [pscustomobject]@{
State = "stopped"
Summary = "stopped"
Pid = $null
Port = $Service.Port
HasStalePid = $false
StdoutLog = $stdoutLogPath
StderrLog = $stderrLogPath
}
}
function Build-ServiceBinary {
param(
[Parameter(Mandatory = $true)]
[pscustomobject]$Service
)
Initialize-DevState
Invoke-ExternalCommand -FilePath "go" -Arguments @(
"build",
"-o",
$Service.BinaryPath,
$Service.Package
) -WorkingDirectory $BackendRoot
}
function Start-ServiceProcess {
param(
[Parameter(Mandatory = $true)]
[pscustomobject]$Service
)
$logPaths = New-ServiceLogPaths -Service $Service
$stdoutLog = $logPaths.Stdout
$stderrLog = $logPaths.Stderr
$process = Start-Process -FilePath $Service.BinaryPath `
-WorkingDirectory $BackendRoot `
-WindowStyle Hidden `
-RedirectStandardOutput $stdoutLog `
-RedirectStandardError $stderrLog `
-PassThru
Write-ServicePidInfo -Service $Service -Process $process -StdoutLogPath $stdoutLog -StderrLogPath $stderrLog
if (-not (Wait-ServiceReady -Service $Service -TimeoutSec $Service.StartTimeoutSec -ProcessId $process.Id)) {
if (Test-ProcessAlive -ProcessId $process.Id) {
Stop-Process -Id $process.Id -Force -ErrorAction SilentlyContinue
}
Remove-ServicePidInfo -Service $Service
throw "Service start failed: $($Service.Name). Check logs: $stdoutLog and $stderrLog"
}
return $process
}
function Stop-ServiceProcess {
param(
[Parameter(Mandatory = $true)]
[pscustomobject]$Service
)
$pidInfo = Read-ServicePidInfo -Service $Service
if ($null -eq $pidInfo) {
return [pscustomobject]@{
Name = $Service.Name
Action = "skip"
Message = "no managed process"
}
}
$managedProcessId = [int]$pidInfo.Pid
if (Test-ProcessAlive -ProcessId $managedProcessId) {
try {
Stop-Process -Id $managedProcessId -ErrorAction Stop
}
catch {
Stop-Process -Id $managedProcessId -Force -ErrorAction SilentlyContinue
}
$deadline = (Get-Date).AddSeconds(15)
while ((Get-Date) -lt $deadline -and (Test-ProcessAlive -ProcessId $managedProcessId)) {
Start-Sleep -Milliseconds 500
}
if (Test-ProcessAlive -ProcessId $managedProcessId) {
Stop-Process -Id $managedProcessId -Force -ErrorAction SilentlyContinue
}
}
Remove-ServicePidInfo -Service $Service
return [pscustomobject]@{
Name = $Service.Name
Action = "stopped"
Message = "managed process stopped"
}
}
function Ensure-ServiceStarted {
param(
[Parameter(Mandatory = $true)]
[pscustomobject]$Service
)
$status = Get-ServiceStatus -Service $Service
if ($status.HasStalePid) {
Remove-ServicePidInfo -Service $Service
$status = Get-ServiceStatus -Service $Service
}
switch ($status.State) {
"managed" {
return [pscustomobject]@{
Name = $Service.Name
Port = $Service.Port
Action = "skip"
Detail = "managed and healthy"
}
}
"external" {
return [pscustomobject]@{
Name = $Service.Name
Port = $Service.Port
Action = "skip"
Detail = "external process already healthy"
}
}
"managed_unhealthy" {
if (Wait-ServiceReady -Service $Service -TimeoutSec 15 -ProcessId $status.Pid) {
return [pscustomobject]@{
Name = $Service.Name
Port = $Service.Port
Action = "skip"
Detail = "existing managed process became healthy"
}
}
throw "Service $($Service.Name) already has a managed process, but it is still not healthy after waiting. Check log: $($status.StdoutLog)"
}
"conflict" {
throw "Service $($Service.Name) port $($Service.Port) is occupied by process $($status.Pid), but the health check failed. Resolve the conflict first."
}
}
Assert-ServiceDependenciesReady -Service $Service
Build-ServiceBinary -Service $Service
$process = Start-ServiceProcess -Service $Service
return [pscustomobject]@{
Name = $Service.Name
Port = $Service.Port
Action = "start"
Detail = "started, PID=$($process.Id)"
}
}
function Restart-BackendService {
param(
[Parameter(Mandatory = $true)]
[pscustomobject]$Service
)
$status = Get-ServiceStatus -Service $Service
if ($status.HasStalePid) {
Remove-ServicePidInfo -Service $Service
$status = Get-ServiceStatus -Service $Service
}
switch ($status.State) {
"external" {
throw "Service $($Service.Name) is managed by an external process. Refuse to restart it automatically."
}
"conflict" {
throw "Service $($Service.Name) port $($Service.Port) is occupied by process $($status.Pid), but the health check failed. Resolve the conflict first."
}
"managed" {
Stop-ServiceProcess -Service $Service | Out-Null
}
"managed_unhealthy" {
Stop-ServiceProcess -Service $Service | Out-Null
}
}
Assert-ServiceDependenciesReady -Service $Service
Build-ServiceBinary -Service $Service
$process = Start-ServiceProcess -Service $Service
return [pscustomobject]@{
Name = $Service.Name
Port = $Service.Port
Action = "restart"
Detail = "restarted, PID=$($process.Id)"
}
}
function Get-ContainerStatus {
param(
[Parameter(Mandatory = $true)]
[string]$ContainerName
)
$status = cmd.exe /d /c "docker inspect --format ""{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}"" $ContainerName 2>nul"
if ($LASTEXITCODE -ne 0) {
return "unavailable"
}
return (($status | Select-Object -First 1).Trim())
}
function Wait-ContainerStatus {
param(
[Parameter(Mandatory = $true)]
[pscustomobject]$ContainerDefinition
)
$deadline = (Get-Date).AddSeconds([int]$ContainerDefinition.TimeoutSec)
while ((Get-Date) -lt $deadline) {
$status = Get-ContainerStatus -ContainerName $ContainerDefinition.Container
if ($status -eq "healthy") {
return
}
Start-Sleep -Seconds 2
}
throw "Container did not become healthy in time: $($ContainerDefinition.Name)"
}
function Start-BackendInfrastructure {
Assert-ToolExists -Name "docker"
if (-not (Test-Path -LiteralPath $ComposeFile)) {
throw "docker-compose.yml not found: $ComposeFile"
}
$composeServices = Get-InfrastructureComposeServices
Invoke-ExternalCommand -FilePath "docker" -Arguments (@(
"compose",
"-f",
$ComposeFile,
"up",
"-d"
) + $composeServices) -WorkingDirectory $RepoRoot
foreach ($definition in (Get-InfrastructureDefinitions)) {
Wait-ContainerStatus -ContainerDefinition $definition
}
}
function Stop-BackendInfrastructure {
Assert-ToolExists -Name "docker"
$composeServices = Get-InfrastructureComposeServices
Invoke-ExternalCommand -FilePath "docker" -Arguments (@(
"compose",
"-f",
$ComposeFile,
"stop"
) + $composeServices) -WorkingDirectory $RepoRoot
}
function Get-InfrastructureStatus {
$rows = @()
foreach ($definition in (Get-InfrastructureDefinitions)) {
$rows += [pscustomobject]@{
Name = $definition.Name
Container = $definition.Container
Status = (Get-ContainerStatus -ContainerName $definition.Container)
}
}
return $rows
}
function Assert-ServiceDependenciesReady {
param(
[Parameter(Mandatory = $true)]
[pscustomobject]$Service
)
foreach ($dependencyName in $Service.Dependencies) {
$dependency = Get-BackendServiceDefinition -Name $dependencyName
$status = Get-ServiceStatus -Service $dependency
if ($status.State -notin @("managed", "external")) {
throw "Dependency not ready for service $($Service.Name): $dependencyName (current state: $($status.Summary))"
}
}
}