Skip to content

Commit 1624d5e

Browse files
aapelivclaude
andcommitted
mobile: report bundle identity in OTA manifest requests
Stamp git-hash, native-version, and native-build into the Expo-Extra-Params header via Updates.setExtraParamAsync so the manifest server can identify the running bundle in human-readable terms. The standard expo-current-update-id and expo-runtime-version headers are opaque (UUID / fingerprint hash) by design. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 7338a3e commit 1624d5e

2 files changed

Lines changed: 44 additions & 0 deletions

File tree

app/mobile/app/_layout.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import "react-native-reanimated";
22
import "@/i18n";
33
import "@/service/sentry";
4+
import "@/service/updateExtraParams";
45

56
import {
67
Ubuntu_300Light,
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import * as Sentry from "@sentry/react-native";
2+
import * as Application from "expo-application";
3+
import Constants from "expo-constants";
4+
import * as Updates from "expo-updates";
5+
6+
// Stamp the running bundle's identity into the manifest request via the
7+
// Expo-Extra-Params header (Updates.setExtraParamAsync persists natively and
8+
// the native client serializes the dictionary on every update check). This is
9+
// the protocol's intended escape hatch for human-readable client identifiers —
10+
// the standard expo-current-update-id / expo-runtime-version headers are
11+
// opaque UUIDs / fingerprint hashes by design.
12+
//
13+
// Steady-state accuracy: a bundle sets its own values on first launch, so
14+
// every later check reports them. The only stale check is the very first
15+
// launch after an OTA applies, because expo-updates fires the check before
16+
// JS runs setExtraParamAsync (checkAutomatically=ON_LOAD); from the next
17+
// launch onward it's accurate.
18+
19+
const extra = Constants.expoConfig?.extra as
20+
| { gitHash?: string; appVariant?: string }
21+
| undefined;
22+
const gitHash = extra?.gitHash ?? "unknown";
23+
const appVariant = extra?.appVariant ?? "unknown";
24+
25+
// Only the store-distributed staging and production apps talk to the OTA
26+
// manifest server. The dev tool loads bundles via the dev-launcher deep link
27+
// and local dev has no manifest server, so setting params there is a no-op
28+
// at best.
29+
const enabled = appVariant === "production" || appVariant === "staging";
30+
31+
if (enabled) {
32+
(async () => {
33+
await Updates.setExtraParamAsync("git-hash", gitHash);
34+
await Updates.setExtraParamAsync(
35+
"native-version",
36+
Application.nativeApplicationVersion ?? null,
37+
);
38+
await Updates.setExtraParamAsync(
39+
"native-build",
40+
Application.nativeBuildVersion ?? null,
41+
);
42+
})().catch((err) => Sentry.captureException(err));
43+
}

0 commit comments

Comments
 (0)