Skip to content

Commit 7fc79e5

Browse files
committed
chore: merge master into release
2 parents 1ab2860 + 4deacd5 commit 7fc79e5

219 files changed

Lines changed: 14862 additions & 1070 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/commands/pr.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,8 @@ When the user provides custom instructions after `--`:
136136
#### Automated Checks
137137
```
138138
- Keep local verification commands, Gradle tasks, detekt, lint, unit tests, build passes, cargo test, cargo clippy, npm test, typecheck, CI coverage, or similar automated checks out of `#### Manual Tests`; summarize them under `#### Automated Checks` when they add useful context.
139-
- Use `#### Automated Checks` to summarize automated verification evidence, prioritizing coverage added, modified, or removed with file paths and a short explanation.
139+
- Use `#### Automated Checks` to summarize automated verification evidence, prioritizing coverage added, modified, or removed, each with the test file name and a short explanation.
140+
- Reference test files by bare file name only (e.g. `HwWalletRepoTest.kt`), never the full path. Only when two referenced test files share the same name, prefix the shortest leading path segment(s) that disambiguate them (e.g. `repositories/FooTest.kt` vs `viewmodels/FooTest.kt`).
140141
- For removed automated coverage, state why it was removed.
141142
- Do not list standard CI or PR bot commands as checkbox items just because they run for every PR. If standard CI coverage is worth mentioning, summarize it in one sentence.
142143
- List raw commands only when they were run locally, are non-standard, use special flags or environment values, validate workflow behavior, or explain a meaningful verification gap.
@@ -184,9 +185,9 @@ Concrete style target:
184185
- [ ] **5b.** back: returns to Connections List.
185186
- [ ] **6.** `regression:` Channel Detail → tap Close Connection: works.
186187
#### Automated Checks
187-
- Unit tests added: cover invoice timeout handling in `app/src/test/.../SendInvoiceTest.kt`.
188-
- Unit tests modified: update channel navigation assertions in `app/src/test/.../ChannelDetailTest.kt`.
189-
- Test coverage removed: delete stale mock-only assertions from `app/src/test/.../OldFlowTest.kt` because the flow no longer exists.
188+
- Unit tests added: cover invoice timeout handling in `SendInvoiceTest.kt`.
189+
- Unit tests modified: update channel navigation assertions in `ChannelDetailTest.kt`.
190+
- Test coverage removed: delete stale mock-only assertions from `OldFlowTest.kt` because the flow no longer exists.
190191
- CI: standard compile, unit test, and detekt checks run by the PR bot.
191192
```
192193

.agents/commands/release.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -210,16 +210,20 @@ just release
210210

211211
Expected APK path: `app/build/outputs/apk/mainnet/release/bitkit-mainnet-release-{newVersionCode}-universal.apk`
212212
Expected AAB path: `app/build/outputs/bundle/mainnetRelease/bitkit-mainnet-release-{newVersionCode}.aab`
213+
Expected native debug symbols path: `app/build/outputs/native-debug-symbols/mainnetRelease/native-debug-symbols-{newVersionCode}.zip`
213214

214-
Verify both files exist. If the build fails, stop and report the error to the user.
215+
Verify all three files exist. The native debug symbols file must be from the same `just release` build as the APK/AAB. Keep the build-numbered filename, e.g. `native-debug-symbols-{newVersionCode}.zip`, so it matches the APK/AAB build number. `just release` resolves upstream native debug symbol artifacts from the Rust dependency packages, merges them into the final archive, and refuses placeholder symbols from stripped packaged `.so` files.
215216

216-
### 8. Upload APK to Draft Release
217+
### 8. Upload APK and Native Symbols to Draft Release
217218

218219
```bash
219220
gh release upload v{newVersionName} \
220-
app/build/outputs/apk/mainnet/release/bitkit-mainnet-release-{newVersionCode}-universal.apk
221+
app/build/outputs/apk/mainnet/release/bitkit-mainnet-release-{newVersionCode}-universal.apk \
222+
app/build/outputs/native-debug-symbols/mainnetRelease/native-debug-symbols-{newVersionCode}.zip
221223
```
222224

225+
For the Play Store release, upload the AAB as usual, then upload `native-debug-symbols-{newVersionCode}.zip` for the exact version/build in Play Console: App bundle explorer → Downloads → Assets. Verify Play lists the native debug symbols after upload. Keep the release-built archive in GitHub releases or internal release storage; Play Console may only show delete/replace controls after upload, which is enough for release verification.
226+
223227
### 9. Return to Master
224228

225229
```bash
@@ -236,6 +240,7 @@ Release branch: release-{newVersionName}
236240
Tag: v{newVersionName}
237241
Draft release: {release URL}
238242
APK uploaded: bitkit-mainnet-release-{newVersionCode}-universal.apk
243+
Native debug symbols uploaded: native-debug-symbols-{newVersionCode}.zip
239244
Store release notes: .ai/release-notes-{newVersionName}.md
240245
241246
Next steps:

.github/workflows/ci.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,14 @@ jobs:
3535
uses: gradle/actions/setup-gradle@v5
3636

3737
- name: Decode google-services.json
38-
run: echo "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/google-services.json
38+
run: |
39+
set -euo pipefail
40+
if [ -z "${GOOGLE_SERVICES_JSON_BASE64:-}" ]; then
41+
echo "GOOGLE_SERVICES_JSON_BASE64 is empty; using checked-in placeholder."
42+
exit 0
43+
fi
44+
mkdir -p app/src/debug
45+
printf '%s' "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/src/debug/google-services.json
3946
env:
4047
GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.GOOGLE_SERVICES_JSON_BASE64 }}
4148

.github/workflows/e2e.yml

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,14 @@ jobs:
5757
uses: gradle/actions/setup-gradle@v5
5858

5959
- name: Decode google-services.json
60-
run: echo "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/google-services.json
60+
run: |
61+
set -euo pipefail
62+
if [ -z "${GOOGLE_SERVICES_JSON_BASE64:-}" ]; then
63+
echo "GOOGLE_SERVICES_JSON_BASE64 is empty; using checked-in placeholder."
64+
exit 0
65+
fi
66+
mkdir -p app/src/debug
67+
printf '%s' "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/src/debug/google-services.json
6168
env:
6269
GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.GOOGLE_SERVICES_JSON_BASE64 }}
6370

@@ -100,7 +107,14 @@ jobs:
100107
uses: gradle/actions/setup-gradle@v5
101108

102109
- name: Decode google-services.json
103-
run: echo "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/google-services.json
110+
run: |
111+
set -euo pipefail
112+
if [ -z "${GOOGLE_SERVICES_JSON_BASE64:-}" ]; then
113+
echo "GOOGLE_SERVICES_JSON_BASE64 is empty; using checked-in placeholder."
114+
exit 0
115+
fi
116+
mkdir -p app/src/debug
117+
printf '%s' "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/src/debug/google-services.json
104118
env:
105119
GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.GOOGLE_SERVICES_JSON_BASE64 }}
106120

@@ -112,6 +126,8 @@ jobs:
112126
E2E: true
113127
E2E_BACKEND: network
114128
GEO: false
129+
TREZOR_BRIDGE: true
130+
TREZOR_BRIDGE_URL: http://10.0.2.2:21325
115131
run: ./gradlew assembleDevDebug
116132

117133
- name: Rename APK
@@ -306,6 +322,7 @@ jobs:
306322
shard:
307323
- { name: multi_address_2_regtest, grep: "@multi_address_2" }
308324
- { name: pubky_paykit, grep: "@pubky" }
325+
- { name: hardware_wallet, grep: "@hardware_wallet" }
309326

310327
name: e2e-tests-staging - ${{ matrix.shard.name }}
311328

@@ -376,6 +393,13 @@ jobs:
376393
working-directory: bitkit-e2e-tests
377394
run: npm ci
378395

396+
- name: Pull Trezor emulator image
397+
if: matrix.shard.name == 'hardware_wallet'
398+
working-directory: bitkit-e2e-tests
399+
run: |
400+
cd docker
401+
docker compose --profile trezor pull --quiet trezor-user-env
402+
379403
- name: Run E2E Tests 1 (${{ matrix.shard.name }})
380404
continue-on-error: true
381405
id: test1

.github/workflows/e2e_migration.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,14 @@ jobs:
3737
uses: gradle/actions/setup-gradle@v5
3838

3939
- name: Decode google-services.json
40-
run: echo "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/google-services.json
40+
run: |
41+
set -euo pipefail
42+
if [ -z "${GOOGLE_SERVICES_JSON_BASE64:-}" ]; then
43+
echo "GOOGLE_SERVICES_JSON_BASE64 is empty; using checked-in placeholder."
44+
exit 0
45+
fi
46+
mkdir -p app/src/debug
47+
printf '%s' "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/src/debug/google-services.json
4148
env:
4249
GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.GOOGLE_SERVICES_JSON_BASE64 }}
4350

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
name: Release Internal
2+
3+
on:
4+
workflow_dispatch:
5+
6+
concurrency:
7+
group: ${{ github.workflow }}-${{ github.ref }}
8+
cancel-in-progress: false
9+
10+
env:
11+
TERM: xterm-256color
12+
FORCE_COLOR: 1
13+
NDK_VERSION: 28.1.13356709
14+
15+
jobs:
16+
build-internal:
17+
runs-on: ubuntu-latest
18+
timeout-minutes: 45
19+
environment: release-internal
20+
21+
permissions:
22+
contents: read
23+
packages: read
24+
25+
steps:
26+
- name: Checkout
27+
uses: actions/checkout@v6
28+
29+
- name: Setup Java
30+
uses: actions/setup-java@v5
31+
with:
32+
java-version: '17'
33+
distribution: 'adopt'
34+
35+
- name: Setup Gradle
36+
uses: gradle/actions/setup-gradle@v5
37+
38+
- name: Setup Android NDK
39+
run: |
40+
set -euo pipefail
41+
android_sdk_root="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}"
42+
test -n "$android_sdk_root"
43+
sdkmanager_path="$android_sdk_root/cmdline-tools/latest/bin/sdkmanager"
44+
test -x "$sdkmanager_path"
45+
yes | "$sdkmanager_path" --licenses >/dev/null || true
46+
"$sdkmanager_path" --install "ndk;$NDK_VERSION"
47+
48+
- name: Decode mainnet release google-services.json
49+
env:
50+
MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64 }}
51+
run: |
52+
set -euo pipefail
53+
test -n "$MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64"
54+
mkdir -p app/src/mainnetRelease
55+
printf '%s' "$MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64" | base64 --decode > app/src/mainnetRelease/google-services.json
56+
57+
- name: Decode internal keystore
58+
env:
59+
INTERNAL_KEYSTORE_BASE64: ${{ secrets.INTERNAL_KEYSTORE_BASE64 }}
60+
run: |
61+
set -euo pipefail
62+
test -n "$INTERNAL_KEYSTORE_BASE64"
63+
umask 077
64+
keystore_path="$RUNNER_TEMP/internal.keystore"
65+
printf '%s' "$INTERNAL_KEYSTORE_BASE64" | base64 --decode > "$keystore_path"
66+
echo "KEYSTORE_FILE=$keystore_path" >> "$GITHUB_ENV"
67+
68+
- name: Build internal release APK
69+
env:
70+
GPR_USER: ${{ secrets.GPR_USER || github.actor }}
71+
GPR_TOKEN: ${{ secrets.GPR_TOKEN || github.token }}
72+
GITHUB_TOKEN: ${{ secrets.GPR_TOKEN || github.token }}
73+
KEYSTORE_PASSWORD: ${{ secrets.INTERNAL_KEYSTORE_PASSWORD }}
74+
KEY_ALIAS: ${{ secrets.INTERNAL_KEY_ALIAS }}
75+
KEY_PASSWORD: ${{ secrets.INTERNAL_KEY_PASSWORD }}
76+
run: |
77+
set -euo pipefail
78+
./gradlew assembleMainnetRelease --no-daemon --stacktrace
79+
./gradlew :app:syncNativeDebugSymbolArtifacts --no-daemon --stacktrace
80+
scripts/create-native-debug-symbols.sh
81+
82+
- name: Verify native libraries are stripped
83+
run: |
84+
set -euo pipefail
85+
android_sdk_root="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}"
86+
test -n "$android_sdk_root"
87+
llvm_readelf_candidates=("$android_sdk_root/ndk/$NDK_VERSION"/toolchains/llvm/prebuilt/*/bin/llvm-readelf)
88+
llvm_readelf_path="${llvm_readelf_candidates[0]}"
89+
test -x "$llvm_readelf_path"
90+
91+
native_count=0
92+
extract_root="$(mktemp -d)"
93+
trap 'rm -rf "$extract_root"' EXIT
94+
while IFS= read -r -d '' artifact_path; do
95+
extract_dir="$extract_root/$(basename "$artifact_path")"
96+
mkdir -p "$extract_dir"
97+
unzip -q "$artifact_path" 'lib/*/*.so' -d "$extract_dir" 2>/dev/null || true
98+
while IFS= read -r -d '' native_path; do
99+
native_count=$((native_count + 1))
100+
if "$llvm_readelf_path" -S "$native_path" | grep -q '\.debug_'; then
101+
native_entry="${native_path#"$extract_dir"/}"
102+
echo "Native library contains debug sections: $artifact_path:$native_entry"
103+
exit 1
104+
fi
105+
done < <(find "$extract_dir" -name '*.so' -print0)
106+
done < <(find app/build/outputs/apk/mainnet/release -type f -name '*.apk' -print0)
107+
test "$native_count" -gt 0
108+
109+
- name: Verify internal release signature
110+
run: |
111+
set -euo pipefail
112+
android_sdk_root="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}"
113+
test -n "$android_sdk_root"
114+
apksigner_path="$(find "$android_sdk_root/build-tools" -name apksigner -type f | sort -V | tail -n 1)"
115+
test -n "$apksigner_path"
116+
117+
apk_count=0
118+
while IFS= read -r -d '' apk_path; do
119+
apk_count=$((apk_count + 1))
120+
"$apksigner_path" verify --verbose --print-certs "$apk_path"
121+
done < <(find app/build/outputs/apk/mainnet/release -name 'bitkit-mainnet-release-*.apk' -print0)
122+
test "$apk_count" -gt 0
123+
124+
- name: Collect internal artifacts
125+
id: artifacts
126+
run: |
127+
set -euo pipefail
128+
artifact_dir="$RUNNER_TEMP/internal-release"
129+
mkdir -p "$artifact_dir"
130+
mapfile -d '' apk_paths < <(find app/build/outputs/apk/mainnet/release -name 'bitkit-mainnet-release-*.apk' -print0)
131+
mapfile -d '' symbol_paths < <(find app/build/outputs/native-debug-symbols/mainnetRelease -name 'native-debug-symbols-*.zip' -print0)
132+
test "${#apk_paths[@]}" -gt 0
133+
test "${#symbol_paths[@]}" -gt 0
134+
135+
for apk_path in "${apk_paths[@]}"; do
136+
cp "$apk_path" "$artifact_dir/"
137+
done
138+
for symbol_path in "${symbol_paths[@]}"; do
139+
cp "$symbol_path" "$artifact_dir/"
140+
done
141+
142+
apk_file="$(basename "${apk_paths[0]}")"
143+
build_number="${apk_file#bitkit-mainnet-release-}"
144+
build_number="${build_number%%-*}"
145+
[[ "$build_number" =~ ^[0-9]+$ ]]
146+
147+
(cd "$artifact_dir" && sha256sum -- *.apk native-debug-symbols-*.zip > SHA256SUMS.txt)
148+
echo "artifact_name=bitkit-release-internal-$build_number-$GITHUB_RUN_NUMBER" >> "$GITHUB_OUTPUT"
149+
echo "artifact_dir=$artifact_dir" >> "$GITHUB_OUTPUT"
150+
151+
- name: Upload internal artifacts
152+
uses: actions/upload-artifact@v6
153+
with:
154+
name: ${{ steps.artifacts.outputs.artifact_name }}
155+
path: ${{ steps.artifacts.outputs.artifact_dir }}
156+
retention-days: 30

0 commit comments

Comments
 (0)