modified release pipeline
Pull request / build (pull_request) Successful in 54s

This commit is contained in:
2026-08-13 02:21:50 +05:00
parent 5e11b16758
commit 460ce52017
7 changed files with 345 additions and 42 deletions
+109 -13
View File
@@ -1,14 +1,17 @@
<#
.SYNOPSIS
Builds the CursorLang MSIX package for the Microsoft Store.
Builds the CursorLang MSIX package.
.DESCRIPTION
Neither Visual Studio nor the Windows SDK is needed: makeappx arrives as a
NuGet package (Tools\SdkTools.csproj) and the application is built by the
plain .NET SDK.
Neither Visual Studio nor the Windows SDK is needed: makeappx and signtool
arrive as a NuGet package (Tools\SdkTools.csproj) and the application is
built by the plain .NET SDK.
The package is handed to Partner Center as it comes out of here — the Store
is where it gets everything else done to it.
The package comes out of here in one of two shapes. Left unsigned it goes to
Partner Center as it is — the Store puts its own signature on it and does the
rest. Signed with -CertificateThumbprint it is a package anyone can install
from a release, because Windows only takes a package whose signature it
trusts.
The application is published with its own copy of .NET: Windows does not
carry one, and MSIX cannot install the runtime as a package dependency.
@@ -18,6 +21,17 @@
section. It is handed out there together with the reserved application name;
the default is only good enough for a check on your own machine.
.PARAMETER PackageSuffix
Goes at the end of the file names. The two builds of a release differ in the
identity inside them and in nothing a file listing shows, so the one meant
for the Store is told apart by a suffix of its own.
.PARAMETER CertificateThumbprint
Signs the packages with the certificate of this thumbprint from the personal
store of the current user. The private key is none of this script's business:
signtool asks the store for it, and behind a cloud certificate — eSigner CKA
among them — the store answers over the network.
.PARAMETER Install
Puts the package onto this machine to see it working. Developer mode has to
be on; a signature is not needed, because what gets registered is the layout
@@ -33,8 +47,14 @@
.EXAMPLE
# A build for the Store — the identity comes from Partner Center
pwsh -File Packaging\build-msix.ps1 -Version 1.0.1.0 -PackageSuffix store `
-IdentityName 12345AleksandrNeichev.CursorLang -Publisher "CN=ABCD1234-..."
.EXAMPLE
# A build for a release — the publisher is the subject of the certificate
pwsh -File Packaging\build-msix.ps1 -Version 1.0.1.0 `
-IdentityName 12345AleksandrNeychev.CursorLang -Publisher "CN=ABCD1234-..."
-Publisher "CN=Aleksandr Neichev, O=Aleksandr Neichev, C=KZ" `
-CertificateThumbprint A1B2C3D4E5F60718293A4B5C6D7E8F9012345678
#>
[CmdletBinding()]
param(
@@ -42,12 +62,21 @@ param(
[string] $Version = '1.0.0.0',
[string] $IdentityName = 'CursorLang',
[string] $Publisher = 'CN=Aleksandr Neychev',
[string] $PublisherDisplayName = 'Aleksandr Neychev',
[string] $Publisher = 'CN=Aleksandr Neichev',
[string] $PublisherDisplayName = 'Aleksandr Neichev',
[ValidateSet('x64', 'arm64')]
[string[]] $Architectures = @('x64', 'arm64'),
[string] $PackageSuffix,
[string] $CertificateThumbprint,
# SSL.com's timestamp server, to go with the certificate the pipeline signs
# with. A signature without a timestamp is only good while the certificate
# is: the day it expires the package stops installing everywhere at once
[string] $TimestampUrl = 'http://ts.ssl.com',
[switch] $Install,
[string] $OutputPath
@@ -64,7 +93,14 @@ $assets = Join-Path $root 'Assets'
$manifestTemplate = Join-Path $root 'AppxManifest.xml'
$toolsProject = Join-Path $root 'Tools\SdkTools.csproj'
if (-not $OutputPath) { $OutputPath = Join-Path $repository 'artifacts' }
if (-not $OutputPath) {
$OutputPath = Join-Path $repository 'artifacts'
} elseif (-not [System.IO.Path]::IsPathRooted($OutputPath)) {
# Windows names the folder a package was registered from in full, and the
# layout is looked for by that name below. A relative path would never be
# found there, and the registration of a build gone by would be left behind
$OutputPath = Join-Path (Get-Location).Path $OutputPath
}
$layoutRoot = Join-Path $OutputPath 'layout'
$packagesPath = Join-Path $OutputPath 'packages'
@@ -76,6 +112,26 @@ if (-not (Test-Path $assets)) {
throw "No logos found in '$assets'. Run Packaging\New-Assets.ps1 first."
}
if ($CertificateThumbprint) {
# Both things below are checked before the build for the same reason the
# ones under -Install are: the build takes minutes and neither answer
# changes while it runs
$certificate = Get-ChildItem "Cert:\CurrentUser\My\$CertificateThumbprint" -ErrorAction SilentlyContinue
if (-not $certificate) {
throw "No certificate with the thumbprint '$CertificateThumbprint' in the personal store of this user. A cloud certificate has to be loaded into the store first — eSigner CKA is what does that."
}
# The Publisher of a package is not a name of the publisher's choosing: it
# is the subject of the certificate the package is signed with, letter for
# letter. signtool turns down a package that claims any other, and it does
# so at the very end — after everything has already been built
if ($certificate.Subject -ne $Publisher) {
throw "The publisher '$Publisher' is not the subject of the certificate, which reads '$($certificate.Subject)'. A package carries the name of whoever signs it."
}
}
if ($Install) {
# Both things below are checked before the build rather than after it: the
# build takes minutes, and neither of them gets any truer while it runs
@@ -160,11 +216,34 @@ function Get-SdkTool {
return $tool.FullName
}
function Invoke-Signing {
<#
.SYNOPSIS
Signs a package with the certificate the build was given.
#>
param(
[string] $Path,
[string] $SignTool
)
Write-Host "Signing $([System.IO.Path]::GetFileName($Path))..." -ForegroundColor Cyan
Invoke-Tool -Path $SignTool -Arguments @(
'sign',
'/fd', 'sha256',
'/tr', $TimestampUrl,
'/td', 'sha256',
'/sha1', $CertificateThumbprint,
$Path
)
}
Write-Host 'Fetching the Windows SDK programs...' -ForegroundColor Cyan
Invoke-Tool -Path 'dotnet' -Arguments @('restore', $toolsProject, '--nologo')
$sdkTools = Get-SdkToolsPath
$makeappx = Get-SdkTool -Name 'makeappx.exe' -PackagePath $sdkTools
$signtool = if ($CertificateThumbprint) { Get-SdkTool -Name 'signtool.exe' -PackagePath $sdkTools } else { $null }
# A package registered out of the layout runs straight from that folder, and
# the folder is about to be wiped. Left in place, the registration would point
@@ -189,6 +268,7 @@ foreach ($installed in @(Get-AppxPackage -Name $IdentityName)) {
Remove-Item $layoutRoot -Recurse -Force -ErrorAction SilentlyContinue
New-Item -ItemType Directory -Path $packagesPath -Force | Out-Null
$suffix = if ($PackageSuffix) { "-$PackageSuffix" } else { '' }
$built = @()
foreach ($architecture in $Architectures) {
@@ -223,7 +303,7 @@ foreach ($architecture in $Architectures) {
Set-Content -Path (Join-Path $layout 'AppxManifest.xml') -Value $manifest -Encoding UTF8
$package = Join-Path $packagesPath "CursorLang-$Version-$architecture.msix"
$package = Join-Path $packagesPath "CursorLang-$Version-$architecture$suffix.msix"
Invoke-Tool -Path $makeappx -Arguments @('pack', '/o', '/d', $layout, '/p', $package)
$built += $package
@@ -242,12 +322,23 @@ if ($built.Count -gt 1) {
New-Item -ItemType Directory -Path $bundleInput -Force | Out-Null
$built | ForEach-Object { Copy-Item $_ -Destination $bundleInput }
$result = Join-Path $packagesPath "CursorLang-$Version.msixbundle"
$result = Join-Path $packagesPath "CursorLang-$Version$suffix.msixbundle"
Invoke-Tool -Path $makeappx -Arguments @('bundle', '/o', '/d', $bundleInput, '/p', $result, '/bv', $Version)
Remove-Item $bundleInput -Recurse -Force
}
if ($CertificateThumbprint) {
# Signing comes after the bundle rather than before it: makeappx copies the
# packages into the bundle as they are, and a signature on what lies inside
# says nothing about the bundle around it. Windows asks the outer file, so
# every file that leaves here is signed on its own — each one of them is a
# package someone may install
foreach ($package in (@($built) + @($result) | Select-Object -Unique)) {
Invoke-Signing -Path $package -SignTool $signtool
}
}
if ($Install) {
Write-Host "Installing the $machineArchitecture build..." -ForegroundColor Cyan
@@ -261,7 +352,12 @@ Write-Host 'Done.' -ForegroundColor Green
Write-Host " $result"
Write-Host ''
Write-Host ' This file is uploaded to Partner Center as it is.'
if ($CertificateThumbprint) {
Write-Host ' This file is signed: Windows installs it on any machine.'
} else {
Write-Host ' This file is uploaded to Partner Center as it is.'
}
if ($Install) {
Write-Host ''