await certificate from ssl.com Reviewed-on: #4 Co-authored-by: Aleksandr Neychev <alexnejchev73@gmail.com>
This commit was merged in pull request #4.
This commit is contained in:
+80
-66
@@ -81,8 +81,8 @@ MSIX всегда выполняются в контексте вошедшег
|
||||
открытии окна.
|
||||
|
||||
`CursorLang.Core.dll` — общее для обоих: модели, файл настроек, слежение за
|
||||
раскладкой, обновления. Без UI, и так должно остаться: всё, что попадёт туда,
|
||||
попадёт и в фоновый процесс.
|
||||
раскладкой. Без UI, и так должно остаться: всё, что попадёт туда, попадёт и
|
||||
в фоновый процесс.
|
||||
|
||||
Связь между ними — только `settings.json`. Окно пишет его целиком, во временный
|
||||
файл, который одним движением встаёт на место, и посылает агенту
|
||||
@@ -167,56 +167,21 @@ MSIX всегда выполняются в контексте вошедшег
|
||||
|
||||
## Обновления
|
||||
|
||||
Проверка выполняется при открытии окна настроек, а не при включении машины:
|
||||
фоновая половина в сеть больше не ходит вовсе, да и показать ответ ей нечем.
|
||||
Настройка в окне так и написана.
|
||||
Приложение обновляет Store, а само приложение об этом не заботится: раздела
|
||||
обновлений в окне нет, запросов в сеть нет и кода для них тоже нет.
|
||||
|
||||
Приложение ищет новые версии среди выпусков собственного репозитория. Выпуск
|
||||
годится, если его тег — это просто версия (`v1.2.3` или `1.2.3`) и к нему
|
||||
приложен пакет MSIX. Тег, в котором есть что-то ещё, — в том числе `v1.2.3-beta`
|
||||
— пропускается: предварительную версию берут намеренно, приложение её не
|
||||
предлагает.
|
||||
Дело не во вкусе, а в цене подписи. MSIX Windows установит только тогда, когда
|
||||
доверяет подписи на нём, а публично доверенный сертификат для подписи кода
|
||||
оказался недосягаем: удостоверяющие центры, которые их продают, здесь его не
|
||||
выдают, а те, что держали бы ключ в облачном HSM, — тем более, а сертификат на
|
||||
USB-токене сюда не привезти. Без подписи пакет установится только на машине в
|
||||
режиме разработчика, значит выкладывать в выпуск нечего и искать обновления
|
||||
негде. Store подписывает пакет своим сертификатом и обновляет приложение сам —
|
||||
на этом вопрос и закрыт.
|
||||
|
||||
Из приложенных файлов предпочитается `.msixbundle` — он несёт обе архитектуры.
|
||||
Если его нет, берётся пакет, в имени которого стоит архитектура этой машины:
|
||||
`CursorLang-1.2.3.0-x64.msix`. Такие имена даёт `build-msix.ps1`, так что выпуск
|
||||
делается прикладыванием того, что он собрал.
|
||||
|
||||
Пакет скачивается во временную папку и передаётся установщику приложений
|
||||
Windows: тот показывает издателя, спрашивает подтверждение и заменяет
|
||||
установленную версию. Подпись проверяет Windows, поэтому приложенный к выпуску
|
||||
пакет должен быть подписан — неподписанный установится только на машине в режиме
|
||||
разработчика. Работающее приложение до перезапуска продолжает жить на старых
|
||||
файлах.
|
||||
|
||||
У приложения, установленного из Store, раздела обновлений нет вовсе: его
|
||||
обновляет Store, а пакет со стороны Windows поверх него всё равно не примет.
|
||||
|
||||
Приложение спрашивает о выпусках при каждом появлении окна настроек, и ответ
|
||||
остаётся в разделе как есть — включая недоступную сеть. Открытие этого окна —
|
||||
намеренное действие, достаточно редкое, чтобы запрос ничего не стоил, поэтому
|
||||
между открытиями ничего не запоминается и выключать нечего. Кнопка рядом с
|
||||
версией задаёт тот же вопрос заново по требованию.
|
||||
|
||||
Где искать выпуски, задаётся в `UpdateOptions`: репозиторий принадлежит тому, кто
|
||||
выпускает приложение, а не пользователю, поэтому значения живут в сборке, а не в
|
||||
`settings.json`:
|
||||
|
||||
```csharp
|
||||
services.AddSingleton(new UpdateOptions
|
||||
{
|
||||
ServiceUri = new Uri("https://git.alrakis.kz/"), // сам сервер Gitea
|
||||
Project = "alrakis/cursor-lang",
|
||||
});
|
||||
```
|
||||
|
||||
Выпуски берутся из Gitea, а её API живёт на самом сервере: адрес — тот же, по
|
||||
которому репозиторий открывают в браузере, и под ним приложение спрашивает
|
||||
`/api/v1/repos/{владелец}/{репозиторий}/releases`.
|
||||
|
||||
Закрытому репозиторию нужен токен. Он читается из переменной окружения
|
||||
`CURSORLANG_UPDATE_TOKEN`, а не хранится в исходниках: секрет, встроенный в
|
||||
сборку, — это секрет, отданный всем, кто эту сборку получил.
|
||||
Версия работающего приложения стоит в заголовке окна настроек:
|
||||
`CursorLang 1.2.3 — Настройки`. Больше её в интерфейсе нигде нет, и стоит она
|
||||
там для того, чтобы её можно было назвать в сообщении об ошибке.
|
||||
|
||||
## Тесты
|
||||
|
||||
@@ -262,11 +227,11 @@ dotnet test --collect:"XPlat Code Coverage" --settings coverage.runsettings
|
||||
|
||||
Пайплайны лежат в `.gitea/workflows` и работают на Gitea Actions. Запрос
|
||||
на слияние в `master` собирается и проверяется тестами; по тегу вида `v1.2.3`
|
||||
проект собирается, проходит тесты, пакуется в MSIX и выкладывается релизом
|
||||
с приложенными пакетами. Номер версии берётся только из тега — тег любого
|
||||
другого вида останавливает прогон в самом начале. Версия пакета получается
|
||||
`1.2.3.0`: Store принимает четыре числа и последнее оставляет себе, так что тег
|
||||
на него не влияет.
|
||||
проект собирается, проходит тесты и пакуется в MSIX, который остаётся
|
||||
в артефактах прогона. Номер версии берётся только из тега — тег любого другого
|
||||
вида останавливает прогон в самом начале. Версия пакета получается `1.2.3.0`:
|
||||
Store принимает четыре числа и последнее оставляет себе, так что тег на него не
|
||||
влияет.
|
||||
|
||||
Обоим пайплайнам нужен runner под Windows с меткой `windows-x64`, на нём —
|
||||
.NET 10 SDK и git-lfs. Выгрузка исходников тянет файлы LFS: без них иконка
|
||||
@@ -276,10 +241,18 @@ dotnet test --collect:"XPlat Code Coverage" --settings coverage.runsettings
|
||||
окно на переднем плане или каретку, — на runner’е, живущем службой в нулевом
|
||||
сеансе, пропускают себя: показать окно там негде.
|
||||
|
||||
Пакет, который несёт релиз, загружается в Partner Center как есть. Identity
|
||||
берётся из переменных репозитория, а если те не заданы — из значений по
|
||||
Пакет идёт в Store и больше никуда: он не подписан, подпись на него ставит сам
|
||||
Partner Center. Поэтому прогон оставляет его в артефактах под именем
|
||||
`msix-1.2.3.0`, откуда его забирают и загружают руками; к релизу не прикладывается
|
||||
ничего — релиз по тегу Gitea заводит сама, и в нём один только тег. Неподписанный
|
||||
пакет, висящий в релизе, выглядел бы как то, что можно установить, и не
|
||||
устанавливался бы нигде — см. раздел об обновлениях.
|
||||
|
||||
Identity берётся из переменных репозитория, а если те не заданы — из значений по
|
||||
умолчанию в скрипте: `MSIX_IDENTITY_NAME`, `MSIX_PUBLISHER`
|
||||
и `MSIX_PUBLISHER_DISPLAY_NAME`.
|
||||
и `MSIX_PUBLISHER_DISPLAY_NAME`. Вместе identity и publisher задают family name
|
||||
пакета, поэтому от версии к версии оба должны оставаться прежними — иначе Store
|
||||
примет следующую за другое приложение.
|
||||
|
||||
## Сборка пакета MSIX
|
||||
|
||||
@@ -292,17 +265,22 @@ dotnet test --collect:"XPlat Code Coverage" --settings coverage.runsettings
|
||||
# Разово: отрисовать логотипы и иконку exe (уже в репозитории, повторить после правок)
|
||||
powershell -File Packaging\New-Assets.ps1
|
||||
|
||||
# Проверка на своей машине: только своя архитектура
|
||||
powershell -File Packaging\build-msix.ps1 -Architectures x64
|
||||
# Проверка на своей машине
|
||||
powershell -File Packaging\build-msix.ps1
|
||||
|
||||
# Для Partner Center — identity та, что зарезервирована там
|
||||
powershell -File Packaging\build-msix.ps1 -Version 1.0.1.0 `
|
||||
-IdentityName 12345AleksandrNeychev.CursorLang -Publisher "CN=ABCD1234-..."
|
||||
-IdentityName 12345AleksandrNeichev.CursorLang -Publisher "CN=ABCD1234-..."
|
||||
```
|
||||
|
||||
Результат — `artifacts\packages\CursorLang-<версия>.msixbundle`, покрывающий x64
|
||||
и arm64; рядом лежат пакеты отдельных архитектур. Bundle загружается в Partner
|
||||
Center как есть.
|
||||
Результат — `artifacts\packages\CursorLang-<версия>-x64.msix`. Он загружается в
|
||||
Partner Center как есть.
|
||||
|
||||
Собирается только x64. Сборка под arm64 удвоила бы вес каждого релиза ради
|
||||
машин, которые и так выполняют x64 через эмуляцию.
|
||||
|
||||
Здесь ничего не подписывается: подпись на пакет ставит сам Store, а для установки
|
||||
на эту машину вместо пакета регистрируется layout — см. `-Install` ниже.
|
||||
|
||||
Приложение поставляется с собственной копией .NET: Windows не включает .NET 10, а
|
||||
MSIX не может установить среду выполнения как зависимость пакета.
|
||||
@@ -310,7 +288,7 @@ MSIX не может установить среду выполнения как
|
||||
Чтобы посмотреть, как пакет работает на этой машине, соберите его с `-Install`:
|
||||
|
||||
```powershell
|
||||
pwsh -File Packaging\build-msix.ps1 -Architectures x64 -Install
|
||||
pwsh -File Packaging\build-msix.ps1 -Install
|
||||
```
|
||||
|
||||
Приложение появится в меню «Пуск» как любое установленное. Регистрируется не сам
|
||||
@@ -324,5 +302,41 @@ pwsh -File Packaging\build-msix.ps1 -Architectures x64 -Install
|
||||
Удалить вручную:
|
||||
|
||||
```powershell
|
||||
Remove-AppxPackage (Get-AppxPackage -Name AleksandrNeychev.CursorLang).PackageFullName
|
||||
Remove-AppxPackage (Get-AppxPackage -Name AleksandrNeichev.CursorLang).PackageFullName
|
||||
```
|
||||
|
||||
## Сборка установщика
|
||||
|
||||
То же приложение собирается и обычным MSI — чтобы раздавать помимо Store: пока
|
||||
Store не вынес решение или если так его и не вынесет. Ставить ничего, кроме .NET
|
||||
SDK, не нужно: WiX приезжает пакетом NuGet, как и `makeappx`.
|
||||
|
||||
```powershell
|
||||
# Собрать и запустить — посмотреть глазами пользователя
|
||||
pwsh -File Packaging\build-installer.ps1 -Install
|
||||
|
||||
# Всё, что нужно релизу
|
||||
pwsh -File Packaging\build-installer.ps1 -Version 1.0.1
|
||||
```
|
||||
|
||||
Результат — `artifacts\installers\CursorLang-<версия>-x64.msi`. Всё лежит внутри
|
||||
`.msi` — отдельного архива рядом с ним нет.
|
||||
|
||||
Устанавливается только для текущего пользователя, в
|
||||
`%LOCALAPPDATA%\Programs\CursorLang`, поэтому не просит ни прав администратора,
|
||||
ни подтверждения. Удаление идёт через «Параметры» — «Приложения», как у любой
|
||||
программы, и уносит с собой запись автозапуска: иначе Windows продолжала бы
|
||||
показывать в автозагрузке приложение, которого уже нет.
|
||||
|
||||
Установщик никто не подписывает, поэтому Windows предупреждает о неизвестном
|
||||
издателе и пользователю приходится настоять. Покупка сертификата это сразу не
|
||||
снимет: SmartScreen смотрит на репутацию, а у нового сертификата её нет, пока
|
||||
приложение не наберёт установок.
|
||||
|
||||
Про проект WiX стоит знать две вещи, прежде чем его править. Он закреплён на WiX
|
||||
5, а не на нынешней 7: начиная с шестой версии инструмент требует принимать
|
||||
лицензию Open Source Maintenance Fee — бесплатную при доходе меньше $10 000 в
|
||||
год, но принимать её должен человек, а не сборочный скрипт. И он отключает три
|
||||
проверки ICE: MSI по-прежнему исходит из установки на всю машину, а установка в
|
||||
профиль пользователя нарушает правила, которые описывают ровно то, что здесь и
|
||||
задумано.
|
||||
|
||||
Reference in New Issue
Block a user