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.
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
<#
|
||||
.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
|
||||
Reference in New Issue
Block a user