feat: auto-update, тесты и CI/CD
Some checks failed
CI / Typecheck + Tests (push) Has been cancelled
CI / Build (Windows) (push) Has been cancelled

Полная автоматизация релизного цикла.

== Auto-update (electron-updater) ==
- src/main/updater.ts — обёртка над autoUpdater с дискриминированным
  UpdaterStatus union и broadcast через IPC. autoDownload=false,
  пользователь сам жмёт «Скачать». allowDowngrade=false. Проверка
  каждые 6 часов, первая через 5с после старта.
- В dev-режиме (app.isPackaged=false) статус сразу становится
  'unsupported' с пояснением — никаких exceptions из updater'а.
- build.publish в package.json: provider=generic, url указывает на
  Gitea release assets конкретной версии.
- src/main/ipc.ts: 4 новых канала — status/check/download/install.
- src/preload: API window.api.updater* + onUpdaterStatus.
- src/renderer/src/components/UpdaterCard.tsx: HUD-карточка в Settings
  с состояниями idle/checking/available/downloading/downloaded/error,
  прогресс-бар с скоростью в МБ/с.

== Тесты (vitest) ==
- vitest.config.ts с алиасами @shared / @renderer
- 23 теста, все зелёные:
  * format.test.ts — formatCountdown, formatInterval (8 cases)
  * vdf.test.ts — parseVdf / stringifyVdf / round-trip (11 cases)
  * types.test.ts — DEFAULT_SETTINGS, SAMPLE_EXERCISES sanity (4)
- npm scripts: test (watch), test:run (CI)

== CI/CD (Gitea Actions) ==
- .gitea/workflows/ci.yml — на push/PR: typecheck + тесты + smoke-сборка
- .gitea/workflows/release.yml — на тег v*.*.*: сборка NSIS + Gitea release

== Локальный релизный скрипт ==
- scripts/release.ps1 — один скрипт от бампа версии до публикации
  через Gitea API (params: -Bump patch/minor/major, -Version, -DryRun)
- npm run release — обёртка
- RELEASING.md — полная инструкция

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
AnRil
2026-05-16 20:32:59 +07:00
parent 757352e447
commit 92e15e69a3
16 changed files with 1149 additions and 3 deletions

146
RELEASING.md Normal file
View File

@@ -0,0 +1,146 @@
# Релиз и автообновления
Документ описывает три способа выпустить новую версию. Все опираются на
один и тот же артефакт — NSIS-инсталлятор `Exercise-Reminder-Setup-X.Y.Z.exe`,
который сам решает: устанавливать заново или обновлять существующую копию.
## TL;DR
```pwsh
$env:GITEA_TOKEN = '<token из Gitea Settings → Applications>'
npm run release -- -Bump patch # 0.2.0 → 0.2.1
# или
npm run release -- -Version 0.3.0
```
Скрипт сделает всё сам: бамп версии, коммит, тег, push, тесты, сборка
инсталлятора, создание Gitea release с заметками из коммитов, загрузка
артефактов.
После публикации релиза установленные у пользователей копии в течение
~6 часов проверят `latest.yml` на Gitea и предложат обновление через UI.
---
## Как работает auto-update
1. На каждом релизе вместе с `.exe` публикуется `latest.yml`
манифест с версией, размером, sha512 хешем.
2. Приложение (через `electron-updater`) каждые 6 часов делает HTTP
GET на `<gitea>/AnRil/laude/releases/download/v<current>/latest.yml`.
3. Если версия в манифесте выше текущей — статус становится
`available`, в Settings → Обновления появляется кнопка «Скачать».
4. После скачивания — статус `downloaded`, кнопка «Перезапустить».
5. При перезапуске NSIS установщик из дельты или полный накатывается
поверх существующей инсталляции. Данные в `%APPDATA%\Exercise Reminder\`
сохраняются.
**Важно:** репозиторий `laude` приватный. Чтобы auto-update работал на
машинах конечных пользователей, либо:
- сделать репозиторий публичным, либо
- сделать публичными только релизы (Gitea: Release Settings),
- либо подписывать запросы токеном (нужен код в `updater.ts`,
использующий `autoUpdater.requestHeaders`).
## Способ 1 — скрипт релиза (рекомендованный сейчас)
Самый прямой путь, не зависит от Gitea Actions runners.
```pwsh
# Один раз — получить токен в Gitea (Settings → Applications)
# и сохранить в переменную окружения. Право — write:repository.
[Environment]::SetEnvironmentVariable('GITEA_TOKEN', '<token>', 'User')
# Релиз
npm run release -- -Bump patch # patch (0.2.0 → 0.2.1)
npm run release -- -Bump minor # minor (0.2.0 → 0.3.0)
npm run release -- -Bump major # major (0.2.0 → 1.0.0)
npm run release -- -Version 1.2.3 # точная версия
npm run release -- -DryRun # посмотреть план без действий
```
Что делает скрипт:
1. Проверяет что нет незакоммиченных изменений
2. Бампит версию в `package.json`, коммитит
3. Прогоняет `npm run typecheck` и `npm run test:run`
4. Собирает `npm run dist` (NSIS + блокмап + latest.yml)
5. Создаёт тег `vX.Y.Z`, пушит main и тег в origin
6. Через Gitea API создаёт release с заметками из git log
7. Загружает три файла как assets: `.exe`, `.exe.blockmap`, `latest.yml`
## Способ 2 — Gitea Actions (если есть runners)
Workflows лежат в `.gitea/workflows/`:
- **`ci.yml`** — на push в main и на PR. Запускает typecheck +
unit-тесты + smoke-сборку (без NSIS). Кладёт распакованную сборку
как artifact на 7 дней.
- **`release.yml`** — на push тега `v*.*.*`. Сверяет тег с версией
в `package.json`, прогоняет тесты, собирает NSIS-инсталлятор,
создаёт Gitea release с заметками, загружает артефакты.
Чтобы release workflow работал — в репозитории нужен secret
`GITEA_TOKEN` (Gitea Repo Settings → Secrets). Этот же токен может быть
переиспользован из `Способа 1`.
Для запуска release workflow:
```bash
git tag v0.3.0
git push origin v0.3.0
```
## Способ 3 — руками
Если что-то сломалось в автоматизации:
```pwsh
npm run typecheck
npm run test:run
npm run dist
# В release/ появятся:
# Exercise-Reminder-Setup-X.Y.Z.exe
# Exercise-Reminder-Setup-X.Y.Z.exe.blockmap
# latest.yml
```
Затем в Gitea UI: Releases → Draft new release → загрузить три файла.
## Тестирование auto-update
Удобный способ проверить, что цикл работает:
1. Релизнуть `0.x.0` через `npm run release`.
2. Установить полученный `.exe` на машину.
3. Релизнуть `0.x.1` (любой бамп).
4. На установленной копии открыть Settings → Обновления → Проверить.
Должно показать «Доступно обновление v0.x.1».
5. Скачать → Перезапустить → проверить версию в окне «О программе»
(или в Settings).
Для dev-режима (`npm run dev`) auto-updater отключён — статус сразу
становится `unsupported` с пояснением.
## Откат релиза
Если опубликовали плохой релиз:
1. Удалить release в Gitea UI (или через API).
2. Удалить тег: `git push origin :refs/tags/vX.Y.Z` и локально
`git tag -d vX.Y.Z`.
3. Откатить bump-коммит: `git revert <hash>` или `git reset --hard HEAD~1`
(если ещё не пушили дальше).
4. Релизнуть тот же номер заново — auto-updater на клиентах увидит
тот же манифест и не предложит обновление (если sha512 совпадёт).
Если содержание поменялось — увидит и предложит обновиться. На
практике лучше выпустить hotfix-патч `X.Y.Z+1`, чем переписывать
существующий релиз.
## Что попадает в установщик
См. `build` секцию `package.json`:
- `out/**/*` — собранный код (main + preload + renderer)
- `resources/**/*` — иконки
Никаких node_modules, исходников, тестов, README — `electron-builder`
сам распаковывает и упаковывает только необходимое.