test: gate unified compose deployment

This commit is contained in:
2026-08-05 13:41:33 +02:00
parent b44a1b9ad9
commit 2ae89c075e
6 changed files with 1313 additions and 0 deletions
+108
View File
@@ -0,0 +1,108 @@
param(
[string]$RepositoryRoot = "",
[string]$ThothctlPath = ""
)
$ErrorActionPreference = "Stop"
Set-StrictMode -Version Latest
if ([string]::IsNullOrWhiteSpace($RepositoryRoot)) {
$RepositoryRoot = (& git rev-parse --show-toplevel).Trim()
if ($LASTEXITCODE -ne 0) {
throw "git could not resolve the repository root"
}
}
$RepositoryRoot = [System.IO.Path]::GetFullPath($RepositoryRoot)
$tracked = @(& git -C $RepositoryRoot ls-files)
if ($LASTEXITCODE -ne 0) {
throw "git ls-files failed"
}
$scriptRelativePath = "scripts/test-windows-clone-contract.ps1"
$eolAttribute = (& git -C $RepositoryRoot check-attr eol -- $scriptRelativePath).Trim()
if ($LASTEXITCODE -ne 0 -or -not $eolAttribute.EndsWith("eol: crlf", [System.StringComparison]::OrdinalIgnoreCase)) {
throw "the Windows contract script must have the repository eol=crlf attribute"
}
$scriptBytes = [System.IO.File]::ReadAllBytes((Join-Path $RepositoryRoot $scriptRelativePath))
if (-not ($scriptBytes -contains [byte]0x0D)) {
throw "the Windows contract script was not checked out with CRLF bytes"
}
$offenders = [System.Collections.Generic.List[string]]::new()
foreach ($relativePath in $tracked) {
$name = [System.IO.Path]::GetFileName($relativePath)
$mustBeLf = $relativePath.EndsWith(".sh", [System.StringComparison]::OrdinalIgnoreCase) -or
$relativePath.EndsWith(".yml", [System.StringComparison]::OrdinalIgnoreCase) -or
$relativePath.EndsWith(".yaml", [System.StringComparison]::OrdinalIgnoreCase) -or
$name.Equals("Dockerfile", [System.StringComparison]::OrdinalIgnoreCase) -or
$name.StartsWith("Dockerfile.", [System.StringComparison]::OrdinalIgnoreCase) -or
$name.EndsWith(".Dockerfile", [System.StringComparison]::OrdinalIgnoreCase)
if (-not $mustBeLf) {
continue
}
$absolutePath = Join-Path $RepositoryRoot $relativePath
$bytes = [System.IO.File]::ReadAllBytes($absolutePath)
if ($bytes -contains [byte]0x0D) {
$offenders.Add($relativePath)
}
}
if ($offenders.Count -ne 0) {
throw "CR byte 0x0D found in tracked LF contract files: $($offenders -join ', ')"
}
$temporaryRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("thothii-windows-contract-" + [guid]::NewGuid().ToString("N"))
$savedEnvironment = @{
THT_WORKSPACE_GIT_REMOTE = $env:THT_WORKSPACE_GIT_REMOTE
PI_AUTH_FILE = $env:PI_AUTH_FILE
THT_SECRETS_FILE = $env:THT_SECRETS_FILE
}
try {
[System.IO.Directory]::CreateDirectory($temporaryRoot) | Out-Null
$piAuth = Join-Path $temporaryRoot "pi-auth.json"
$secrets = Join-Path $temporaryRoot "thothii.secrets"
[System.IO.File]::WriteAllText($piAuth, "{}`n", [System.Text.UTF8Encoding]::new($false))
[System.IO.File]::WriteAllText($secrets, "THT_MODEL_API_KEY=windows-contract`n", [System.Text.UTF8Encoding]::new($false))
$env:THT_WORKSPACE_GIT_REMOTE = "https://git.example.invalid/platform/thoth-workspaces.git"
$env:PI_AUTH_FILE = $piAuth
$env:THT_SECRETS_FILE = $secrets
$composeFiles = @(
"--project-directory", $RepositoryRoot,
"-f", (Join-Path $RepositoryRoot "compose.yaml"),
"-f", (Join-Path $RepositoryRoot "deploy/compose.local.yaml")
)
$services = @(& docker compose @composeFiles config --services)
if ($LASTEXITCODE -ne 0) {
throw "Docker Compose could not render the Windows clone"
}
if ((($services | Sort-Object) -join ",") -ne "core,frontend") {
throw "rendered Windows stack must contain exactly core and frontend"
}
& docker compose @composeFiles config --quiet
if ($LASTEXITCODE -ne 0) {
throw "Docker Compose rejected the Windows clone"
}
if ([string]::IsNullOrWhiteSpace($ThothctlPath)) {
$ThothctlPath = Join-Path $RepositoryRoot "dist/thothctl/thothctl-windows-amd64.exe"
}
$ThothctlPath = [System.IO.Path]::GetFullPath($ThothctlPath)
if (-not [System.IO.File]::Exists($ThothctlPath)) {
throw "Windows thothctl binary is missing: $ThothctlPath"
}
& $ThothctlPath --help | Out-Null
if ($LASTEXITCODE -ne 0) {
throw "Windows thothctl invocation failed"
}
}
finally {
foreach ($name in $savedEnvironment.Keys) {
[System.Environment]::SetEnvironmentVariable($name, $savedEnvironment[$name], "Process")
}
if ([System.IO.Directory]::Exists($temporaryRoot)) {
Remove-Item -LiteralPath $temporaryRoot -Recurse -Force
}
}
Write-Output "Windows clone, LF-byte, Compose render, and thothctl invocation contracts passed."