125 lines
7.6 KiB
PowerShell
125 lines
7.6 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Prüft hugin-install.ps1 unter Windows PowerShell 5.1 gegen einen laufenden Hugin.
|
|
|
|
.DESCRIPTION
|
|
Gegenstück zu scripts/integration-test.sh für Windows. Braucht einen Hugin mit
|
|
den Beispielpaketen, z. B. scripts/demo.sh auf einem anderen Rechner:
|
|
|
|
HOST=0.0.0.0 scripts/demo.sh # auf dem Mac/Linux-Rechner
|
|
|
|
Dann unter Windows (PowerShell 5.1, nicht pwsh):
|
|
|
|
powershell -ExecutionPolicy Bypass -File integration-test.ps1 `
|
|
-Server http://<rechner>:8080 -Key <Key "Softwareverteilung" aus .demo/keys.txt>
|
|
|
|
Ändert nichts außerhalb eines Temp-Ordners. Rückgabewert 0, wenn alles passt.
|
|
#>
|
|
param(
|
|
[Parameter(Mandatory = $true)][string]$Server,
|
|
[Parameter(Mandatory = $true)][string]$Key,
|
|
# Nur zum Vorabtest auf anderen Systemen, z. B. -PowerShell pwsh; unter Windows weglassen
|
|
[string]$PowerShell = 'powershell.exe'
|
|
)
|
|
|
|
Set-StrictMode -Version 2
|
|
$ErrorActionPreference = 'Stop'
|
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
|
$Utf8 = New-Object System.Text.UTF8Encoding($false)
|
|
$failures = 0
|
|
|
|
function Check([string]$Name, [bool]$Ok, [string]$Info = '') {
|
|
if ($Ok) { Write-Host "OK $Name" } else { Write-Host "FEHLER $Name $Info" -ForegroundColor Red; $script:failures++ }
|
|
}
|
|
|
|
Write-Host "PowerShell $($PSVersionTable.PSVersion) ($($PSVersionTable.PSEdition))"
|
|
if ($PSVersionTable.PSVersion.Major -ne 5) { Write-Warning 'Dieser Test ist für Windows PowerShell 5.1 gedacht.' }
|
|
|
|
$work = Join-Path ([IO.Path]::GetTempPath()) ("hugin-test-" + [Guid]::NewGuid().ToString('N').Substring(0, 8))
|
|
$repo = Join-Path $work 'repo'
|
|
New-Item -ItemType Directory -Path $repo -Force | Out-Null
|
|
try {
|
|
# Skript vom Server laden, wie eine Softwareverteilung es tun würde
|
|
$script = Join-Path $work 'hugin-install.ps1'
|
|
Invoke-WebRequest -UseBasicParsing -Uri ($Server.TrimEnd('/') + '/api/v1/delivery/client.ps1') `
|
|
-Headers @{ Authorization = "Bearer $Key" } -OutFile $script
|
|
$raw = [IO.File]::ReadAllBytes($script)
|
|
Check 'client.ps1 mit BOM (für PowerShell 5.1)' ($raw[0] -eq 0xEF -and $raw[1] -eq 0xBB -and $raw[2] -eq 0xBF)
|
|
|
|
[IO.File]::WriteAllText((Join-Path $repo 'CLAUDE.md'), "# Werk`r`n`r`nUnser Projekt.`r`n", $Utf8)
|
|
[IO.File]::WriteAllText((Join-Path $repo 'opencode.json'), "{`n `"model`": `"anthropic/claude-sonnet-5-5`"`n}`n", $Utf8)
|
|
|
|
function Run([string[]]$More) {
|
|
$output = & $PowerShell -NoProfile -ExecutionPolicy Bypass -File $script -Server $Server -Key $Key `
|
|
-Repo $repo -Tools 'claude=2.1.302,opencode=1.19.3' -Client 'windows-test' @More 2>&1 | Out-String
|
|
return @{ Text = $output; Rc = $LASTEXITCODE }
|
|
}
|
|
|
|
$first = Run @('-Packages', 'pub-munin-nutzen,pub-hausregeln,pub-code-review')
|
|
Check 'Erstinstallation' ($first.Rc -eq 0) $first.Text
|
|
foreach ($rel in '.claude\skills\pub-munin-nutzen\SKILL.md', '.claude\agents\pub-code-review.md',
|
|
'.opencode\agents\pub-code-review.md', '.hugin\rules\pub-hausregeln.md', '.mcp.json', 'hugin.lock.json') {
|
|
Check "Datei $rel" (Test-Path -LiteralPath (Join-Path $repo $rel))
|
|
}
|
|
$skill = [IO.File]::ReadAllBytes((Join-Path $repo '.claude\skills\pub-munin-nutzen\SKILL.md'))
|
|
Check 'SKILL.md ohne BOM' (-not ($skill[0] -eq 0xEF -and $skill[1] -eq 0xBB))
|
|
Check 'SKILL.md mit LF, ohne CRLF' (-not $Utf8.GetString($skill).Contains("`r`n"))
|
|
$claude = [IO.File]::ReadAllText((Join-Path $repo 'CLAUDE.md'), $Utf8)
|
|
Check 'CLAUDE.md behält Inhalt und CRLF' ($claude.StartsWith("# Werk`r`n`r`nUnser Projekt.`r`n`r`n<!-- hugin:begin") -and -not ($claude -replace "`r`n", '').Contains("`n"))
|
|
foreach ($json in 'opencode.json', '.mcp.json', 'hugin.lock.json') {
|
|
$text = [IO.File]::ReadAllText((Join-Path $repo $json), $Utf8)
|
|
$valid = $true; try { $null = $text | ConvertFrom-Json } catch { $valid = $false }
|
|
Check "$json gültiges JSON ohne BOM" ($valid -and -not $text.StartsWith([char]0xFEFF))
|
|
}
|
|
$oc = [IO.File]::ReadAllText((Join-Path $repo 'opencode.json'), $Utf8) | ConvertFrom-Json
|
|
Check 'opencode.json: eigenes Modell bleibt, MCP und instructions ergänzt' (
|
|
$oc.model -eq 'anthropic/claude-sonnet-5-5' -and $oc.mcp.munin.type -eq 'remote' -and @($oc.instructions) -contains '.hugin/rules/*.md')
|
|
# Eine Liste mit einem Eintrag muss eine Liste bleiben (Eigenheit von ConvertTo-Json in 5.1)
|
|
Check 'instructions bleibt eine Liste' ([IO.File]::ReadAllText((Join-Path $repo 'opencode.json'), $Utf8) -match '"instructions":\s*\[')
|
|
|
|
$second = Run @('-Packages', 'pub-munin-nutzen,pub-hausregeln,pub-code-review')
|
|
Check 'Zweiter Lauf ändert nichts' ($second.Rc -eq 0 -and $second.Text -match 'Dateien: 0 schreiben, 0 löschen') $second.Text
|
|
|
|
$dryRun = Run @('-Packages', 'pub-munin-nutzen', '-DryRun')
|
|
Check 'Trockenlauf schreibt nichts' ($dryRun.Text -match 'Trockenlauf' -and (Test-Path (Join-Path $repo '.claude\agents\pub-code-review.md')))
|
|
|
|
$unsubscribed = Run @('-Packages', 'pub-munin-nutzen')
|
|
Check 'Abbestellen räumt auf' ($unsubscribed.Rc -eq 0 -and -not (Test-Path (Join-Path $repo '.claude\agents\pub-code-review.md')) -and
|
|
-not ([IO.File]::ReadAllText((Join-Path $repo 'CLAUDE.md'), $Utf8).Contains('hugin:begin'))) $unsubscribed.Text
|
|
Check 'CLAUDE.md wieder wie vorher' ([IO.File]::ReadAllText((Join-Path $repo 'CLAUDE.md'), $Utf8) -eq "# Werk`r`n`r`nUnser Projekt.`r`n")
|
|
|
|
[IO.File]::AppendAllText((Join-Path $repo '.claude\skills\pub-munin-nutzen\SKILL.md'), "von Hand`n", $Utf8)
|
|
$manual = Run @('-Packages', 'pub-munin-nutzen,pub-hausregeln')
|
|
Check 'Handänderung wird erkannt und nichts geschrieben' ($manual.Rc -ne 0 -and $manual.Text -match 'von Hand' -and
|
|
-not (Test-Path (Join-Path $repo '.hugin\rules\pub-hausregeln.md'))) $manual.Text
|
|
$overwritten = Run @('-Packages', 'pub-munin-nutzen,pub-hausregeln', '-Overwrite')
|
|
Check '-Overwrite setzt zurück' ($overwritten.Rc -eq 0 -and (Test-Path (Join-Path $repo '.hugin\rules\pub-hausregeln.md')))
|
|
|
|
$repo2 = Join-Path $work 'repo2'; New-Item -ItemType Directory -Path $repo2 | Out-Null
|
|
[IO.File]::WriteAllText((Join-Path $repo2 'opencode.json'), "{`n // eigenes`n `"model`": `"x`"`n}`n", $Utf8)
|
|
$jsonc = & $PowerShell -NoProfile -ExecutionPolicy Bypass -File $script -Server $Server -Key $Key -Repo $repo2 `
|
|
-Tools 'opencode=1.19.3' -Packages pub-hausregeln 2>&1 | Out-String
|
|
Check 'JSON mit Kommentaren bleibt unangetastet' ($LASTEXITCODE -ne 0 -and $jsonc -match 'Kommentare' -and
|
|
[IO.File]::ReadAllText((Join-Path $repo2 'opencode.json'), $Utf8).Contains('// eigenes'))
|
|
|
|
# Eine Junction im Repo darf nicht nach draußen führen (z. B. bei Ausführung als Admin)
|
|
$repo3 = Join-Path $work 'repo3'; $outside = Join-Path $work 'outside'
|
|
New-Item -ItemType Directory -Path $repo3, $outside | Out-Null
|
|
if ($PSVersionTable.PSEdition -eq 'Desktop' -or $IsWindows) {
|
|
New-Item -ItemType Junction -Path (Join-Path $repo3 '.opencode') -Target $outside | Out-Null
|
|
} else {
|
|
New-Item -ItemType SymbolicLink -Path (Join-Path $repo3 '.opencode') -Target $outside | Out-Null
|
|
}
|
|
$link = & $PowerShell -NoProfile -ExecutionPolicy Bypass -File $script -Server $Server -Key $Key -Repo $repo3 `
|
|
-Tools 'opencode=1.19.3' -Packages pub-code-review 2>&1 | Out-String
|
|
Check 'Junction nach draußen wird abgelehnt' ($LASTEXITCODE -ne 0 -and $link -match 'Junction' -and
|
|
@(Get-ChildItem -LiteralPath $outside -Force).Count -eq 0) $link
|
|
# Junction vor dem Aufräumen lösen, damit Remove-Item nicht dem Link folgt
|
|
(Get-Item -LiteralPath (Join-Path $repo3 '.opencode') -Force).Delete()
|
|
} finally {
|
|
Remove-Item -LiteralPath $work -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|
|
|
|
if ($failures -eq 0) { Write-Host "`nAlles ok." -ForegroundColor Green; exit 0 }
|
|
Write-Host "`n$failures Prüfung(en) fehlgeschlagen." -ForegroundColor Red
|
|
exit 1
|