153 lines
4.9 KiB
PowerShell
153 lines
4.9 KiB
PowerShell
param(
|
|
[string]$EnvPath = (Join-Path $PSScriptRoot "..\Server\devtools\deployments\qsfera_full\.env"),
|
|
[switch]$AllowWarnings
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
$errors = New-Object System.Collections.Generic.List[string]
|
|
$warnings = New-Object System.Collections.Generic.List[string]
|
|
|
|
function Add-Error([string]$Message) {
|
|
[void]$script:errors.Add($Message)
|
|
Write-Error $Message -ErrorAction Continue
|
|
}
|
|
|
|
function Add-WarningMessage([string]$Message) {
|
|
[void]$script:warnings.Add($Message)
|
|
Write-Warning $Message
|
|
}
|
|
|
|
function Read-EnvFile([string]$Path) {
|
|
if (-not (Test-Path -LiteralPath $Path)) {
|
|
throw "Env file not found: $Path"
|
|
}
|
|
|
|
$result = @{}
|
|
foreach ($line in Get-Content -LiteralPath $Path) {
|
|
$trimmed = $line.Trim()
|
|
if ($trimmed.Length -eq 0 -or $trimmed.StartsWith("#")) {
|
|
continue
|
|
}
|
|
|
|
$separator = $trimmed.IndexOf("=")
|
|
if ($separator -le 0) {
|
|
continue
|
|
}
|
|
|
|
$key = $trimmed.Substring(0, $separator).Trim()
|
|
$value = $trimmed.Substring($separator + 1).Trim()
|
|
$value = $value.Trim('"').Trim("'")
|
|
$result[$key] = $value
|
|
}
|
|
|
|
return $result
|
|
}
|
|
|
|
function Get-EnvValue([hashtable]$Values, [string]$Key) {
|
|
if ($Values.ContainsKey($Key)) {
|
|
return [string]$Values[$Key]
|
|
}
|
|
return ""
|
|
}
|
|
|
|
function Test-Placeholder([string]$Value) {
|
|
if ([string]::IsNullOrWhiteSpace($Value)) {
|
|
return $true
|
|
}
|
|
if ($Value.StartsWith("<") -and $Value.EndsWith(">")) {
|
|
return $true
|
|
}
|
|
return $Value -in @("change-me", "changeme", "password", "secret") -or $Value.Contains("example.com")
|
|
}
|
|
|
|
function Test-Enabled([hashtable]$Values, [string]$Key) {
|
|
return -not [string]::IsNullOrWhiteSpace((Get-EnvValue $Values $Key))
|
|
}
|
|
|
|
function Require-Value([hashtable]$Values, [string]$Key, [string]$Label = $Key) {
|
|
$value = Get-EnvValue $Values $Key
|
|
if (Test-Placeholder $value) {
|
|
Add-Error "$Label must be set to a production value."
|
|
}
|
|
}
|
|
|
|
function Require-Secret([hashtable]$Values, [string]$Key) {
|
|
$value = Get-EnvValue $Values $Key
|
|
if (Test-Placeholder $value) {
|
|
Add-Error "$Key must be set."
|
|
return
|
|
}
|
|
if ($value -in @("admin", "demo", "keycloak", "qsfera", "qsfera-secret-key")) {
|
|
Add-Error "$Key uses a known demo/default value."
|
|
return
|
|
}
|
|
if ($value.Length -lt 16) {
|
|
Add-Error "$Key must be at least 16 characters."
|
|
}
|
|
}
|
|
|
|
$resolvedEnvPath = (Resolve-Path -LiteralPath $EnvPath -ErrorAction Stop).Path
|
|
$values = Read-EnvFile $resolvedEnvPath
|
|
|
|
Require-Value $values "OC_DOMAIN"
|
|
Require-Value $values "TRAEFIK_ACME_MAIL"
|
|
Require-Secret $values "ADMIN_PASSWORD"
|
|
|
|
if ((Get-EnvValue $values "INSECURE") -in @("true", "TRUE", "1", "yes", "YES")) {
|
|
Add-Error "INSECURE must be false for production."
|
|
}
|
|
|
|
if ((Get-EnvValue $values "DEMO_USERS") -in @("true", "TRUE", "1", "yes", "YES")) {
|
|
Add-Error "DEMO_USERS must be false for production."
|
|
}
|
|
|
|
switch (Get-EnvValue $values "OC_DOCKER_IMAGE") {
|
|
"qsfera/qsfera" { }
|
|
"" { Add-WarningMessage "OC_DOCKER_IMAGE is empty; compose default must be verified before rollout." }
|
|
default { Add-Error "OC_DOCKER_IMAGE must be qsfera/qsfera for production." }
|
|
}
|
|
|
|
$dockerTag = Get-EnvValue $values "OC_DOCKER_TAG"
|
|
if ([string]::IsNullOrWhiteSpace($dockerTag) -or $dockerTag -eq "latest" -or ($dockerTag.StartsWith("<") -and $dockerTag.EndsWith(">"))) {
|
|
Add-Error "OC_DOCKER_TAG must pin a concrete production release tag."
|
|
}
|
|
|
|
if (-not (Test-Enabled $values "QSFERA")) {
|
|
Add-Error "QSFERA=:qsfera.yml must be enabled."
|
|
}
|
|
|
|
if (Test-Enabled $values "DECOMPOSEDS3") {
|
|
Require-Value $values "DECOMPOSEDS3_ENDPOINT"
|
|
Require-Value $values "DECOMPOSEDS3_REGION"
|
|
Require-Secret $values "DECOMPOSEDS3_ACCESS_KEY"
|
|
Require-Secret $values "DECOMPOSEDS3_SECRET_KEY"
|
|
Require-Value $values "DECOMPOSEDS3_BUCKET"
|
|
}
|
|
|
|
if (Test-Enabled $values "DECOMPOSEDS3_MINIO") {
|
|
Add-Error "DECOMPOSEDS3_MINIO is for test/lab installs and must not be enabled for public production."
|
|
}
|
|
|
|
if (Test-Enabled $values "KEYCLOAK") {
|
|
Require-Value $values "KEYCLOAK_DOMAIN"
|
|
Require-Value $values "KEYCLOAK_REALM"
|
|
Require-Secret $values "KEYCLOAK_POSTGRES_PASSWORD"
|
|
Require-Value $values "KEYCLOAK_ADMIN_USER"
|
|
Require-Secret $values "KEYCLOAK_ADMIN_PASSWORD"
|
|
}
|
|
|
|
if ([string]::IsNullOrWhiteSpace((Get-EnvValue $values "OC_CONFIG_DIR")) -or [string]::IsNullOrWhiteSpace((Get-EnvValue $values "OC_DATA_DIR"))) {
|
|
Add-WarningMessage "OC_CONFIG_DIR and OC_DATA_DIR are not both set; docker volumes are harder to back up and restore."
|
|
}
|
|
|
|
if ($errors.Count -gt 0) {
|
|
throw "Production env validation failed: $($errors.Count) error(s), $($warnings.Count) warning(s)."
|
|
}
|
|
|
|
if (-not $AllowWarnings -and $warnings.Count -gt 0) {
|
|
throw "Production env validation passed with $($warnings.Count) warning(s). Re-run with -AllowWarnings if this is expected."
|
|
}
|
|
|
|
Write-Output "Production env validation passed: $($warnings.Count) warning(s)."
|