maksit-webui/utils/modules/ComposeDeploySupport.psm1
Maksym Sadovnychyy b74e9250a7
Some checks are pending
Storybook tests / storybook-tests (push) Waiting to run
(bugfix): HOSTNAMES_ARRAY_POLICY and deepDelta fixes
2026-07-06 21:34:08 +02:00

190 lines
6.1 KiB
PowerShell

#requires -Version 7.0
#requires -PSEdition Core
<#
.SYNOPSIS
Shared helpers for DockerCompose and PodmanCompose release plugins.
#>
$containerEngineSupportModulePath = Join-Path $PSScriptRoot 'ContainerEngineSupport.psm1'
if (Test-Path $containerEngineSupportModulePath -PathType Leaf) {
Import-Module $containerEngineSupportModulePath -Force -Global
}
function Get-ComposeInvocation {
param(
[Parameter(Mandatory = $false)]
[psobject]$SharedSettings = $null
)
if ($null -ne $SharedSettings -and $SharedSettings.PSObject.Properties.Name -contains 'containerEngineProbeCompleted' -and $SharedSettings.containerEngineProbeCompleted) {
return Get-SharedComposeInvocation -SharedSettings $SharedSettings
}
if (Test-ComposeSubcommand 'docker') {
return @{ Executable = 'docker'; BaseArgs = @('compose'); Engine = 'docker' }
}
if (Test-ComposeSubcommand 'podman') {
return @{ Executable = 'podman'; BaseArgs = @('compose'); Engine = 'podman' }
}
if (Get-Command docker-compose -ErrorAction SilentlyContinue) {
return @{ Executable = 'docker-compose'; BaseArgs = @(); Engine = 'docker-compose' }
}
throw 'Compose is not available. Run ContainerEngineProbe first, or install docker compose / podman compose.'
}
function Resolve-ComposeProjectDirectory {
param(
[Parameter(Mandatory = $true)]
[string]$ScriptDir,
[Parameter(Mandatory = $true)]
[string]$ComposePath
)
$projectDir = [System.IO.Path]::GetFullPath((Join-Path $ScriptDir $ComposePath))
if (-not (Test-Path $projectDir -PathType Container)) {
throw "Compose project directory not found: $projectDir"
}
return $projectDir
}
function Get-ComposeFileArguments {
param(
[Parameter(Mandatory = $true)]
[string[]]$ComposeFiles
)
if ($ComposeFiles.Count -eq 0) {
throw 'At least one compose file is required.'
}
$args = New-Object System.Collections.Generic.List[string]
foreach ($fileName in $ComposeFiles) {
if ([string]::IsNullOrWhiteSpace($fileName)) {
continue
}
$args.Add('-f')
$args.Add([string]$fileName)
}
if ($args.Count -eq 0) {
throw 'At least one non-empty compose file name is required.'
}
return @($args)
}
function Invoke-ComposeConfigValidation {
param(
[Parameter(Mandatory = $true)]
[string]$ProjectDir,
[Parameter(Mandatory = $true)]
[string[]]$ComposeFiles,
[Parameter(Mandatory = $false)]
[psobject]$SharedSettings = $null
)
$compose = Get-ComposeInvocation -SharedSettings $SharedSettings
$fileArgs = Get-ComposeFileArguments -ComposeFiles $ComposeFiles
foreach ($fileName in $ComposeFiles) {
$composeFilePath = Join-Path $ProjectDir $fileName
if (-not (Test-Path $composeFilePath -PathType Leaf)) {
throw "Compose file not found: $composeFilePath"
}
}
$commandArgs = @($compose.BaseArgs) + $fileArgs + @('config', '--quiet')
Write-Log -Level 'INFO' -Message "Validating Compose project at $ProjectDir ($($ComposeFiles -join ', ')) via $($compose.Engine) ..."
Push-Location $ProjectDir
try {
$output = & $compose.Executable @commandArgs 2>&1
if ($LASTEXITCODE -ne 0) {
throw "$($compose.Engine) compose config failed: $($output | Out-String)"
}
}
finally {
Pop-Location
}
Write-Log -Level 'OK' -Message ' Compose configuration is valid.'
}
function Invoke-ComposeStackPlugin {
param(
[Parameter(Mandatory = $true)]
$Settings,
[Parameter(Mandatory = $true)]
[ValidateSet('docker', 'podman')]
[string]$RequiredEngine,
[Parameter(Mandatory = $true)]
[string]$PluginName
)
Import-PluginDependency -ModuleName 'Logging' -RequiredCommand 'Write-Log'
Import-PluginDependency -ModuleName 'ContainerEngineSupport' -RequiredCommand 'Test-ShouldRunContainerEnginePlugin'
$pluginSettings = $Settings
$shared = $Settings.context
$scriptDir = [string]$shared.scriptDir
if (-not (Test-ShouldRunContainerEnginePlugin -RequiredEngine $RequiredEngine -SharedSettings $shared -PluginName $PluginName)) {
return
}
if ([string]::IsNullOrWhiteSpace($pluginSettings.composePath)) {
throw "$PluginName requires 'composePath' (directory with docker-compose.yml, relative to engines/release)."
}
$composeFiles = @('docker-compose.yml', 'docker-compose.override.yml')
if ($pluginSettings.PSObject.Properties.Name -contains 'composeFiles' -and $null -ne $pluginSettings.composeFiles) {
$composeFiles = @($pluginSettings.composeFiles | ForEach-Object { [string]$_ })
}
$projectDir = Resolve-ComposeProjectDirectory -ScriptDir $scriptDir -ComposePath ([string]$pluginSettings.composePath)
Invoke-ComposeConfigValidation -ProjectDir $projectDir -ComposeFiles $composeFiles -SharedSettings $shared
$dryRun = Test-PluginSkipsRemoteMutation -Plugin $pluginSettings -SharedSettings $shared
if ($dryRun) {
Write-Log -Level 'INFO' -Message 'Dry run: compose up/build skipped.'
return
}
$compose = Get-ComposeInvocation -SharedSettings $shared
$fileArgs = Get-ComposeFileArguments -ComposeFiles $composeFiles
$upArgs = @($compose.BaseArgs) + $fileArgs + @('up', '--build', '-d')
Write-Log -Level 'STEP' -Message "Starting Compose stack at $projectDir via $($compose.Engine) ..."
Push-Location $projectDir
try {
$output = & $compose.Executable @upArgs 2>&1
if ($LASTEXITCODE -ne 0) {
throw "$($compose.Engine) compose up failed: $($output | Out-String)"
}
}
finally {
Pop-Location
}
Write-Log -Level 'OK' -Message " Compose stack started via $($compose.Engine)."
$shared | Add-Member -NotePropertyName composeDeployCompleted -NotePropertyValue $true -Force
}
Export-ModuleMember -Function `
Get-ComposeInvocation, `
Resolve-ComposeProjectDirectory, `
Get-ComposeFileArguments, `
Invoke-ComposeConfigValidation, `
Invoke-ComposeStackPlugin