Выпуск релизов
Релиз — это тег. CI (.github/workflows/android.yml) собирает каждый тег v*, подписывает APK и публикует их релизом на GitHub; F-Droid пересобирает релизные теги и раздаёт наш APK, если его сборка совпала с нашей байт в байт (DISTRIBUTION_RU.md). На пуш в main ничего не собирается, а пушит только автор.
Два канала
Заголовок раздела «Два канала»Одно приложение, один пакет, один ключ подписи. К какому каналу относится сборка, говорит тег.
| Стабильный | Бета | |
|---|---|---|
| Тег | v5.1.0 |
v5.1.0-beta.1 … v5.1.0-beta.9 |
VERSION_NAME |
5.1.0 |
5.1.0-beta.1 |
| Релиз на GitHub | последний (latest) | pre-release, никогда не latest; заголовок кончается на «(beta)» |
| Описание релиза | раздел ## [5.1.0] из CHANGELOG.md |
постоянная шапка (scripts/release/beta-notes.md), затем ## [Unreleased] — как есть на момент тега |
| Обновлятор в приложении | предлагает всем | предлагает, только если включено «Получать бета-версии» |
| F-Droid | пересобирает и публикует | не видит: его проверка обновлений берёт только релизные теги |
| Obtainium | следит по умолчанию | только с опцией Include prereleases |
Бета-тег собирается ровно так же, как релизный: те же задания, те же закреплённые инструменты, те же имена APK (TailSocks-v5.1.0-beta.1-arm64-v8a-release.apk). Отличается только публикация.
Что предлагает обновлятор (Настройки → Оформление и язык → «Получать бета-версии», по умолчанию выключено):
| Установлено | Переключатель | Что предложит |
|---|---|---|
| стабильная | выкл. | следующий стабильный релиз |
| стабильная | вкл. | самое новое по версии — стабильное или бету |
| бета | выкл. | ничего, пока не выйдет стабильный релиз новее этой беты, — тогда его |
| бета | вкл. | следующую бету или финальный релиз |
Версии сравниваются так: 5.1.0-beta.2 < 5.1.0-beta.10 < 5.1.0 < 5.1.1-beta.1 (core/AppVersion.kt); pre-release старше установленной версии не предлагается никогда. У копии, которую поставил магазин, обновлятора нет вовсе (UpdateChannel), и переключателя там нет.
В бета-сборке окно «О приложении» называет приложение «TailSocks бета», отчёт диагностики добавляет «beta» в строку с версией, а «Что нового» показывает раздел [Unreleased] под именем сборки — заново после каждой беты, по-русски или по-английски.
Бета и «экспериментально» — разные вещи
Заголовок раздела «Бета и «экспериментально» — разные вещи»- Бета — это сборка: следующая версия, пока она не закончена. Сломаться в ней может что угодно; получают её только те, кто сам попросил.
- Экспериментальная функция выходит в стабильной сборке — выключенной по умолчанию или с явной пометкой, — и её поведение ещё может поменяться. Строка о ней в списке изменений начинается с «Экспериментально:» (пример — удалённое управление в 5.0.0).
Одно не заменяет другое: функция, не готовая для всех, остаётся экспериментальной и в финальном релизе, а бета — не место для недоделок без пометки.
Коды версий
Заголовок раздела «Коды версий»В version.properties две строки, и код следует из имени:
- релиз:
major×1000000 + minor×10000 + patch×100; - его бета N, где N от 1 до 9: этот код
− 100 + 10×N.
Каждый APK добавляет к коду номер своей ABI: 0 у универсального, 1–4 у armeabi-v7a, arm64-v8a, x86, x86_64.
| Версия | VERSION_CODE |
APK |
|---|---|---|
| 5.0.0 | 5000000 | 5000000–5000004 |
| 5.0.1-beta.1 | 5000010 | 5000010–5000014 |
| 5.0.1 | 5000100 | 5000100–5000104 |
| 5.1.0-beta.1 | 5009910 | 5009910–5009914 |
| 5.1.0-beta.2 | 5009920 | 5009920–5009924 |
| 5.1.0 | 5010000 | 5010000–5010004 |
| 6.0.0-beta.1 | 5999910 | 5999910–5999914 |
| 6.0.0 | 6000000 | 6000000–6000004 |
Почему порядок верный: версия вместе со своими бетами занимает коды от код − 90 до код + 4, а между двумя версиями не меньше сотни. Значит, любая бета обновляет любой более ранний релиз и любую более раннюю бету, релиз обновляет свои беты, и ни один уже опубликованный код не меняется.
Ограничения:
- Девять бет на версию. У десятой был бы код самого релиза. Если девяти не хватило, следующая — бета 1 следующего патча, и выпускается тогда уже этот патч.
- Minor и patch — до 99, как и раньше.
- Код беты похож на версию ниже. Беты
5.1.0лежат в конце сотни5.0.99, беты6.0.0— чуть ниже шести миллионов. Пересечений нет: релиз занимает только первые пять кодов своей сотни, и5.0.99тоже. А сам код никто не читает, от него нужен только порядок.
Правило записано один раз — в buildSrc/src/main/java/io/github/bropines/tailscaled/release/ReleaseVersion.java. Gradle отказывается конфигурировать сборку, в которой код не следует из имени; CI запускает тот же файл на теге перед сборкой; ReleaseVersionTest проверяет порядок, описанный выше. Проверить тег руками, из корня репозитория:
java buildSrc/src/main/java/io/github/bropines/tailscaled/release/ReleaseVersion.java v5.1.0-beta.1Как выпустить бету
Заголовок раздела «Как выпустить бету»- Список изменений не трогать: работа остаётся под
## [Unreleased]вCHANGELOG.mdиCHANGELOG_RU.md. Своего раздела у беты нет. - В
version.propertiesпоставитьVERSION_NAME=5.1.0-beta.1иVERSION_CODE=5009910. - Проверить: команда выше печатает
kind=beta, затем./gradlew :app:testDebugUnitTest :app:assembleDebug. - Коммит (
chore: release 5.1.0-beta.1), тегv5.1.0-beta.1, запушить коммит и тег. - CI опубликует pre-release. На странице релизов у него должна стоять метка Pre-release, а Latest — оставаться на последнем стабильном релизе: именно его спрашивает у GitHub каждая установленная копия без переключателя.
Следующая бета — то же самое с 5.1.0-beta.2 и 5009920. [Unreleased] продолжает расти, и в описании каждой беты — весь раздел, а не разница с предыдущей.
Финальный релиз
Заголовок раздела «Финальный релиз»- Переименовать
## [Unreleased]в## [5.1.0] - ГГГГ-ММ-ДДв обоих списках изменений. - Поставить
VERSION_NAME=5.1.0иVERSION_CODE=5010000. - Коммит (
chore: release 5.1.0), тегv5.1.0, пуш. - CI опубликует его последним релизом, с этим разделом в описании. Тем, кто на бете, он предлагается так же, как всем.
- Перевести рецепт F-Droid на новую версию (ниже).
Что нельзя менять ради F-Droid
Заголовок раздела «Что нельзя менять ради F-Droid»F-Droid публикует наш APK, только пока его пересборка релизного тега совпадает с нашей, а новые версии находит сам. Это держится на следующем:
- Релизный тег —
v<major>.<minor>.<patch>и ничего больше. Беты отсекает строка рецептаUpdateCheckMode: Tags ^v\d+\.\d+\.\d+$; без$бета-тег был бы принят за релиз. - В
version.propertiesостаются две строки,VERSION_NAME=иVERSION_CODE=, которые читаются из коммита с тегом, а код релиза — по-прежнемуmajor×1000000 + minor×10000 + patch×100с добавками 1–4 для ABI (VercodeOperation). F-Droid берёт первое совпадение каждой из них в файле, поэтому в комментариях над ними присваивание целиком писать нельзя. - APK релиза называются
TailSocks-v<версия>-<abi>-release.apkи лежат в релизе с тегомv<версия>— это адресbinary:в рецепте. - Путь сборки релизного тега: ядро на Go собирается в
/home/vagrant/build/io.github.bropines.tailscaledзакреплёнными Go, gomobile и NDK, C-библиотеки — через Gradle, сборка чистая, APK один для всех каналов. Бета идёт тем же путём, чтобы проверять ту сборку, которую получит релиз; до шага публикации ничто не должно зависеть от вида тега. buildSrcостаётся простой Java без зависимостей: он компилируется перед каждой сборкой, у F-Droid тоже, и репозитории ему не нужны.
Сам рецепт живёт в форке fdroiddata; distribution/io.github.bropines.tailscaled.yml — его черновик. Релиз меняет там versionName, versionCode и commit в каждой из четырёх записей, а также CurrentVersion и CurrentVersionCode; бета не меняет ничего.