Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

React Native CodePush is a native module that enables over-the-air updates for React Native apps. It consists of native implementations for iOS (Objective-C), Android (Java), and Windows (C++), unified through a JavaScript bridge layer.

## Development Commands

### Testing
- `npm test` - Run all tests with TypeScript compilation
- `npm run test:android` - Run Android-specific tests
- `npm run test:ios` - Run iOS-specific tests
- `npm run test:setup-android` - Set up Android emulator for testing
- `npm run test:setup-ios` - Set up iOS simulator for testing

### Build
- `npm run build` - Build TypeScript tests to bin/ directory
- `npm run tsc` - TypeScript compilation

### Platform Testing
- Tests run on actual emulators/simulators with real React Native apps
- Test apps are created dynamically in `test/` directory
- Both old and new React Native architecture testing supported

## Architecture

### Core Components
- **JavaScript Bridge** (`CodePush.js`): Main API layer exposing update methods
- **Native Modules**: Platform-specific implementations handling file operations, bundle management
- **Update Manager**: Handles download, installation, and rollback logic
- **Acquisition SDK**: Manages server communication and update metadata

### Platform Structure
- **iOS**: `ios/` - Objective-C implementation with CocoaPods integration
- **Android**: `android/` - Java implementation with Gradle plugin
- **Windows**: `windows/` - C++ implementation for Windows React Native
- **JavaScript**: Root level - TypeScript definitions and bridge code

### Key Patterns
- **Higher-Order Component**: `codePush()` wrapper for automatic update management
- **Promise-based Native Bridge**: All native operations return promises
- **Platform Abstraction**: Unified JavaScript API with platform-specific implementations
- **Error Handling**: Automatic rollback on failed updates with telemetry

### Testing Framework
- **Custom Test Runner**: TypeScript-based test framework in `test/`
- **Emulator Management**: Automated setup and teardown of test environments
- **Real App Testing**: Creates actual React Native apps for integration testing
- **Scenario Testing**: Update, rollback, and error scenarios

### Build Integration
- **Android Gradle Plugin**: Automatically generates bundle hashes and processes assets
- **iOS CocoaPods**: Manages native dependencies and build configuration
- **Bundle Processing**: Automated zip creation and hash calculation for OTA updates

## Development Workflow

1. **Making Changes**: Edit native code or JavaScript bridge
2. **Testing**: Run platform-specific tests with real emulators
3. **Integration**: Test with actual React Native apps via test framework
4. **Validation**: Ensure compatibility with both RN architectures

## Key Files
- `CodePush.js` - Main JavaScript API
- `test/TestRunner.ts` - Test framework entry point
- `android/build.gradle` - Android build configuration
- `ios/CodePush.podspec` - iOS CocoaPods specification
- `plugin.xml` - Cordova plugin configuration

## Special Considerations
- Native module requires platform-specific knowledge (iOS/Android/Windows)
- Testing requires emulator setup and can be time-intensive
- Updates must be backward compatible with existing app installations
- Bundle hash calculation is critical for update integrity
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ local.properties
.cxx/
*.keystore
!debug.keystore
.kotlin/

# node.js
#
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
module.exports = {
arrowParens: 'avoid',
bracketSameLine: true,
bracketSpacing: false,
singleQuote: true,
trailingComma: 'all',
};
45 changes: 45 additions & 0 deletions Examples/CodePushDemo/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
*/

import { NewAppScreen } from '@react-native/new-app-screen';
import { StatusBar, StyleSheet, useColorScheme, View } from 'react-native';
import {
SafeAreaProvider,
useSafeAreaInsets,
} from 'react-native-safe-area-context';

function App() {
const isDarkMode = useColorScheme() === 'dark';

return (
<SafeAreaProvider>
<StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} />
<AppContent />
</SafeAreaProvider>
);
}

function AppContent() {
const safeAreaInsets = useSafeAreaInsets();

return (
<View style={styles.container}>
<NewAppScreen
templateFileName="App.tsx"
safeAreaInsets={safeAreaInsets}
/>
</View>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
},
});

export default App;
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,9 @@ gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1'
gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0'
gem 'xcodeproj', '< 1.26.0'
gem 'concurrent-ruby', '< 1.3.4'

# Ruby 3.4.0 has removed some libraries from the standard library.
gem 'bigdecimal'
gem 'logger'
gem 'benchmark'
gem 'mutex_m'
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ GEM
base64
nkf
rexml
activesupport (7.2.2.1)
activesupport (7.2.2.2)
base64
benchmark (>= 0.3)
bigdecimal
Expand All @@ -23,9 +23,9 @@ GEM
httpclient (~> 2.8, >= 2.8.3)
json (>= 1.5.1)
atomos (0.1.3)
base64 (0.2.0)
benchmark (0.4.0)
bigdecimal (3.1.9)
base64 (0.3.0)
benchmark (0.5.0)
bigdecimal (3.3.1)
claide (1.1.0)
cocoapods (1.15.2)
addressable (~> 2.8)
Expand Down Expand Up @@ -66,34 +66,34 @@ GEM
cocoapods-try (1.2.0)
colored2 (3.1.2)
concurrent-ruby (1.3.3)
connection_pool (2.5.0)
drb (2.2.1)
connection_pool (2.5.4)
drb (2.2.3)
escape (0.0.4)
ethon (0.16.0)
ethon (0.15.0)
ffi (>= 1.15.0)
ffi (1.17.1)
ffi (1.17.2)
fourflusher (2.3.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
httpclient (2.9.0)
mutex_m
i18n (1.14.7)
concurrent-ruby (~> 1.0)
json (2.10.1)
logger (1.6.6)
minitest (5.25.4)
json (2.15.2)
logger (1.7.0)
minitest (5.26.0)
molinillo (0.8.0)
mutex_m (0.3.0)
nanaimo (0.3.0)
nap (1.1.0)
netrc (0.11.0)
nkf (0.2.0)
public_suffix (4.0.7)
rexml (3.4.1)
rexml (3.4.4)
ruby-macho (2.5.1)
securerandom (0.4.1)
typhoeus (1.4.1)
ethon (>= 0.9.0)
typhoeus (1.5.0)
ethon (>= 0.9.0, < 0.16.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
xcodeproj (1.25.1)
Expand All @@ -109,12 +109,16 @@ PLATFORMS

DEPENDENCIES
activesupport (>= 6.1.7.5, != 7.1.0)
benchmark
bigdecimal
cocoapods (>= 1.13, != 1.15.1, != 1.15.0)
concurrent-ruby (< 1.3.4)
logger
mutex_m
xcodeproj (< 1.26.0)

RUBY VERSION
ruby 3.3.6p108
ruby 3.1.6p260

BUNDLED WITH
2.6.2
2.5.9
Original file line number Diff line number Diff line change
Expand Up @@ -63,23 +63,23 @@ def enableProguardInReleaseBuilds = false
* The preferred build flavor of JavaScriptCore (JSC)
*
* For example, to use the international variant, you can use:
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
* `def jscFlavor = io.github.react-native-community:jsc-android-intl:2026004.+`
*
* The international variant includes ICU i18n library and necessary data
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
* give correct results when using with locales other than en-US. Note that
* this variant is about 6MiB larger per architecture than default.
*/
def jscFlavor = 'org.webkit:android-jsc:+'
def jscFlavor = 'io.github.react-native-community:jsc-android:2026004.+'

android {
ndkVersion rootProject.ext.ndkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
compileSdk rootProject.ext.compileSdkVersion

namespace "com.codepushdemoswiftnewarch"
namespace "com.codepushdemo"
defaultConfig {
applicationId "com.codepushdemoswiftnewarch"
applicationId "com.codepushdemo"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:allowBackup="false"
android:theme="@style/AppTheme"
android:usesCleartextTraffic="${usesCleartextTraffic}"
android:supportsRtl="true">
<activity
android:name=".MainActivity"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.codepushdemoappnewarch
package com.codepushdemo

import com.facebook.react.ReactActivity
import com.facebook.react.ReactActivityDelegate
Expand All @@ -11,7 +11,7 @@ class MainActivity : ReactActivity() {
* Returns the name of the main component registered from JavaScript. This is used to schedule
* rendering of the component.
*/
override fun getMainComponentName(): String = "CodePushDemoAppNewArch"
override fun getMainComponentName(): String = "CodePushDemo"

/**
* Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.codepushdemo

import android.app.Application
import com.facebook.react.PackageList
import com.facebook.react.ReactApplication
import com.facebook.react.ReactHost
import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
import com.microsoft.codepush.react.CodePush

class MainApplication : Application(), ReactApplication {

override val reactHost: ReactHost by lazy {
getDefaultReactHost(
context = applicationContext,
packageList =
PackageList(this).packages.apply {
// Packages that cannot be autolinked yet can be added manually here, for example:
// add(MyReactNativePackage())
},
jsBundleFilePath = CodePush.getJSBundleFile()
)
}

override fun onCreate() {
super.onCreate()
loadReactNative(this)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<resources>
<string name="app_name">CodePushDemo</string>
<string name="CodePushDeploymentKey">CodePushDeploymentKey</string>
</resources>
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
buildscript {
ext {
buildToolsVersion = "35.0.0"
buildToolsVersion = "36.0.0"
minSdkVersion = 24
compileSdkVersion = 35
targetSdkVersion = 34
compileSdkVersion = 36
targetSdkVersion = 36
ndkVersion = "27.1.12297006"
kotlinVersion = "2.0.21"
kotlinVersion = "2.1.20"
}
repositories {
google()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,8 @@ newArchEnabled=true
# Use this property to enable or disable the Hermes JS engine.
# If set to false, you will be using JSC instead.
hermesEnabled=true

# Use this property to enable edge-to-edge display support.
# This allows your app to draw behind system bars for an immersive UI.
# Note: Only works with ReactActivity and should not be used with custom Activity.
edgeToEdgeEnabled=false
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
Loading
Loading