Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
* text=auto
*.sh text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
5 changes: 5 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ jobs:
- name: Checkout
uses: actions/checkout@v7

- name: Set up Python
uses: actions/setup-python@v7
with:
python-version: "3.11"

- name: Test install and uninstall
shell: powershell
run: .\scripts\test-installers.ps1
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
- Added `CAVEMAN_OPENCODE_DEFAULT_LEVEL` install-time override for `lite`, `full`, `ultra`, `wenyan`, `wenyan-lite`, and `wenyan-ultra`.
- Accepted trailing commas in existing OpenCode configuration without changing comma-like text inside JSON strings.
- Added functional install/uninstall tests for POSIX and Windows PowerShell environments.
- Replaced the Windows `Expand-Archive` dependency with .NET ZIP extraction for hosts where `Microsoft.PowerShell.Archive` cannot load.
- Enforced LF endings for shell scripts and workflow files across Windows and POSIX checkouts.

## 0.2.0

Expand Down
9 changes: 7 additions & 2 deletions install-opencode.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ function ConvertFrom-OpenCodeJson([string]$Content) {
}
}

function Expand-ZipArchive([string]$Path, [string]$DestinationPath) {
Add-Type -AssemblyName System.IO.Compression.FileSystem -ErrorAction Stop
[System.IO.Compression.ZipFile]::ExtractToDirectory($Path, $DestinationPath)
}

if (-not (Test-Path -LiteralPath (Join-Path $sourceOpenCode "AGENTS.md"))) {
$archiveUrl = $env:CAVEMAN_OPENCODE_ARCHIVE_URL
if ([string]::IsNullOrWhiteSpace($archiveUrl)) {
Expand All @@ -103,8 +108,8 @@ if (-not (Test-Path -LiteralPath (Join-Path $sourceOpenCode "AGENTS.md"))) {
New-Item -ItemType Directory -Force $tempDir | Out-Null

Write-Host "Downloading Caveman for OpenCode from $archiveUrl"
Invoke-WebRequest -Uri $archiveUrl -OutFile $zipPath
Expand-Archive -LiteralPath $zipPath -DestinationPath $tempDir -Force
Invoke-WebRequest -UseBasicParsing -Uri $archiveUrl -OutFile $zipPath
Expand-ZipArchive -Path $zipPath -DestinationPath $tempDir

$sourceDir = Get-ChildItem -LiteralPath $tempDir -Directory | Select-Object -First 1
if (-not $sourceDir) {
Expand Down
64 changes: 62 additions & 2 deletions scripts/test-installers.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ $repo = Split-Path -Parent $PSScriptRoot
$testRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("caveman-opencode-test-" + [guid]::NewGuid().ToString("N"))
$originalUserProfile = $env:USERPROFILE
$originalDefaultLevel = $env:CAVEMAN_OPENCODE_DEFAULT_LEVEL
$originalArchiveUrl = $env:CAVEMAN_OPENCODE_ARCHIVE_URL
$serverProcess = $null

try {
$env:USERPROFILE = $testRoot
$env:CAVEMAN_OPENCODE_DEFAULT_LEVEL = "ultra"

$configDir = Join-Path $testRoot ".config\opencode"
Expand All @@ -37,7 +38,57 @@ try {
}
'@ | Set-Content -LiteralPath $configFile -Encoding UTF8

& (Join-Path $repo "install-opencode.ps1")
Add-Type -AssemblyName System.IO.Compression.FileSystem -ErrorAction Stop
$archiveRoot = Join-Path $testRoot "archive"
$archiveSource = Join-Path $archiveRoot "caveman-opencode-main"
$archivePath = Join-Path $testRoot "source.zip"
New-Item -ItemType Directory -Force -Path $archiveSource | Out-Null
Copy-Item -Recurse -Force -LiteralPath (Join-Path $repo ".opencode") -Destination $archiveSource
[System.IO.Compression.ZipFile]::CreateFromDirectory($archiveRoot, $archivePath)

$portProbe = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Loopback, 0)
$portProbe.Start()
$port = ([System.Net.IPEndPoint]$portProbe.LocalEndpoint).Port
$portProbe.Stop()
$serverUrl = "http://127.0.0.1:$port/source.zip"
$serverOutputPath = Join-Path $testRoot "archive-server.out"
$serverErrorPath = Join-Path $testRoot "archive-server.err"
$python = (Get-Command python -ErrorAction Stop).Source
$pythonArguments = "-m http.server $port --bind 127.0.0.1 --directory `"$testRoot`""
$serverProcess = Start-Process -FilePath $python -ArgumentList $pythonArguments -PassThru -WindowStyle Hidden -RedirectStandardOutput $serverOutputPath -RedirectStandardError $serverErrorPath

$remoteDir = Join-Path $testRoot "remote-installer"
$remoteInstaller = Join-Path $remoteDir "install-opencode.ps1"
New-Item -ItemType Directory -Force -Path $remoteDir | Out-Null
Copy-Item -Force -LiteralPath (Join-Path $repo "install-opencode.ps1") -Destination $remoteInstaller
$env:CAVEMAN_OPENCODE_ARCHIVE_URL = $serverUrl

$serverDeadline = [DateTime]::UtcNow.AddSeconds(10)
$serverReady = $false
while (-not $serverReady -and [DateTime]::UtcNow -lt $serverDeadline) {
if ($serverProcess.HasExited) {
$serverDetails = Get-Content -LiteralPath $serverErrorPath -Raw -ErrorAction SilentlyContinue
throw "Archive server exited before startup. $serverDetails"
}
$client = [System.Net.Sockets.TcpClient]::new()
try {
$client.Connect("127.0.0.1", $port)
$serverReady = $client.Connected
} catch {
Start-Sleep -Milliseconds 100
} finally {
$client.Close()
}
}
Assert-True $serverReady "archive server should start"

$env:USERPROFILE = $testRoot
try {
& $remoteInstaller
} catch {
$serverDetails = Get-Content -LiteralPath $serverErrorPath -Raw -ErrorAction SilentlyContinue
throw "Remote installer failed: $($_.Exception.Message). Archive server: $serverDetails"
}

$config = Get-Content -LiteralPath $configFile -Raw -Encoding UTF8 | ConvertFrom-Json
$skills = @("caveman", "caveman-commit", "caveman-review", "caveman-help", "caveman-compress")
Expand Down Expand Up @@ -74,6 +125,15 @@ try {
} else {
$env:CAVEMAN_OPENCODE_DEFAULT_LEVEL = $originalDefaultLevel
}
if ($null -eq $originalArchiveUrl) {
Remove-Item Env:CAVEMAN_OPENCODE_ARCHIVE_URL -ErrorAction SilentlyContinue
} else {
$env:CAVEMAN_OPENCODE_ARCHIVE_URL = $originalArchiveUrl
}
if ($null -ne $serverProcess -and -not $serverProcess.HasExited) {
Stop-Process -Id $serverProcess.Id -Force -ErrorAction SilentlyContinue
[void]$serverProcess.WaitForExit(5000)
}
if (Test-Path -LiteralPath $testRoot) {
Remove-Item -Recurse -Force -LiteralPath $testRoot
}
Expand Down