Skip to content

Generate example/android via Expo prebuild + with-android-tests plugin#9

Closed
gmaclennan wants to merge 2 commits into
claude/ios-support-graceful-shutdown-GCAXbfrom
claude/hardcore-gould-e8f235
Closed

Generate example/android via Expo prebuild + with-android-tests plugin#9
gmaclennan wants to merge 2 commits into
claude/ios-support-graceful-shutdown-GCAXbfrom
claude/hardcore-gould-e8f235

Conversation

@gmaclennan

@gmaclennan gmaclennan commented Apr 21, 2026

Copy link
Copy Markdown
Member

Summary

All Android-specific work for this repo, separated out of the iOS support PR (#6).

1. Generate example/android/ via Expo prebuild + config plugin

  • Stop checking in the generated example/android/ tree so Expo SDK upgrades can regenerate it cleanly.
  • New example/plugins/with-android-tests/ config plugin mirrors with-ios-tests: copies the Kotlin instrumented-test sources from example/tests/android/ into app/src/androidTest/ at prebuild time, and patches app/build.gradle with the AndroidJUnit test runner and androidTestImplementation deps via mergeContents (idempotent — safe on re-prebuild).
  • CI (.github/workflows/android-tests.yml) and the local e2e/run-instrumented-tests.sh runner now call npx expo prebuild --platform android before Gradle. The runner is restructured so prebuild runs once in a shared-setup block rather than once per path.

2. Android CI / test / nodejs-project changes (moved from PR #6)

These changes were originally authored on the iOS branch but are Android-only; PR #6 has reverted them so this PR can own them explicitly.

  • .github/workflows/android-tests.yml — Node 18 → 20 upgrade; replace emulator-runner's input keyevent 82 (which races snapshot-restore and crashes the emulator) with settings put to disable animations; per-test -i gradle flag; 15-minute test timeout.
  • android/src/main/assets/nodejs-project/index.js — track a readinessPhase in the state IPC server and replay started / ready to late-connecting clients. The iOS copy at ios/nodejs-project/index.js carries the same fix (iOS PR keeps that side); this is the Android side of the same logical change.
  • android/src/main/assets/nodejs-project/lib/control-rpc.js — remove unused stub.
  • example/tests/android/ServiceLifecycleTest.kt and example/tests/android/ShutdownPathTest.kt — add a PID-stability check in waitForServiceRunning so a :ComapeoCore process that dies at startup is surfaced as a clear assertion failure instead of the test hanging.

Commit trail

59ecc56 Apply Android CI/test/nodejs-project changes (moved from iOS PR #6)
686c19d Rewind shared files to origin/main before re-applying Android changes
393b612 Generate example/android via Expo prebuild + tests config plugin

The rewind / re-apply pair has net-zero tree diff, but is required so the final "Apply" commit's diff is meaningful: without it, the changes would be inherited silently through the shared merge-base with PR #6 rather than attributed to this PR.

Base branch

Stacked on #6. Merge #6 first, then rebase this onto main — the rewind + re-apply pair will resolve to a single introduction of the Android-specific changes on the post-#6-merge main.

Test plan

  • npx expo prebuild --platform android produces example/android/app/src/androidTest/java/com/comapeo/core/example/*.kt matching example/tests/android/
  • Generated app/build.gradle contains the AndroidJUnit test runner line and all five androidTestImplementation deps
  • Re-running prebuild produces no further gradle patches (mergeContents is idempotent)
  • ./gradlew connectedAndroidTest (or e2e/run-instrumented-tests.sh) runs the instrumented tests to green
  • android-tests CI job completes within 15 min and shows per-test output

🤖 Generated with Claude Code

gmaclennan added a commit that referenced this pull request Apr 21, 2026
Revert five files to origin/main state. They're re-added on the
Android PR (#9), which is where they belong:

- .github/workflows/android-tests.yml — Node 20 upgrade, emulator
  snapshot-restore crash fix (avoid `input keyevent 82`), test
  timeout, gradle verbose flag
- android/src/main/assets/nodejs-project/index.js — late-client
  replay of started/ready events (the iOS copy at ios/nodejs-project/
  keeps this fix; the Android-bundled copy is Android's to maintain)
- android/src/main/assets/nodejs-project/lib/control-rpc.js — restore
  the file that was deleted as part of Android cleanup
- example/android/.../ServiceLifecycleTest.kt — PID-stability check
  in waitForServiceRunning
- example/android/.../ShutdownPathTest.kt — same PID-stability check

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
gmaclennan and others added 2 commits April 21, 2026 12:43
These five files carried Android-specific changes that had been
authored on the iOS branch but logically belong to this PR. PR #6 has
reverted them to origin/main state; the next commit re-applies them
here so each change is explicitly owned by the Android PR rather than
inherited through the shared merge-base.

This intermediate commit restores the origin/main content so the
re-apply diff is meaningful. No user-visible behavior changes between
this commit and the next one — they cancel each other for these paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
These changes had been authored on the iOS branch but are Android-only;
PR #6 has reverted them so this PR can own them explicitly. Paired with
the previous "rewind" commit so the diff is meaningful against the
post-revert origin/main.

- .github/workflows/android-tests.yml — Node 18 → 20 upgrade; replace
  emulator-runner's `input keyevent 82` (which races snapshot-restore
  and crashes the emulator) with `settings put` to disable animations;
  add per-test `-i` gradle flag and a 15-minute timeout
- android/src/main/assets/nodejs-project/index.js — track a
  `readinessPhase` in the state IPC server and replay `started`/`ready`
  to late-connecting clients (same fix already applied to the iOS copy
  at ios/nodejs-project/index.js)
- android/src/main/assets/nodejs-project/lib/control-rpc.js — delete
  unused stub
- example/tests/android/ServiceLifecycleTest.kt — add a PID-stability
  check to `waitForServiceRunning` so a `:ComapeoCore` process that
  dies at startup is surfaced instead of letting the test hang
- example/tests/android/ShutdownPathTest.kt — same PID-stability check

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@gmaclennan gmaclennan force-pushed the claude/ios-support-graceful-shutdown-GCAXb branch from 9baf548 to 68863c4 Compare April 21, 2026 13:19
@gmaclennan

Copy link
Copy Markdown
Member Author

see #10 instead

@gmaclennan gmaclennan closed this Apr 21, 2026
@gmaclennan gmaclennan deleted the claude/hardcore-gould-e8f235 branch April 21, 2026 14:15
gmaclennan added a commit that referenced this pull request May 6, 2026
Addresses the high- and medium-priority issues from the
review subagent's pass over Phase 1+2a+2b:

Boot transaction lifecycle (was: review #1, #15)
- applyAndEmit now closes bootTx and drains in-flight phase
  spans on STOPPING / STOPPED transitions too — not just
  STARTED / ERROR. stop()-from-STARTING transitions to
  STOPPING (rule 3 of deriveLifecycleState) and bypassed
  both terminals; destroy() forcing STOPPED-via-stopRequested
  did the same. Status mapped: STARTED→ok, ERROR→
  internal_error, STOPPING/STOPPED→cancelled.
- startBootTransaction now passes a TransactionContext
  carrying TracesSamplingDecision(true, 1.0). The previous
  TransactionOptions-only setup didn't actually force
  sampling, so with the SDK default tracesSampleRate=0.0 the
  boot transaction was dropped before reaching the wire.

SentryConfig misconfig handling (was: review #3)
- Both Kotlin and Swift readers used to crash on (DSN-set,
  environment-missing) — meant to be "fail loud" but a stale
  prebuild from before the validation was added would crash
  every cold start with no recovery. Now log loud (System.err
  on Android since android.util.Log isn't mocked on JVM
  tests; NSLog on iOS) and return null (Sentry off). Updated
  test renamed to assert "returns null, doesn't throw".

Span op/description ordering (was: review #19)
- transaction.startChild(op, description) — op is the indexed
  dashboard column. Was passing ("boot", "boot.<phase>"),
  swapped to ("boot.<phase>", human-readable description) so
  the dashboard groups by the phase taxonomy that matches
  the bench backend's boot-spans.js helper.

Plugin idempotency (was: review #7)
- Previously, dropping `props.sentry` from the plugin
  registration left stale meta-data / plist entries from a
  previous prebuild (with `expo prebuild --no-clean`). Plugin
  now passes through a no-Sentry cleanup mod that strips
  every key it owns; consumer-owned keys (e.g. io.sentry.* set
  by @sentry/react-native's plugin) are untouched.

messageerror payload truncation (was: review #8)
- src/sentry.ts now truncates the wrapped error message to
  256 chars before forwarding to captureException. The
  control-frame parser surfaces offending input verbatim,
  which can include arbitrary bytes from a corrupted frame —
  truncating keeps Sentry events small and readable.

IPC + SEND_ERROR_NATIVE breadcrumbs/events (was: review #9, #10)
- NodeJSIPC's onConnectionStateChange callback wired in the
  FGS-side controlIpc construction; emits comapeo.ipc
  breadcrumbs at info (warning on Error). Per §7.4.5.
- SEND_ERROR_NATIVE_TIMEOUT_MS firing now captures a
  level=warning event with timeout:errorNativeForward tag.
  Per §7.4.4.

Logging swallowed surprises (was: review low-priority)
- SentryFgsBridge's empty `catch (t: Throwable) {}` blocks
  now Log.w so debug builds notice swallowed bridge / SDK
  bugs.

Post-init bridge tests (was: review #6)
- New SentryFgsBridgeImplTest spins up a real Sentry hub via
  the cross-platform Sentry.init(SentryOptions) path with an
  in-memory ITransport. Covers: addBreadcrumb (no envelope on
  its own), captureException + captureMessage (envelope
  enqueued), startBootTransaction with global
  tracesSampleRate=0.0 (must still reach transport thanks
  to the TracesSamplingDecision override — regression test
  for the §15 bug above), boot span lifecycle, finishSpan
  with cancelled status, unknown level fallback to INFO.

All Sentry-related tests pass: 25 cases across
SentryConfigTest (8), SentryFgsBridgeTest (10),
SentryFgsBridgeImplTest (7).

Verified locally:
- npm run lint clean
- npx tsc --noEmit clean
- ./gradlew :comapeo-core-react-native:testDebugUnitTest
  passes
- ./gradlew :comapeo-core-react-native:compileDebugKotlin
  succeeds with sentry-android on the compile classpath
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.

1 participant