react-native-code-push

repository·master·Indexed 41 days ago

https://github.com/soomgo-mobile/react-native-code-push

A fork of react-native-code-push (version 13.3.1) that supports the React Native New Architecture (TurboModules) and enables Over-The-Air (OTA) updates via static hosting. It includes the @bravemobile/code-push-cli for bundling, releasing, and managing updates without requiring AppCenter or a dedicated API server, featuring binary patching and asset diff archives to reduce download sizes.

Tokens
49.2K
Snippets
141
Records
203
Agent score
47%

What's inside react-native-code-push

  1. Overview of @bravemobile/react-native-code-push

    master
    The @bravemobile/react-native-code-push library provides Over-The-Air (OTA) update capabilities for React Native applications. Unlike the standard implementation, it allows for CodePush without a dedicated API server by using static hosting (like AWS S3). It is built on top of microsoft/react-native-code-push but has migrated the native module to TurboModule for New Architecture support. It also supports publishing binary patch and asset diff releases to minimize download sizes.
  2. Understand the E2E Test Execution Process

    master

    The E2E test runner (e2e/run.ts) executes a series of automated phases to validate CodePush functionality across different scenarios. The process is divided into several distinct phases:

    1. Phase 1 — Basic Flow (flows/): Validates standard app launch, restart stability, and successful update installation (detecting UPDATED! and specific metadata).
    2. Phase 2 — Rollback to Binary (flows-rollback/): Tests the ability to automatically roll back to the original binary version when a release is disabled via npx code-push update-history -e false.
    3. Phase 3 — Partial Rollback (flows-partial-rollback/): Verifies rolling back from one update (e.g., v1.0.2) to a previous update (e.g., v1.0.1) rather than the binary.
    4. Phase 4 — Optional Install Mode (flows-optional/): Validates non-mandatory updates based on app lifecycle events (relaunch, restart button, background/foreground transitions) and the --minimum-background-duration setting.
    5. Phase 5 — Binary Patch Updates (flows-binary-patch/): Tests the application of patches directly onto the app binary. It validates successful patch installation and fallback mechanisms (to full archives) when patches are corrupted, mismatched, or invalid.
    6. Phase 6 — Asset Diff Updates (flows-asset-diff/): Tests efficient asset updates by downloading only the differences (diffPackages). It validates the merging of assets and fallback logic when diffs fail due to corruption or manifest mismatches.
  3. Use the @bravemobile/code-push-cli for OTA updates

    master

    The @bravemobile/code-push-cli is a CLI tool for @bravemobile/react-native-code-push that allows you to bundle, deploy, and manage Over-The-Air (OTA) updates without requiring an API server like AppCenter. It enables integration with your own storage backends (e.g., Firebase, Supabase, S3) via a configuration file.

    npx code-push
  4. Verify Asset Diff and Fallback Logic

    master

    The asset-diffing mechanism allows clients to download only the differences between updates. The test runner verifies the integrity of this process by asserting the archive types requested from the mock server:

    • [asset-diff]: Successful diff install.
    • [binary-patch]: Client cannot use the diff (e.g., starting from binary).
    • [asset-diff, binary-patch]: Asset-side failure (corrupted asset) triggers fallback to patch.
    • [asset-diff, full]: Bundle-side failure (manifest/bundle mismatch) triggers fallback to full.

    Key Verification Points:

    • Merge Integrity: Verifies that merged contents reproduce the expected package hash.
    • Deletion Manifest: Ensures the hotcodepush.json deletion manifest correctly removes dropped assets.
    • Error Handling: Verifies that corrupted assets or invalid manifests trigger the appropriate fallback chain.
  5. Understand the Release History structure

    master

    The release history is a JSON object where each key is an app version. This structure allows the client to determine which OTA (Over-The-Air) update to download based on the current binary version.

    Key fields within a version object include:

    • enabled: Boolean indicating if the update is active.
    • mandatory: Boolean indicating if the update must be installed before the app can be used.
    • downloadUrl: The URL to the bundle.
    • packageHash: The hash of the package.
    • rollout: Percentage of users to receive the update.
    • binaryPatchDownloadUrl: Present only if the release was published using --binary-bundle-path.
    • diffPackages: An object containing asset diff archives. Each entry maps a packageHash (the version the archive was diffed against) to its download URL.
    {
      "1.0.0": {
        "enabled": true,
        "mandatory": false,
        "downloadUrl": "",
        "packageHash": ""
      },
      "1.0.1": {
        "enabled": true,
        "mandatory": false,
        "downloadUrl": "https://storage.example.com/bundles/ios/staging/a1b2c3...",
        "packageHash": "a1b2c3...",
        "rollout": 100,
        "minimumBackgroundDuration": 600
      }
    }
  6. Run both iOS and Android platforms simultaneously

    master

    Using --platform both executes iOS and Android scenarios side-by-side from a single checkout.

    Requirements & Behavior:

    • Requires both a booted iOS Simulator and a running Android emulator/device to be active at the same time.
    • Isolation: Each platform maintains its own app entry (App.ios.tsx / App.android.tsx), mock server port (18081 / 18082), artifact records, and CLI output roots under the app's build/ directory. Nothing is serialized between pipelines.
    • Shared Resources: Native builds share the app directory and node_modules. The watchman reset and library sync are performed once at the start.
    • Output: Both pipelines write to a single terminal. Output is prefixed with platform identifiers (e.g., via runner phase banners or mock server lines).
    • Failure Handling: A failure on one platform does not stop the other. The command exits with a non-zero status if either platform fails.
    • Performance Note: Running both platforms shares the machine's CPU between two simulators, two bundlers, and two Maestro drivers. If tests flake due to load, use --exclude-timing-sensitive and --retry-count to stabilize.
  7. Understand onUpdateArchiveResult and UpdateArchiveResult

    master

    When performing a diff update, CodePush may attempt to use a patch archive before falling back to a full archive. onUpdateArchiveResult provides details on this process.

    Archive Types:

    • asset-diff: Differences relative to the previous OTA update.
    • binary-patch: Differences relative to the bundle embedded in the app binary.
    • full: The entire update.

    UpdateArchiveResult Fields:

    • status: Either "applied" (patch worked) or "fallback" (patch failed, full archive used).
    • archive: The UpdateArchive used or the last attempted patch archive.
    • fallbackReason: The ArchiveFallbackReason explaining why a full archive was needed (only if status is "fallback").
    • totalDurationMs: Time from the first patch download start to the end of the last patch attempt (excludes full archive download time).
    • attempts: An array of UpdateArchiveAttempt[] containing all attempted patch archives in order.
  8. Understand the E2E Test Runner Phases

    master

    The E2E test runner (e2e/run.ts) executes a series of automated testing phases to verify CodePush functionality across different scenarios. Understanding these phases helps in interpreting test results and knowing which flows are being covered:

    1. Basic Flows (flows/): Verifies app launch, restart stability, and standard update installation (syncing to a specific version).
    2. Rollback to Binary (flows-rollback/): Verifies that if a release is disabled via npx code-push update-history -e false, the app automatically rolls back to the version bundled in the binary.
    3. Partial Rollback (flows-partial-rollback/): Verifies that if an update is disabled, the app rolls back to the previous successful update rather than the binary.
    4. Optional Install Modes (flows-optional/): Tests how non-mandatory updates (-m false) behave under different lifecycle events (relaunch, restart button, background/foreground transitions) and how --minimum-background-duration affects application timing.
    5. Binary Patch Updates (flows-binary-patch/): Tests the efficiency and reliability of binary patches. It verifies successful patch application, handling of image assets, and fallback mechanisms when patches are stale, corrupt, or built for the wrong platform.
    6. Asset Diff Updates (flows-asset-diff/): Tests the advanced asset-diffing mechanism. It verifies that the client can download only changed assets (diff archives) and correctly merges them, while also testing fallbacks to patch or full archives if the diff is corrupted or the manifest is invalid.
  9. Run E2E tests on both iOS and Android simultaneously

    master

    Using the --platform both flag executes iOS and Android scenarios in parallel from a single checkout. This can nearly halve the wall-clock time but increases CPU load on the host machine.

    Key Characteristics

    • Parallelism: Each platform runs its own device, app entry points (App.ios.tsx, App.android.tsx), mock server ports (18081, 18082), and artifact recordings. There is no serialization between the two pipelines except for the shared native build (which shares the app directory and node_modules), Watchman initialization, and library synchronization.
    • Output: Logs from both platforms are interleaved in the same terminal. While runner banners and mock server logs are prefixed with the platform name, raw outputs from gradle, xcodebuild, and maestro will be mixed.
    • Error Handling: If one platform fails, the other continues to run. The command will exit with a non-zero code if either platform fails.
    • Resource Management: Because running two simulators, two bundlers, and two Maestro drivers simultaneously is resource-intensive, timing-sensitive scenarios might become unstable. If you encounter timing issues, use --exclude-timing-sensitive or --retry-count to stabilize the run.
    npm run e2e -- --app RN0840 --platform both
  10. Understand the CodePush architecture

    master

    The CodePush plugin consists of two distinct layers:

    1. JavaScript module: An importable/requireable module used to interact with the CodePush service during runtime (e.g., checking for updates, inspecting metadata).
    2. Native API (Objective-C and Java): A layer that allows the React Native app host to bootstrap itself using the correct JavaScript bundle location.
  11. Verify Binary Patch Fallback Behavior

    master

    The test suite includes scenarios to ensure that if a binary patch fails, the library correctly falls back to a full archive install. The runner distinguishes these by asserting which archive type the mock server was requested:

    • [binary-patch]: Successful patch install.
    • [binary-patch, full]: Fallback from a patch to a full archive (e.g., due to a stale base bundle or corrupt compressed body).
    • [full]: A release published without a patch.

    Key scenarios tested include:

    • Stale Base: Patch computed against a stale base bundle falls back to full.
    • Corruption: Patch with a corrupt compressed body or header falls back.
    • Platform Mismatch: Patch built for the wrong platform falls back.
    • Manifest Mismatch: Patch that restores a bundle not described in its manifest falls back.