Перейти к содержимому

Как опубликовать Electron-приложение в Microsoft Store: от создания пакета MSIX до отправки в магазин

HagiCode for Windows Microsoft Store artwork
HagiCode for Windows is now on Microsoft Store
HagiCode for Windows is officially live on Microsoft Store. Windows users can install it directly from the storefront and stay on the store-managed update path. Open the listing and take a look.
Open Microsoft Store

Как опубликовать Electron-приложение в Microsoft Store: от создания пакета MSIX до отправки в магазин

В сущности, Electron — это всего лишь обычное классическое приложение Win32, но Microsoft Store принимает только MSIX. В этой статье, опираясь на реально проверенную конфигурацию сборки нашего HagiCode Desktop, мы полностью разберём цепочку «регистрация учётной записи разработчика → создание пакета MSIX → отправка в магазин» и заодно расскажем о проблемах, с которыми мы столкнулись — ведь после преодоления проблем они становятся историями.

Предпосылки

У нас есть Electron-приложение, которое нужно распространять конечным пользователям в Windows. Кроме постоянно используемых установочных пакетов NSIS и портативной версии, мы также хотели бы, чтобы оно появилось в Microsoft Store. Причины на самом деле довольно реалистичны:

  1. Надёжный канал распространения: Приложения из магазина подписаны и проверены, при установке их не будет блокировать SmartScreen, и пользователям не придётся сталкиваться с холодным «Неизвестный издатель».
  2. Автоматическое обновление и монетизация: Обновления берёт на себя магазин; подписки и постоянные лицензии также можно напрямую подключить.
  3. Охват встроенных точек входа Windows 10/11: winget, поиск в магазине, рекомендации в меню «Пуск»… Эти точки входа реально полезны для привлечения новых пользователей.

Но Electron в конечном счёте не UWP. Чтобы опубликовать приложение в Microsoft Store, по сути нужно сделать только одно — заново упаковать артефакты Electron в пакет MSIX, который понимает Microsoft Store, а затем честно пройти процессы регистрации и отправки. Звучит просто, но когда начинаешь на практике, проблем не мало. Чтобы заполнить эти проблемы, мы потратили немало усилий, чтобы полностью разобраться во всей цепочке, и ниже подробно расскажем о каждом шаге.

О HagiCode

Решение, описанное в этой статье, основано на практике нашего проекта HagiCode. HagiCode Desktop — это классическое приложение на базе Electron, которое должно распространяться пользователям через три канала: официальный сайт, GitHub Release и Microsoft Store. Как был настроен канал магазина — это то, о чём рассказывает эта статья. В конце статьи есть больше информации о HagiCode, если вам интересно, можете прокрутить вниз и посмотреть.

Анализ: четыре вопроса, которые нужно прояснить перед публикацией

Для публикации в Microsoft Store есть четыре ключевых решения в технической цепочке. Если вы их проясните, не придётся переделывать работу снова и снова —毕竟 никто не хочет переделывать.

1. Microsoft Store принимает только MSIX / AppX, не принимает традиционные NSIS/EXE

Поддержка Microsoft Store для классических приложений (Desktop Bridge) основана на формате MSIX. Традиционные установочные пакеты NSIS нельзя отправить напрямую, нужно сначала заново упаковать в MSIX с помощью MakeAppx. К счастью, Electron Forge предоставляет maker @electron-forge/maker-msix, который может напрямую создавать MSIX на этапе упаковки, экономя усилия на обратное определение упаковки из уже установленного каталога.

В нашем проекте есть именно такой maker:

{
name: '@electron-forge/maker-msix',
platforms: ['win32'],
config: {
appManifest: msixManifestPath,
packageAssets: msixAssetsPath,
logLevel: 'warn',
...(windowsKitPath ? { windowsKitPath } : {}),
...(windowsKitVersion ? { windowsKitVersion } : {}),
...msixSigningConfig,
},
},

Ключевые входные данные — всего два: appManifest (то есть AppxManifest.xml, определяющий идентификацию и возможности пакета) и packageAssets (иконки магазина). Если эти два неверны, всё остальное, как бы красиво ни было, будет напрасно.

2. Идентификация пакета должна быть зарезервирована в Partner Center заранее

Поле Identity в пакете MSIX (Name, Publisher) нельзя заполнять как угодно — оно должно полностью совпадать с идентификацией приложения, зарезервированной в Partner Center, даже одна отличная символ заставит отклонить. Наша зарезервированная идентификация записана в forge.store-config.json:

{
"packageIdentity": {
"displayName": "Hagicode",
"publisherDisplayName": "newbe36524",
"publisher": "CN=8B6C8A94-AAE5-4C8B-9202-A29EA42B042F",
"identityName": "newbe36524.Hagicode",
"backgroundColor": "transparent",
"languages": ["en-US", "zh-CN", "zh-TW", "ja-JP", "ko-KR", "de-DE", "fr-FR", "es-ES", "pt-BR", "ru-RU"]
}
}

Строка publisher здесь происходит из темы сертификата, выданного Microsoft после регистрации учётной записи разработчика, и должна совпадать символ в символ. А identityName — это префикс имени пакета, который вы зарезервировали. Эту строку нужно скопировать из Partner Center как есть, никогда не набирайте вручную — об этом мы ещё поговорим в разделе «Частые проблемы» ниже.

3. Классическое приложение должно объявить возможность runFullTrust

Electron-приложениям нужен полный доступ к файловой системе, нужно запускать подпроцессы, и нужно выполнять Node runtime — всё это возможно только в режиме «полного доверия». Поэтому в манифесте MSIX нужно честно объявить возможность runFullTrust, иначе приложение будет заблокировано песочницей при запуске, что проявится в виде непонятных сбоев. Наша конфигурация выглядит так:

{
"msix": {
"minVersion": "10.0.17763.0",
"maxVersionTested": "10.0.19045.0",
"capabilities": [
"runFullTrust",
"internetClient",
"internetClientServer",
"privateNetworkClientsServer"
]
}
}

runFullTrust — это стандарт для классических приложений. minVersion установлена на 17763 (то есть Windows 10 1809), потому что с этой версии MSIX стабильно поддерживает классические приложения Win32, если меньше — пользователи не смогут установить, если больше — не покроете старые машины.

4. Отправка в магазин требует среды Windows + Microsoft Store CLI

Упаковку можно делать в кроссплатформенном CI, но отправку в магазин (msstore publish) — нельзя, нужно запускать Microsoft Store CLI в среде Windows и настроить учётные данные приложения Azure AD. Именно поэтому в автоматизированном пайплайне задание publish_store должно выполняться на runner windows-latest. Это неизбежное жёсткое ограничение, в отличие от упаковки, которую можно запихнуть в Linux контейнер.

Решение: полный восьмишаговый процесс публикации

Соединив вышеуказанный анализ, чтобы опубликовать Electron-приложение в Microsoft Store, полные шаги примерно такие.

Шаг 1: Регистрация учётной записи разработчика

Сначала зарегистрируйте учётную запись разработчика (личную или корпоративную) в Partner Center и оплатите разовый сбор. После активации учётной записи вы получите строку темы Publisher сертификата, примерно такого вида: CN=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX. Это единственный источник для поля publisher позже.

Шаг 2: Резервирование идентификации приложения в магазине

Создайте новое приложение в Partner Center, укажите имя, которое хотите сохранить. Система назначит вам identityName, и в сочетании с вашим Publisher сформируется полная идентификация пакета. Скопируйте эту идентификацию как есть в локальную конфигурацию:

forge.store-config.json
{
"packageIdentity": {
"displayName": "Hagicode",
"publisherDisplayName": "newbe36524",
"publisher": "CN=8B6C8A94-AAE5-4C8B-9202-A29EA42B042F",
"identityName": "newbe36524.Hagicode"
}
}

Шаг 3: Подготовка иконок магазина

Microsoft Store требует набор PNG фиксированных размеров: StoreLogo.png, Square44x44Logo.png, Square150x150Logo.png, Wide310x150Logo.png и т. д. Наш скрипт prepare-msix.js перед упаковкой проверяет, все ли эти активы на месте:

// Проверяем необходимые иконки магазина, не хватит одной — нельзя
const requiredAssets = ['StoreLogo.png', 'Square44x44Logo.png', 'Square150x150Logo.png', 'Wide310x150Logo.png'];
for (const assetName of requiredAssets) {
const assetPath = path.join(paths.generatedAssetsPath, assetName);
if (!fs.existsSync(assetPath)) {
throw new Error(`Missing required MSIX asset after preparation: ${assetPath}`);
}
}

Зачем это делать? Потому что при отсутствии одного размера MakeAppx при упаковке не скажет, где именно ошибка, а отклонят только при проверке в магазине — к этому моменту вы уже ждёте несколько дней. Предварительная проверка — это очень эффективная защита.

Шаг 4: Генерация AppxManifest.xml

В манифест нужно поместить идентификацию пакета, возможности, визуальные активы, исполняемый файл точки входа. Мы используем конфигурацию перекрытия (forge.store-config.json) для управления генерацией манифеста через prepare-msix.js, чтобы убедиться, что идентификация совпадает с магазином. Ключевые части манифеста примерно такие:

<!-- Идентификация пакета: должна совпадать с Partner Center -->
<Identity Name="newbe36524.Hagicode"
Publisher="CN=8B6C8A94-AAE5-4C8B-9202-A29EA42B042F"
Version="1.2.3.0" />
<Applications>
<Application Id="Hagicode" Executable="Hagicode.exe" EntryPoint="Windows.FullTrustApplication">
<uap:VisualElements ... />
</Application>
</Applications>
<!-- Объявление возможностей: runFullTrust — ключ для классических приложений -->
<Capabilities>
<rescap:Capability Name="runFullTrust" />
<Capability Name="internetClientServer" />
</Capabilities>

Обратите внимание на строку EntryPoint="Windows.FullTrustApplication" — это ключевой маркер для классических приложений, в сочетании с возможностью runFullTrust позволяет работать с полными разрешениями. Без него приложение может только послушно сидеть в песочнице, довольно уныло.

Шаг 5: Упаковка с maker-msix

Команда сборки записана в package.json:

{
"scripts": {
"build:win:store": "npm run generate:store-bindings && node scripts/build-store-package.js"
}
}

В конечном итоге она вызывает Electron Forge, передавая forge.store-config.json как конфигурацию перекрытия, maker-msix вызывает MakeAppx из Windows SDK и выдаёт файл .msix. Здесь есть жёсткое ограничение: упаковка должна делаться на Windows (или в контейнере с Windows SDK), так как зависит от MakeAppx, это не обойти.

Шаг 6: Подпись (при отправке в магазин можно не подписывать)

Этот шаг легко пропустить — пакеты, отправленные в магазин, Microsoft заново подпишет своим сертификатом, поэтому на этапе разработки и самотестирования, кроме «официальной отправки», можно не подписывать. Но если хотите локально установить для тестирования, нужно подписать доверенным сертификатом, иначе Windows откажется устанавливать. Наш resolveMsixSigningConfig при отсутствии материалов подписи возвращает пустой объект, чтобы процесс продолжился:

// Если нет материалов подписи — не подписываем, магазин сам переподпишет
function resolveMsixSigningConfig() {
if (!process.env.MSIX_CERT_FILE) return {};
return {
signMethod: 'signtool',
certFilePath: process.env.MSIX_CERT_FILE,
certPassword: process.env.MSIX_CERT_PASSWORD,
};
}

Разделение путей «самотестирование с подписью» и «отправка без подписи» — это ключевая практика.

Шаг 7: Настройка учётных данных Microsoft Store CLI

Создайте приложение Azure AD на портале Azure, предоставьте ему доступ к Partner Center, затем получите следующий набор учётных данных:

  • AZURE_AD_APPLICATION_CLIENT_ID
  • AZURE_AD_APPLICATION_SECRET
  • AZURE_AD_TENANT_ID
  • SELLER_ID (ID продавца в Partner Center)
  • MICROSOFT_STORE_PRODUCT_ID (ID продукта зарезервированного приложения)

Этот шаг немного запутан, но в документации портала Azure и Partner Center всё очень подробно описано, просто делайте по инструкции.

Шаг 8: Отправка в магазин

В среде Windows отправьте через Microsoft Store CLI:

Terminal window
# Настройка учётных данных
msstore reconfigure --tenantId $env:AZURE_AD_TENANT_ID `
--clientId $env:AZURE_AD_APPLICATION_CLIENT_ID `
--clientSecret $env:AZURE_AD_APPLICATION_SECRET `
--sellerId $env:SELLER_ID
# Отправка пакета MSIX в зарезервированный продукт
msstore publish "$packagePath" -id $env:MICROSOFT_STORE_PRODUCT_ID

После отправки нужно вернуться в Partner Center и заполнить детали магазина (описание, скриншоты, цену, рейтинг), наконец нажать отправить на проверку. Проверка обычно занимает 1–3 рабочих дня, в первый раз всегда дольше.

Практика: закрепление конфигурации и опыта преодоления проблем

После прохождения всего процесса следующие практики помогут вам избежать некоторых обходных путей — ведь после многих обходных путей уже не кажется, что они обходные, просто некоторые вещи можно пропустить.

Конфигурационные файлы хранить отдельно

Раздельное хранение «общей конфигурации сборки» и «конфигурации для магазина» — это ключ. Наш подход: forge.config.js используется для ежедневных сборок (NSIS, portable, macOS dmg), forge.store-config.json только при сборке для магазина через extends наследует и перекрывает:

{
"extends": "forge.config.js",
"buildVersion": "0.1.0.0",
"packageIdentity": { /* идентификация зарезервированная в магазине */ },
"msix": {
"minVersion": "10.0.17763.0",
"maxVersionTested": "10.0.19045.0",
"capabilities": ["runFullTrust", "internetClient", "internetClientServer", "privateNetworkClientsServer"]
}
}

Таким образом, магазинная версия и релизная версия не загрязняют друг друга. HagiCode Desktop одновременно поддерживает три канала распространения, разделение конфигураций — предпосылка нашей стабильной итерации.

Номер версии должен быть четырёхсегментным

Номер версии MSIX должен быть четырёхсегментным Major.Minor.Build.Revision (например 1.2.3.0), но package.json Electron обычно пишет только три сегмента. Поле buildVersion используется для дополнения последнего сегмента — при отправке в магазин номер версии должен увеличиваться, четвёртый сегмент очень удобен для различения нескольких отправок одной и той же семантической версии. Те, кто наступил на это, понимают; те, кто не наступил — наступят рано или поздно.

Объявление множества языков

Магазин поддерживает multi-language листинг, в манифесте это теги <Resource Language="..." />. Мы объявили десять языков, магазин потребует заполнить описание для каждого языка (сначала можно пройти проверку с машинным переводом, потом постепенно локализовать). Логика рендеринга в prepare-msix.js такая:

// Рендеринг списка языков в теги Resource в манифесте MSIX
function renderResourceTags(languages) {
return languages
.map((language) => ` <Resource Language="${escapeXml(language)}" />`)
.join('\n');
}

Частые проблемы (обратите внимание)

Ниже HagiCode Desktop наступал почти на каждую:

  1. Несовпадение Publisher: При копировании строки publisher из Partner Center случайно теряется пробел или меняется регистр, отправка сразу отклоняется. Рекомендуем сразу записать в файл конфигурации, не набирайте вручную.
  2. Отсутствие runFullTrust: После запуска приложения нет доступа к файловой системе, нельзя запустить подпроцессы, проявляется как различные странные сбои, разбираться довольно сложно.
  3. Неполные размеры иконок: MakeAppx не проверяет, но проверка магазина отклонит. Предварительная проверка в prepare-msix.js — эффективная защита.
  4. Номер версии не увеличивается: Магазин отклоняет получение той же или более низкой версии, CI пайплайн должен гарантировать bump при каждой сборке.
  5. Запуск maker-msix в не-Windows среде: Не найдёт MakeAppx, нужно использовать runner windows-latest.
  6. Смесь подписей: Для самотестирования используйте самоподписанный сертификат, для отправки в магазин — пустую подпись чтобы Microsoft переподписал, эти два пути должны быть разделены, не запихивайте самоподписанный сертификат в отправляемый пакет.

Рекомендации по автоматизации

После первого прохождения полного процесса вручную и разбирательства в каждом шаге настоятельно рекомендуем подключить GitHub Actions для автоматизации. Мы в конечном итоге соединили разбор версии, сборку MSIX, публикацию GitHub Release и публикацию в магазине в один пайплайн, проверяем новую версию каждые 4 часа. Эти детали полностью разобраны в другой нашей статье «Практика автоматизации публикации Windows-приложений в Microsoft Store».

Если хотите сначала просто повесить приложение в магазин, потом подключить монетизацию (подписка / постоянная лицензия), можете посмотреть нашу статью «Как Electron-классические приложения подключают подписки и постоянные лицензии Microsoft Store», там говорится о подключении возможностей монетизации после публикации в магазин.

Справочные материалы

Резюме

Вокруг темы «Как опубликовать Electron-приложение в Microsoft Store: от создания пакета MSIX до отправки в магазин», более надёжный способ продвижения — сначала постепенно запустить ключевые конфигурации, границы зависимостей и пути реализации, потом дополнить детали оптимизации.

Когда цели, шаги и точки приёмки чётко определены, такие решения обычно могут более плавно войти в реальную доставку.

开始使用 HagiCode

一次安装,几分钟上手

HagiCode for Windows 在 Microsoft Store 免费提供。打开商店即可安装并保持更新;也可以先对比各版本与定价,再决定从哪个渠道开始。