Add package sign to pipeline (#5)
Release / release (push) Failing after 4m39s

leftovers

Reviewed-on: #5
Co-authored-by: Aleksandr Neychev <alexnejchev73@gmail.com>
This commit was merged in pull request #5.
This commit is contained in:
2026-08-13 11:55:20 +00:00
committed by alex
parent 42974ecc8f
commit 03a3dd4c6e
3 changed files with 594 additions and 0 deletions
+48
View File
@@ -0,0 +1,48 @@
<!--
The installer project. It is not part of CursorLang.sln and is not meant to be
built on its own: build-installer.ps1 publishes the application first and then
builds this, passing in the folder it published to.
WiX 5 rather than the current 7: from version 6 onwards the toolset asks every
build to accept the Open Source Maintenance Fee licence, which is a decision for
a person to make and not for a build script. Version 5 carries no such
requirement. Moving up later means changing the version here and adding
-p:AcceptEula=true to the build.
-->
<Project Sdk="WixToolset.Sdk/5.0.2">
<PropertyGroup>
<OutputName>CursorLang</OutputName>
<OutputType>Package</OutputType>
<!--
MSI validation still assumes an installation for the whole machine.
Installing into the user's own profile trips three of its rules: a
component whose key path is a file (ICE38), a folder it wants listed
for removal (ICE64), and a warning that the files will not follow other
users of the machine (ICE91). All three describe exactly what was
intended here, so they are turned off rather than worked around.
-->
<SuppressIces>ICE38;ICE64;ICE91</SuppressIces>
<!--
What the script passes in, gathered into the constants the source file
reads. They are gathered here rather than handed over as one property
from the command line: a semicolon in a property value is where MSBuild
stops reading it, and the separator between constants is a semicolon.
-->
<DefineConstants>
Version=$(CursorLangVersion);
PayloadDir=$(PayloadDir);
IconFile=$(IconFile);
LicenseFile=$(LicenseFile)
</DefineConstants>
</PropertyGroup>
<ItemGroup>
<!-- CloseApplication and the launch at the end of the wizard come from here -->
<PackageReference Include="WixToolset.Util.wixext" Version="5.0.2" />
<PackageReference Include="WixToolset.UI.wixext" Version="5.0.2" />
</ItemGroup>
</Project>
+383
View File
@@ -0,0 +1,383 @@
# Публикация в Microsoft Store
Инструкция с нуля: от регистрации разработчика до отправки пакета на проверку.
Документ существует только на русском — переводить его не нужно.
Ссылки на первоисточники стоят по ходу текста, сводный список — в конце.
Правила Store меняются, поэтому перед подачей стоит сверяться с ними, а не с
пересказом здесь: этот документ отражает положение дел на август 2026 года.
## 1. Аккаунт разработчика
С 2026 года регистрация бесплатна для обоих типов аккаунта. Прежние взносы
($19 и $99) отменены — [объявление для компаний][free-company],
[объявление для частных лиц][free-individual]. Новостные заметки за 2025 год,
где у Company ещё указан взнос $99, устарели.
**Начинать нужно с https://storedeveloper.microsoft.com** — это единственный
вход в бесплатный процесс регистрации. Если зайти через Partner Center, Visual
Studio или Xbox, откроется старый порядок со взносом.
### Какой тип выбрать
Критерии обоих типов и порядок регистрации — [«Открытие аккаунта
разработчика»][open-account].
| | Individual | Company |
|---|---|---|
| Кому | частное лицо, хобби, некоммерческие проекты | ИП, ТОО, организация — всё, что связано с предпринимательской деятельностью |
| Имя издателя в Store | ваше имя | название организации |
| Вход | только личный аккаунт Microsoft | личный аккаунт или рабочий Microsoft Entra ID |
| Проверка | документ с фотографией и селфи | D-U-N-S или уставные документы плюс рабочая почта на домене организации |
| Срок | минуты, если снимки читаемые | от минут до 2–5 рабочих дней при ручной проверке |
Company доступен только тем, у кого уже есть зарегистрированное юридическое лицо
или ИП: проверка требует либо номера D-U-N-S, либо регистрационных документов.
Без них остаётся Individual, и для личного бесплатного приложения этого
достаточно.
Три вещи после регистрации **не меняются** — тип аккаунта, страна и имя издателя
([FAQ по управлению аккаунтом][account-faq]). Что с этим делать при отсутствии
компании и планах на платный продукт, разобрано в
[разделе 9](#9-регистрация-из-казахстана).
### Что подготовить
**Для Individual:** удостоверение личности или паспорт (оригинал, не копия) и
телефон с камерой — снимок документа и селфи делаются на мобильном.
**Для Company:**
- номер D-U-N-S, если он есть — тогда данные подтянутся автоматически и проверка
пройдёт быстрее. Иначе понадобится справка о регистрации, устав или выписка из
государственного реестра;
- почта на домене организации. Gmail и подобные не принимаются; если домен почты
не совпадает с доменом организации, попросят подтвердить владение доменом —
счётом от регистратора или записью из реестра доменов.
На каждый вид проверки даётся не более трёх попыток, поэтому данные лучше
перепроверить до отправки. Ответить на запрос документов нужно в течение 30 дней.
### Налоги и выплаты
Приложение бесплатное, встроенных покупок и рекламы нет — значит платёжный
профиль и налоговые формы не нужны ([FAQ по управлению аккаунтом][account-faq]).
Они потребуются, только если появятся платные функции, — тогда см.
[раздел 8](#8-если-приложение-будет-платным).
После создания аккаунта данные расходятся по Partner Center до получаса. Если
раздел «Apps & Games» не появился сразу — подождать и обновить страницу.
## 2. Резервирование имени
Partner Center → **Apps and games****New product****MSIX or PWA app**.
Ввести имя, нажать **Check availability**, затем **Reserve product name**.
Подробности — [«Резервирование имени приложения MSIX»][reserve-name] и
[«Управление резервированием имён»][manage-names].
Имя можно занять за три месяца до публикации, даже если приложение ещё не
готово. Стоит сразу занять запасные написания — например `CursorLang` и
`Cursor Lang`.
## 3. Данные пакета
После резервирования: **Product management****Product identity**. На странице
будут три значения:
| Значение в Partner Center | Куда подставить |
|---|---|
| Package/Identity/Name | параметр `-IdentityName` |
| Package/Identity/Publisher | параметр `-Publisher`, вида `CN=` и длинный идентификатор |
| Package/Properties/PublisherDisplayName | параметр `-PublisherDisplayName` |
Эти значения должны совпадать с манифестом пакета до символа. Расхождение даёт
при загрузке невнятную ошибку, которая не называет поле, — поэтому копировать
их нужно буквально, а не набирать руками. Разбор таких ошибок —
[«Устранение ошибок отправки MSIX»][submission-errors].
В сборочном конвейере эти значения лежат в переменных репозитория
`MSIX_IDENTITY_NAME`, `MSIX_PUBLISHER` и `MSIX_PUBLISHER_DISPLAY_NAME`. Это
переменные, а не секреты: identity виден в любом опубликованном пакете, а
маскирование в логах только мешало бы разбирать ошибки сборки.
## 4. Сборка пакета
```powershell
pwsh -File Packaging\build-msix.ps1 `
-Version 1.0.0.0 `
-IdentityName "<Package/Identity/Name>" `
-Publisher "<Package/Identity/Publisher>" `
-PublisherDisplayName "<PublisherDisplayName>"
```
Получится `artifacts\packages\CursorLang-1.0.0.0-x64.msix`. Загружать его нужно
**без подписи**: Store подписывает пакет своим сертификатом.
Версия при каждой следующей отправке должна расти, а последнее число всегда
остаётся нулём.
## 5. Заявка
**Product release****Start submission**. Разделы можно заполнять в любом
порядке. Полный перечень полей с пометками обязательности —
[«Создание заявки для приложения MSIX»][create-submission].
### Pricing and availability
Обязательны рынки, аудитория, обнаружимость, расписание и цена. Всё, кроме цены,
имеет разумные значения по умолчанию — цену выставить **Free**
([подробности о ценах и доступности][price-availability]).
### Properties
- **Category** — обязательна. Подходит *Productivity* либо *Utilities & tools*.
- **Privacy policy URL** — обязателен, только если приложение собирает или
передаёт персональные данные. CursorLang не собирает ничего: настройки лежат
в папке приложения, наружу ничего не уходит. Поле можно оставить пустым, но
безопаснее выложить короткую страницу с фразой о том, что данные не
собираются, — это снимает вопросы у проверяющих.
- **Contact details** — обязательны для аккаунта Company.
### Age ratings
Обязательна вся анкета. Приложение не игровое, ничего не собирает — ответы
однозначные, рейтинг присвоится автоматически.
### Packages
Загрузить `.msix`. Раздел остаётся «Incomplete», пока не заполнены все
обязательные поля, даже если сам пакет уже помечен как «Validated».
### Store listings
Хотя бы для одного языка. Стоит заполнить и английский, и русский — приложение
переведено на оба. Основным лучше сделать английский: он работает как запасной
вариант для рынков, где отдельного перевода нет.
Обязательны описание и **минимум один снимок экрана** (рекомендуется четыре и
больше). Снимки: PNG, от 1366×768. Показать стоит окно настроек и саму подсказку
у курсора — вторую снять сложнее, поможет отложенный снимок экрана.
### Submission options
- **Restricted capabilities** — заполнить обязательно, потому что пакет объявляет
`runFullTrust`. Для обычной программы рабочего стола это подтверждается без
разбирательств; в обосновании достаточно написать, что приложение — обычная
программа Windows, а не UWP.
- **Notes for certification** — не обязательно, но здесь лучше не молчать.
Приложение ставит низкоуровневый перехватчик клавиатуры, и без объяснений это
выглядит подозрительно. Стоит написать примерно так:
> The app installs a WH_KEYBOARD_LL hook to offer an optional Caps Lock
> shortcut that switches the keyboard layout instead of toggling case. The
> hook only inspects the Caps Lock key, is off by default, and is enabled by
> the user in the app's settings. No keystrokes are recorded, stored or
> transmitted anywhere.
Затем **Submit for certification**.
## 6. Чего ждать от проверки
Проверка занимает обычно от нескольких часов до трёх дней. Проверяющие смотрят
сам пакет, а не исходники: ссылка на репозиторий им не нужна и, скорее всего,
останется без внимания. Всё, что важно донести, идёт в Notes for certification.
Слабые места именно этого приложения:
- **Перехватчик клавиатуры.** Главный повод для вопросов. Снимается пояснением в
Notes for certification и тем, что настройка выключена по умолчанию.
- **Права администратора.** Их больше нет — ни в манифесте, ни в поведении.
Возможности `allowElevation` в пакете тоже нет. Возвращать их нельзя:
«приложения, которым права администратора нужны хоть для какой-то части
работы, в Store не принимаются» — [«Подготовка к упаковке»][prepare-package].
- **Чистое удаление.** Настройки упакованной версии лежат в папке данных пакета
и уходят вместе с ним — это проверено.
Общие требования, по которым идёт проверка, — [правила Microsoft Store][policies].
## 7. Обновления
Порядок тот же, короче: поднять версию, собрать пакет теми же значениями
identity, создать новую заявку, загрузить пакет. Имя резервировать заново не
нужно.
## 8. Если приложение будет платным
Раздел на будущее: для CursorLang ничего из этого не нужно.
### Нужен ли для этого Company
Не обязательно. Платёжный профиль доступен обоим типам аккаунта, и запрета
продавать с Individual в документации нет ([FAQ по управлению
аккаунтом][account-faq]). Критерий Individual против Company описывает характер
деятельности ([типы аккаунтов][account-types]), а не техническую возможность
брать деньги.
Практический вывод: заводить ИП **ради требований Microsoft** не нужно. Оно
скорее понадобится по местному праву — чтобы легально оформлять регулярный доход
из-за рубежа. Это вопрос к бухгалтеру, а не к Partner Center.
Если всё же понадобится именно Company, помните: сменить тип у существующего
аккаунта нельзя, придётся заводить второй, а уже опубликованные приложения в
него штатно не переедут — см. [раздел 9](#9-регистрация-из-казахстана).
### Платёжный и налоговый профиль
Account settings → **Payout and tax profile**
([порядок настройки][payout-setup]). Без них платное приложение не отправить на
проверку. Понадобятся банковский счёт и налоговая форма: для тех, кто не платит
налоги в США, это W-8BEN (частное лицо) или W-8BEN-E (организация)
([налоговые сведения][tax-info], [налоговые обязанности][tax-details]).
Между Казахстаном и США действует соглашение об избежании двойного
налогообложения, поэтому удержание с продаж в США ниже базовых 30%. Точную
ставку показывают при заполнении формы — гадать заранее не нужно.
### Выплаты в Казахстан
Поддерживаются. По [таблице выплат по регионам][payout-regions] для Казахстана
доступны и Microsoft Store, и PayPal.
| | |
|---|---|
| Порог выплаты | 50 USD — ниже суммы накапливаются |
| PayPal | около одного рабочего дня |
| ACH/SEPA | два–три рабочих дня |
| Банковский перевод | семь–десять рабочих дней |
### Доля Microsoft
По действующим условиям — 15% с продаж приложений и 12% с игр; для неигровых
приложений можно подключить собственную платёжную систему и оставлять себе всю
выручку. Эти цифры широко приводятся в отраслевых публикациях, но отдельной
страницы с ними в документации нет: обязывающий документ —
[соглашение разработчика приложений][developer-agreement], и сверяться нужно с
его действующей редакцией.
### Что меняется в самом приложении
Почти ничего. Store выдаёт лицензию при покупке и без неё приложение не
устанавливает, так что проверка в коде не обязательна. При желании лицензию
читают через [`Windows.Services.Store`][store-api]. Пробный период включается в
Partner Center и правок в коде не требует.
### Рынки
Для платного приложения список стран стоит выбрать осознанно, а не оставлять
«все»: где-то автоматически пересчитанная цена окажется неуместной, где-то
появятся местные налоговые обязанности.
## 9. Регистрация из Казахстана
Раздел для случая, когда разработчик живёт и платит налоги в Казахстане, а
гражданство у него другое. Юридических советов здесь нет — только то, что
касается устройства Partner Center.
### Страна аккаунта важнее гражданства
Microsoft смотрит на страну проживания и налогового резидентства, а не на
паспорт. Страна указывается при регистрации и **после неё не меняется**
([FAQ по управлению аккаунтом][account-faq]); штатного способа исправить её нет,
остаётся обращение в поддержку без гарантии результата.
Разница между Казахстаном и Россией существенная и необратимая:
| | Казахстан | Россия |
|---|---|---|
| Выплаты Microsoft Store | да | да, но PayPal помечен как приостановленный |
| Налоговое соглашение с США | [действует][irs-kazakhstan], предел по роялти 10% | [приостановлено с 16 августа 2024][irs-russia] |
| Удержание в США | 10% при поданной форме W-8BEN | 30%, льготные ставки не применяются |
Данные о выплатах — из [таблицы выплат по регионам][payout-regions], налоговые —
из перечня [действующих соглашений США][irs-treaties].
### Документ и автозаполнение профиля
Регистрация Individual идёт через снимок документа и селфи, после чего профиль
**заполняется данными из документа**. Отсюда практическое правило: если есть
казахстанский документ — вид на жительство или удостоверение личности, —
верифицироваться лучше по нему. Если под рукой только иностранный паспорт,
страну и адрес нужно проверить и при необходимости исправить на казахстанские
**до** подтверждения профиля.
### Имя издателя задаётся один раз
Publisher display name — то, что видят пользователи в карточке приложения, — тоже
не меняется после регистрации. Если в Store должно значиться название бренда, а
не ФИО, это задаётся на шаге заполнения профиля. Переименование потом — только
через поддержку, с вероятным ответом «заводите новый аккаунт».
### Приложения между аккаунтами не переносятся
Штатной процедуры передачи приложения из одного аккаунта в другой в Partner
Center нет. Значит, если позже завести отдельный Company-аккаунт под платный
продукт, ранее опубликованное бесплатное приложение останется на первом.
Для бесплатной утилиты это не проблема: она продолжит работать и обновляться там,
где опубликована. Но переиграть решение задним числом не выйдет, поэтому выбор
имени издателя и страны стоит сделать вдумчиво с первого раза.
### Порядок действий
1. Начать с https://storedeveloper.microsoft.com — иначе откроется старый платный
процесс.
2. Выбрать **Individual**, если зарегистрированного юридического лица или ИП нет.
3. Верифицироваться по казахстанскому документу, если он есть.
4. Проверить, что страна в профиле — Казахстан, а адрес казахстанский.
5. Проверить имя издателя: оно останется навсегда.
6. Платёжный и налоговый профиль пропустить — для бесплатного приложения он не
нужен.
Когда дойдёт до платного приложения, добавится форма
[W-8BEN][w8ben] с указанием Казахстана как страны налогового резидентства и ИИН в
поле иностранного налогового номера. Сертификат налогового резидентства РК
Microsoft не запрашивает, но он подтверждает право на ставку 10%, если вопрос
возникнет.
## Источники
- [Открытие аккаунта разработчика][open-account]
- [Типы аккаунтов разработчика][account-types]
- [Бесплатная регистрация для компаний][free-company]
- [Бесплатная регистрация для частных лиц][free-individual]
- [FAQ по управлению аккаунтом][account-faq]
- [Резервирование имени приложения MSIX][reserve-name]
- [Управление резервированием имён][manage-names]
- [Создание заявки для приложения MSIX][create-submission]
- [Цены и доступность][price-availability]
- [Устранение ошибок отправки MSIX][submission-errors]
- [Подготовка к упаковке программы рабочего стола][prepare-package]
- [Правила Microsoft Store][policies]
- [Настройка платёжного и налогового профиля][payout-setup]
- [Выплаты по регионам][payout-regions]
- [Налоговые сведения][tax-info]
- [Налоговые обязанности][tax-details]
- [Соглашение разработчика приложений][developer-agreement]
- [API лицензий и покупок Windows.Services.Store][store-api]
- [Форма W-8BEN, IRS][w8ben]
- [Действующие налоговые соглашения США, IRS][irs-treaties]
- [Соглашение США — Казахстан, IRS][irs-kazakhstan]
- [Соглашение США — Россия и его приостановка, IRS][irs-russia]
[open-account]: https://learn.microsoft.com/en-us/windows/apps/publish/partner-center/open-a-developer-account
[account-types]: https://learn.microsoft.com/en-us/windows/apps/publish/partner-center/partner-center-developer-account
[free-company]: https://learn.microsoft.com/en-us/windows/apps/publish/whats-new-company-developer
[free-individual]: https://learn.microsoft.com/en-us/windows/apps/publish/whats-new-individual-developer
[account-faq]: https://learn.microsoft.com/en-us/windows/apps/publish/faq/manage-your-account
[reserve-name]: https://learn.microsoft.com/en-us/windows/apps/publish/publish-your-app/msix/reserve-your-apps-name
[manage-names]: https://learn.microsoft.com/en-us/windows/apps/publish/partner-center/msix/manage-app-name-reservations
[create-submission]: https://learn.microsoft.com/en-us/windows/apps/publish/publish-your-app/msix/create-app-submission
[price-availability]: https://learn.microsoft.com/en-us/windows/apps/publish/publish-your-app/msix/price-and-availability
[submission-errors]: https://learn.microsoft.com/en-us/windows/apps/publish/publish-your-app/msix/resolve-submission-errors
[prepare-package]: https://learn.microsoft.com/en-us/windows/msix/desktop/desktop-to-uwp-prepare
[policies]: https://learn.microsoft.com/en-us/windows/apps/publish/store-policies
[payout-setup]: https://learn.microsoft.com/en-us/partner-center/account-settings/set-up-your-payout-account
[payout-regions]: https://learn.microsoft.com/en-us/partner-center/marketplace-offers/payment-thresholds-methods-timeframes
[tax-info]: https://learn.microsoft.com/en-us/partner-center/marketplace-offers/tax-information-for-commercial-marketplace
[tax-details]: https://learn.microsoft.com/en-us/partner-center/marketplace-offers/tax-details-marketplace
[developer-agreement]: https://learn.microsoft.com/en-us/legal/windows/agreements/app-developer-agreement
[store-api]: https://learn.microsoft.com/en-us/windows/uwp/monetize/in-app-purchases-and-trials
[w8ben]: https://www.irs.gov/forms-pubs/about-form-w-8-ben
[irs-treaties]: https://www.irs.gov/businesses/international-businesses/united-states-income-tax-treaties-a-to-z
[irs-kazakhstan]: https://www.irs.gov/businesses/international-businesses/kazakhstan-tax-treaty-documents
[irs-russia]: https://www.irs.gov/businesses/international-businesses/russia-tax-treaty-documents
+163
View File
@@ -0,0 +1,163 @@
<#
.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.'