67cc851b15
- signingConfig.release читает F7_KEYSTORE_FILE/PASSWORD, F7_KEY_ALIAS/PASSWORD из env или -P; фолбэк на debug-подпись если не заданы. Схемы v2+v3. - R8: app/proguard-rules.pro (kotlinx.serialization, Firebase, JS-мосты, Coil); vendor consumer-rules.pro через consumerProguardFiles (WebRTC/LoganSquare/Dagger/ RxJava/Parceler/EventBus + dontwarn geogson/joda из missing_rules.txt). - Проверено на инфре B (JDK 21 + Android SDK 36): assembleRelease BUILD SUCCESSFUL, подписанный APK 99 МБ (было ~130 debug), apksigner verify v2+v3 OK. - Доки: docs/RELEASE-SIGNING.md, docs/AUDIT-2026-07-07.md.
64 lines
3.8 KiB
Markdown
64 lines
3.8 KiB
Markdown
# Release-подпись и минификация f7cloud_mobile
|
||
|
||
## TL;DR
|
||
`assembleRelease` теперь собирает **подписанный, минифицированный** APK. Ключ подписи
|
||
берётся из окружения; при его отсутствии release подписывается debug-ключом (для локального QA).
|
||
|
||
## Переменные окружения (боевой keystore — вне git, в CI — из secrets)
|
||
|
||
| Переменная | Назначение |
|
||
|---|---|
|
||
| `F7_KEYSTORE_FILE` | путь к keystore (`.jks`/`.p12`) |
|
||
| `F7_KEYSTORE_PASSWORD` | пароль хранилища |
|
||
| `F7_KEY_ALIAS` | алиас ключа |
|
||
| `F7_KEY_PASSWORD` | пароль ключа |
|
||
|
||
Можно задавать и как gradle-property (`-PF7_KEYSTORE_FILE=…`). Логика — в `app/build.gradle`
|
||
(`signingConfigs.release`): если `F7_KEYSTORE_FILE` задан и файл существует → подпись боевым
|
||
ключом (схемы v2+v3, v1 отключён — minSdk 26); иначе → debug-ключ (APK **не для распространения**).
|
||
|
||
## Генерация боевого keystore (один раз, хранить в секрете и с бэкапом!)
|
||
|
||
```bash
|
||
keytool -genkeypair -v \
|
||
-keystore f7cloud-release.p12 -storetype PKCS12 \
|
||
-alias f7cloud -keyalg RSA -keysize 4096 -validity 10000 \
|
||
-dname "CN=F7cloud Mobile, O=F7cloud, C=RU"
|
||
```
|
||
|
||
⚠️ **PKCS12 (дефолт современного keytool): пароль ключа ОБЯЗАН совпадать с паролем хранилища.**
|
||
Отдельный `-keypass` игнорируется → при сборке `KeytoolException: Given final block not properly
|
||
padded`. Поэтому `F7_KEY_PASSWORD == F7_KEYSTORE_PASSWORD`.
|
||
|
||
⚠️ Боевой ключ подписи **неизменен на весь срок жизни приложения**: потеря = невозможность
|
||
выката обновлений существующим пользователям. Хранить в секрете + офлайн-бэкап.
|
||
|
||
## Локальная сборка (пример)
|
||
|
||
```bash
|
||
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
|
||
export ANDROID_HOME=/opt/android-sdk
|
||
source /path/to/signing.env # экспорт четырёх F7_* переменных
|
||
./gradlew :app:assembleRelease
|
||
# APK: app/build/outputs/apk/release/f7cloud-mobile-v<ver>-<code>-release.apk
|
||
# проверка: $ANDROID_HOME/build-tools/36.0.0/apksigner verify --verbose <apk>
|
||
```
|
||
|
||
## Минификация (R8)
|
||
|
||
Release: `minifyEnabled true` + `shrinkResources true`. Keep-правила:
|
||
- **app-уровень** — `app/proguard-rules.pro` (kotlinx.serialization, Firebase, JS-мосты WebView, Coil).
|
||
- **vendor-зависимости** — `vendor/talk-android/app/consumer-rules.pro`, подключён через
|
||
`consumerProguardFiles` (WebRTC JNI, LoganSquare `$$JsonObjectMapper`, Dagger, RxJava, Parceler,
|
||
EventBus, conscrypt + `-dontwarn` geogson/joda). Vendor — library без своей минификации, поэтому
|
||
его правила доезжают до R8 приложения ТОЛЬКО через consumer-файл.
|
||
|
||
При добавлении рефлексивных библиотек: собрать release, при `Missing class` R8 сгенерирует точные
|
||
правила в `app/build/intermediates/mapping/release/minifyReleaseWithR8/missing_rules.txt` — перенести
|
||
их в соответствующий proguard-файл.
|
||
|
||
## Размер APK
|
||
|
||
R8 + shrinkResources: **~130 МБ (debug) → 99 МБ (release, все 4 ABI)**. Дальнейшее сокращение
|
||
до ~30–45 МБ — ABI-split/`resConfigs` (пункт 2 этапа 0 плана аудита), пока не сделано.
|