test: add portainer stack env guard

This commit is contained in:
ginnoir
2026-06-14 22:52:13 -05:00
parent 1289e0a615
commit 0b58c11f2b
2 changed files with 113 additions and 0 deletions
+106
View File
@@ -0,0 +1,106 @@
[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."
@@ -0,0 +1,7 @@
{
"authentik": ["authentik-redis"],
"dev": ["dbx", "redis_plane"],
"foundry": ["5etools"],
"monitoring": ["uptime-kuma"],
"owncloud": ["redis"]
}