From 03a3dd4c6ee35ea785abd19ed9a01e9c77ae49b1 Mon Sep 17 00:00:00 2001 From: Aleksandr Neychev Date: Thu, 13 Aug 2026 11:55:20 +0000 Subject: [PATCH] Add package sign to pipeline (#5) leftovers Reviewed-on: https://git.alrakis.kz/alrakis/cursor-lang/pulls/5 Co-authored-by: Aleksandr Neychev --- Packaging/Installer/CursorLang.wixproj | 48 ++++ Packaging/PUBLISHING.RU.md | 383 +++++++++++++++++++++++++ Packaging/build-installer.ps1 | 163 +++++++++++ 3 files changed, 594 insertions(+) create mode 100644 Packaging/Installer/CursorLang.wixproj create mode 100644 Packaging/PUBLISHING.RU.md create mode 100644 Packaging/build-installer.ps1 diff --git a/Packaging/Installer/CursorLang.wixproj b/Packaging/Installer/CursorLang.wixproj new file mode 100644 index 0000000..3877049 --- /dev/null +++ b/Packaging/Installer/CursorLang.wixproj @@ -0,0 +1,48 @@ + + + + + CursorLang + Package + + + ICE38;ICE64;ICE91 + + + + Version=$(CursorLangVersion); + PayloadDir=$(PayloadDir); + IconFile=$(IconFile); + LicenseFile=$(LicenseFile) + + + + + + + + + + diff --git a/Packaging/PUBLISHING.RU.md b/Packaging/PUBLISHING.RU.md new file mode 100644 index 0000000..bbe08b3 --- /dev/null +++ b/Packaging/PUBLISHING.RU.md @@ -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 "" ` + -Publisher "" ` + -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 diff --git a/Packaging/build-installer.ps1 b/Packaging/build-installer.ps1 new file mode 100644 index 0000000..61445a2 --- /dev/null +++ b/Packaging/build-installer.ps1 @@ -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.'