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

127 lines
7.2 KiB
Markdown

# 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:
```yaml
- 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.