Repository navigation
342 lines (304 loc) · 15.3 KB
/
Copy pathandroid.yml
File metadata and controls
342 lines (304 loc) · 15.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
name: Android
# Android-версия: сборка APK (release arm64 без подписи, debug x86_64 для эмулятора), проверка содержимого APK,
# тесты Kotlin-плагинов, clippy под Android и прогон настоящего приложения в эмуляторе (Playwright через
# DevTools WebView). Описание, артефакты и как читать журналы: docs/android.md, раздел «CI».
on:
push:
branches: ["main"]
pull_request:
paths:
- "src-tauri/**"
- "plugins/**"
- "engine/**"
- "native/**"
- "ui/**"
- "Cargo.toml"
- "Cargo.lock"
- "scripts/android-*"
- "scripts/android-emulator-tests/**"
- ".github/workflows/android.yml"
workflow_dispatch:
inputs:
emulator_gpu:
description: "Emulator -gpu mode (swangle_indirect, software, swiftshader_indirect, guest)"
required: false
default: swangle_indirect
concurrency:
group: android-${{ github.workflow }}-${{ github.ref }}-${{ inputs.emulator_gpu || 'default' }}
cancel-in-progress: true
permissions:
contents: read
env:
NDK_VERSION: 27.2.12479018
CARGO_TERM_COLOR: never
jobs:
build:
name: Build APK, checks
runs-on: ubuntu-24.04
timeout-minutes: 90
outputs:
debug_ready: ${{ steps.uploaded.outputs.debug_ready }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
# Свободное место: NDK, Gradle и два набора Rust-артефактов занимают много. Удаляем только то, что точно
# не нужно (.NET, Haskell, CodeQL); Android SDK не трогаем.
- name: Free disk space
run: |
df -h /
sudo rm -rf /usr/share/dotnet /opt/ghc /opt/hostedtoolcache/CodeQL || true
df -h /
- name: Rust and Android targets
id: rust
run: |
set -euxo pipefail
rustup toolchain install stable --profile minimal --component clippy --no-self-update
rustup default stable
rustup target add aarch64-linux-android x86_64-linux-android
rustc -Vv
cargo -V
echo "cache_key=$(rustc -Vv | sha256sum | cut -d ' ' -f1)" >> "$GITHUB_OUTPUT"
# Reuse compiled dependencies, while Cargo still checks every changed source and UI asset.
# Credentials, SDK/NDK installations and signed artifacts are not cached here.
- name: Cache Rust dependencies and Android build outputs
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
~/.cargo/registry
~/pm-android-target
!~/pm-android-target/**/incremental
key: android-rust-v1-${{ runner.os }}-${{ runner.arch }}-ndk${{ env.NDK_VERSION }}-${{ steps.rust.outputs.cache_key }}-${{ hashFiles('Cargo.lock', '**/Cargo.toml') }}-${{ github.sha }}
restore-keys: |
android-rust-v1-${{ runner.os }}-${{ runner.arch }}-ndk${{ env.NDK_VERSION }}-${{ steps.rust.outputs.cache_key }}-${{ hashFiles('Cargo.lock', '**/Cargo.toml') }}-
# The Gradle distribution (~140 MB) comes from services.gradle.org, which is sometimes slow or down:
# keep it between runs so a short outage there does not block every build.
# Saved right after the verified download (below), not at the end of the job: a later failure must not
# throw away a good distribution.
- name: Restore the Gradle distribution
id: gradle-dist
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.gradle/wrapper/dists
key: gradle-dist-${{ runner.os }}-${{ hashFiles('**/gradle-wrapper.properties') }}
# Plugin and library jars (Android Gradle plugin, the Kotlin compiler, AndroidX). Some of them are
# redirected from Maven Central to GitHub releases, so a GitHub outage (504) failed the build even with
# the distribution in place; with this cache a warm run downloads nothing.
- name: Cache Gradle dependencies
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
~/.gradle/caches/modules-2/files-2.1
~/.gradle/caches/modules-2/metadata-*
key: gradle-deps-v1-${{ runner.os }}-${{ hashFiles('**/*.gradle.kts', '**/*.gradle', '**/gradle.properties', '**/libs.versions.toml', '**/gradle-wrapper.properties') }}
restore-keys: |
gradle-deps-v1-${{ runner.os }}-
- name: Fetch the Gradle distribution (retries, verified mirror)
run: |
for props in src-tauri/gen/android/gradle/wrapper/gradle-wrapper.properties plugins/media-session/android/gradle/wrapper/gradle-wrapper.properties; do
scripts/gradle-dist-prefetch.sh "$props"
done
# Only a complete set is worth saving; the wrapper downloads whatever is missing itself.
if ls ~/.gradle/wrapper/dists/*/*/*.zip >/dev/null 2>&1; then echo "GRADLE_DIST_READY=1" >> "$GITHUB_ENV"; fi
- name: Save the Gradle distribution
if: steps.gradle-dist.outputs.cache-hit != 'true' && env.GRADLE_DIST_READY == '1'
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.gradle/wrapper/dists
key: gradle-dist-${{ runner.os }}-${{ hashFiles('**/gradle-wrapper.properties') }}
# SDK на раннерах GitHub уже стоит в $ANDROID_HOME; здесь только то, чего не хватает. Лицензии принимаются
# автоматически. Скрипт сам объясняет, чего нет и что установлено.
- name: Android SDK, NDK, JDK, environment
run: |
set -euxo pipefail
echo "ANDROID_HOME on the runner: ${ANDROID_HOME:-<unset>}"
. scripts/android-ci-env.sh
scripts/android-ci-sdk.sh "ndk;${NDK_VERSION}" "platforms;android-35" "platforms;android-36" "build-tools;35.0.0" "build-tools;36.0.0"
# Повторно: теперь NDK точно есть, переменные для следующих шагов.
. scripts/android-ci-env.sh
node --version
npm --version
"$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/clang" --version | head -1
ls "$ANDROID_HOME/build-tools" "$ANDROID_HOME/platforms"
# Release arm64: подписывается только вне CI (владелец проекта), см. docs/android.md.
- name: Build release APK (arm64, unsigned)
run: scripts/android-build.sh release aarch64 dist/release
- name: Check the release APK
run: |
. scripts/android-ci-env.sh >/dev/null
scripts/android-verify-apk.sh dist/release/*.apk arm64-v8a release
- name: Checksum
working-directory: dist/release
run: |
set -euxo pipefail
for apk in *.apk; do sha256sum "$apk" | tee "$apk.sha256"; done
# Для эмулятора: x86_64 debug (debuggable: DevTools и run-as нужны тестам).
- name: Build debug APK (x86_64, for the emulator)
run: scripts/android-build.sh debug x86_64 dist/debug
- name: Check the debug APK
run: |
. scripts/android-ci-env.sh >/dev/null
scripts/android-verify-apk.sh dist/debug/*-debug.apk x86_64 debug
# Тот же код с большим versionCode: «обновление поверх» для scripts/android-upgrade-check.sh. Rust уже собран,
# так что это в основном Gradle.
- name: Build debug APK with a higher versionCode (for the update test)
run: |
set -euxo pipefail
. scripts/android-ci-env.sh >/dev/null
aapt2="$(find "$ANDROID_HOME/build-tools" -name aapt2 -type f | sort -V | tail -1)"
current="$("$aapt2" dump badging dist/debug/*-debug.apk | sed -n "s/.*versionCode='\([0-9]*\)'.*/\1/p" | head -1)"
echo "current versionCode: $current"
ANDROID_VERSION_CODE_OVERRIDE=$((current + 1)) scripts/android-build.sh debug x86_64 dist/debug
ls -l dist/debug
sha256sum dist/debug/*.apk
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: android-release-unsigned
path: |
dist/release/*.apk
dist/release/*.sha256
if-no-files-found: error
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: android-debug-x86_64
path: dist/debug/*.apk
if-no-files-found: error
# Эмулятор не ждёт clippy и Kotlin-тесты: ему нужны только готовые APK.
- id: uploaded
run: echo "debug_ready=true" >> "$GITHUB_OUTPUT"
# Rust под Android (код cfg(target_os = "android") на хосте не компилируется). Три крейта одной командой:
# зависимости общие, поэтому это быстрее, чем по отдельности. Если станет слишком долго, оставьте
# `-p private-music-desktop`: в нём всё Android-специфичное приложение. Заодно build.rs плагинов
# создаёт plugins/*/android/.tauri для тестов Kotlin ниже.
- name: Clippy for Android
timeout-minutes: 30
run: |
set -euxo pipefail
. scripts/android-ci-env.sh >/dev/null
cargo clippy --target aarch64-linux-android -p private-music-desktop -p tauri-plugin-vk-login -p tauri-plugin-media-session --locked -- -D warnings
# JUnit без эмулятора. Обёртка Gradle 9.6.1 от плагина media-session подходит и vk-login (AGP 9.3.3 в обоих).
- name: Kotlin unit tests (media-session)
timeout-minutes: 20
run: |
set -euxo pipefail
. scripts/android-ci-env.sh >/dev/null
plugins/media-session/scripts/android-test.sh testDebugUnitTest
- name: Kotlin unit tests (vk-login)
timeout-minutes: 20
run: |
set -euxo pipefail
. scripts/android-ci-env.sh >/dev/null
test -d plugins/vk-login/android/.tauri/tauri-api || { echo "::error::plugins/vk-login/android/.tauri/tauri-api is missing: the cargo step above should have created it (build.rs)"; ls -la plugins/vk-login/android; exit 1; }
plugins/media-session/android/gradlew -p plugins/vk-login/android --no-daemon testDebugUnitTest
- name: Kotlin test reports
if: always()
run: |
mkdir -p kotlin-reports
for plugin in media-session vk-login; do
[ -d "plugins/$plugin/android/build/reports" ] && cp -r "plugins/$plugin/android/build/reports" "kotlin-reports/$plugin" || true
done
ls kotlin-reports || true
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
if: always()
with:
name: android-kotlin-test-reports
path: kotlin-reports
if-no-files-found: ignore
emulator:
name: Emulator (API ${{ matrix.api }})
strategy:
fail-fast: false
matrix:
api: [34, 36]
needs: build
# Даже если clippy или Kotlin-тесты упали, APK уже собраны и их можно проверить на эмуляторе.
if: ${{ !cancelled() && needs.build.outputs.debug_ready == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 45
permissions:
contents: read
actions: read
env:
PM_OUT: ${{ github.workspace }}/emulator-out
PM_PACKAGE: com.dec.privatemusic2.android
SYSTEM_IMAGE: system-images;android-${{ matrix.api }};google_apis;x86_64
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Download the debug APKs built above
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
run: |
set -euxo pipefail
gh run download "$GITHUB_RUN_ID" --repo "$REPO" --name android-debug-x86_64 --dir apk
ls -l apk
- name: Enable KVM
run: |
set -euxo pipefail
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
ls -l /dev/kvm
- name: Emulator, system image, tools
timeout-minutes: 15
run: |
set -euxo pipefail
. scripts/android-ci-env.sh
scripts/android-ci-sdk.sh "emulator" "platform-tools" "build-tools;35.0.0" "$SYSTEM_IMAGE"
scripts/android-emulator-pin.sh
adb version
emulator -version | head -3 || true
- name: Test dependencies
working-directory: scripts/android-emulator-tests
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1"
# connectOverCDP говорит с WebView самого эмулятора: собственный Chromium Playwright не нужен.
run: |
npm install --no-audit --no-fund
node --test ./*.test.mjs
- name: Boot the emulator
timeout-minutes: 20
env:
# swangle_indirect (SwiftShader through ANGLE): with "software" and "guest" the host qemu process
# segfaulted on render-heavy screens (the VK page, the player) in about half of the runs since the
# November runner images; swangle_indirect ran both API levels clean in the comparison run.
PM_EMULATOR_GPU: ${{ inputs.emulator_gpu || 'swangle_indirect' }}
run: |
. scripts/android-ci-env.sh >/dev/null
scripts/android-emulator-tests/boot-emulator.sh
- name: Install the debug APK
timeout-minutes: 5
run: |
set -euxo pipefail
. scripts/android-ci-env.sh >/dev/null
first="$(ls apk/*.apk | grep -v versioncode | head -1)"
echo "installing $first"
adb install -r -g "$first"
adb shell pm list packages | grep "$PM_PACKAGE"
- name: Emulator tests (the real app, over WebView DevTools)
timeout-minutes: 20
working-directory: scripts/android-emulator-tests
env:
PM_CLEAR: "1"
PM_REQUIRE_NETWORK: "1"
run: |
. ../android-ci-env.sh >/dev/null
node run.mjs
- name: Update check (install a newer APK over the old one)
timeout-minutes: 10
if: ${{ !cancelled() }}
run: |
. scripts/android-ci-env.sh >/dev/null
old="$(ls apk/*.apk | grep -v versioncode | head -1)"
new="$(ls apk/*versioncode*.apk | head -1)"
scripts/android-upgrade-check.sh "$old" "$new"
- name: Collect logs
if: always()
timeout-minutes: 10
run: |
. scripts/android-ci-env.sh >/dev/null || true
scripts/android-emulator-tests/collect-artifacts.sh "$PM_OUT"
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
if: always()
with:
name: android-emulator-logs-api-${{ matrix.api }}
path: emulator-out
if-no-files-found: warn