From d80674d8eb27c19e30a9f70f29897e6524662ccf Mon Sep 17 00:00:00 2001 From: Miklos Boros Date: Wed, 23 Sep 2026 14:14:08 +0200 Subject: [PATCH 1/3] Expose versionLabel on packages from update_check Map the server's new version_label ("vN") onto RemotePackage.versionLabel so it is persisted with the package and reaches native like label does. Document it in the typings and API docs, and cover it in the E2E mock. Co-Authored-By: Claude Fable 5.1 --- api-compat/compat.ts | 1 + code-push-plugin-testing-framework/script/serverUtil.js | 2 ++ .../typings/code-push-plugin-testing-framework.d.ts | 1 + docs/api-js.md | 3 ++- src/acquisition-sdk/__tests__/acquisition-rest-mock.ts | 1 + src/acquisition-sdk/__tests__/acquisition-sdk.test.ts | 1 + src/acquisition-sdk/acquisition-sdk.ts | 4 ++++ src/acquisition-sdk/types.ts | 3 +++ test/test.ts | 1 + typings/react-native-code-push.d.ts | 6 ++++++ 10 files changed, 22 insertions(+), 1 deletion(-) diff --git a/api-compat/compat.ts b/api-compat/compat.ts index bc4bc427..5c085993 100644 --- a/api-compat/compat.ts +++ b/api-compat/compat.ts @@ -37,6 +37,7 @@ const _isFirstRun: boolean = pkg.isFirstRun; const _isMandatory: boolean = pkg.isMandatory; const _isPending: boolean = pkg.isPending; const _label: string = pkg.label; +const _versionLabel: string | undefined = pkg.versionLabel; const _packageHash: string = pkg.packageHash; const _packageSize: number = pkg.packageSize; diff --git a/code-push-plugin-testing-framework/script/serverUtil.js b/code-push-plugin-testing-framework/script/serverUtil.js index bc3fafec..580c2597 100644 --- a/code-push-plugin-testing-framework/script/serverUtil.js +++ b/code-push-plugin-testing-framework/script/serverUtil.js @@ -125,6 +125,7 @@ function createDefaultResponse() { defaultResponse.target_binary_range = ""; defaultResponse.package_hash = ""; defaultResponse.label = ""; + defaultResponse.version_label = ""; defaultResponse.package_size = 0; defaultResponse.should_run_binary_version = false; defaultResponse.update_app_version = false; @@ -144,6 +145,7 @@ function createUpdateResponse(mandatory, targetPlatform, randomHash) { updateResponse.download_url = "mock.url/v0.1/public/codepush/report_status/download"; updateResponse.is_mandatory = mandatory; updateResponse.label = "mock-update"; + updateResponse.version_label = "v1"; updateResponse.package_hash = "12345-67890"; updateResponse.package_size = 12345; updateResponse.should_run_binary_version = false; diff --git a/code-push-plugin-testing-framework/typings/code-push-plugin-testing-framework.d.ts b/code-push-plugin-testing-framework/typings/code-push-plugin-testing-framework.d.ts index 7502411f..0fb83590 100644 --- a/code-push-plugin-testing-framework/typings/code-push-plugin-testing-framework.d.ts +++ b/code-push-plugin-testing-framework/typings/code-push-plugin-testing-framework.d.ts @@ -309,6 +309,7 @@ declare module 'code-push-plugin-testing-framework/script/serverUtil' { is_disabled: boolean; description: string; label: string; + version_label: string; package_hash: string; is_mandatory: boolean; } diff --git a/docs/api-js.md b/docs/api-js.md index 3b96cfcf..759c8ef7 100644 --- a/docs/api-js.md +++ b/docs/api-js.md @@ -536,9 +536,10 @@ Contains details about an update that has been downloaded locally or already ins - __isFirstRun__: Indicates whether this is the first time the update has been run after being installed. This is useful for determining whether you would like to show a "What's New?" UI to the end user after installing an update. *(Boolean)* - __isMandatory__: Indicates whether the update is considered mandatory. This is the value that was specified in the CLI when the update was released. *(Boolean)* - __isPending__: Indicates whether this update is in a "pending" state. When `true`, that means the update has been downloaded and installed, but the app restart needed to apply it hasn't occurred yet, and therefore, it's changes aren't currently visible to the end-user. *(Boolean)* -- __label__: The internal label automatically given to the update by the CodePush server, such as `v5`. This value uniquely identifies the update within it's deployment. *(String)* +- __label__: The internal label automatically given to the update by the CodePush server. This value uniquely identifies the update within its deployment. *(String)* - __packageHash__: The SHA hash value of the update. *(String)* - __packageSize__: The size of the code contained within the update, in bytes. *(Number)* +- __versionLabel__: The human-readable version label of the update within its deployment, such as `v5`, matching what the Bitrise UI and CLI show. This is `undefined` for updates downloaded by an older version of this SDK, or served by a server that does not yet provide it. *(String)* ###### Methods diff --git a/src/acquisition-sdk/__tests__/acquisition-rest-mock.ts b/src/acquisition-sdk/__tests__/acquisition-rest-mock.ts index 4a67cbe1..5b483248 100644 --- a/src/acquisition-sdk/__tests__/acquisition-rest-mock.ts +++ b/src/acquisition-sdk/__tests__/acquisition-rest-mock.ts @@ -12,6 +12,7 @@ export var latestPackage = { description: "Angry flappy birds", target_binary_range: "1.5.0", label: "2.4.0", + version_label: "v3", is_mandatory: false, is_available: true, update_app_version: false, diff --git a/src/acquisition-sdk/__tests__/acquisition-sdk.test.ts b/src/acquisition-sdk/__tests__/acquisition-sdk.test.ts index 454c2be4..71e3068d 100644 --- a/src/acquisition-sdk/__tests__/acquisition-sdk.test.ts +++ b/src/acquisition-sdk/__tests__/acquisition-sdk.test.ts @@ -34,6 +34,7 @@ var scriptUpdateResult: acquisitionSdk.RemotePackage = { description: latestPackage.description, downloadUrl: latestPackage.download_url, label: latestPackage.label, + versionLabel: latestPackage.version_label, appVersion: latestPackage.target_binary_range, isMandatory: latestPackage.is_mandatory, packageHash: latestPackage.package_hash, diff --git a/src/acquisition-sdk/acquisition-sdk.ts b/src/acquisition-sdk/acquisition-sdk.ts index d570a2c1..30c8be0f 100644 --- a/src/acquisition-sdk/acquisition-sdk.ts +++ b/src/acquisition-sdk/acquisition-sdk.ts @@ -25,6 +25,9 @@ export interface Package { deploymentKey: string; description: string; label: string; + // "vN" release number of the update within its deployment. Undefined for packages + // persisted by an older SDK version, or served by a server that predates the field. + versionLabel?: string; appVersion: string; isMandatory: boolean; packageHash: string; @@ -165,6 +168,7 @@ export class AcquisitionManager { deploymentKey: this._deploymentKey, description: updateInfo.description, label: updateInfo.label, + versionLabel: updateInfo.version_label, appVersion: updateInfo.target_binary_range, isMandatory: updateInfo.is_mandatory, packageHash: updateInfo.package_hash, diff --git a/src/acquisition-sdk/types.ts b/src/acquisition-sdk/types.ts index 798f45ee..d478598f 100644 --- a/src/acquisition-sdk/types.ts +++ b/src/acquisition-sdk/types.ts @@ -34,6 +34,9 @@ export interface UpdateCheckResponse { is_disabled?: boolean; target_binary_range: string; /*generated*/ label?: string; + // Human-readable "vN" release number of the update within its deployment. + // Absent from servers that predate it. + /*generated*/ version_label?: string; /*generated*/ package_hash?: string; package_size?: number; should_run_binary_version?: boolean; diff --git a/test/test.ts b/test/test.ts index 579e4c60..4c3bc67d 100644 --- a/test/test.ts +++ b/test/test.ts @@ -852,6 +852,7 @@ PluginTestingFramework.initializeTests(new RNProjectManager(), supportedTargetPl assert.strictEqual(remotePackage.downloadUrl, updateResponse.download_url); assert.strictEqual(remotePackage.isMandatory, updateResponse.is_mandatory); assert.strictEqual(remotePackage.label, updateResponse.label); + assert.strictEqual(remotePackage.versionLabel, updateResponse.version_label); assert.strictEqual(remotePackage.packageHash, updateResponse.package_hash); assert.strictEqual(remotePackage.packageSize, updateResponse.package_size); assert.strictEqual(remotePackage.deploymentKey, targetPlatform.getDefaultDeploymentKey()); diff --git a/typings/react-native-code-push.d.ts b/typings/react-native-code-push.d.ts index bd412d11..4bc0476f 100644 --- a/typings/react-native-code-push.d.ts +++ b/typings/react-native-code-push.d.ts @@ -75,6 +75,12 @@ export interface Package { */ label: string; + /** + * The human-readable version label of the update within its deployment, such as `v5`, matching what the Bitrise UI and CLI show. + * Undefined for updates downloaded by an older version of this SDK, or served by a server that does not yet provide it. + */ + versionLabel?: string; + /** * The SHA hash value of the update. */ From c089a82ae8cb5c78369ddf7bd613f28d638f4dbb Mon Sep 17 00:00:00 2001 From: Miklos Boros Date: Wed, 23 Sep 2026 14:42:28 +0200 Subject: [PATCH 2/3] Trim versionLabel comments Co-Authored-By: Claude Fable 5.1 --- src/acquisition-sdk/acquisition-sdk.ts | 3 +-- src/acquisition-sdk/types.ts | 2 -- typings/react-native-code-push.d.ts | 4 ++-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/acquisition-sdk/acquisition-sdk.ts b/src/acquisition-sdk/acquisition-sdk.ts index 30c8be0f..98f080f4 100644 --- a/src/acquisition-sdk/acquisition-sdk.ts +++ b/src/acquisition-sdk/acquisition-sdk.ts @@ -25,8 +25,7 @@ export interface Package { deploymentKey: string; description: string; label: string; - // "vN" release number of the update within its deployment. Undefined for packages - // persisted by an older SDK version, or served by a server that predates the field. + // Optional because packages persisted by an older SDK have no versionLabel. versionLabel?: string; appVersion: string; isMandatory: boolean; diff --git a/src/acquisition-sdk/types.ts b/src/acquisition-sdk/types.ts index d478598f..cdeb8346 100644 --- a/src/acquisition-sdk/types.ts +++ b/src/acquisition-sdk/types.ts @@ -34,8 +34,6 @@ export interface UpdateCheckResponse { is_disabled?: boolean; target_binary_range: string; /*generated*/ label?: string; - // Human-readable "vN" release number of the update within its deployment. - // Absent from servers that predate it. /*generated*/ version_label?: string; /*generated*/ package_hash?: string; package_size?: number; diff --git a/typings/react-native-code-push.d.ts b/typings/react-native-code-push.d.ts index 4bc0476f..c965dc51 100644 --- a/typings/react-native-code-push.d.ts +++ b/typings/react-native-code-push.d.ts @@ -76,8 +76,8 @@ export interface Package { label: string; /** - * The human-readable version label of the update within its deployment, such as `v5`, matching what the Bitrise UI and CLI show. - * Undefined for updates downloaded by an older version of this SDK, or served by a server that does not yet provide it. + * The `vN` release label of the update within its deployment, as shown in the Bitrise UI and CLI. + * Undefined for updates downloaded by an older SDK version or from a server that does not send it. */ versionLabel?: string; From 4601948c762643274a996504233fe1291f4f41a1 Mon Sep 17 00:00:00 2001 From: Miklos Boros Date: Wed, 23 Sep 2026 14:55:06 +0200 Subject: [PATCH 3/3] Assert versionLabel on the downloaded package, fix docs example The download E2E test now checks that label and versionLabel come back on the package read from native storage, not only on the update_check response. The checkForUpdate docs example no longer calls the CodePush label "v1". Co-Authored-By: Claude Fable 5.1 --- docs/api-js.md | 2 +- test/test.ts | 33 +++++++++++++++++++++++++++------ 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/docs/api-js.md b/docs/api-js.md index 759c8ef7..9551b01d 100644 --- a/docs/api-js.md +++ b/docs/api-js.md @@ -217,7 +217,7 @@ codePush.checkForUpdate(deploymentKey: String = null, handleBinaryVersionMismatc Queries the CodePush service to see whether the configured app deployment has an update available. By default, it will use the deployment key that is configured in your `Info.plist` file (iOS), or `strings.xml` file (Android), but you can override that by specifying a value via the optional `deploymentKey` parameter. This can be useful when you want to dynamically "redirect" a user to a specific deployment, such as allowing "early access" via an easter egg or a user setting switch. Second optional parameter `handleBinaryVersionMismatchCallback` is an optional callback function that can be used to notify user if there are any binary update. -E.g. consider a use-case where currently installed binary version is 1.0.1 with label(codepush label) v1. Later native code was changed in the dev cycle and binary version was updated to 1.0.2. When code-push update check is triggered we ignore updates having binary version mismatch (because the update is not targeting to the binary version of currently installed app). In this case installed app (1.0.1) will ignore the update targeting version 1.0.2. You can use `handleBinaryVersionMismatchCallback` to provide a hook to handle such situations. +E.g. consider a use-case where the currently installed binary version is 1.0.1 and it runs the CodePush update whose `versionLabel` is `v1`. Later native code was changed in the dev cycle and binary version was updated to 1.0.2. When code-push update check is triggered we ignore updates having binary version mismatch (because the update is not targeting to the binary version of currently installed app). In this case installed app (1.0.1) will ignore the update targeting version 1.0.2. You can use `handleBinaryVersionMismatchCallback` to provide a hook to handle such situations. **NOTE:** Be cautious to use Alerts within this callback if you are developing iOS application, due to [App Store](https://developer.apple.com/app-store/review/guidelines/) review process: diff --git a/test/test.ts b/test/test.ts index 4c3bc67d..8a511e05 100644 --- a/test/test.ts +++ b/test/test.ts @@ -916,17 +916,38 @@ PluginTestingFramework.initializeTests(new RNProjectManager(), supportedTargetPl () => { TestBuilder.it("remotePackage.download.success", false, (done: Mocha.Done) => { - ServerUtil.updateResponse = { update_info: ServerUtil.createUpdateResponse(false, targetPlatform) }; + const updateResponse = ServerUtil.createUpdateResponse(false, targetPlatform); + ServerUtil.updateResponse = { update_info: updateResponse }; /* pass the path to any file for download (here, index.js) to make sure the download completed callback is invoked */ ServerUtil.updatePackagePath = path.join(TestConfig.templatePath, "index.js"); - projectManager.runApplication(TestConfig.testRunDirectory, targetPlatform); + let sawUpdateAvailable = false; + let finished = false; + ServerUtil.testMessageCallback = (requestBody: any) => { + if (finished) { + return; + } + try { + if (requestBody.message === ServerUtil.TestMessage.CHECK_UPDATE_AVAILABLE) { + sawUpdateAvailable = true; + return; + } + assert.strictEqual(requestBody.message, ServerUtil.TestMessage.DOWNLOAD_SUCCEEDED); + assert(sawUpdateAvailable, "download reported before the update check"); + // The local package is read back from native storage, so this covers the metadata round trip. + const localPackage: any = requestBody.args[0]; + assert.strictEqual(localPackage.label, updateResponse.label); + assert.strictEqual(localPackage.versionLabel, updateResponse.version_label); + finished = true; + done(); + } catch (e) { + finished = true; + done(e); + } + }; - ServerUtil.expectTestMessages([ - ServerUtil.TestMessage.CHECK_UPDATE_AVAILABLE, - ServerUtil.TestMessage.DOWNLOAD_SUCCEEDED]) - .then(() => { done(); }, (e) => { done(e); }); + projectManager.runApplication(TestConfig.testRunDirectory, targetPlatform); }); TestBuilder.it("remotePackage.download.error", false,