Skip to content

fix: unblock Android e2e build (fonts + react-native-keys patch) - #39

Merged
MounirDhahri merged 10 commits into
mainfrom
claude/fix-e2e-fonts
Jul 11, 2026
Merged

fix: unblock Android e2e build (fonts + react-native-keys patch)#39
MounirDhahri merged 10 commits into
mainfrom
claude/fix-e2e-fonts

Conversation

@MounirDhahri

Copy link
Copy Markdown
Member

This PR resolves [the Android agent-device e2e build failing before it could reach the app]

Description

Two fixes that let the manual Android e2e job actually build, install, and boot the app on a real emulator in CI. Both were found by triggering the real workflow_dispatch Android run and reading the logs.

1. Placeholder fonts before expo prebuild
The expo-font config plugin (see app.json) reads Artsy's Unica77LL-*.otf fonts from assets/fonts. Those are gitignored and normally fetched by yarn setup:artsy from a private S3 bucket, which isn't available in OSS CI — so expo prebuild failed with ENOENT ... Unica77LL-Italic.otf. A CI step now creates empty placeholder .otfs before prebuild so it resolves; the smoke test doesn't depend on the real glyphs (the app falls back to system fonts). Artsy engineers still get the real fonts locally via yarn setup:artsy.

2. Patch react-native-keys for RN 0.83 Android
react-native-keys@0.7.13's android/build.gradle couldn't locate the React Native android sources on RN 0.81+ (sources moved to react-native/ReactAndroid) and used the removed compileSdkVersion DSL, so ./gradlew assembleDebug failed configuring :react-native-keys. This applies the same patch Eigen uses (.yarn/patches/react-native-keys-npm-0.7.13-...patch): add the ReactAndroid source path + resolver fallbacks, rename compileSdkVersioncompileSdk, and add SDK-version fallbacks.

Verified on a real emulator

Triggered the Android workflow_dispatch run after these fixes. It now goes green through the whole native path:

  • expo prebuild (was ❌ missing fonts)
  • ./gradlew assembleDebug — debug APK built (was ❌ on :react-native-keys)
  • ✅ Emulator boots, ✅ APK installs (Streamed Install: Success)
  • ✅ agent-device launches the app and runs the launch-smoke flow

Known follow-up (not fixed here)

The launch-smoke flow itself still fails its assertion on the emulator: the debug build boots into the Expo dev-client launcher ("Fetch development servers", exp://) rather than the app, because there is no Metro bundler / embedded JS bundle in CI, so "Log In" never renders. Making the smoke assertion pass needs either Metro running + a dev-client deep link, or a bundled/release build. The emulator step stays continue-on-error: true and the job stays manual and non-blocking, so this does not gate anything.

PR Checklist

  • I have tested my changes on iOS and Android.
  • I have included screenshots or videos, or I have not changed the UI. (CI/build config only)
  • I have added tests, or my changes don't require any. (unblocks the existing e2e job)
  • I added an [app state migration], or my changes do not require one.
  • I have documented any follow-up work that this PR will require, or it does not require any. (see "Known follow-up")

To the reviewers 👀

  • I would like at least one of the reviewers to run this PR on the simulator or device.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KPWjhmaeq8DnsiiFD9Eg7A


Generated by Claude Code

Bring the Android agent-device e2e to a genuinely working, gated state on
SDK 56 / RN 0.85:

- Placeholder Unica77LL fonts before prebuild (gitignored; from private S3).
- Compile Relay before the release bundle (Metro resolves co-located queries).
- Build a RELEASE APK (embeds JS, drops the expo-dev-client launcher) with
  SENTRY_DISABLE_AUTO_UPLOAD (no token in OSS CI); install app-release.apk.
- Harden launch: retry the flow up to 3x, force-stopping the app between
  tries, to absorb agent-device's intermittent app-launch flake (home
  launcher occasionally stays foreground). Drop continue-on-error so the
  manual job now reflects the real launch-smoke result.
- Fix the flow assertion syntax: `is visible "Folio"`.
- Cache Gradle + Yarn across runs to speed the build.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPWjhmaeq8DnsiiFD9Eg7A
@MounirDhahri
MounirDhahri force-pushed the claude/fix-e2e-fonts branch from 1082b45 to d7f0531 Compare July 10, 2026 22:02
claude added 9 commits July 10, 2026 22:11
react-native-keys@0.7.13's android/build.gradle doesn't declare compileSdk
and can't locate the RN android sources on RN 0.81+, so AGP fails to
configure the :react-native-keys project (`does not specify compileSdk`).
Apply the same patch Eigen uses (adds the ReactAndroid source path +
resolver fallbacks, renames compileSdkVersion->compileSdk, adds SDK
fallbacks). Without this, ANY Android build on main fails — not just e2e.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPWjhmaeq8DnsiiFD9Eg7A
The iOS job ran only agent-device doctor + test with no prebuild, pod
install, Xcode build, or booted simulator — so it reported green while
validating nothing. A misleading pass is worse than no job. Replaced it
with a clearly-labeled commented-out block plus a step-by-step guide for
wiring up a real build+simulator flow (mirroring the Android job).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPWjhmaeq8DnsiiFD9Eg7A
The android-emulator-runner action runs its `script:` input line-by-line
(each line in its own `sh -c`), so the inline multi-line `until … done`
retry loop was split across shells and failed with 'Syntax error: end of
file unexpected (expecting done)' — the e2e test never actually ran even
though the release APK built and installed cleanly.

Move install + retry into e2e/scripts/android-e2e.sh and invoke it as a
single line so the loop stays intact in one shell.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPWjhmaeq8DnsiiFD9Eg7A
agent-device's `is` command takes a predicate plus a selector expression
(e.g. `is visible text="Folio"`). The flow used a bare quoted string
(`is visible "Folio"`), which the runtime rejects with 'is requires a
selector expression' — so the smoke check failed at step 3 on every retry
even though the app launched and the Login screen rendered (steps 1-2,
`open`/`wait "Log In"`, both passed). Confirmed 'Folio' is rendered on
the Login screen (src/Scenes/Login/Login.tsx).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPWjhmaeq8DnsiiFD9Eg7A
Run on 643077f failed at step 2 on all 3 retries: 'nexuslauncher is
foreground instead of net.artsy.energy' — the app didn't come to the
foreground. This is a boot-timing flake (the same build launched and
rendered fine on the prior run); the test started ~9s after boot_completed
while the launcher/PackageManager were still settling, so open's launch
intent was dropped.

Two-layer fix:
- Flow: 'open Energy --relaunch' (real cold launch by package) + 'wait
  --connected' (block until the app is actually foreground) before asserting.
- Script: wait for the boot animation to stop + a short settle before the
  first attempt, so we don't race a half-booted system.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPWjhmaeq8DnsiiFD9Eg7A
…acts

Run the suite with --debug (verbose CLI/daemon diagnostics), --record-video
and --artifacts-dir, and on every attempt (pass or fail) capture a device
screenshot, the foreground window/activity, and recent logcat into
e2e-artifacts/. Upload e2e-artifacts/ plus agent-device's own test-artifacts/
via actions/upload-artifact (if: always()) so a run can be inspected after
the fact — this is what lets us see what's actually on screen when a launch
flakes, instead of inferring it from the foreground-package error.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPWjhmaeq8DnsiiFD9Eg7A
Alongside the screenshot/logcat, capture an agent-device accessibility
snapshot (structured --json + human-readable text) of whatever is on screen
on every attempt. Uses --platform android so it attaches to the foreground
app without the now-closed test session, and runs before the force-stop so
it reflects the failed state. The snapshot shows the actual view tree
(labels/roles/refs) — more diagnostic than pixels for 'did the app render'.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPWjhmaeq8DnsiiFD9Eg7A
The bb98a12 run proved the app launches and renders on RN 0.85 — the
agent-device 'Current surface' at the failure was 'Folio, Log In, With Your
Artsy Partner Account, show password icon' (the Login screen). The launcher-
foreground flake is gone (open --relaunch fixed it). The only failure was a
bad flow step: in a .ad replay, 'wait <arg>' waits for on-screen TEXT, so
'wait --connected' was interpreted as the literal text '--connected' and
timed out. --connected is a standalone-CLI-only flag. Remove it; 'wait
"Log In"' already blocks until the first screen renders.

Also echo foreground.txt + snapshot.txt into the CI log from capture_evidence,
since the uploaded artifact is on an egress-blocked host — this keeps runs
diagnosable from the logs alone.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPWjhmaeq8DnsiiFD9Eg7A
The captured foreground dump showed the real cause of the wait "Log In"
failures: net.artsy.energy/.MainActivity IS the topResumedActivity (the app
launches fine on RN 0.85), but under CI emulator load the Pixel launcher
itself ANRs and its 'Application Not Responding' dialog steals window focus,
so agent-device sees nexuslauncher as foreground and wait fails.

Fix: suppress system error dialogs (settings put global hide_error_dialogs 1)
so nothing steals focus, extend the post-boot settle, and give the emulator
more headroom (cores: 3, ram-size: 4096M, disable-animations) so the launcher
is less likely to ANR while the app cold-starts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPWjhmaeq8DnsiiFD9Eg7A
@MounirDhahri
MounirDhahri merged commit b7ee31f into main Jul 11, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants