Files
openobserve/tests/mobile-testing/docs/CI-NOTES.md
T

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 / org default / 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 at 10.0.2.2. So the app posts to http://10.0.2.2:5080 while the suite's API/UI checks (on the host) use localhost: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 ATS NSAllowsLocalNetworking key 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_linux finds 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 at 10.0.2.2.
  • iOS → macos-15 + simulator (pinned Xcode 16.4), runs the macOS binary; app uses localhost.
  • merge_reports combines 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.sh keep the committed placeholders when no env is set, and substitute O2_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-id attribute is no longer needed for isolation (each run has its own instance); only relevant if you ever move back to a shared cluster.