Repository navigation
226 lines (201 loc) · 9.71 KB
/
Copy pathtest-android.yaml
File metadata and controls
226 lines (201 loc) · 9.71 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
name: Test Android
# Builds the bdk-rn tarball for the emulator's ABI, installs it into the standalone
# tests/ app the way a downstream consumer would, and runs the integration suite on
# an Android emulator.
#
# The suite reports through logcat rather than an instrumentation runner, so
# tests/scripts/run-android-tests.sh is what turns its output into an exit code.
on:
workflow_dispatch:
push:
branches:
- master
pull_request:
permissions: {}
env:
# tests/android/build.gradle pins this exact NDK; gradle and cargo-ndk must agree.
NDK_VERSION: "27.1.12297006"
# Split the way android-emulator-runner wants it. Kept here so the pre-install
# step and the emulator step below cannot drift apart.
#
# 36 matches the targetSdkVersion in tests/android/build.gradle, so the suite runs
# on the platform it is built for. google_atd is the Automated Test Device variant
# of google_apis: same Play services, stripped of the apps a headless CI run never
# uses, which takes the download from 1.90 GB to 0.86 GB. That download is the step
# that keeps failing, so halving it is the point. ATD is headless-only, which the
# -no-window / swiftshader_indirect options below already are.
EMULATOR_API_LEVEL: "36"
EMULATOR_TARGET: "google_atd"
EMULATOR_ARCH: "x86_64"
jobs:
test-android:
name: "Test Android library"
runs-on: ubuntu-24.04
services:
# Backs the esplora background-sync test, which talks to http://10.0.2.2:3002
# (the emulator's route back to the runner host).
bitcoin-regtest:
image: ghcr.io/thunderbiscuit/podman-regtest-infinity-pro:0.4.0
ports:
- 18443:18443
- 18444:18444
- 3002:3002
- 60401:60401
# Holds other steps until regtest has mined enough blocks to be usable.
options: >-
--health-cmd "test $(bitcoin-cli -regtest -rpcuser=regtest -rpcpassword=password getblockcount) -gt 100"
--health-interval 3s
--health-timeout 5s
--health-retries 20
steps:
- name: "Checkout"
uses: actions/checkout@v5
with:
# bdk-ffi is a submodule and the Rust build needs it.
submodules: true
persist-credentials: false
# Up front rather than just before the emulator: the Rust target directory,
# node_modules and the emulator system image are each multi-GB. The regtest
# image is referenced by a running container, so the docker prune skips it.
- name: "Free disk space"
uses: AdityaGarg8/remove-unwanted-software@v5
with:
remove-dotnet: true
remove-haskell: true
remove-codeql: true
remove-docker-images: true
remove-large-packages: false
- name: "Set up JDK"
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: 17
# The runner image already provides the SDK and sets ANDROID_HOME, so the only
# thing missing is usable tools in cmdline-tools/latest: it ships 12.0 there,
# which predates the v4 SDK schema and cannot represent a multi-ABI system image
# (every image at API 34 and above is one). android-emulator-runner runs
# avdmanager from latest/ specifically, so that directory alone decides whether
# the AVD can be created. Upgrading it in place is what keeps this job and the
# emulator step on one set of tools, which is the whole ballgame here: a package
# installed by a different copy of the tools writes a package.xml the
# AVD-creating tools may not be able to read back.
- name: "Set up the Android cmdline-tools"
run: |
sdkmanager="${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager"
yes | "${sdkmanager}" --licenses > /dev/null
"${sdkmanager}" --install "cmdline-tools;latest"
# sdkmanager refuses to overwrite the directory it is running from, so the
# new copy lands in latest-2 and we swap it into place ourselves.
if [ -d "${ANDROID_HOME}/cmdline-tools/latest-2" ]; then
rm -rf "${ANDROID_HOME}/cmdline-tools/latest"
mv "${ANDROID_HOME}/cmdline-tools/latest-2" "${ANDROID_HOME}/cmdline-tools/latest"
fi
echo "${ANDROID_HOME}/cmdline-tools/latest/bin" >> "${GITHUB_PATH}"
"${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager" --version
- name: "Set up Android NDK"
run: |
"${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager" --install "ndk;${NDK_VERSION}"
echo "ANDROID_NDK_HOME=${ANDROID_HOME}/ndk/${NDK_VERSION}" >> "${GITHUB_ENV}"
echo "ANDROID_NDK_ROOT=${ANDROID_HOME}/ndk/${NDK_VERSION}" >> "${GITHUB_ENV}"
# The emulator prerequisites run here, ahead of the ~13 minutes of Rust and
# gradle work below, because they fail for reasons that have nothing to do with
# the library. Waiting for a build that always succeeds before finding out the
# SDK is unusable just makes the loop slower.
- name: "Enable KVM"
run: |
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
# Two separate failures live here, so the loop checks two things.
#
# The download is ~1 GB from dl.google.com and drops often enough on hosted
# runners to fail a job outright ("Failed to download package!"), and sdkmanager
# neither retries nor says why.
#
# Separately, a reported-successful install is sometimes invisible to the very
# next avdmanager, which then fails inside the emulator action with "contains no
# system images" and takes the job down with it. Creating a throwaway AVD here
# is the only check that actually exercises what the emulator action needs, and
# a failed probe is recoverable by reinstalling, which it is not once we have
# handed control to the action.
- name: "Install the emulator system image"
run: |
tools="${ANDROID_HOME}/cmdline-tools/latest/bin"
image="system-images;android-${EMULATOR_API_LEVEL};${EMULATOR_TARGET};${EMULATOR_ARCH}"
for attempt in 1 2 3; do
if "${tools}/sdkmanager" --install "${image}" --channel=0; then
if echo no | "${tools}/avdmanager" create avd --force --name probe \
--package "${image}" --device "Nexus 6"; then
"${tools}/avdmanager" delete avd --name probe || true
exit 0
fi
echo "::warning::'${image}' installed but avdmanager cannot see it."
fi
echo "::warning::Attempt ${attempt} of 3 to provide '${image}' failed; retrying."
"${tools}/sdkmanager" --uninstall "${image}" --channel=0 || true
sleep $(( attempt * 20 ))
done
echo "::error::Could not provide a usable '${image}' after 3 attempts."
exit 1
- name: "Set up Rust"
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
target: x86_64-linux-android
cache: false
- name: "Install cargo-ndk"
run: cargo install cargo-ndk --locked
- name: "Set up just"
uses: extractions/setup-just@v3
- name: "Set up pnpm"
uses: pnpm/action-setup@v5
- name: "Set up Node"
uses: actions/setup-node@v5
with:
node-version-file: .nvmrc
cache: pnpm
# Required for the async Electrum and Esplora methods the tests exercise.
- name: "Apply the bdk-ffi async-sync patches"
run: just submodule-apply-patch
# x86_64 only. The emulator below runs that ABI, and also building the
# arm64-v8a we ship would double the Rust build for nothing.
- name: "Build the library and pack the tarball"
run: just build-tarball-android x86_64
# tests/package.json pins the tarball by filename, so a version bump that
# misses it would otherwise fail deep inside pnpm.
- name: "Check tests/ references the tarball we just built"
run: |
expected="bdk-rn-$(node --print "require('./package.json').version").tgz"
referenced="$(node --print "require('./tests/package.json').dependencies['bdk-rn']")"
if [ "${referenced}" != "file:../${expected}" ]; then
echo "::error::tests/package.json depends on '${referenced}' but the library packs '${expected}'."
exit 1
fi
test -f "${expected}"
# --no-frozen-lockfile is required, not laziness: the lockfile records an
# integrity hash for the local tarball, and that hash changes on every build.
- name: "Install the test app dependencies"
working-directory: tests
run: |
rm -rf node_modules/bdk-rn
pnpm install --ignore-workspace --no-frozen-lockfile
# Assembled before the emulator boots so that a gradle failure is reported on
# its own, and so the emulator step stays short.
- name: "Build the test app"
working-directory: tests/android
run: ./gradlew :app:assembleRelease --console=plain
- name: "Run the integration tests"
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ env.EMULATOR_API_LEVEL }}
target: ${{ env.EMULATOR_TARGET }}
arch: ${{ env.EMULATOR_ARCH }}
profile: Nexus 6
emulator-options: "-no-window -gpu swiftshader_indirect -no-snapshot"
script: ./tests/scripts/run-android-tests.sh
- name: "Upload the test log"
if: always()
uses: actions/upload-artifact@v7
with:
name: android-test-log
path: tests/tests.log
if-no-files-found: ignore