7.2 KiB
Mobile RUM SDK — CI notes
How .github/workflows/mobile-sdk-e2e.yml runs. Model B: self-contained, no secrets — each
device job stands up its own OpenObserve on the runner and points the app at it, exactly like
playwright.yml does for the web tests.
Why self-contained (and why it's possible)
- RUM ingestion is OSS — a plain OpenObserve accepts the data, so no special/enterprise cluster.
- The other test workflows already run a throwaway OpenObserve on the runner with the well-known
defaults (
root@example.com/Complexpass#123/ orgdefault/localhost:5080) as plain values — no secrets. This workflow does the same. - Result: every run is isolated (its own instance), so the Android and iOS jobs run in parallel with no collision — this removes the shared-service problem entirely.
How the app gets pointed at the local instance (build-time config injection)
Each app reads its RUM target from a build-time-overridable config. The committed values are
placeholders (REPLACE_ME / openobserve.example.com); CI regenerates them to point at the
local instance, and mints its own RUM token per run:
| App | Mechanism | Regenerated by |
|---|---|---|
| RN (o2-rum-tester) | inline @gen:* consts in App.tsx (NOT a JSON import — that breaks the Hermes release upload) |
scripts/gen-config.js (postinstall, seds App.tsx from O2_RUM_*) |
| Android-native | BuildConfig.OO_* fields |
build.gradle.kts reads O2_RUM_* via System.getenv |
| iOS-native | Sources/GeneratedConfig.swift |
gen-config.sh (reads O2_RUM_*) |
Env the CI job sets before building: O2_RUM_HOST, O2_RUM_ORG, O2_RUM_TOKEN, O2_RUM_ENV.
With none set, every generator keeps the committed placeholders — so set these (or .env) for a
real local target.
Networking
- Android emulator can't see the host's
localhost; it reaches the host at10.0.2.2. So the app posts tohttp://10.0.2.2:5080while the suite's API/UI checks (on the host) uselocalhost:5080. - iOS simulator shares the host network → app and suite both use
localhost:5080. - Cleartext HTTP to the local instance: Android manifests set
usesCleartextTraffic="true"; the RN iOS build adds an ATSNSAllowsLocalNetworkingkey at build time (committed source stays HTTPS).
Testing the latest SDK (version injection)
The fixture apps pin the SDK version (RN @openobserve/mobile-react-native 0.1.1; the Android
and iOS native SDKs 0.1.0 — separate packages, independently versioned) so ordinary runs are
reproducible. But on a release we want to test the new version, not the frozen pin. So the SDK
version is a build input, defaulting to the pin:
| App | Override mechanism | Env |
|---|---|---|
| RN | workflow npm install @openobserve/mobile-react-native@<ver> before build |
O2_RN_SDK_VERSION |
| Android-native | build.gradle.kts: System.getenv("O2_ANDROID_SDK_VERSION") ?: "<pin>" |
O2_ANDROID_SDK_VERSION |
| iOS-native | workflow sed of project.yml exactVersion before xcodegen |
O2_IOS_SDK_VERSION |
The mobile-sdk-release dispatch carries {sdk, version}; the "Resolve SDK version override" step
maps it to the right env so a release builds + tests against the latest SDK. A manual run can do the
same via the sdk + sdk_version inputs. Unset → the committed pin (reproducible).
Triggers
A decide job turns each event into run_android / run_ios, so device jobs run only where relevant
(macOS runners are scarce):
| Trigger | Fires when | Runs |
|---|---|---|
repository_dispatch (mobile-sdk-release) |
an SDK repo publishes a release (cross-repo) | the SDK's platform (sdk = ios→iOS, android→Android, react-native→both) |
pull_request (paths) |
a PR touches Android/shared mobile-RUM code | preflight + Android device jobs (iOS is label-gated) |
pull_request + mobile-e2e label |
full pre-merge gate | preflight + both platforms |
workflow_dispatch |
manual; run_full_suite=true + platforms |
preflight + the chosen platforms |
The cross-repo trigger
The SDK source lives in other repos, so an SDK change never appears as a PR here. Each SDK repo
sends a dispatch on release. The dispatch workflows are already opened as PRs on the three SDK repos
(openobserve-react-native-rum, openobserve-sdk-android, openobserve-sdk-ios); each needs a repo
secret OO_E2E_DISPATCH_TOKEN = a token that can dispatch on openobserve/openobserve (classic PAT
with repo, or fine-grained with Contents:read + Actions:write). The call they make:
- name: Dispatch mobile-sdk-release
env:
GH_TOKEN: ${{ secrets.OO_E2E_DISPATCH_TOKEN }}
run: |
echo "{\"event_type\":\"mobile-sdk-release\",\"client_payload\":{\"sdk\":\"ios\",\"version\":\"$VER\"}}" \
| gh api repos/openobserve/openobserve/dispatches --input -
sdk is react-native / android / ios per repo; the decide job maps it to the platform(s) to run.
Runners & the OpenObserve build
OpenObserve is downloaded and run natively by the device jobs so the app posts real RUM to it.
The Linux side first looks for the exact merge-SHA release-ci binary + frontend already produced
by playwright.yml; when both artifacts exist it reuses them instead of compiling OpenObserve again.
Mobile-only/manual/SDK-release runs may not have matching Playwright artifacts, so the workflow keeps
an exact-source local build fallback:
resolve_oo_linuxfinds an exact-SHA Playwright artifact;build_oo_linux(ubuntu-latest) builds the Linux binary + UI only when none exists.build_oo_macos(macos-15) still compiles the macOS binary + UI: a Linux binary cannot run on the iOS simulator host, and no other PR workflow produces an exact-SHA Darwin artifact.- Android →
ubuntu-latest+ KVM emulator, runs the Linux binary; app reaches it at10.0.2.2. - iOS →
macos-15+ simulator (pinned Xcode 16.4), runs the macOS binary; app useslocalhost. merge_reportscombines both platforms into one HTML report (survives a failed device job).
release-ci sets debug-assertions=true, which puts rust-embed in disk-read mode. The resolver therefore
requires the matching Playwright frontend-dist-<sha> alongside release-ci-binary-<sha>; fallback
builds upload oo-<os>-frontend. Device jobs extract either source to /tmp/dist so /web serves and
the dashboard-UI tests run.
Status
Green end-to-end on GitHub runners (Android + iOS device jobs + merge). Known SDK build fixes and the cleartext-HTTP handling are recorded below / in the git history.
Validated locally
- Suite parses (
playwright --list): 42 tests, 4 platforms (+ sdk-drift project). gen-config.js/gen-config.shkeep the committed placeholders when no env is set, and substituteO2_RUM_*when set.- Android/iOS native config wiring is in place (
BuildConfig.OO_*,GeneratedConfig).
Optional later
- Host next to the enterprise/alpha pipeline (mobile RUM is enterprise-flavored) — but note macOS is free here (public) and billed there (private).
- A per-run RUM
run-idattribute is no longer needed for isolation (each run has its own instance); only relevant if you ever move back to a shared cluster.