Add CI
All checks were successful
release / release (push) Successful in 12m40s

This commit is contained in:
2026-10-27 17:38:55 +03:00
parent 2f38d27b3f
commit 3ef78bd49c
12 changed files with 363 additions and 10 deletions

View File

@@ -372,7 +372,7 @@ sudo apt remove sleepyowl # ~/.config/sleepyowl остаётс
- **.deb собирается своей задачей** (`PackageDeb` в `desktop/build.gradle.kts`, через `dpkg-deb`), а не `packageDeb` jpackage: его пакет не умеет зависеть от `libmpv2` — libmpv загружается через JNA, и зависимость сама не находится. Свой пакет даёт ещё короткое имя в `/usr/bin` и меню без лишних скриптов.
- **libmpv — системная** `libmpv2 (>= 0.37)`: на проверенных файлах 0.37 не хуже 0.41 (§6), своя libmpv не нужна. Остальные зависимости — то, что `dpkg-shlibdeps` нашёл у библиотек JRE, Skiko и JNA (имена Mint 22 / Ubuntu 24.04: `libc6 (>= 2.34)`, `libstdc++6`, `libfontconfig1`, `libx11-6`, `libxext6`, `libxi6`, `libxrender1`, `libxtst6`, `libasound2t64 | libasound2`), плюс то, что грузится через JNA: `libegl1`, `libopengl0`, `libgl1`.
- **Версия пакета** — `0.1.0-ГГГГММДДЧЧММ` (время сборки): `apt install` любой новой сборки идёт как обновление. Номер версии приложения — одно место, `appVersion` в `desktop/build.gradle.kts`; `BuildInfo.VERSION` генерируется из него.
- **Версия пакета** — `0.1.0-ГГГГММДДЧЧММ` (время сборки): `apt install` любой новой сборки идёт как обновление. Номер версии — `sleepyowl.versionName` в `gradle.properties`, общий с ТВ (CI поднимает его на релизе); `BuildInfo.VERSION` генерируется из него.
- **Модули JRE:** `suggestRuntimeModules` дал `java.instrument`, `jdk.security.auth` (dbus-java), `jdk.unsupported`. Добавлены два, которых jdeps не видит: `jdk.crypto.ec` (TLS к публичному адресу сервера по https) и `jdk.localedata` — без него `Locale.getDisplayLanguage(ru)` даёт названия языков дорожек по-английски.
- **Права файлов в пакете** выставляются явно (755/644): при `umask 002` иначе получается групповая запись.

View File

@@ -7,7 +7,7 @@
| Что | Команда |
|---|---|
| ТВ, debug | `./gradlew :app:assembleDebug` → `app/build/outputs/apk/debug/app-debug.apk` |
| ТВ, release | `./gradlew :app:assembleRelease` — R8 без обфускации, пока подписан debug-ключом; ABI `armeabi-v7a` + `arm64-v8a` |
| ТВ, release | `./gradlew :app:assembleRelease` — R8 без обфускации, ключ — ниже; ABI `armeabi-v7a` + `arm64-v8a` |
| ПК, запуск | `./gradlew :desktop:run` или `tools/linux/desktop.sh` (без Gradle вокруг процесса) |
| ПК, пакет | `./gradlew :desktop:packageDeb` → `desktop/build/deb/sleepyowl_<версия>_amd64.deb` (зависит от `libmpv2`) |
| Тесты | `./gradlew test` (в т.ч. рендер-тесты экранов ПК и `ui:shared` без дисплея) |
@@ -15,6 +15,47 @@
Производительность интерфейса на ТВ мерить только на release-сборке (см. [ui-performance.md](ui-performance.md)).
Baseline profile лежит в `app/src/release/generated/baselineProfiles/` и снимается заново на ТВ (`gen_profile`, ниже).
Версия обоих приложений — `sleepyowl.versionName` в `gradle.properties` (или `-Psleepyowl.versionName=…`);
`versionCode` APK следует за ней (`1.2.3` → `1002003`). Release-APK подписывается ключом из `ANDROID_KEYSTORE`
(+ `ANDROID_KEYSTORE_PASSWORD`, `ANDROID_KEY_ALIAS`, по умолчанию `sleepyowl`), без него — debug-ключом машины.
## CI: релизы в Gitea
`.gitea/workflows/release.yaml`: на пуш в `master` APK и .deb собираются в Docker и выкладываются на страницу
Releases репозитория как тег `v<версия>` (APK `sleepyowl-<версия>.apk`, `.deb` как его назвал `packageDeb`).
- **Версия.** Каждый релиз — patch на единицу выше прошлого тега (`v0.1.1` → `0.1.2`). Чтобы начать новую minor или
major, поднять `sleepyowl.versionName` в `gradle.properties` выше последнего релиза — следующий релиз возьмёт её.
Первый релиз — версия из `gradle.properties`. Пуш, который не менял приложения (только `docs/`, `tools/`,
`.gitea/`, `baselineprofile/`, тесты, `*.md`), релиза не делает.
- **Сборка.** `tools/ci/Dockerfile` — образ с JDK 21 и Android SDK; `tools/ci/release.Dockerfile` собирает в нём
`:app:assembleRelease :desktop:packageDeb` (`tools/ci/release.sh`) целиком внутри BuildKit: исходники — контекст
сборки, ключ — BuildKit-секреты (в слои не попадает), кэш Gradle — cache mount, который живёт между сборками,
результат — `--output`. Так раннеру не нужны bind mount'ы.
- **Раннер.** Нужен Gitea Actions runner с меткой `ubuntu-latest`, видящий этот репозиторий (на уровне репозитория,
организации `StenFreddSoft` или всего сервера) и с доступом к Docker.
- **Секреты** (*Settings → Actions → Secrets*):
- `ANDROID_KEYSTORE_BASE64` — `base64 -w0 <keystore>`;
- `ANDROID_KEYSTORE_PASSWORD` — его пароль (у PKCS12 он же пароль ключа);
- `ANDROID_KEY_ALIAS` — alias ключа, если не `sleepyowl`;
- `RELEASE_TOKEN` — только если автоматический токен Actions не может создавать релизы: токен с `write:repository`.
Без ключа джоба падает: APK на ключе, придуманном в контейнере, не встанет поверх ни одной установленной версии.
Ключ должен быть один и тот же навсегда — APK на другом ключе ставится только после удаления приложения (с его
настройками и входом). Свой ключ: `keytool -genkeypair -keystore sleepyowl.jks -alias sleepyowl -keyalg RSA
-keysize 4096 -validity 36500`, хранить вне репозитория и с резервной копией.
- **Переменные** (*Settings → Actions → Variables*, необязательные): `RELEASES_KEEP` (10) — сколько релизов остаётся
на странице (старые удаляются вместе с файлами, теги остаются); `BUILD_CACHE_KEEP` (`10GB`) — потолок кэша
сборки Docker на раннере.
Локально то же самое (команды — в шапке `tools/ci/release.Dockerfile`):
```sh
docker build -t sleepyowl-ci tools/ci
docker build --no-cache -f tools/ci/release.Dockerfile --secret id=keystore,src=<keystore> \
--secret id=keystore_password,src=<файл с паролем> --secret id=key_alias,src=<файл с alias> -o out .
```
## Личные настройки инструментов: `local/`
Папка `local/` целиком в `.gitignore`: там всё, что относится к конкретной машине, серверу и аккаунтам —

View File

@@ -150,7 +150,7 @@ SleepyOwl — open-source клиент Jellyfin на Kotlin (GPL-3.0-or-later).
- `ui/theme` (`Tokens`): цвета, типографика, отступы, радиусы, свечение фокуса из макета. `ui/components`: `FocusCard`/`focusCard` (фокус-узел со свечением), `GlowCache`, `Cards` (постер, широкая, большая), `Buttons` (основная с градиентом, круглые, чип; `Chip(swatch = …)` — цветная точка), `Icons`, `RemoteImage` + `CardImageTransformation`, `BlurHash`, `Avatar`, `StatusDot`, `Brand`, `Grid`, `Section`, `Scrolling`, `TextInput`.
- **Оболочка** (`ui/shell/Shell.kt`): «Назад» на вкладке → фокус в рейл, «Назад» в рейле → выход из приложения.
- **Упрощения макета ради скорости:** нет сетки точек на фоне; у постеров нет затемнения снизу — значок «4K» на маленькой тёмной плашке; полоска прогресса на постере — со скруглёнными концами и отступами, а не во всю ширину по краю; тень раскрытого рейла — узкая полоса 40 dp вместо размытия 80 px. Длинное название в большой карточке обрезается многоточием.
- **Release-сборка:** R8 с keep-правилами (ass-media лезет рефлексией в поля `MatroskaExtractor` — `extractorOutput`, `subtitleSample`; libVLC; ffmpeg; SDK), `profileinstaller`, подпись пока debug-ключом. Воспроизведение Exo и VLC в release проверено.
- **Release-сборка:** R8 с keep-правилами (ass-media лезет рефлексией в поля `MatroskaExtractor` — `extractorOutput`, `subtitleSample`; libVLC; ffmpeg; SDK), `profileinstaller`, подпись ключом из `ANDROID_KEYSTORE` (в CI — секрет, см. development.md), локально без него — debug-ключом. Воспроизведение Exo и VLC в release проверено.
- **Открытие из adb** (`am start -n ru.stenfredd.sleepyowl/.MainActivity …`), работает и в release: `--es item <id> [--ez play true] [--el start <мс>]`; `--es series <id> [--es episode <id>]` — страница сериала. Только в debug: `--es engine exo|vlc`, `--ez tour true [--ei tour_file N]` (режим обхода). Без `--el start` фильм с `--ez play true` начинается с нуля и выпадает из «Продолжить просмотр».
### 5.2 Данные

View File

@@ -43,7 +43,7 @@
### 1.2 Правила замеров
- **Только release**: R8 (минификация и оптимизация, без обфускации — проект open source, а стеки и теги логов остаются читаемыми), подпись debug-ключом, `profileinstaller`.
- **Только release**: R8 (минификация и оптимизация, без обфускации — проект open source, а стеки и теги логов остаются читаемыми), `profileinstaller`.
- После установки — сразу компиляция по baseline profile:
```
adb shell am broadcast -a androidx.profileinstaller.action.INSTALL_PROFILE <pkg>/androidx.profileinstaller.ProfileInstallReceiver