Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,8 @@ jobs:
--generate-notes \
--title "MegaProxy ${GITHUB_REF_NAME}"
fi

- name: Upload Google Play internal draft
env:
SUPPLY_JSON_KEY_DATA: ${{ secrets.SUPPLY_JSON_KEY_DATA }}
run: bundle exec fastlane android play_release track:internal release_status:draft
3 changes: 3 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ branch names, credentials, signing material, or other secrets.

- Release builds and signing are separate from PR CI. Release artifacts are created only through
the dedicated release workflow/Fastlane lane.
- Google Play uploads use `SUPPLY_JSON_KEY_DATA` with the complete service-account JSON in both
local env and GitHub Actions secrets. The tag release workflow uploads an internal draft after
publishing GitHub artifacts; PR workflows must never receive this secret.
- After creating and verifying a release, update the corresponding F-Droid submission/build recipe
when required. The repository's F-Droid-related files are for reproducible verification, not an
excuse to maintain a duplicate unused build path.
Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -342,8 +342,15 @@ and `SHA256SUMS` are written to `dist/release`. The App Bundle contains every su
stores generate and serve optimized device-specific APK splits from it. Go native symbols are
provided as `mega-proxy-native-debug-symbols.zip` for upload in Play Console.

To upload the AAB and native symbols through Fastlane, configure a Play service account and run
`bundle exec fastlane android play_release` (internal draft by default). See the
[Google Play release instructions](docs/en/fastlane.md#google-play-releases)
([по-русски](docs/ru/fastlane.md#релизы-в-google-play)) for credentials, validation and production uploads.

Pushing a version tag runs the same Fastlane release lane in GitHub Actions, builds and verifies
every APK and the App Bundle, and attaches the artifacts to a GitHub Release. The tag must match
every APK and the App Bundle, attaches the artifacts to a GitHub Release, then uploads the AAB
and native symbols as a Google Play internal draft. Configure the repository Actions secret
`SUPPLY_JSON_KEY_DATA` with the complete service-account JSON key for that upload. The tag must match
`v` followed by the current `versionName` in `app/build.gradle.kts`. Create that tag with
`git tag` and push the specific tag with `git push origin`; do not reuse a historical release tag.

Expand Down
73 changes: 73 additions & 0 deletions docs/en/fastlane.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ That command lists the lanes available in the checked-out version of the project
| `bundle exec fastlane android test` | Runs `native_tests` and `android_checks`; this is the normal pre-commit command. |
| `bundle exec fastlane android debug_artifact` | Builds `app/build/outputs/apk/debug/app-debug.apk`. |
| `bundle exec fastlane android release_artifacts` | Builds and verifies the signed release APKs, AAB, native debug symbols, and `SHA256SUMS` in `dist/release`. |
| `bundle exec fastlane android play_release` | Uploads an existing signed AAB and native symbols to Google Play; defaults to an internal draft. |

The release lane requires the signing configuration described in
[Signed release builds](../../README.md#signed-release-builds). It builds artifacts but does not
Expand All @@ -65,6 +66,78 @@ creates or updates one APK-links comment on the pull request. It does not check
execute pull-request code or artifacts. These are test artifacts only: neither APK is signed with
the MegaProxy release key, and neither is published as a GitHub Release or sent to an app store.

## Google Play releases

`play_release` uploads an existing signed AAB and its matching native symbols for
`net.megaproxy487`. Build them with `release_artifacts` or download both from the same verified
GitHub Release. The lane does not build or sign files. Use an unused, increasing `versionCode`.
The AAB must be signed with the upload key registered in Play Console.

Before the first API upload, create the app in Play Console, configure Play App Signing and upload
an initial build manually. Enable the Google Play Developer API in a Google Cloud project, create
a service account and invite its email in Play Console with access to this app and permissions
for the intended test/production tracks. Keep the JSON key outside the repository and supply its contents through `SUPPLY_JSON_KEY_DATA`.
See [Google API setup](https://developers.google.com/android-publisher/getting_started) and
[Fastlane supply setup](https://docs.fastlane.tools/actions/upload_to_play_store/#setup).

Store `export SUPPLY_JSON_KEY_DATA=...` with the complete, shell-quoted JSON in the local
`~/.config/megaproxy/release.env` file. Keep this file outside the repository with permissions
`0600`. Run from the repository root:

```shell
source "$HOME/.config/megaproxy/release.env"
bundle exec fastlane android release_artifacts
bundle exec fastlane android play_release validate_only:true
bundle exec fastlane android play_release
```

`SUPPLY_JSON_KEY_DATA` is Fastlane's standard environment variable for the complete JSON key,
not a file path or Base64 string. Sourcing `release.env` exports it for Fastlane; if your
environment already supplies it, no additional key setup is needed.

In GitHub, create an Actions secret named `SUPPLY_JSON_KEY_DATA` containing the same complete JSON.
The existing tag-triggered release workflow passes it only to the upload step:

```yaml
- name: Upload Google Play draft
env:
SUPPLY_JSON_KEY_DATA: ${{ secrets.SUPPLY_JSON_KEY_DATA }}
run: bundle exec fastlane android play_release track:internal release_status:draft
```

After building and verifying the artifacts and publishing the GitHub Release, the workflow uploads
the matching AAB and symbols from `dist/release` as an internal draft. A missing secret or failed
Play upload fails the workflow; the already published GitHub Release remains available. Do not
pass the key as a lane argument or print it in logs. PR workflows must not receive it.

The default upload creates a **draft on the internal track**. `validate_only:true` uploads to a
temporary Google Play edit and asks the API to validate it without committing a release; it needs
credentials and network access and is not an offline dry run. Review/complete a draft in Play Console.
For a new AAB that should be released directly to testers or production, explicitly select:

```shell
bundle exec fastlane android play_release track:internal release_status:completed
bundle exec fastlane android play_release track:production release_status:completed
```

Run only the command for the intended destination. Google review, app eligibility and managed
publishing can still delay availability. To promote an already uploaded version, use Play Console;
this lane uploads a new AAB and does not promote existing releases.

| Option | Default / behavior |
| --- | --- |
| `aab` | `dist/release/mega-proxy.aab` |
| `symbols` | `dist/release/mega-proxy-native-debug-symbols.zip`; required and must match the AAB |
| `track` | `internal`; also accepts `alpha`, `beta`, `production` or a custom track ID |
| `release_status` | `draft`; supports `draft` or `completed` |
| `validate_only` | `false`; accepts only `true` or `false` |

`MEGAPROXY_RELEASE_DIR` overrides the default artifact directory. Relative file paths are resolved
from the repository root. Metadata, changelogs, images and screenshots are not uploaded; the
F-Droid listing under `fastlane/metadata/android` is left separate from Play listing management.
The tag workflow publishes a GitHub Release and a Google Play internal draft. PR CI must not
receive the Play JSON key or invoke `play_release`.

## Updating Fastlane

Update Fastlane deliberately and commit both dependency files:
Expand Down
74 changes: 74 additions & 0 deletions docs/ru/fastlane.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ bundle exec fastlane lanes
| `bundle exec fastlane android test` | Выполняет `native_tests` и `android_checks`; основная команда перед коммитом. |
| `bundle exec fastlane android debug_artifact` | Собирает `app/build/outputs/apk/debug/app-debug.apk`. |
| `bundle exec fastlane android release_artifacts` | Собирает и проверяет подписанные APK, AAB, native debug symbols и `SHA256SUMS` в `dist/release`. |
| `bundle exec fastlane android play_release` | Загружает готовый подписанный AAB и native symbols в Google Play; по умолчанию создаёт internal-черновик. |

Для release lane нужна конфигурация подписи из раздела
[Signed release builds](../../README.md#signed-release-builds). Lane только собирает артефакты: он
Expand All @@ -67,6 +68,79 @@ checkout, не скачивает и не исполняет код или ар
один из APK не подписан официальным release-ключом MegaProxy, не публикуется в GitHub Releases и не
отправляется в магазин приложений.

## Релизы в Google Play

`play_release` загружает готовый подписанный AAB и соответствующие native symbols для
`net.megaproxy487`. Соберите их через `release_artifacts` или скачайте оба файла из одного
проверенного GitHub Release. Lane не собирает и не подписывает файлы. Для нового релиза нужен
неиспользованный возрастающий `versionCode`. AAB должен быть подписан upload key,
зарегистрированным в Play Console.

Перед первой загрузкой через API создайте приложение в Play Console, настройте Play App Signing
и вручную загрузите первоначальную сборку. Включите Google Play Developer API в проекте Google
Cloud, создайте сервисный аккаунт и пригласите его email в Play Console с доступом к приложению
и правами для нужных тестовых/production-треков. Храните JSON-ключ вне репозитория и передавайте его содержимое через `SUPPLY_JSON_KEY_DATA`.
См. [настройку Google API](https://developers.google.com/android-publisher/getting_started) и
[настройку Fastlane supply](https://docs.fastlane.tools/actions/upload_to_play_store/#setup).

Сохраните `export SUPPLY_JSON_KEY_DATA=...` с полным JSON в корректных shell-кавычках в локальном
файле `~/.config/megaproxy/release.env`. Храните его вне репозитория с правами `0600`.
Запускайте из корня репозитория:

```shell
source "$HOME/.config/megaproxy/release.env"
bundle exec fastlane android release_artifacts
bundle exec fastlane android play_release validate_only:true
bundle exec fastlane android play_release
```

`SUPPLY_JSON_KEY_DATA` — штатная переменная окружения Fastlane с полным содержимым JSON-ключа,
а не путём к файлу или строкой Base64. Загрузка `release.env` экспортирует её для Fastlane;
если окружение уже задаёт переменную, дополнительная настройка ключа не нужна.

В GitHub создайте Actions secret `SUPPLY_JSON_KEY_DATA` с тем же полным JSON.
Существующий release-workflow по тегу передаёт его только шагу загрузки:

```yaml
- name: Upload Google Play draft
env:
SUPPLY_JSON_KEY_DATA: ${{ secrets.SUPPLY_JSON_KEY_DATA }}
run: bundle exec fastlane android play_release track:internal release_status:draft
```

После сборки и проверки артефактов и публикации GitHub Release workflow загружает соответствующие
AAB и symbols из `dist/release` как internal-черновик. Отсутствие секрета или ошибка загрузки в Play
завершает workflow с ошибкой; уже опубликованный GitHub Release остаётся доступным. Не передавайте
ключ аргументом lane и не выводите его в логи. Workflow для PR не должны получать этот ключ.

По умолчанию создаётся **черновик в треке internal**. `validate_only:true` загружает файлы во
временную транзакцию Google Play и проверяет её через API без сохранения релиза; нужны ключ
и сеть, это не локальный dry run. Проверьте и завершите черновик в Play Console.
Для нового AAB, который нужно сразу отправить тестировщикам или в production, явно укажите:

```shell
bundle exec fastlane android play_release track:internal release_status:completed
bundle exec fastlane android play_release track:production release_status:completed
```

Выполняйте только команду для нужного направления. Проверка Google, доступность публикации для
приложения и managed publishing могут задержать появление релиза. Уже загруженную версию
продвигайте через Play Console: lane загружает новый AAB и не продвигает существующие релизы.

| Параметр | Значение по умолчанию / поведение |
| --- | --- |
| `aab` | `dist/release/mega-proxy.aab` |
| `symbols` | `dist/release/mega-proxy-native-debug-symbols.zip`; обязателен и должен соответствовать AAB |
| `track` | `internal`; также принимает `alpha`, `beta`, `production` или ID пользовательского трека |
| `release_status` | `draft`; поддерживаются `draft` и `completed` |
| `validate_only` | `false`; принимает только `true` или `false` |

`MEGAPROXY_RELEASE_DIR` меняет каталог артефактов по умолчанию. Относительные пути считаются от
корня репозитория. Метаданные, changelog, изображения и скриншоты не загружаются; каталог F-Droid
`fastlane/metadata/android` остаётся отдельным от управления карточкой Play.
Workflow по тегу публикует GitHub Release и internal-черновик Google Play. CI для PR не должен
получать JSON-ключ Play или вызывать `play_release`.

## Обновление Fastlane

Fastlane следует обновлять явно, после чего проверить и закоммитить оба файла зависимостей:
Expand Down
59 changes: 59 additions & 0 deletions fastlane/Fastfile
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,63 @@ platform :android do
sh(File.join(project_root, "scripts", "build-release-bundle.sh"))
UI.success("Release artifacts: dist/release")
end

desc "Upload a signed release AAB and native symbols to Google Play (internal draft by default)"
lane :play_release do |options|
allowed_options = %i[aab symbols track release_status validate_only]
unknown_options = options.keys - allowed_options
UI.user_error!("Unknown play_release options: #{unknown_options.join(', ')}") unless unknown_options.empty?

track = options.fetch(:track, "internal").to_s
UI.user_error!("track must not be empty") if track.strip.empty?
release_status = options.fetch(:release_status, "draft").to_s
unless %w[draft completed].include?(release_status)
UI.user_error!("release_status must be draft or completed")
end
validate_only = options.fetch(:validate_only, false).to_s
unless %w[true false].include?(validate_only)
UI.user_error!("validate_only must be true or false")
end

json_key_data = ENV["SUPPLY_JSON_KEY_DATA"]
if json_key_data.to_s.strip.empty?
UI.user_error!("Set SUPPLY_JSON_KEY_DATA to the service-account JSON contents")
end
begin
credentials = JSON.parse(json_key_data)
rescue JSON::ParserError
UI.user_error!("SUPPLY_JSON_KEY_DATA must contain valid JSON")
end
unless credentials.is_a?(Hash) && credentials["type"] == "service_account" &&
%w[client_email private_key token_uri].all? { |key| credentials[key].is_a?(String) && !credentials[key].strip.empty? }
UI.user_error!("SUPPLY_JSON_KEY_DATA must contain a service-account key with client_email, private_key and token_uri")
end
release_dir = File.expand_path(ENV.fetch("MEGAPROXY_RELEASE_DIR", "dist/release"), project_root)
files = {
aab: File.expand_path(options.fetch(:aab, File.join(release_dir, "mega-proxy.aab")), project_root),
mapping: File.expand_path(options.fetch(:symbols, File.join(release_dir, "mega-proxy-native-debug-symbols.zip")), project_root)
}
files.each do |name, path|
unless File.file?(path) && File.readable?(path) && File.size?(path)
UI.user_error!("#{name} must be a readable, non-empty file: #{path}")
end
end
UI.user_error!("aab must have the .aab extension") unless File.extname(files[:aab]) == ".aab"
UI.user_error!("symbols must have the .zip extension") unless File.extname(files[:mapping]) == ".zip"

upload_to_play_store(
**files,
json_key_data: json_key_data,
package_name: "net.megaproxy487",
track: track,
release_status: release_status,
validate_only: validate_only == "true",
skip_upload_apk: true,
skip_upload_aab: false,
skip_upload_metadata: true,
skip_upload_changelogs: true,
skip_upload_images: true,
skip_upload_screenshots: true
)
end
end
4 changes: 3 additions & 1 deletion fastlane/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ Fastlane is the supported entry point for tests and build artifacts. Install Rub
| `bundle exec fastlane android test` | Run all native and Android checks |
| `bundle exec fastlane android debug_artifact` | Produce `app/build/outputs/apk/debug/app-debug.apk` |
| `bundle exec fastlane android release_artifacts` | Produce signed APKs, AAB, native symbols, and checksums in `dist/release` |
| `bundle exec fastlane android play_release` | Upload AAB and native symbols to Google Play (internal draft by default) |

The release lane deliberately delegates signing and artifact verification to the repository's
existing release scripts. It requires the signing environment documented in the root README.
Publishing to an app store is not performed by any lane.
`play_release` uploads an existing signed AAB and matching native symbols to Google Play. It
defaults to an internal draft and requires the JSON key contents in `SUPPLY_JSON_KEY_DATA`; see the setup guides below.

Full setup and CI scope rules: [English](../docs/en/fastlane.md) / [Русский](../docs/ru/fastlane.md).
Loading