main's scheduled run has been failing most cycles since 2026-07-06 with
either a corrupted SDK package download (Error on ZipFile unknown archive)
or a plain emulator boot timeout - both manifest as adb never reaching
emulator-5554. The job never cached the AVD, so every single run re-hit
Google's SDK servers for a fresh system image download, keeping it exposed
to this exact flake on every run and every 8-hour schedule tick.
Cache the AVD (skips the download entirely once warm) and duplicate the
test-run step as a same-job retry for the residual flake (cold cache, or
a rare boot timeout even with a warm cache).
This is what would have caught the 2026-07-09 HEZ-on-Asset-Hub silent sync
regression at PR time instead of hours into a live-app investigation after
merge. Not yet wired as a required status check - want to confirm it runs
clean on real PRs first (TronBalancesIntegrationTest has a known TronGrid
rate-limit flake that would need addressing before this can safely block
merges).
BalancesUpdateSystem.launchChainUpdaters() wrapped updater.listenForUpdates() in a
try/catch that discarded synchronous exceptions with zero logging - a silent failure
point that could explain a chain's balances never syncing with no trace in logcat.
Also add a full-architecture instrumented test that persists a real watch-only
MetaAccount and polls AssetDao through the actual BalancesUpdateSystem pipeline,
instead of bypassing it like the existing BalancesIntegrationTest does.
master was confirmed unused before deletion: no webhook/deploy-key
writes to it, no branch protection, last commit 2026-03-10 (4 months
stale), and no open PR targeted it. Its content was already fully
contained in main's history (main had since diverged far ahead with
v1.0.2/v1.0.3 releases), so nothing was lost.
These four workflows existed solely to serve or react to that branch
and can never trigger again now that it's gone:
- auto-pr.yml / auto-merge.yml: the master->main sync mechanism
itself - its entire purpose is gone along with master.
- pr_workflow.yml: triggers on `pull_request: branches: [master]`
(rc/hotfix release-notes automation) - can't fire, no PRs target
master anymore.
- update_tag.yml: triggers on `push: branches: ['master']` (auto
version tagging) - can't fire either.
The last two encode logic (release-notes extraction, auto-tagging)
that might still be wanted against main - left that as a separate
decision rather than silently re-pointing them, since that would
activate new automation behavior nobody asked for here.
Deliberately not touching .github/workflows/appium-mobile-tests.yml's
`ref: 'master'` - that's a workflow_dispatch call into a *different*
repository (pezkuwichain/appium-mobile-tests) and has nothing to do
with this repo's now-deleted branch.
* fix: use arm64-v8a emulator image on macos-14 runners
The "Run balances tests" job has been failing on every scheduled run
for weeks: the emulator requested an x86 system image while
macos-14 GitHub-hosted runners are Apple Silicon (aarch64) - QEMU2
refuses to boot a mismatched-architecture image ("Avd's CPU
Architecture 'x86' is not supported by the QEMU2 emulator on
aarch64 host"), so every adb command against the never-booted
emulator failed with "device not found" for the full 10-minute
boot timeout on every run.
* fix(ci): run balances-test emulator on ubuntu-latest with KVM instead of macos-14
macos-14 (Apple Silicon) GitHub-hosted runners don't expose nested
virtualization to guests, so the Android emulator's HVF backend fails
with HV_UNSUPPORTED and the job times out waiting for boot - this
affects both x86 and arm64-v8a system images, so the arch: arm64-v8a
change alone wasn't sufficient. macos-13 (which previously supported
this via HAXM) was retired by GitHub.
Linux hosted runners support KVM-accelerated emulators once the kvm
device's udev permissions are relaxed, which is the documented setup
for reactivecircus/android-emulator-runner and also faster/cheaper
than macOS runners.
* fix(ci): build instrumentialTest androidTest APK to match AllureAndroidJUnitRunner
run_balances_test.sh has always targeted
io.novafoundation.nova.debug.test/io.qameta.allure.android.runners.AllureAndroidJUnitRunner,
but android_build.yml's "Build debug tests" step built the plain
debug variant's androidTest APK, whose manifest declares the module
default runner (androidx.test.runner.AndroidJUnitRunner, from
allmodules.gradle) - AllureAndroidJUnitRunner is only wired via
testInstrumentationRunner on the dedicated instrumentialTest build
type (app/build.gradle), which was apparently created for exactly
this purpose but never actually wired into CI. Result:
INSTRUMENTATION_FAILED as soon as the emulator boot issue was fixed
and this step was reached for the first time.
instrumentialTest initWith buildTypes.debug (same applicationIdSuffix
'.debug', same default debug signing), so it installs under the same
package next to the already-built debug app APK - only the
androidTest APK's build type needs to change, not the main app build.
Only balances_test.yml sets build-debug-tests: true, so this doesn't
affect any other android_build.yml caller (pull_request.yml has it
explicitly disabled with its own separate pre-existing TODO).
* test: temporarily point android_build.yml ref at this branch for verification
TEMPORARY - reusable workflow calls (uses: ...@ref) pin to that ref's
copy regardless of the caller's branch, so android_build.yml's fix
never actually ran in the previous verification attempts even though
it was committed to this branch. Must revert to @main before merging
this PR, once android_build.yml's changes are on main.
* fix(ci): declare AllureAndroidJUnitRunner on debug build type, not instrumentialTest
AGP only ever compiles one androidTest APK per module, tied to
android.testBuildType (default "debug", never overridden in this
project) - so a testInstrumentationRunner override on any other build
type, including instrumentialTest, is unreachable regardless of which
gradle task or main-app variant is used. That's why the previous
attempt (assembleInstrumentialTestAndroidTest) failed outright: no
such task exists, since androidTest is only ever generated for
testBuildType.
Reverts the previous commit's task/path changes back to
assembleDebugAndroidTest / app/androidTest/debug/..., and instead
moves the testInstrumentationRunner override onto the debug build
type itself, which is what androidTest actually compiles against and
what run_balances_test.sh has always targeted. Also drops the dead
override from instrumentialTest now that it's confirmed non-functional
there.
* debug(ci): dump pm list instrumentation before running balances test
Diagnostic only - the previous fix (testInstrumentationRunner moved
to the debug build type's defaultConfig) still hit the identical
INSTRUMENTATION_FAILED for AllureAndroidJUnitRunner even though both
APKs now install successfully with the corrected paths. Need to see
what PackageManager actually registered for the installed test APK
rather than keep guessing from Gradle DSL evaluation-order theory -
allmodules.gradle's subprojects{} block also sets
testInstrumentationRunner on every module including app, and it's not
yet confirmed which assignment wins.
* fix(ci): use current io.pezkuwichain.wallet package instead of stale upstream name
Diagnostic (pm list instrumentation) confirmed the real, final root
cause: AllureAndroidJUnitRunner IS correctly registered on-device
after the previous fix, but under the app's real applicationId
(io.pezkuwichain.wallet, set in build.gradle since the Pezkuwi rebrand)
- not the pre-rebrand upstream Nova Wallet package
(io.novafoundation.nova) these scripts still hardcoded. Every previous
INSTRUMENTATION_FAILED was simply am instrument targeting a package
that was never installed.
Also fixes the identical stale reference in run_instrumental_tests.sh
(currently unused by any workflow, but has the same bug).
The BalancesIntegrationTest class's own Kotlin package
(io.novafoundation.nova.balances) is untouched - that's the test
class's real source package on disk, unrelated to the app's
applicationId, and was never part of the bug.
* fix(ci): bound run-tests to 25min and unbuffer python output
Latest verification run hung for 1h15m+ inside am instrument with
zero visible progress - had to be cancelled manually. Post-mortem:
Python's stdout is fully buffered (not line-buffered) when piped
under GitHub Actions' `run:`, so the script's own 5s heartbeat prints
(explicitly there to avoid CI's inactivity-based cancellation) never
actually reached the log until the buffer filled near the very end,
making the run look identical to a genuine hang for its entire
duration with no way to tell what BalancesIntegrationTest was doing.
- timeout-minutes: 25 on run-tests bounds any future hang to a fixed,
reasonable ceiling instead of requiring a manual cancel after an
hour+.
- python -u unbuffers stdout so the next run's logs show real-time
heartbeat/output, giving actual visibility into where a hang (or
slow chain RPC) occurs instead of a silent multi-hour gap.
Root cause of the hang itself is still open - this only makes it
diagnosable and bounds its cost.
* fix: skip WSS ChainConnection setup for Tron-based chains
Real root cause of the 1h15m+ hang in the previous verification run
(had to be cancelled manually): ChainRegistry registers all chains
sequentially (diff.newOrUpdated.forEach { registerChain(it) }), and
registerConnection() unconditionally called
connectionPool.setupConnection(chain), which creates a ChainConnection
backed by a WSS-only SocketService - for every chain, with no check
for chain type. Tron's node (https://api.trongrid.io) is a plain REST
API with no WebSocket support, so this connection attempt just hangs
forever with no timeout, and since registration is sequential and
Pezkuwi chains (including Tron) are ordered first in the merged chain
list, every chain after it in the list never finishes registering
either - including all the third-party chains BalancesIntegrationTest
actually exercises.
This isn't just a test problem: any real user with the Tron chain
enabled would hit the same registry-wide freeze during chain sync.
Tron balance/transfer operations already go through their own
TronGridApi REST client (built in the Phase 1/2 Tron work),
independent of ConnectionPool/ChainConnection - confirmed nothing else
reads chainRegistry.getConnection() for a Tron chain id - so skipping
ChainConnection setup entirely for isTronBased chains is safe.
* debug(ci): stream live logcat during test run
Diagnostic only. Previous fix (skip WSS ChainConnection for
isTronBased chains) did NOT resolve the hang - identical symptom
(silent for the full 25min timeout, just heartbeats, nothing from the
actual test process). Need real device-side visibility into what's
actually happening (network calls, coroutine timeouts, ANRs) instead
of guessing blind from am instrument's own stdout/stderr, which stays
completely silent until the process exits.
* debug(ci): SIGQUIT thread dump 3min into a hung run, bump logcat to debug level
Previous diagnostic (live logcat at info level) showed the app staying
genuinely alive (GC cycles, system chatter) but produced zero signal
from the test itself after the initial successful chains.json fetch -
no WSS/DB/coroutine activity visible, likely because relevant logging
is below info level. Static code reading (ChainConnection.setup(),
ChainSyncService.syncUp(), ChainFetcher) hasn't turned up an obvious
blocking call, and this exact hang predates this session by at least
a month (zero successes in the last 100 scheduled runs going back to
2026-06-09) - so it's a real, previously-latent bug in the app/test
itself, never previously reached because every earlier run failed at
an earlier CI-infra stage.
kill -3 on the app process forces ART to dump every thread's Java
stack trace to logcat (the standard ANR-diagnosis technique) - this
should show exactly which coroutine/thread is parked and where,
instead of continuing to guess from log filtering.
* fix(ci): bump run-tests timeout to 90min, drop now-unneeded SIGQUIT diagnostic
With the chain blacklist fixed, the previous run genuinely started
executing the 160-case parameterized test suite (visible TestRunner
started/finished events for real chains) instead of hanging silently
- first time this has happened in this entire investigation.
It still hit the 25min ceiling: each test case has an explicit
withTimeout(80.seconds), and several chains that aren't fully dead
(so not blacklisted) but are slow/unresponsive burn the full 80s
before failing. With 160 total test cases, the worst-case aggregate
easily exceeds 25 minutes even with zero actual hangs - so the
timeout needs to be realistic for the test's own design, not just
long enough to catch a true infinite hang. 90min matches
android_build.yml's existing precedent for this project's CI jobs.
The SIGQUIT-after-3-minutes diagnostic added earlier is now
counterproductive noise: any healthy run legitimately takes far
longer than 3 minutes to get through 160 cases, so it would fire on
every run rather than only genuine hangs. Removed now that its actual
purpose (finding the real hang) is done - the blacklist was the
answer, not something a thread dump would have caught anyway (dead
sockets, not deadlocked application code).
* chore: revert temporary android_build.yml ref pin back to @main
Was pointed at this branch only to verify android_build.yml's own
fixes actually took effect during iteration (reusable workflow calls
resolve against the ref they're pinned to, not the caller's branch).
android_build.yml is now byte-identical to main (the wrong task-name
attempt was fully reverted in an earlier commit), so this is a no-op
functionally - just restoring the correct long-term reference before
merge.
pull_request-triggered runs of this workflow have stopped firing
entirely for at least one recent PR (no check-suite created at all
for the GitHub Actions app, while two other installed apps sit stuck
in "queued") - root cause is outside what's fixable via the repo's
own Actions config/API access. This adds a manual workflow_dispatch
path (branch input) so a build+test run can still be triggered
directly against any branch while that's being sorted out.
Add an 'inProgress' status + a user_fraction input so production releases can
roll out gradually (e.g. 0.2 = 20%) instead of only 100% (completed).
userFraction is passed only when status=inProgress; ignored otherwise.
* ci: wire develop_key.jks into PR build (fix validateSigningDevelop)
The PR build runs assembleDevelop (signingConfigs.dev = develop_key.jks) but the
reusable workflow only decoded github/market keystores, so validateSigningDevelop
always failed. Add a develop-keystore decode step (reusing the existing
BASE64_DEV_KEYSTORE_FILE secret + CI_KEYSTORE_* passwords) and pass
keystore-file-name: develop_key.jks from pull_request.yml.
[temp] pin reusable workflow to the branch to validate before merge; reverted to
@main in the next commit.
* ci: revert reusable-workflow pin back to @main
Fix validated green on PR #4 (test/Build app and test passed, develop signing
works). Restore @main pin for the final merged state.
The scheduled 'Run balances tests' workflow has been failing for weeks
with 'No space left on device' — assembleDebug + assembleDebugAndroidTest
for every module exceeds the ~14 GB free on GitHub-hosted runners.
Add an opt-in free-disk-space input to the reusable build workflow that
removes preinstalled toolchains we never use (dotnet, ghc, CodeQL, swift,
boost, etc., ~30+ GB), and enable it for the balances test build. Other
callers of the reusable workflow are unaffected.
- Add 16KB page size alignment for native Rust libraries (Google Play requirement)
- Make Play Store workflow parametric (track/status as inputs)
- Update release notes for all languages (en, tr, ku)
- Add .gitignore entries for node_modules and version.properties
CI_BUILD_ID was using github.run_number which is per-workflow, causing
different workflows to produce different versionCodes (144 vs 9).
Now computed from git commit count + offset, consistent across all workflows.
Also added bundle task to local auto-increment.
Rust Cargo.toml had strip=true which removes ALL symbols including the
symbol table needed by AGP to generate native-debug-symbols.zip for
Play Store. Changed to strip="debuginfo" which keeps symbol tables
(needed for crash symbolication) but removes debug info (keeps .so small).
Also made debugSymbols conditional in distribute workflow so missing
symbols don't fail the upload.
Build doesn't generate native debug symbols artifact, causing the
Market publication step to fail with ENOENT. Remove the parameter
to unblock the upload. Debug symbols can be added back when NDK
symbols are actually generated.
The tw3lveparsecs/github-actions-setvars action was loading env vars
from variables/android.env that are never referenced in this workflow.
It caused a build failure when GitHub services were unavailable.
- Switch from assembleReleaseMarket (APK) to bundleReleaseMarket (AAB)
Google Play requires AAB for new app submissions since August 2021
- Add bundle and mapping artifact uploads to reusable build workflow
- Fix mapping file path: release → releaseMarket
- Remove debugSymbols (not included in build artifact)
- Remove userFraction (incompatible with draft status)
- Remove whatsnew-ku (Play Store does not support Kurdish locale)
- Add if-no-files-found: ignore to APK upload for bundle-only builds
releaseMarket and releaseGithub build types need their own copy
of the production google-services.json. Without this, the
google-services plugin falls back to the dev config in app/,
causing Google Sign-In to fail on release builds.
Support both develop and releaseMarket variants via build_variant
input. Release builds are signed with market_key and distributed
to testers for validation before Play Store upload.