Files
cursor-lang/Packaging/build-installer.ps1
T
alex 03a3dd4c6e
Release / release (push) Failing after 4m39s
Add package sign to pipeline (#5)
leftovers

Reviewed-on: #5
Co-authored-by: Aleksandr Neychev <alexnejchev73@gmail.com>
2026-08-13 11:55:20 +00:00

164 lines
5.7 KiB
PowerShell

<#
.SYNOPSIS
Builds the CursorLang installer for handing the application round outside
the Store.
.DESCRIPTION
Nothing has to be installed beyond the .NET SDK: WiX arrives as a NuGet
package, the same way makeappx does for the MSIX build.
Nobody signs the result, so Windows warns about an unknown publisher and the
user has to insist. Short of a certificate from a trusted authority there is
no way round that, and it is the reason this installer is meant for people who
already know where the file came from.
The application is published with its own copy of .NET: Windows carries no
runtime of its own, and a self-contained build is tied to x64.
.PARAMETER Install
Runs the installer once it is built, to see the thing through the way a user
would.
.EXAMPLE
# Build and run it, to see what a user sees
pwsh -File Packaging\build-installer.ps1 -Install
.EXAMPLE
# What a release needs
pwsh -File Packaging\build-installer.ps1 -Version 1.0.1
#>
[CmdletBinding()]
param(
# Three numbers: unlike the Store, an installer keeps no place for a fourth
[string] $Version = '1.0.0',
[switch] $Install,
[string] $OutputPath
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
$root = $PSScriptRoot
$repository = Split-Path -Parent $root
$agentProject = Join-Path $repository 'CursorLang.Agent\CursorLang.Agent.csproj'
$settingsProject = Join-Path $repository 'CursorLang.Settings\CursorLang.Settings.csproj'
$installerProject = Join-Path $root 'Installer\CursorLang.wixproj'
$icon = Join-Path $repository 'CursorLang.Core\Resources\CursorLang.ico'
$license = Join-Path $repository 'LICENSE'
if (-not $OutputPath) { $OutputPath = Join-Path $repository 'artifacts' }
$stagingRoot = Join-Path $OutputPath 'installer-staging'
$installersPath = Join-Path $OutputPath 'installers'
if ($Version -notmatch '^\d+\.\d+\.\d+$') {
throw "The version '$Version' does not fit: an installer is versioned by three numbers, for example 1.0.0."
}
if (-not (Test-Path $icon)) {
throw "The icon is missing from '$icon'. Git LFS may have left a pointer in its place: run git lfs pull."
}
function Invoke-Tool {
<#
.SYNOPSIS
Runs a program and fails the build if it returned an error.
.DESCRIPTION
A wrapper of our own is needed because PowerShell does not treat the
failure of an external program as an error and quietly moves on.
#>
param(
[string] $Path,
[string[]] $Arguments
)
& $Path @Arguments
if ($LASTEXITCODE -ne 0) {
throw "'$([System.IO.Path]::GetFileName($Path))' exited with code $LASTEXITCODE."
}
}
function Write-LicenseRtf {
<#
.SYNOPSIS
Turns the plain-text LICENSE into the RTF the wizard needs.
.DESCRIPTION
The licence page of a Windows Installer wizard reads RTF and nothing
else, and keeping a second copy of the licence in the repository would
mean keeping the two in step by hand. The conversion is as plain as it
looks: the text carries no formatting to preserve.
#>
param([string] $Path)
$text = (Get-Content $license -Raw) -replace '\\', '\\\\' -replace '{', '\{' -replace '}', '\}'
$body = ($text -split "`r?`n") -join '\par' + "`r`n"
Set-Content -Path $Path -Value "{\rtf1\ansi\deff0{\fonttbl{\f0 Segoe UI;}}\fs18 $body}" -Encoding ASCII
}
Remove-Item $stagingRoot -Recurse -Force -ErrorAction SilentlyContinue
New-Item -ItemType Directory -Path $installersPath -Force | Out-Null
$licenseRtf = Join-Path $stagingRoot 'License.rtf'
New-Item -ItemType Directory -Path $stagingRoot -Force | Out-Null
Write-LicenseRtf -Path $licenseRtf
Write-Host 'Building...' -ForegroundColor Cyan
$staging = Join-Path $stagingRoot 'x64'
# Both halves go into one folder: each of them looks for the other beside
# itself — the tray menu opens the settings window, and the settings window
# registers the agent for startup
foreach ($half in @($agentProject, $settingsProject)) {
Invoke-Tool -Path 'dotnet' -Arguments @(
'publish', $half,
'--configuration', 'Release',
'--runtime', 'win-x64',
'--self-contained', 'true',
"-p:Version=$Version",
'--output', $staging,
'--nologo'
)
}
# Debug symbols only make the download bigger; they are of no use to anyone
# who installs the application
Get-ChildItem $staging -Recurse -Filter '*.pdb' | Remove-Item -Force
$installer = Join-Path $installersPath "CursorLang-$Version-x64.msi"
Invoke-Tool -Path 'dotnet' -Arguments @(
'build', $installerProject,
'--configuration', 'Release',
# The architecture of the package is the architecture of what goes in it:
# a self-contained build fits nothing else
'-p:InstallerPlatform=x64',
# Handed over one by one rather than as a ready list of constants: a
# semicolon in a property value is where MSBuild stops reading it. The
# project gathers them into DefineConstants itself
"-p:CursorLangVersion=$Version",
"-p:PayloadDir=$staging",
"-p:IconFile=$icon",
"-p:LicenseFile=$licenseRtf",
"-p:OutputPath=$installersPath\",
"-p:OutputName=CursorLang-$Version-x64",
'--nologo'
)
if ($Install) {
Write-Host "Running $([System.IO.Path]::GetFileName($installer))..." -ForegroundColor Cyan
# Started the way a user would, with the wizard showing: the point of -Install
# is to see what the person on the other end sees
Start-Process 'msiexec.exe' -ArgumentList '/i', "`"$installer`"" -Wait
}
Write-Host ''
Write-Host 'Done.' -ForegroundColor Green
Write-Host " $installer"
Write-Host ''
Write-Host ' Nobody signed it, so Windows warns about an unknown publisher.'