107 lines
3.1 KiB
PowerShell
107 lines
3.1 KiB
PowerShell
[CmdletBinding()]
|
|
param(
|
|
[string]$StacksRoot = (Join-Path $PSScriptRoot "..\stacks"),
|
|
[string]$ExemptionsPath = (Join-Path $PSScriptRoot "portainer-stack-env-exemptions.json")
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
$failures = New-Object System.Collections.Generic.List[string]
|
|
$exemptions = @{}
|
|
|
|
if (Test-Path $ExemptionsPath) {
|
|
$raw = Get-Content -Raw -LiteralPath $ExemptionsPath | ConvertFrom-Json
|
|
foreach ($property in $raw.PSObject.Properties) {
|
|
$exemptions[$property.Name] = @($property.Value)
|
|
}
|
|
}
|
|
|
|
function Get-ServiceBlocks {
|
|
param([string[]]$Lines)
|
|
|
|
$inServices = $false
|
|
$currentName = $null
|
|
$currentLines = New-Object System.Collections.Generic.List[string]
|
|
$blocks = New-Object System.Collections.Generic.List[object]
|
|
|
|
foreach ($line in $Lines) {
|
|
if ($line -match '^services:\s*$') {
|
|
$inServices = $true
|
|
continue
|
|
}
|
|
|
|
if (-not $inServices) {
|
|
continue
|
|
}
|
|
|
|
if ($line -match '^[A-Za-z0-9_-]+:\s*$') {
|
|
break
|
|
}
|
|
|
|
if ($line -match '^ ([A-Za-z0-9_-]+):\s*$') {
|
|
if ($null -ne $currentName) {
|
|
$blocks.Add([pscustomobject]@{
|
|
Name = $currentName
|
|
Lines = @($currentLines)
|
|
})
|
|
}
|
|
$currentName = $Matches[1]
|
|
$currentLines = New-Object System.Collections.Generic.List[string]
|
|
continue
|
|
}
|
|
|
|
if ($null -ne $currentName) {
|
|
$currentLines.Add($line)
|
|
}
|
|
}
|
|
|
|
if ($null -ne $currentName) {
|
|
$blocks.Add([pscustomobject]@{
|
|
Name = $currentName
|
|
Lines = @($currentLines)
|
|
})
|
|
}
|
|
|
|
return $blocks
|
|
}
|
|
|
|
Get-ChildItem -LiteralPath $StacksRoot -Directory | Sort-Object Name | ForEach-Object {
|
|
$stackName = $_.Name
|
|
$composePath = Join-Path $_.FullName "docker-compose.yml"
|
|
$stackEnvPath = Join-Path $_.FullName "stack.env"
|
|
|
|
if (-not (Test-Path $composePath)) {
|
|
return
|
|
}
|
|
|
|
if (-not (Test-Path $stackEnvPath)) {
|
|
$failures.Add("${stackName}: missing stack.env")
|
|
}
|
|
|
|
$lines = Get-Content -LiteralPath $composePath
|
|
$interpolations = Select-String -LiteralPath $composePath -Pattern '\$\{' |
|
|
Where-Object { $_.Line -notmatch '^\s*#' }
|
|
|
|
foreach ($match in $interpolations) {
|
|
$failures.Add("${stackName}: compose interpolation at $($match.Path):$($match.LineNumber): $($match.Line.Trim())")
|
|
}
|
|
|
|
$allowedMissing = @()
|
|
if ($exemptions.ContainsKey($stackName)) {
|
|
$allowedMissing = @($exemptions[$stackName])
|
|
}
|
|
|
|
foreach ($service in Get-ServiceBlocks -Lines $lines) {
|
|
$hasEnvFile = $service.Lines | Where-Object { $_ -match '^\s+env_file:\s*$' -or $_ -match '^\s+- stack\.env\s*$' }
|
|
if (-not $hasEnvFile -and $allowedMissing -notcontains $service.Name) {
|
|
$failures.Add("${stackName}/${service.Name}: missing env_file: stack.env")
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($failures.Count -gt 0) {
|
|
$failures | ForEach-Object { Write-Error $_ }
|
|
exit 1
|
|
}
|
|
|
|
Write-Host "Portainer stack env checks passed."
|