<# .SYNOPSIS Exercises publish.ps1, install.ps1 and ci-release.sh against a stub Gitea. .DESCRIPTION Runs the real scripts, pointed at test/fake-gitea.js instead of a server, with a stub "code" on PATH so nothing is installed into the editor. What it checks: - the token reaches the API, and the download after the redirect - the .vsix arrives byte-for-byte (it is a zip, so any text encoding step shows up) - republishing a tag replaces assets instead of duplicating them - install passes --force, without which VS Code silently skips a same-version reinstall - build.ps1 and install.ps1 -Local work with no Gitea configured - ci-release.sh agrees with publish.ps1 Needs dist/*.vsix, so it runs build.ps1 first unless -SkipBuild. .EXAMPLE .\test\run.ps1 .\test\run.ps1 -SkipBuild #> [CmdletBinding()] param( [int] $Port = 5602, [switch] $SkipBuild ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' $root = Split-Path -Parent $PSScriptRoot $scripts = Join-Path $root 'scripts' $dist = Join-Path $root 'dist' $base = "http://127.0.0.1:$Port" $failures = New-Object System.Collections.ArrayList function Assert-That { param([string] $What, [bool] $Condition, [string] $Detail = '') if ($Condition) { Write-Host " PASS $What" -ForegroundColor Green } else { Write-Host " FAIL $What" -ForegroundColor Red if ($Detail) { Write-Host " $Detail" -ForegroundColor DarkGray } [void] $failures.Add($What) } } function Get-State { Invoke-RestMethod -Uri "$base/__state" -UseBasicParsing } # A stub code CLI, so an install never touches the real editor. It has to be found by # Get-CodeCommand, which looks at PATH first. $stubDir = Join-Path ([System.IO.Path]::GetTempPath()) "vscode-setup-test-$PID" New-Item -ItemType Directory -Path $stubDir -Force | Out-Null $callLog = Join-Path $stubDir 'calls.log' Set-Content -Path (Join-Path $stubDir 'code.cmd') -Encoding ASCII -Value @( '@echo off', "echo %* >> `"$callLog`"", 'exit /b 0' ) $server = $null $savedPath = $env:PATH try { if (-not $SkipBuild) { Write-Host 'building...' -ForegroundColor Cyan & (Join-Path $scripts 'build.ps1') | Out-Null } $packages = @(Get-ChildItem -Path $dist -Filter '*.vsix') if ($packages.Count -eq 0) { throw "no .vsix in $dist" } Write-Host "starting the stub on $base" -ForegroundColor Cyan # Set before spawning: the child inherits the environment as it is at that moment. $env:PORT = "$Port" $server = Start-Process -FilePath 'node' ` -ArgumentList (Join-Path $PSScriptRoot 'fake-gitea.js') ` -WindowStyle Hidden -PassThru $up = $false foreach ($attempt in 1..20) { try { Get-State | Out-Null; $up = $true; break } catch { Start-Sleep -Milliseconds 250 } } if (-not $up) { throw "the stub did not come up on $base" } $env:GITEA_URL = $base $env:GITEA_OWNER = 'max' $env:GITEA_REPO = 'vscode-extensions' $env:GITEA_TOKEN = 'TESTTOKEN' $env:PATH = "$stubDir;$savedPath" Write-Host '' Write-Host 'publish.ps1' -ForegroundColor Cyan & (Join-Path $scripts 'publish.ps1') -Tag 'test' | Out-Null $state = Get-State $release = $state.releases | Where-Object { $_.tag_name -eq 'test' } Assert-That 'the release is created' ($null -ne $release) Assert-That "all $($packages.Count) packages are uploaded" ` ($release.assets.Count -eq $packages.Count) "got $($release.assets.Count)" $intact = $true foreach ($asset in $release.assets) { $local = Join-Path $dist $asset.name if ((Get-FileHash $local -Algorithm SHA256).Hash -ne $asset.sha256) { $intact = $false } } Assert-That 'the uploaded bytes match dist/ exactly' $intact $quoted = @($state.log | Where-Object { $_ -like '*disposition*' }) Assert-That 'the multipart field is name="attachment"' ` (($quoted.Count -gt 0) -and ($quoted[-1] -like '*name="attachment"*')) ` ($(if ($quoted.Count) { $quoted[-1] } else { 'no disposition logged' })) Write-Host '' Write-Host 'publish.ps1 again' -ForegroundColor Cyan & (Join-Path $scripts 'publish.ps1') -Tag 'test' | Out-Null $release = (Get-State).releases | Where-Object { $_.tag_name -eq 'test' } Assert-That 'republishing replaces rather than duplicates' ` ($release.assets.Count -eq $packages.Count) "got $($release.assets.Count)" Write-Host '' Write-Host 'install.ps1' -ForegroundColor Cyan Remove-Item $callLog -ErrorAction SilentlyContinue & (Join-Path $scripts 'install.ps1') -Tag 'test' | Out-Null $calls = @(Get-Content $callLog -ErrorAction SilentlyContinue) Assert-That "code was called once per extension" ($calls.Count -eq $packages.Count) ` "got $($calls.Count)" Assert-That 'every install passes --force' ` (($calls.Count -gt 0) -and -not ($calls | Where-Object { $_ -notmatch '--force' })) $downloadedOk = $calls.Count -gt 0 foreach ($call in $calls) { $path = ($call -replace '^--install-extension\s+', '' -replace '\s+--force\s*$', '').Trim() $local = Join-Path $dist (Split-Path -Leaf $path) if (-not (Test-Path $path) -or (Get-FileHash $path -Algorithm SHA256).Hash -ne (Get-FileHash $local -Algorithm SHA256).Hash) { $downloadedOk = $false } } Assert-That 'the downloaded files match dist/ (token survived the redirect)' $downloadedOk Write-Host '' Write-Host 'no Gitea configured' -ForegroundColor Cyan $env:GITEA_URL = ''; $env:GITEA_OWNER = ''; $env:GITEA_REPO = '' Remove-Item $callLog -ErrorAction SilentlyContinue $localWorked = $true try { & (Join-Path $scripts 'install.ps1') -Local | Out-Null } catch { $localWorked = $false } Assert-That 'install.ps1 -Local needs no Gitea config' $localWorked $env:GITEA_URL = $base; $env:GITEA_OWNER = 'max'; $env:GITEA_REPO = 'vscode-extensions' # Git's bash, not the one on PATH: on Windows that is usually # System32\bash.exe, the WSL launcher, which sees D:\ as /mnt/d and so cannot open # the script by the path we have. $bash = @( 'C:\Program Files\Git\bin\bash.exe', 'C:\Program Files (x86)\Git\bin\bash.exe', (Join-Path $env:LOCALAPPDATA 'Programs\Git\bin\bash.exe') ) | Where-Object { Test-Path $_ } | Select-Object -First 1 if (-not $bash) { $onPath = Get-Command bash -ErrorAction SilentlyContinue if ($onPath -and $onPath.Source -notlike "$env:WINDIR*") { $bash = $onPath.Source } } if ($bash) { Write-Host '' Write-Host 'ci-release.sh' -ForegroundColor Cyan $env:GITEA_SERVER = $base $env:TAG = 'test-ci' $env:SKIP_PACKAGE = '1' # Forward slashes too: bash reads the backslashes in a Windows path as escapes, # so D:\a\b arrives as D:ab. $shPath = (Join-Path $scripts 'ci-release.sh') -replace '\\', '/' & $bash $shPath 2>&1 | Out-Null $ci = (Get-State).releases | Where-Object { $_.tag_name -eq 'test-ci' } Assert-That 'ci-release.sh publishes the same set' ` (($null -ne $ci) -and ($ci.assets.Count -eq $packages.Count)) $ciIntact = $null -ne $ci if ($ci) { foreach ($asset in $ci.assets) { $local = Join-Path $dist $asset.name if ((Get-FileHash $local -Algorithm SHA256).Hash -ne $asset.sha256) { $ciIntact = $false } } } Assert-That 'ci-release.sh uploads intact bytes' $ciIntact Remove-Item Env:\SKIP_PACKAGE, Env:\TAG, Env:\GITEA_SERVER -ErrorAction SilentlyContinue } else { Write-Host ' SKIP ci-release.sh (no bash on PATH)' -ForegroundColor Yellow } } finally { $env:PATH = $savedPath Remove-Item Env:\GITEA_URL, Env:\GITEA_OWNER, Env:\GITEA_REPO, Env:\GITEA_TOKEN, Env:\PORT ` -ErrorAction SilentlyContinue if ($server -and -not $server.HasExited) { Stop-Process -Id $server.Id -Force } Remove-Item $stubDir -Recurse -Force -ErrorAction SilentlyContinue } Write-Host '' if ($failures.Count -eq 0) { Write-Host 'all checks passed' -ForegroundColor Green exit 0 } Write-Host "$($failures.Count) check(s) failed:" -ForegroundColor Red $failures | ForEach-Object { Write-Host " - $_" -ForegroundColor Red } exit 1