Files
vs-code-setup/test/run.ps1
T
max d45639bf2f Build, publish to Gitea releases, and install the three extensions
Answers two things: how to install these without a marketplace, and how to keep
settings in sync when VS Code's Settings Sync cannot be pointed at Gitea.

- build.ps1 packages each extension into dist/
- publish.ps1 creates a release and uploads them, replacing same-named assets so a
  rolling "latest" tag stays clean
- install.ps1 downloads a release's assets and installs them, or -Local from dist/
- settings.ps1 copies User/ between the repo and the machine, and installs the
  marketplace extensions recorded in extensions.txt
- dev-link.ps1 junctions the sources into ~/.vscode/extensions for development
- ci-release.sh plus a Gitea Actions workflow do the build and publish on a tag push

Settings are copied, not symlinked: an atomic save replaces a symlink with a regular
file and the sync stops without looking broken.

vsce/ pins vsce and undici so packaging works on Node 18, which current vsce does not
support.

test/run.ps1 drives the real scripts against a stub of the Gitea release API. It
caught the multipart Content-Disposition being unquoted on .NET Framework, and
Invoke-WebRequest dropping the Authorization header across a redirect.
2026-09-07 19:35:20 +02:00

205 lines
8.3 KiB
PowerShell

<#
.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