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.
136 lines
4.3 KiB
PowerShell
136 lines
4.3 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Installs the extensions into VS Code, from a Gitea release or from dist/.
|
|
|
|
.DESCRIPTION
|
|
By default this downloads the .vsix assets from the -Tag release and installs them,
|
|
so a fresh machine needs nothing but VS Code and this repo -- no Node, no toolchain.
|
|
Pass -Local to install what build.ps1 produced instead.
|
|
|
|
Every install passes --force. Without it VS Code declines to reinstall a version it
|
|
already has, which would silently do nothing every time you republish the same
|
|
version number.
|
|
|
|
.PARAMETER Tag
|
|
Release tag to install from. Defaults to "latest".
|
|
|
|
.PARAMETER Local
|
|
Install from dist/ rather than downloading.
|
|
|
|
.PARAMETER Only
|
|
Restrict to these extension ids.
|
|
|
|
.PARAMETER Uninstall
|
|
Remove the extensions instead of installing them.
|
|
|
|
.EXAMPLE
|
|
.\scripts\install.ps1
|
|
.\scripts\install.ps1 -Local
|
|
.\scripts\install.ps1 -Tag v0.1.0
|
|
.\scripts\install.ps1 -Uninstall
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
[string] $Tag = 'latest',
|
|
[string] $Token,
|
|
[string[]] $Only,
|
|
[switch] $Local,
|
|
[switch] $Uninstall
|
|
)
|
|
|
|
Set-StrictMode -Version Latest
|
|
$ErrorActionPreference = 'Stop'
|
|
. (Join-Path $PSScriptRoot 'common.ps1')
|
|
|
|
$config = Get-SetupConfig
|
|
$code = Get-CodeCommand
|
|
|
|
function Select-Configured {
|
|
param([string[]] $Ids)
|
|
$chosen = $config.extensions
|
|
if ($Ids) {
|
|
$chosen = $chosen | Where-Object { $Ids -contains $_.id }
|
|
$missing = $Ids | Where-Object { $config.extensions.id -notcontains $_ }
|
|
if ($missing) { throw "not in config.json: $($missing -join ', ')" }
|
|
}
|
|
$chosen
|
|
}
|
|
|
|
if ($Uninstall) {
|
|
foreach ($extension in Select-Configured $Only) {
|
|
# The manifest's publisher is "local", so the installed id is local.<name>.
|
|
$id = "local.$($extension.id)"
|
|
Write-Host "uninstalling $id" -ForegroundColor DarkGray
|
|
& $code --uninstall-extension $id
|
|
}
|
|
Write-Host 'Reload the window for this to take effect.' -ForegroundColor Cyan
|
|
return
|
|
}
|
|
|
|
$wanted = Select-Configured $Only
|
|
$packages = @()
|
|
|
|
if ($Local) {
|
|
$dist = Get-DistDir
|
|
foreach ($extension in $wanted) {
|
|
$found = @(Get-ChildItem -Path $dist -Filter "$($extension.id)-*.vsix" |
|
|
Sort-Object Name -Descending)
|
|
if ($found.Count -eq 0) {
|
|
throw "no package for $($extension.id) in $dist. Run build.ps1 first."
|
|
}
|
|
$packages += $found[0].FullName
|
|
}
|
|
} else {
|
|
$Token = Get-GiteaToken -Token $Token
|
|
$release = Get-GiteaRelease -Tag $Tag -Token $Token
|
|
if (-not $release) {
|
|
throw ("no release tagged '$Tag' in " +
|
|
"$($config.gitea.owner)/$($config.gitea.repo). Run publish.ps1 first.")
|
|
}
|
|
|
|
$assets = @()
|
|
if ($release.PSObject.Properties.Name -contains 'assets' -and $release.assets) {
|
|
$assets = @($release.assets)
|
|
}
|
|
if ($assets.Count -eq 0) { throw "release '$Tag' has no assets" }
|
|
|
|
# Downloads go to a temp folder, not dist/, so an install never disturbs a build.
|
|
$staging = Join-Path ([System.IO.Path]::GetTempPath()) "vsix-$Tag-$PID"
|
|
New-Item -ItemType Directory -Path $staging -Force | Out-Null
|
|
|
|
foreach ($extension in $wanted) {
|
|
$match = @($assets |
|
|
Where-Object { $_.name -like "$($extension.id)-*.vsix" } |
|
|
Sort-Object name -Descending)
|
|
if ($match.Count -eq 0) {
|
|
Write-Warning "release '$Tag' has no asset for $($extension.id), skipping"
|
|
continue
|
|
}
|
|
$asset = $match[0]
|
|
$target = Join-Path $staging $asset.name
|
|
Write-Host "downloading $($asset.name)" -ForegroundColor DarkGray
|
|
Save-GiteaAsset -Url $asset.browser_download_url -Destination $target -Token $Token
|
|
$packages += $target
|
|
}
|
|
}
|
|
|
|
if ($packages.Count -eq 0) { throw 'nothing to install' }
|
|
|
|
$failed = @()
|
|
foreach ($package in $packages) {
|
|
Write-Host "installing $(Split-Path -Leaf $package)" -NoNewline
|
|
& $code --install-extension $package --force
|
|
if ($LASTEXITCODE -eq 0) {
|
|
Write-Host ' ok' -ForegroundColor Green
|
|
} else {
|
|
Write-Host ' FAILED' -ForegroundColor Red
|
|
$failed += $package
|
|
}
|
|
}
|
|
|
|
Write-Host ''
|
|
if ($failed.Count -gt 0) {
|
|
throw "$($failed.Count) of $($packages.Count) failed to install: $($failed -join ', ')"
|
|
}
|
|
Write-Host "installed $($packages.Count) extension(s). Reload the window." -ForegroundColor Cyan
|