From 7cc5baea154f16d32730bc47f1a14c6e04d6d2d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliv=C3=A9r=20Falvai?= Date: Thu, 5 Nov 2026 11:01:51 +0100 Subject: [PATCH 1/2] Send the binary hash in update_check on Android too Co-Authored-By: Claude Sonnet 5.5 --- CodePush.js | 17 ++++------------ src/__tests__/build-query-package.test.ts | 24 +++++++++++++++++++++++ src/build-query-package.ts | 20 +++++++++++++++++++ 3 files changed, 48 insertions(+), 13 deletions(-) create mode 100644 src/__tests__/build-query-package.test.ts create mode 100644 src/build-query-package.ts diff --git a/CodePush.js b/CodePush.js index 86093d96..a142a4bc 100644 --- a/CodePush.js +++ b/CodePush.js @@ -1,4 +1,5 @@ import { AcquisitionManager as Sdk, DownloadStatus } from "./lib/commonjs/acquisition-sdk/acquisition-sdk"; +import { buildQueryPackage } from "./lib/commonjs/build-query-package"; import { onceUntilRejected } from "./lib/commonjs/once-until-rejected"; import { Alert } from "./AlertAdapter"; import requestFetchAdapter from "./request-fetch-adapter"; @@ -111,15 +112,7 @@ async function checkForUpdate(deploymentKey = null, handleBinaryVersionMismatchC * to send the app version to the server, since we are interested * in any updates for current binary version, regardless of hash. */ - let queryPackage; - if (localPackage) { - queryPackage = localPackage; - } else { - queryPackage = { appVersion: config.appVersion }; - if (Platform.OS === "ios" && config.packageHash) { - queryPackage.packageHash = config.packageHash; - } - } + const queryPackage = buildQueryPackage(localPackage, config); log.info(`Checking for update. ${logFields({ appVersion: queryPackage.appVersion, ...packageLogFields(queryPackage), deploymentKey: config.deploymentKey })}`); const update = await sdk.queryUpdateWithCurrentPackage(queryPackage); @@ -137,10 +130,8 @@ async function checkForUpdate(deploymentKey = null, handleBinaryVersionMismatchC * bug in the server, but we're adding this check just to double-check that the * client app is resilient to a potential issue with the update check. * 4) The server said there is an update, but the update's hash is the same as that - * of the binary's currently running version. This should only happen in Android - - * unlike iOS, we don't attach the binary's hash to the updateCheck request - * because we want to avoid having to install diff updates against the binary's - * version, which we can't do yet on Android. + * of the binary's currently running version. The binary's hash is sent in the + * updateCheck request, so the server can answer with the binary itself. */ if (!update) { log.info("No update available."); diff --git a/src/__tests__/build-query-package.test.ts b/src/__tests__/build-query-package.test.ts new file mode 100644 index 00000000..b744da24 --- /dev/null +++ b/src/__tests__/build-query-package.test.ts @@ -0,0 +1,24 @@ +import * as assert from 'assert'; + +import { buildQueryPackage } from '../build-query-package'; + +describe('buildQueryPackage', () => { + it('sends the binary hash when no update is installed', () => { + assert.deepStrictEqual(buildQueryPackage(null, { appVersion: '1.0.0', packageHash: 'abc' }), { + appVersion: '1.0.0', + packageHash: 'abc', + }); + }); + + it('omits the hash when the binary has none', () => { + assert.deepStrictEqual(buildQueryPackage(undefined, { appVersion: '1.0.0', packageHash: null }), { + appVersion: '1.0.0', + }); + assert.deepStrictEqual(buildQueryPackage(undefined, { appVersion: '1.0.0' }), { appVersion: '1.0.0' }); + }); + + it('uses the installed package as is', () => { + const localPackage = { appVersion: '1.0.0', packageHash: 'installed' }; + assert.strictEqual(buildQueryPackage(localPackage, { appVersion: '1.0.0', packageHash: 'abc' }), localPackage); + }); +}); diff --git a/src/build-query-package.ts b/src/build-query-package.ts new file mode 100644 index 00000000..aac45eb7 --- /dev/null +++ b/src/build-query-package.ts @@ -0,0 +1,20 @@ +export interface QueryPackage { + appVersion?: string; + packageHash?: string; + [key: string]: unknown; +} + +export function buildQueryPackage( + localPackage: QueryPackage | null | undefined, + config: { appVersion: string; packageHash?: string | null }, +): QueryPackage { + if (localPackage) { + return localPackage; + } + const queryPackage: QueryPackage = { appVersion: config.appVersion }; + // Null in Android debug builds, which have no binary hash to report. + if (config.packageHash) { + queryPackage.packageHash = config.packageHash; + } + return queryPackage; +} From a38903e45eb5e811c7fd3a0e91f1e5ce943d9644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliv=C3=A9r=20Falvai?= Date: Thu, 5 Nov 2026 11:02:16 +0100 Subject: [PATCH 2/2] Drop the unused Platform import Co-Authored-By: Claude Sonnet 5.5 --- CodePush.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodePush.js b/CodePush.js index a142a4bc..51f63799 100644 --- a/CodePush.js +++ b/CodePush.js @@ -3,7 +3,7 @@ import { buildQueryPackage } from "./lib/commonjs/build-query-package"; import { onceUntilRejected } from "./lib/commonjs/once-until-rejected"; import { Alert } from "./AlertAdapter"; import requestFetchAdapter from "./request-fetch-adapter"; -import { AppState, NativeEventEmitter, Platform } from "react-native"; +import { AppState, NativeEventEmitter } from "react-native"; import log, { logFields, packageLogFields, redactUrl } from "./logging"; import hoistStatics from 'hoist-non-react-statics';