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:
@@ -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>
|
||||||
@@ -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
|
||||||
@@ -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.'
|
||||||
Reference in New Issue
Block a user